【前端加解密】BurpGuard实战案例
初探网站
请求body被加密:
如请求加密不正确则会报错:
JS逆向
寻找特征
看加密密文,有点像AES/DES/RSA + Base64
,尝试搜索前端常用加密库关键词:
JSEncrypt(RSA)
CryptoJS(AES、DES)
果然使用了JSEncrypt
:
加密定位
定位关键加密方法,JSEncrypt
有一个关键方法setPublicKey
,尝试搜索:
在第一行打上断点:
已看见明文:
进入控制台,打印
a.publicKey
得到公钥:
公钥:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLMgOk8oGP1SYMXq6jSTbaiqUrKMbbRB1Rj93HZghOMJI2pGa55Q3hhHIH3usVRR6WrxQqQ2tZ7XVq7zRMMQBUz2cbYjII3yRl2cn7dNeb+uW30JPypLaqJjwsWhmxYNlVU41rjsUH3RIK7W+4bDlr3JrnHk7X5HDtytiVaYCkVwIDAQAB
手动验证
手动加密替换验证一下是否正确,这里推荐密码学工具:https://github.com/Leon406/ToolsFx,RSA支持大文本加密:
替换发送,服务端成功解密:
利用BurpGuard实现自动化
通常情况下,利用BurpGuard挂上下游代理,解密后修改再加密,但由于使用了非对称加密,且只有公钥没有私钥(私钥通常在服务器上),因此没法解密,需要换一种思路。这里我使用BurpGuard替换加密关键JS代码,让其不加密直接返回明文。尝试手动替换,右键添加覆盖脚本,保存在本地:
在本地打开并修改为直接返回:
刷新浏览器即可看到代码已更改:
尝试抓包看看效果,发现直接抓到了明文,但服务端不认识:
这里可以使用BurpGuard来自动替换,方便移植,如果不需要移植的话可以浏览器替换。
首先移除之前的手动替换:
将之前修改过的
index.39bfdf29.js
放入BurpGuard文件夹中,编写ClientProxyHandler.py,启动BurpGuard.py,浏览器走8081端口:
from mitmproxy import http
from Utils.Crypto import *
import httpx
from base64 import b64encode,b64decode
from urllib.parse import quote,unquote
import json
import traceback
class ClientProxyHandler:
def __init__(self) -> None:
self.client = httpx.Client(timeout=None,verify=False)
# 处理来自客户端的请求,通常在这里对请求进行解密
def request(self,flow: http.HTTPFlow):
try:
req = flow.request # 获取请求对象
# 在这里编写你的代码
# ...
except Exception as e:
traceback.print_exception(e)
finally:
return flow
# 处理返回给客户端的响应,通常在这里对响应进行加密
def response(self,flow: http.HTTPFlow):
try:
req = flow.request # 获取请求对象
rsp = flow.response # 获取响应对象
# 在这里编写你的代码
# ...
# 匹配指定JS
if "index.39bfdf29.js" in req.path:
# 替换整个文件,修改的JS文件在上一级,根据实际需求替换
with open("../index.39bfdf29.js","rb") as f:
rsp.content = f.read()
except Exception as e:
traceback.print_exception(e)
finally:
return flow
addons = [ClientProxyHandler()]
查看是否替换成功:
接着编写BurpProxyHandler.py,来加密修改后的参数,思路就是获取params参数的值,然后利用脚本自带的RSA加密方法加密即可:
from mitmproxy import http
from Utils.Crypto import *
import httpx
from base64 import b64encode,b64decode
from urllib.parse import quote,unquote
import json
import traceback
class BurpProxyHandler:
def __init__(self) -> None:
self.client = httpx.Client(timeout=None,verify=False)
# 处理来自Burp的请求,通常在这里对请求进行加密
def request(self,flow: http.HTTPFlow):
try:
req = flow.request # 获取请求对象
# 在这里编写你的代码
# ...
# 判断POST请求且为JSON类型,且指定host
if req.method == "POST" and req.host == "xxx.com":
if "application/json" in req.headers.get("Content-Type",""):
# 复制请求body的JSON对象
json_data = req.json()
# 拿到明文参数
params = json_data["params"]
# 加密
encrypt_params = RSA.encrypt(params,"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLMgOk8oGP1SYMXq6jSTbaiqUrKMbbRB1Rj93HZghOMJI2pGa55Q3hhHIH3usVRR6WrxQqQ2tZ7XVq7zRMMQBUz2cbYjII3yRl2cn7dNeb+uW30JPypLaqJjwsWhmxYNlVU41rjsUH3RIK7W+4bDlr3JrnHk7X5HDtytiVaYCkVwIDAQAB")
# 替换为加密参数
json_data["params"] = encrypt_params
# 替换请求体
req.text = json.dumps(json_data)
except Exception as e:
traceback.print_exception(e)
finally:
return flow
# 处理返回给Burp的响应,通常在这里对响应进行解密
def response(self,flow: http.HTTPFlow):
try:
req = flow.request # 获取请求对象
rsp = flow.response # 获取响应对象
# 在这里编写你的代码
# ...
except Exception as e:
traceback.print_exception(e)
finally:
return flow
addons = [BurpProxyHandler()]
Burp设置上游代理走8082端口:
加入一个单引号显示参数格式错误,证明参数被服务端正常解析:
配合SQLMap
如果需要跑SQLMap的话可以直接走代理端口8080/8081,建议走8080,这样可以使用burp来观察SQLmap的注入情况,如下:
复制请求到文件:
SQLMap指定请求包文件一把梭:
sqlmap -r 1.txt --proxy http://127.0.0.1:8080 --batch
Burp历史记录可以看到SQLMap的注入情况:
声明
本文所提供的信息仅供学习和研究网络安全技术之用途。读者在使用这些信息时应自行判断其适用性,并对其行为负全责。作者不对任何读者因使用本文中信息而导致的任何直接或间接损失负责。
转载须知:如需转载本文,请务必保留本文末尾的免责声明,并标明文章出处,同时提供原文链接。未经许可,请勿对本文进行修改,以保持信息的完整性。感谢各位师傅们的理解与支持。
2