WebSocket ,通过websocket可以实现浏览器与服务器进行双向通信,浏览器客户端可以给服务端发送数据,服务端能给浏览器客户端发送数据。
我们可以利用这一点让服务端将要加密的参数发送给浏览器客户端,浏览器将接收到的参数传入目标加密函数并运行,就能得到加密结果,最后返回给服务器,实现间接的调用浏览器JS函数。
关于websocket:
http://www.ruanyifeng.com/blog/2017/05/websocket.html
Tornado框架的websocket服务参考:
https://www.xiaoqc.cn/detail/python-tornado-websocket/
实现思路:
同时开启一个http服务端和一个websocket服务端;
http客户端请求http服务端,服务端收到请求后触发websocket服务端给websocket客户端发送消息;
利用中间人代理mitmproxy给浏览器注入js,让浏览器成为webcoket客户端,它收到websocket服务端发来的消息时,调用相关目标加密函数生成值,然后将值发送给websocket服务端
websocket服务端收到值后将值转交给Http服务端,然后返回给http客户端
关于mitmproxy:
https://www.cnblogs.com/20175211lyz/p/12255610.html
http和websocket服务端:
from tornado import websocket, web, ioloop, gen
cl = []
msg = []
msg_flag = False
class IndexHandler(web.RequestHandler):
def get(self):
self.write("hello...")
class SocketHandler(websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self):
"""新的websocket连接后被调动"""
if self not in cl:
cl.append(self)
print('有新客户端连接!')
def on_close(self):
"""websocket连接关闭后被调用"""
if self in cl:
cl.remove(self)
def on_message(self, message):
"""接收到客户端消息时被调用"""
global msg_flag
print('收到客户端发送回来的加密值:%s' % message)
msg.append(message)
if not 'started' in message:
msg_flag = True
class ApiHandler(web.RequestHandler):
@gen.coroutine
def post(self):
global msg_flag,msg
"""接收参数,发送给ws客户端"""
url = self.get_body_argument('url')
print('http server收到参数:%s' % url)
# 将URL推送给客户端
for c in cl:
c.write_message(url)
while not msg_flag:
yield gen.sleep(0.1)
msg_flag = False
self.write(msg[-1])
app = web.Application([
(r'/', IndexHandler),
(r'/ws', SocketHandler), # http://127.0.0.1:8000/ws
(r'/api', ApiHandler), # http://127.0.0.1:8000/api
])
if __name__ == '__main__':
app.listen(8000)
ioloop.IOLoop.instance().start()
代理端
import mitmproxy.http
from mitmproxy import options
from mitmproxy import proxy
from mitmproxy.tools.dump import DumpMaster
# 要注入到页面的js代码
inject = """
//连接websocket服务端
var ws = new WebSocket('ws://localhost:8000/ws');
//连接成功时执行
ws.onopen= function() {
ws.send('browser started')
};
// 收到服务端消息时执行
ws.onmessage= function(evt) {
// evt.data 是websocket服务端发送过来的值
console.log(evt.data);
//调用目标加密函数
signature = window.byted_acrawler.sign({url: evt.data});
//将生成的值发送给websocket服务端
ws.send(signature);
console.log(signature);
};
"""
class Myaddon():
def response(self, flow: mitmproxy.http.HTTPFlow):
if 'acrawler.js' in flow.request.url: # 注入到acrawler.js文件里
flow.response.text = inject + flow.response.text
addons = [
Myaddon()
]
def run():
myaddon = Myaddon()
port = 8080
opts = options.Options(listen_port=port)
pconf = proxy.config.ProxyConfig(opts)
m = DumpMaster(opts)
m.server = proxy.server.ProxyServer(pconf)
m.addons.add(myaddon)
print(f'启动监听 {port} 端口')
try:
m.run()
except KeyboardInterrupt:
m.shutdown()
if __name__ == "__main__":
run()
以代理方式启动浏览器,访问目标URL:
./Chrome.exe http://www.xxxxxxxx.com --proxy-server=127.0.0.1:8080 --ignore-certificate-errors
浏览器启动后,成功注入js代码到acrawler.js文件
请求http服务,将要加密的参数传过去,可以成功拿到加密结果了