mitmdump
最近好几次众测遇到前端js加密,除了需要进行前端加密算法分析之外,还需要mitmdump配合burp进行自动化解密,因此这里就遇到了mitmdump的使用,之前遇到的加密站是朋友帮忙写的,我本想着在此基础上修修改改但是在修改的过程中遇到了很多问题,本质上是对上下游代理的转发不是很熟悉,于是自己在本地进行手动调试做了详细的记录。
上下游代理
在此之前我们先了解一下代理,平时我们正常渗透一般是在浏览器挂代理,将流量转发给burp,在转发给服务器。同时服务器响应先返回给burp,burp在返回给客户端。
那么我们结合mitmdump的时候具体的请求如下:
下游代理
下游代理主要是接收客户端的加密请求密文,进行解密将解密结果发送到burp,burp将解密的数据呈现给我们。同时将burp传递的明文加密然后返回给客户端。
上游代理
上游代理主要是处理burp传递的请求进行加密返回给服务端。同时接收服务端的加密响应密文,进行解密将解密结果发送到burp,burp将解密结果呈现给我们。
burp
burp的主要作用其实就是将解密结果呈现给我们,在一个就是可以使用repeter重发。
mitmproxy
上下游代理均需要使用函数去处理请求和响应。
from mitmproxy import flowfilter,ctx
from mitmproxy.http import HTTPFlow
from mitmproxy import flowfilter
from mitmproxy.http import HTTPFlow
class Mimit():
def request(self,flow):
def response(self,flow):
addons = [Mimit(),]
上游代理:
- request函数:处理burp发送的请求,编写函数进行加密并将结果发送给服务端。因为上游的request请求是处理burp中的数据的,在burp中无法看到处理之后的请求。这里通过控制台演示一下
- response函数:处理服务端返回的响应,编写函数进行解密将结果发送给burp。
下游代理:
- request函数:处理客户端发送的请求,编写函数进行解密并将结果返回给burp。
- response函数:处理burp接收到的明文响应,编写函数进行加密并将结果返回给客户端。
为了方便理解,这里单独对上、下游代理进行调试,梳理一下处理流程。这里我使用GPT简单写了一个客户端和服务端。
主要逻辑就是客户端对数据base64加密,然后服务端解密继续base64加密并将结果返回。
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Base64编码示例</title>
</head>
<body>
<form id="myForm">
<label for="userInput">输入文本:</label>
<input type="text" id="userInput" name="userInput" required>
<button type="button" onclick="encodeAndSend()">提交</button>
</form>
<!-- 添加用于显示服务端返回的数据的元素 -->
<div id="responseDataContainer"></div>
<script>
function encodeAndSend() {
var userInput = document.getElementById("userInput").value;
var encodedData = btoa(userInput);
var jsonData = {
encodedData: encodedData
};
fetch('1.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonData)
})
.then(response => response.text()) // 修改部分:使用 response.text() 获取字符串
.then(data => {
// 修改部分:将服务端返回的字符串显示在页面上
var responseDataContainer = document.getElementById("responseDataContainer");
responseDataContainer.innerHTML = '服务端返回的数据: ' + data;
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
</body>
</html>
后端代码
<?php
header('Content-Type: application/json');
// 获取 POST 参数
$postData = json_decode(file_get_contents("php://input"), true);
// 检查是否接收到有效数据
if (isset($postData['encodedData'])) {
// 获取 Base64 编码的数据
$encodedData = $postData['encodedData'];
// 判断是否以等号("=")结尾
if (substr($encodedData, -1) === '=') {
// 如果以等号结尾,解码并再次进行加密传递给客户端
$decodedData = base64_decode($encodedData);
$decodedData = base64_encode($decodedData);
} else {
// 如果不是 Base64 编码,直接对原始数据进行 Base64 编码
$decodedData = base64_encode($encodedData);
}
// 返回继续加密后的数据
$response = array('reencodedData' => $decodedData);
echo json_encode($response);
} else {
// 返回错误信息
$errorResponse = array('error' => 'Invalid data received');
echo json_encode($errorResponse);
}
?>
正常请求,如图:可以看到正常的请求就是加密处理的,这里我们就需要使用mitmproxy对请求和响应加解密方便我们进行渗透测试。
上游代理调试
下游处理函数
from mitmproxy import flowfilter,ctx
from mitmproxy.http import HTTPFlow
from mitmproxy import flowfilter
from mitmproxy.http import HTTPFlow
import base64
class Mimit():
def request(self,flow):
if flow.request.host=="192.168.0.101":
req = json.loads(flow.request.get_text())
ctx.log.info("浏览器请求数据 => "+req['encodedData'])
data = base64.b64decode(str(req['encodedData']).encode()).decode()
req['encodedData'] = data
ctx.log.info("下游代理解密请求数据 => " + req['encodedData'])
flow.request.set_text((json.dumps(req)))
def response(self,flow):
if flow.request.host=="192.168.0.101":
rep = json.loads(flow.response.get_text())
ctx.log.info("响应数据 => "+rep['decodedData'])
data = base64.b64encode(str(rep['decodedData']).encode()).decode()
ctx.log.info("响应数据 => " + data)
rep['decodedData'] = data
flow.response.set_text(json.dumps(rep))
addons = [Mimit(),]
命令行启动下游代理并将上游代理设置为burp。
mitmdump -p 7070 -s dec.py --mode upstream:http://127.0.0.1:8080 --ssl-insecure
在浏览器中设置下游代理。
此时的流程为
配置好之后发起请求,此时我们查看命令行和burp中的数据
可以看到,没有挂上游代理不会对响应数据进行解密,只是下游代理将客户端的请求解密在burp中呈现,因此请求包是明文,返回包是密文。
下游代理会加密burp传递的参数,也就是服务端传递的bWl0bXByb3h5,最终得到结果YldsMGJYQnliM2g1。因为这里没有上游代理,返回包没有被解密,所以会二次base64加密嘛。
上游代理调试
上游处理函数
from mitmproxy import flowfilter,ctx
from mitmproxy.http import HTTPFlow
from mitmproxy import flowfilter
from mitmproxy.http import HTTPFlow
import base64
class Mimit():
def request(self,flow):
if flow.request.host=="192.168.0.101":
req = json.loads(flow.request.get_text())
ctx.log.info("浏览器请求数据 => "+req['encodedData'])
data = base64.b64decode(str(req['encodedData']).encode()).decode()
req['encodedData'] = data
ctx.log.info("下游代理解密请求数据 => " + req['encodedData'])
flow.request.set_text((json.dumps(req)))
def response(self,flow):
if flow.request.host=="192.168.0.101":
rep = json.loads(flow.response.get_text())
ctx.log.info("响应数据 => "+rep['decodedData'])
data = base64.b64encode(str(rep['decodedData']).encode()).decode()
ctx.log.info("响应数据 => " + data)
rep['decodedData'] = data
flow.response.set_text(json.dumps(rep))
命令行启动上游代理。
mitmdump -p 9091 -s enc.py --ssl-insecure
burp挂上游代理
此时流程为
配置好之后发起请求,此时我们查看命令行和burp中的数据。注意因为我们没有下游代理不会对客户端发送的请求解密,然而上游代理会进行加密,因此进行了二次base64加密。此时为了方便岩石,服务端也需要二次解密。所以修改一下代码。
// 判断是否以等号("=")结尾
if (substr($encodedData, -1) === '=') {
// 如果以等号结尾,解码并再次进行加密传递给客户端
$decodedData = base64_decode($encodedData);
$decodedData = base64_decode($decodedData);
$decodedData = base64_encode($decodedData);
} else {
// 如果不是 Base64 编码,直接对原始数据进行 Base64 编码
$decodedData = base64_encode($encodedData);
}
可以看到上游代理对服务端返回的数据进行解密,呈现给客户端。
此时并没有下游代理对数据进行加密,因此客户端接收的是明文。
上下游调试
最后我们上、下游代理同时挂起进行调试。
下游代理
mitmdump -p 7070 -s dec.py --mode upstream:http://127.0.0.1:8080 --ssl-insecure
上游代理
mitmdump -p 9091 -s enc.py --ssl-insecure
最终burp呈现数据如下
客户端也可以正常接收请求
Debug
平时在测试的时候,编写脚本可能需要debug,在上游代理可以使用以下脚本进行debug
import sys
import os
from mitmproxy.tools.main import mitmdump
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
print(os.path.dirname(os.path.abspath(__file__)))
# 使用 mitmdump 启动并指定端口
mitmdump(['-s', 'enc.py', '-p', str(9091)])
此时在enc.py中下断点就行。
下游代理同理
import sys
import os
from mitmproxy.tools.main import mitmdump
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
print(os.path.dirname(os.path.abspath(__file__)))
# 启动 mitmdump
mitmdump(['-s', 'dec.py','-p', str(7071), '--mode', "upstream:http://127.0.0.1:8080"])
在dec.py下断点即可