让我们首先看看网络上已有的检测 burp 的方法
burp web interface, 也就是直接访问 burp 代理端口, 或者在经过 burp 代理的情况下访问 http://burp 和 http://burpsuite 出来的页面. 主要作用是… 好像也没啥作用? 在老版本貌似可以下载 pac 代理文件, 但目前实际上能用的功能也就一个导出证书了, 但是导出证书完全可以在 burp 里完成, 导致这个 interface 其实有点鸡肋. 但是这个 interface 存在一个 favicon, 导致可以轻松使用 img 的 onerror 和 onload 来判断是否存在 burp.
<h2 id='indicator'>Loading...</h2>
<script>
function burp_found() {
let e = document.getElementById('indicator');
e.innerText = 'Burp FOUND !!!';
}
function burp_not_found() {
let e = document.getElementById('indicator');
e.innerText = 'Burp not found.';
}
</script>
<img style="display: none;" src='http://burp/favicon.ico' onload='burp_found()' onerror='burp_not_found()'/>
当然这个检测方式存在一个致命缺点 = =, 就是攻击者得开全局代理, 否则不通过 burp 的代理请求 burp 这个域名, 当然是不存在的. 这个完全看个人喜好了, 不过至少我从来不开全局代理… 都是将目标域名在 SwitchyOmega 的选项里特定走 burp 代理.
当然, 也完全可以直接请求 burp 默认的代理端口 http://127.0.0.1:8080/favicon.ico
, 这样不管开不开全局代理都无所谓了, 不过这样也有可能误判, 比如 tomcat 默认也是 8080 端口. 为了提高可信度, 我们可以利用上面提到的 burp 仅剩的导出证书的接口, 利用 script 探测是 404 还是 200. 非常滑稽的是 burp 还是做了不少防御的, 包括 X-Frame-Options
和 X-Content-Type-Options
, 但是这个接口一个都没有上, 否则也是利用不了的.
<h2 id='indicator'>Loading...</h2>
<script>
function burp_found() {
let e = document.getElementById('indicator');
e.innerText = 'Burp FOUND !!!';
}
function burp_not_found() {
let e = document.getElementById('indicator');
e.innerText = 'Burp not found.';
}
</script>
<script style="display: none;" src='http://127.0.0.1:8080/cert' onload='burp_found()' onerror='burp_not_found()'></script>
对抗这种探测非常简单, 在 Proxy -> Miscellaneous
里面勾选 Disable web interface at http://burpsuite
和 Suppress Burp error messages in browser
即可. 需要注意 Suppress Burp error messages in browser
也要勾选, 否则在全局代理模式下, 访问一个不存在的域名, burp 会返回一个 HTTP 200 的错误页面, 非常好探测.
<h2 id='indicator'>Loading...</h2>
<script>
function burp_found() {
let e = document.getElementById('indicator');
e.innerText = 'Burp FOUND !!!';
}
function burp_not_found() {
is_burp_not_found = true;
let e = document.getElementById('indicator');
e.innerText = 'Burp not found.';
}
</script>
<script>
fetch('http://not_exists_domain/not_exist', {method: 'GET', mode: 'no-cors'}).then((r)=>{burp_found();}).catch((e)=>{burp_not_found();});
// 200 -> fetch 成功, 触发 then, burp 存在. 超时 -> fetch 失败, 触发 catch, burp 不存在.
</script>
TLS Version + Cipher Suites
Extensions
对 0x0a 0x0b 这两个 Extensions, 会额外提取里面的参数
$ ./python/venv/bin/python python/ja3.py chrome104.pcap | grep 61.183.52.197
[61.183.52.197:443] JA3: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21,29-23-24,0 --> cd08e31494f9531f560d64c695473da9
[61.183.52.197:443] JA3: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21-41,29-23-24,0 --> 0d69ff451640d67ee8b5122752834766
[61.183.52.197:443] JA3: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513-21-41,29-23-24,0 --> 0d69ff451640d67ee8b5122752834766
burp2022.8.1-java11.pcap
$ ./python/venv/bin/python python/ja3.py burp2022.8.1-java11.pcap | grep 61.183.52.197
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-16-17-23-43-45-51,29-23-24-25-30-256-257-258-259-260,0 --> b154bc0fc6157d34cb12295edb07a2f6
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-17-23-43-45-51-41,29-23-24-25-30-256-257-258-259-260,0 --> 41cb1a264f27be250a6575f4c9aba2f1
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-17-23-43-45-51-41,29-23-24-25-30-256-257-258-259-260,0 --> 41cb1a264f27be250a6575f4c9aba2f1
$ ./python/venv/bin/python python/ja3.py burp2022.3.9-java11.pcap | grep 61.183.52.197
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-16-17-23-43-45-51,29-23-24-25-30-256-257-258-259-260,0 --> b154bc0fc6157d34cb12295edb07a2f6
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-16-17-23-43-45-51,29-23-24-25-30-256-257-258-259-260,0 --> b154bc0fc6157d34cb12295edb07a2f6
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-13-50-17-23-43-45-51,29-23-24-25-30-256-257-258-259-260,0 --> 2c7b42976f538f0759bad05220ba8e2d
burp 之间的版本只要不是差的很多, 应该是不会有啥差别的, 差的那几个查了一下应该是 TLSv1.3 为了加速握手速度搞的扩展, 所以连接之间会有小差别.
$ ./python/venv/bin/python python/ja3.py burp2022.3.9-java18.pcap | grep 61.183.52.197
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-16-17-23-35-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 62f6a6727fda5a1104d5b147cd82e520
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-16-17-23-35-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 62f6a6727fda5a1104d5b147cd82e520
[61.183.52.197:443] JA3: 771,4866-4865-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49201-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49156-49166-157-156-61-60-53-47-255,0-5-10-11-17-23-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 45d5544dca9ff99d72d13a58fe4e3ca1
真正差别大的其实是运行 burp 的 java 版本, 可以看到 extensions 部分不管是顺序还是种类都有变化.
$ ./python/venv/bin/python python/ja3.py burp2022.3.9-java18-custom-cipher.pcap | grep 61.183.52.197
[61.183.52.197:443] JA3: 771,4866-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49166-157-156-61-53-47-255,0-5-10-11-17-23-35-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 67a83bc49faeb2499fb8c8526d432076
[61.183.52.197:443] JA3: 771,4866-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49166-157-156-61-53-47-255,0-5-10-11-17-23-35-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 67a83bc49faeb2499fb8c8526d432076
[61.183.52.197:443] JA3: 771,4866-4867-49196-49195-52393-49200-52392-49199-159-52394-163-158-162-49188-49192-49187-49191-107-106-103-64-49198-49202-49197-49190-49194-49189-49193-49162-49172-49161-49171-57-56-51-50-49157-49167-49166-157-156-61-53-47-255,0-5-10-11-17-23-35-13-43-45-50-51,29-23-24-25-30-256-257-258-259-260,0 --> 67a83bc49faeb2499fb8c8526d432076
new WebSocket('ws://target.com/test_burp')
, 在经过 burp 代理和直接访问的流量差别如下:Sec-Websocket-Extensions
这个 header, 只要在服务端侧检测这个 header 是否存在就可以直接判断用户是否使用 burp.var rtc = new RTCPeerConnection({
iceServers:[{"urls":"turn:target.com:19132?transport=tcp", credential: "credential", username: "username"}]
});
rtc.createDataChannel('', { reliable: false});
rtc.createOffer(
function (offerDesc) {
rtc.setLocalDescription(offerDesc);
},
function (e) {}
);
Unsupported or unrecognized SSL message
, 很显然 burp 只能解析正常的 HTTP 或者 HTTPS 请求, 碰到 webrtc 直接就寄了.Transfer-encoding: chunked
, 可以运行以下代码进行测试,#!/usr/bin/env python
import asyncio
from quart import make_response, Quart, render_template, url_for, request
from datetime import datetime
app = Quart(__name__)
@app.route('/')
async def index():
return "index"
@app.route('/test')
async def stream_time():
async def async_generator():
for i in range(5):
time = datetime.now().isoformat()
yield time.encode()
await asyncio.sleep(1)
return async_generator(), 200, {'X-Something': 'value'}
app.run(host='0.0.0.0', port=19132,
certfile="fullchain.pem",
keyfile="privkey.pem",
)
fetch('http://target.com:19132/').then(async function(response) {
console.log(response);
const reader = response.body.getReader();
while (1) {
const result = await reader.read()
if (!result.done) {
console.log(new TextDecoder("utf-8").decode(result.value))
} else {
break;
}
}
})
Project options -> HTTP -> Streaming Response
中 add 相关目标即可, 如果想要匹配全部, 可以打开 advanced scope control 然后写上三个 .* 就行了.Streaming Response
, burp 也无法正常处理, 还是得等流全部结束后才会返回给客户端, 所以这个选项的作用还是很有限的.Transfer-encoding: chunked
而是基于 HTTP/2 自带的流式传输.let veryLoooooongString = 'abcdddddd'.repeat(6553500);
const stream = new ReadableStream({
async start(controller) {
while (1) {
await wait(0);
controller.enqueue(veryLoooooongString);
}
},
}).pipeThrough(new TextEncoderStream());
function wait(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}
fetch('https://target.com/', {
method: 'POST',
headers: {'Content-Type': 'text/plain'},
body: stream,
duplex: 'half',
});
大概 20 多秒就可以直接把内存打满, burp 会直接卡的动不了. 如果把 wait 等待时间调长那么会更隐蔽, 更极限一点可以注册成 serviceWorker 放在后台偷偷恶心攻击者.
文章转自:rmb122's notebook