BurpSuite暴力破解和防御实战
burpsuite暴力破解
工具准备
-
burp suite
用于攻击web 应用程序的集成平台
-
jsEncrypter
一个用于前端加密Fuzz的Burp Suite插件,支持base64、sha、md5、RSA等加密
-
phantomjs
可用于网络监测、网页截屏、无界面 Web 测试、页面自动化的命令行工具
上述文件已打包存储:链接:https://pan.baidu.com/s/1CqYT7toC_qxF0pJn0kWrxQ 提取码:9bl8
本次使用相关工具版本为:Burp_Suite_Pro_v1.7.37、jsEncrypter.0.3.2、phantomjs-2.1.1,各软件安装配置自行网上查找,配置完成后应如下方截图:
接口抓取
- 浏览器配置代理
- burp 启动代理
- 访问对应系统分析接口
当我们随便输入了账号密码点击了登录之后,发现传输的参数是被加密了。
分析加密方法
所以接下来我们需要做的就是实现此加密方法后在爆破请求时把请求的参数信息对表单信息尽心加密。并且得知加密过程为:
# 1. 把输入的账号密码转为对象
loginInfo = {account:unamebalabala,password:pwdbalabala}
# 2. 把上面的对象进行RSA加密
ency_text=JSEncypt.encypt(loginInfo)
# 3. 对加密后的字符进行base64加密
ciphertxt=Base64.encode(ency_text)
# 4. 生成新的对象后调用接口
data={'ciphertxt':ciphertxt}
使用phantomjs进行本地调试
启动报错
查看phantomjs_server.js文件,同级目录下没有script-1.js文件,需要把依赖文件放在同级目录下。
查看官网加密例子
在jsEncrypter-master\test\TestScript\RSA
目录下可以看到RSA加密的例子
已经实现的RSA加密方法
通过官方的例子我们可以看到phantomjs使用jsencrypt加密的过程:
# 1. phantomjs运行加密js文件,即: phantomjs jsEncrypter_rsa.js
# 2. 加密js文件引入了封装好的加密方法的js,即 phantom.injectJs('jsencrypt.js')
# 3. 引入文件后启动一个本地服务,供调用测试(burp在爆破前也是调用此服务接口对参数进行加密的)
抄官方的例子,实现加密方法
在burp中验证加密方法 (或者在postman中也可以验证)
爆破过程
- 添加接口到爆破模块
-
添加爆破点
此处仅添加了一个爆破点即账号密码加密后的
- 添加参数处理方法
-
开始爆破并定位成功请求
可根据请求状态或者响应长短来定位不同请求的结果,例子中根据响应内容长度来确定破解状态。
补充:攻击类型
针对爆破点有四种攻击类型,以账号密码两个爆破点为例:
若爆破点1的枚举值个数为8个,爆破点2的枚举值个数为9个,则下列攻击类型分别产生的攻击次数为:
-
Sniper:仅需设置一个值域,所有爆破点用一个枚举值域进行爆破。
仅针对一个爆破点进行爆破,即爆破过程中其他爆破点的值保持不变。则分别对爆破点1和爆破点2进行8+8=16次爆破。
-
Battering ram:仅需设置一个值域,所有爆破点用一个枚举值域进行爆破。
同时对多个爆破点使用值域中的同一个值进行爆破。则总爆破次数为值域的个数,即8次(此处使用账号值域)
-
Pitchfork:分别设置值域,同时对多个爆破点使用对应爆破点的值域进行爆破。
即账号密码一一的对应取值爆破,则总爆破次数为值域少的爆破点的次数,即8次。
-
Cluster bomb:分别设置值域。同时对多个爆破点的值域取笛卡尔积进行爆破。
则总爆破次数为8*9=72次。
注:爆破过程可分别对各爆破点参数进行参数处理。
爆破过程总结
-
对于未加密的参数,可以直接使用自带的枚举域根据类型进行爆破,也可以根据一定规则导入数据
-
对于简单参数加密,burp自带了参数修改方法:正则替换、前后缀、加解密等
-
复杂的参数处理、加密等需要从页面查找加密方法,
实现
后通过phantomjs启动代理服务,在请求发起时对参数进行处理后请求接口a. 本质上是启动了一个服务,调用接口返回加密结果,ip127.0.0.1 port 1664 body={"playload":para}
b. 可通过jsEncrypter使用js编写加密方法
c. 也可以使用其他脚本语言编写接口实现加密方法
-
本次使用的是JsEncrypt插件进行的加密,可使用python编写插件
补充:使用python编写服务的时候的坑
- 坑1:在burp中测试连接的时候连接不上
虽然不影响加密调用但是心里慌啊,分析jsencrypt例子
原来不发post就直接返回就好了,所以需要多写一个get请求
-
坑2:还以为只是一个简单的post带form就完了,结果写的时候不知道消息体参数呀
只能在原来的JsEncrypt中找消息体了:加上请求的日志
分析请求的数据:请求方法、类型、数据、都出来了
-
坑3:以为写个post参数模型就能搞定json请求,结果postman能访问,但是burp的JsEncrypt请求不了。。。仔细看上面的请求Content-type,不是json啊,,,继续优化
最后终于搞定了...上码:(文中的加密方法参考https://blog.csdn.net/Orangesir/article/details/114990606)
# -*- coding:utf8 -*-
"""
@ Author: 吕文举 wjlv4
@ File: Encrypt.py
@ Time: 2021/11/03
@ Contact: wjlv4@iflytek.com
@ Desc: Encrypt.py 加密实现
"""
import base64
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pksc1_v1_5
from Crypto.PublicKey import RSA
def _encrpt(string, public_key):
rsakey = RSA.importKey(public_key) # 读取公钥
cipher = Cipher_pksc1_v1_5.new(rsakey)
# 因为encryptor.encrypt方法其内部就实现了加密再次Base64加密的过程,所以这里实际是通过下面的1和2完成了JSEncrypt的加密方法
encrypt_text = cipher.encrypt(string.encode()) # 1.对账号密码组成的字符串加密
cipher_text_tmp = base64.b64encode(encrypt_text) # 2.对加密后的字符串base64加密
# 源码除了encryptor.encrypt加密外,又一次对密文进行了Base64加密,所以我们这里也再次加密
cipher_text = base64.b64encode(cipher_text_tmp)
return cipher_text.decode()
def gen_body(account, pwd, public_key=None):
'''根据账号密码生成请求的body然后调用_encrpt方法加密'''
if not public_key: public_key = 'MIGfMA0GxxxxxxxxxxEBAQUAA4+QKBgQCFa8mE++++++++++hMrwZ7+++Vbtb7Rcnjw9Pxxxx++xxabG6QL0G2++++xxxx++JH6Q++++W+BqH920D+++++++++++kG0Z+++++BuSZNH++++xxxx++WX1NHHDDMwSOS3PqFJ+BVOm++++++DAQAB'
key = '-----BEGIN PUBLIC KEY-----\n' + public_key + '\n-----END PUBLIC KEY-----'
encrypt_res = _encrpt("{'account': '%s','password':'%s'}" % (account, pwd), key)
return encrypt_res
# -*- coding:utf8 -*-
"""
@ Author: 吕文举 wjlv4
@ File: server.py
@ Time: 2021/11/03
@ Contact: wjlv4@iflytek.com
@ Desc: server.py 用于burp通过JsEncrypt请求的服务
"""
from typing import Optional
from fastapi import FastAPI, Body
from pydantic import BaseModel
from Encrypt import gen_body
app = FastAPI()
class Payload(BaseModel):
payload: str
@app.post('/')
async def get_encrypt_body(payload: Optional[str] = Payload, payload_info: Optional[str] = Body(None)):
"""
获取生成加密参数
- payload: 支持json传参
- payload_info: 支持body传参,传的是字符串payload=xxxx
- return 加密后的字符串
"""
if payload_info:
print(payload_info)
payload_k, payload = payload_info.split('=')
pub_key = 'MIGfMA0GC++++++++QCFa++++3F+7c+++++++iEKAOW+eabG6QL+++++++++bFhW+BqH92+++++kG++++CZVH3OBuSZNHYz++++++++BVOmlz++++DAQAB'
encrypt_str = gen_body(payload, payload, pub_key)
return encrypt_str
@app.get('/')
async def get_encrypt_status():
"""
随便返回点什么,让burp的jsEncrypt能连上就行
"""
return 'hello jsEncrypter!'
if __name__ == '__main__':
import uvicorn
uvicorn.run('server:app', host='127.0.0.1', port=1664, reload=True)
-
postman么得问题
-
burp使用JsEncryptor也么得问题
爆破防御
针对爆破的防御有如下措施:
- 密码强度增强——不要用常见密码
- 网站对请求加密
- 前端增加重试验证码校验
- 前端项目打包不允许查看项目源码
- 限制ip对接口的访问频率
本次演示的系统使用了nginx代理,直接在nginx中配置访问频率即可 (参考资料https://www.w3cschool.cn/nginxsysc/nginxsysc-limit-req.html)
再次通过burp爆破,查看是否生效。
可以看到上方已经限制了攻击ip的请求
思考
- 接口参数加密可单独对某参数加密,或分别使用不同的加密方法,建议组合参数为新的消息体后加密
- 接口提示信息不要明确到账号错误、密码错误,建议使用账号或密码错误
- 这里仅演示了js和python复写了对于参数加密的方法,依赖的是JSEncrypt,但是是可以使用Jython单独写插件的,有时间再多研究
- 插件除参数加解密还能操作请求头、响应头,增加sql注入等,还有很多要学