大多时候,我们在进行断点调试网页算法的时候,会跟踪到很多莫名其妙的自己写的算法,或者是做了很多混淆的代码,这些我们自己手动转换成其他语言的算法是比较麻烦的,耗神费力。
这个时候可以通过打一个Debug断点在我们可以调用加密/解密函数的地方,然后通过CDP协议进行调用。
1. 在devtools设置中打开Protocol Monitor
用于监测cdp协议的调用记录
只看几个关键的cdp协议请求/响应内容。
Debugger.paused
官方文档说明 此方法会返回当前Debug触发时,暂停点的callFrame信息。
当我们debug在这个点,并且在console输入命令时,我们是可以调用当前暂停断点位置的所有函数作用域内的函数,此时调用时候的CDP内容为Debugger.evaluateOnCallFrame
方法,请求内容包含一个expression
为执行的表达式内容。
基于以上协议的分析,我们可以自己实现一个基于当前断点位置的函数调用,从而实现一个直接调用加密/解密函数的小工具。
伪代码
onEvents(`Debugger.paused`, function(event) {
for(var callFrame in event.callFrames) {
// 遍历所有的callfram信息
}
// 提取某个callframe调用
var resp = CDPSend(`Debugger.evaluateOnCallFrame`, { callFrameId: event.callFrames[0], expression: 'someEncryptFunction(xx)' })
println(resp)
})
通过Networks
查看请求触发的函数调用栈
过去下断点并且触发
跟踪几次发现了加密算法的函数调用点,此处虽然用的是SM4加密,不过还有其他函数在处理,手动还原算法的太过于麻烦。
通过旁边的Call Stack调用栈回溯到上一个地方可以看到当前函数的调用方法(图中e.b这个地方是上图所示的地方,点击上一个也就是下图的(anonymous)地方)
可以看到这个函数作用域下的函数加密方法为 Object(ht.b)
, 第一个参数为待加密的字符串,第二个参数为一个常量。我们在此处打一个debug断点,然后触发到这里
测试加密
工具可以自己通过以上介绍的流程来研究并且编写辅助脚本,本质还是通过CDP协议控制。 此工具之后更加完善之后再进行开源
工具参数
cmd ./remotejs_darwin_amd64 -h
NAME:
remotejs_darwin_amd64 - A new cli application
USAGE:
remotejs_darwin_amd64 [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--chromepath value, --cp value browser bin path
--devtools enable devtools (default: false)
--remote-debug-port value, --rp value browser remote debug port, like ws://127.0.0.1:9999
--headless enable headless (default: false)
--web-port value web api port (default: "8002")
Request API 0.0.0.0:8002/remote, [POST] eval=
打开浏览器(有内置的chrome也可以通过连接远程remote debug port来进行操作)
在浏览器中找到之前找到的断点并且触发
远程调用, 此处通过eval参数可以传递我们的数据到debug处的console进行调用,这里和我们手动执行console是一样的结果。其中的变量都是我们可以自己改变的。
remotejs工具的日志
加密函数
def request_remote_js(self, data: str):
data = data.replace(" ", "")
r = requests.post(
"http://127.0.0.1:8002/remote",
data={
"eval": "Object(ht.b)('{}', _dyn$.t(622))".format(data)
}
)
message = r.json().get("message", None)
if message:
return message
return ""
解密函数
def request_remote_js_decrypt(self, data: str):
data = data.replace(" ", "")
r = requests.post(
"http://127.0.0.1:8002/remote",
data={
"eval": "Object(ht.a)('{}')".format(data)
}
)
message = r.json().get("message", None)
if message:
return message
return ""
调用流程
Burp -> Mitmproxy -- Request ---> 加密(Request Body)
<--- Response -- 解密(Response Body)
完整代码
class RemoteJsDemo(AutoDecoderClass):
def request_remote_js(self, data: str):
data = data.replace(" ", "")
r = requests.post(
"http://127.0.0.1:8002/remote",
data={
"eval": "Object(ht.b)('{}', _dyn$.t(622))".format(data)
}
)
message = r.json().get("message", None)
if message:
return message
return ""
def request_remote_js_decrypt(self, data: str):
data = data.replace(" ", "")
r = requests.post(
"http://127.0.0.1:8002/remote",
data={
"eval": "Object(ht.a)('{}')".format(data)
}
)
message = r.json().get("message", None)
if message:
return message
return ""
def request(self, flow: http.HTTPFlow):
resp = self.request_remote_js(flow.request.content.decode("utf-8"))
flow.request.content = json.dumps({"encryptedData": resp}).encode("utf-8")
def response(self, flow: http.HTTPFlow):
resp = json.loads(flow.response.content.decode("utf-8")).get("data", None)
if resp:
flow.response.content = str(self.request_remote_js_decrypt(resp)).encode("utf-8")
最后的效果, 请求和响应都是解密操作的状态下进行
Chrome Devtools Protocol
https://www.t00ls.com/articles-69054.html
文章引用微信公众号"T00ls安全",如有侵权,请联系管理员删除!