Chisel是一款轻量级的,基于HTTP协议传输的快速TCP/UDP隧道工具,通过SSH实现安全加密,采用GO语言编写。其客户端和服务器端集成于同一个可执行文件中,服务器端作为受控代理节点运行,而客户端通过Chisel创建隧道与服务器通信。Chisel主要用于穿透防火墙,也可作为安全接入端点访问内部网络。Chisel采用客户端-服务器架构,通过多层协议封装实现数据传输:1、多层协议栈:应用数据 → SSH协议 → WebSocket → HTTP(S) → TCP以下是 Chisel 工具的基础使用示例,适用于在内网中,失陷机器主动发起请求,来穿透防火墙的场景:chisel server -port 9999 -reverse
启动一个监听在 9999 端口的服务端,启用反向模式(-reverse参数),允许客户端请求反向隧道chisel client <server_ip>:9999 R:socks
客户端在本地创建SOCKS代理服务,并通过Chisel隧道将此代理服务暴露给服务器端,所有流量通过一个加密的WebSocket隧道传输最后在server端配置完proxychains后,通过代理访问http服务进行验证:- 客户端通过 HTTP/WebSocket 连接到服务器
- WebSocket 连接升级时使用 "Sec-WebSocket-Protocol" 头标识协议版本
- 在 WebSocket 之上建立 SSH 连接进行加密和认证
client/client_connect.go:这里Chisel使用特定的 WebSocket 子协议标识符chshare.ProtocolVersion,可以作为一个特征。HTTP请求头包含 Sec-WebSocket-Protocol: chisel-v3通过报文可以看到,还有一对Sec-WebSocket-Key/Sec-WebSocket-Accept的header,这两个header是websocket协议自带的部分,不能作为指纹来进行识别,大致过程如下:
Sec-WebSocket-Key(客户端生成)
生成方式:
客户端生成一个 16字节(128位)的随机数。
将该随机数进行 Base64编码,结果作为Sec-WebSocket-Key的值。
要求:
随机数必须足够随机(如使用密码学安全的随机数生成器)。
每个握手请求必须使用新的随机值,防止重放攻击。
Sec-WebSocket-Accept(服务器生成)
生成步骤:
服务器从客户端的Sec-WebSocket-Key中提取原始值(Base64解码)。
将解码后的值与固定的 GUID字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接。
对拼接后的字符串进行 SHA-1哈希,得到20字节的哈希值。
将哈希值进行 Base64编码,结果作为Sec-WebSocket-Accept的值。
公式:
accept_value = base64(sha1(sec_websocket_key + GUID))
注意,此时的握手发生在WebSocket连接上,而非TCP22端口。SSH 握手中使用的版本字符串是独特的,包含 chshare.ProtocolVersion 并分别添加了 -client 或 -server 后缀。- 客户端SSH版本: SSH-chisel-v3-client
- 服务端SSH版本: SSH-chisel-v3-server
|
| Sec-WebSocket-Protocol: chisel-v3 (或其他版本) | client/client_connect.go, server/server_handler.go |
| | client/client_connect.go, server/server_handler.go |
|
| | |
| | |
| | client/client_connect.go, server/server_handler.go |
| | tunnel/tunnel.go, tunnel/tunnel_out_ssh.go |
| | tunnel/tunnel_in_proxy.go |
- 只要使用了该库进行加密,就会带有该指纹信息,而非chisel专属指纹
上面的特征中,ssh的config、chisel请求类型,以及ping、pong类型的心跳包,均经过ssh加密,因此,检测点只能落在连理连接的阶段,即:- Sec-WebSocket-Protocol: chisel-v3
- WebSocket协议验证:检查Upgrade和Protocol头部
- 数据帧快速判断:检查最小长度(10字节)和SSH前缀
module ChiselDetection;
redef ignore_checksums = T;
export {
event http_all_headers(c: connection, is_orig: bool, hlist: mime_header_list) {
}
event websocket_frame_data(c: connection, is_orig: bool, data: string) {
}
event connection_state_remove(c: connection) {
}
+-------------------+ WebSocket Upgrade +------------------+
| Initial HTTP |-------------------------->| Potential Chisel |
| Connection | | Connection |
+-------------------+ +------------------+
|
v
+-------------------+
| Check Sec-WebSocket|
| Protocol Header |
+-------------------+
|
Missing |
+---------------------- | Present
| |
v v
+---------------------+ | +---------------------------+
| Regular WebSocket |<---------+ | Create ChiselState & |
| Connection | | Track Connection |
+---------------------+ +---------------------------+
|
v
+------------------+
| Monitor WebSocket|
| Frame Data |
+------------------+
|
v
+---------------------------+
| Is Data Starting with SSH?|
+---------------------------+
|
No |
+----------- |
| |Yes
v v
+---------------+ +-----------------+
| Skip Frame | | Continue |
+---------------+ | Processing |
+-----------------+
|
v
+---------------------------+
| Check for Client Handshake|
| SSH-{protocol}-client |
+---------------------------+
|
v
+---------------------------+
| Check for Server Handshake|
| SSH-{protocol}-server |
+---------------------------+
|
v
+---------------------------+
| Both Handshakes Detected? |
+---------------------------+
|
No |Yes
+--------------------------- |
| |
v v
+---------------------+ +---------------------------+
| Continue Monitoring | | Generate Chisel Tunnel |
| (max 4 frames) | | Detection Alert |
+---------------------+ +---------------------------+
| |
v v
+---------------------+ +---------------------------+
| Detection Incomplete| | Mark Detection Complete |
| (False Negative) | | (True Positive) |
+---------------------+ +---------------------------+
在规则编写过程中,首先需要匹配httpheader,但是始终无法正确匹配,由此开始漫长的debug。先尝试使用各种event打印所有的header头:从打印结果来看,只识别了Server→Client方向的流量,即response的内容,从header来看确实都是响应头信息。到此想到,websocket是全双工的长连接,会不会因为该http请求是用来升级websocket协议的,因此和正常流量有所差异?于是采用trans_depth的方式,不依赖方向标志,来识别流数据:结果不变,依然是只检测到了response,并成功打印出了响应头信息。搜索websocket,发现存在一个http_connection_upgrade event,可以用来检测websocket事件,写一个最简单的脚本,看看该event能否正确识别:module ChiselDetection;
event http_connection_upgrade(c:connection, protocol: string){
print(protocol);
}
同样失败。要检测http升级到websocket协议,首先要保证zeek能够正确加载http分析器,结合前面无法正确识别header,应该是在http层面就出现了问题。当前的pcap,只能识别response,会不会跟抓包也有关?下面的chisel_exchange.pcap是将两台机器执行的命令对调后,抓取的报文,同样使用前面的脚本,打印所有header:此时竟然只识别出了Client→Server,即request,没有识别到response,正常打印出了request header。也就是说,zeek只识别出了一个连接,查看conn.log:conn_state分别为OTH和S0,history分别为^had和SAD。OTH代表TCP异常,而S0代表TCP三次握手未完成。S - The originator sent a SYN segment.
h - The responder sent a SYN ACK segment.
A - The originator sent an ACK segment.
D - The originator sent at least one segment with payload data. In this case, that was HTTP over TCP.
a - The responder replied with an ACK segment.
d - The responder replied with at least one segment with payload data.
F - The originator sent a FIN ACK segment.
f - The responder replied with a FIN ACK segment.
^had:
"^" - 表示TCP序列号异常
接收方发送了一个SYN ACK包
接收方响应了一个ACK包
接收方响应了至少一个带有有效负载数据的分段
SAD:
发起者发送了一个 SYN 包
发起者发送了一个 ACK 包
发起者发送了至少一个带有有效负载数据的分段(基于 TCP 的 HTTP)
至此可以看到,是zeek在解析的时候,未能正确看到完整的TCP握手过程(SYN—>SYN-ACK—>ACK),要么丢了SYN包,要么丢了SYN-ACK包。问题的根源在于,zeek没能看到完整的TCP3次握手过程,导致在TCP重组时出错,进而影响了上层的HTTP以及websocket协议,因此,通过http相关event进行识别也均出错。可以,但是目前还是没有解决只能识别单向流量的问题,从TCP层匹配也只能检测单侧数据,不考虑效率的情况下,结果也不够准确。zeek还有一些日志文件,包括weird.log,其中包含了部分错误信息:在两个包的weird.log中,均有bad_IP_checksum提示,说明可能是在检查校验和的时候出错了。在脚本开头加上redef ignore_checksums = T,忽略checksum的检查,进一步调试看看。TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=S seq=0 ack=0 len=0
** TCPxe6x8fxa1xe6x89x8bxe5x8cx85: S
* xe7xbaxafSYNxe5x8cx85 (xe8xbfx9exe6x8exa5xe5x88x9dxe5xa7x8bxe5x8cx96)
1744884754.543732 expression error in ./Chisel/chisel.zeek, line 45: field value missing (c$conn)
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=SA seq=0 ack=1 len=0
** TCPxe6x8fxa1xe6x89x8bxe5x8cx85: SA
* SYN-ACKxe5x8cx85 (xe6x9cx8dxe5x8axa1xe5x99xa8xe5x93x8dxe5xbax94)
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=AP seq=1 ack=1 len=223
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: 'GET / HTTP/1.1..Host: 192.168....'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=AP seq=1 ack=224 len=129
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: 'HTTP/1.1 101 Switching Protoco...'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=AP seq=224 ack=130 len=28
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '......]F..m}..ky..=8..gp....'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=AP seq=130 ack=224 len=24
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '..SSH-chisel-v3-server..'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=AP seq=252 ack=154 len=1096
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '[email protected]$..r ..f..z...../..|l......'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=AP seq=154 ack=252 len=708
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '.~.........'..y..S..o.D..Y.......'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=AP seq=1348 ack=862 len=54
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '.....I...e...I..T..;...S.vQ......'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=AP seq=862 ack=1402 len=276
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '.~...........h....ecdsa-sha2-n...'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe6x9cx8dxe5x8axa1xe5x99xa8xe2x86x92xe5xaexa2xe6x88xb7xe7xabxaf] flags=AP seq=1138 ack=1402 len=282
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '...........N.
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6] [xe5xaexa2xe6x88xb7xe7xabxafxe2x86x92xe6x9cx8dxe5x8axa1xe5x99xa8] flags=AP seq=1402 ack=1420 len=22
xe6x95xb0xe6x8dxaexe9xa2x84xe8xa7x88: '...C...C...VWr%..W~...'
TCPxe6x8exa7xe5x88xb6xe5x8cx85: [orig_h=192.168.64.1, orig_p=51511/tcp, resp_h=192.168.64.8, resp_p=8000/tcp, proto=6]
......
第2个包: flags=SA (服务器SYN-ACK)客户端发送HTTPGET请求: GET / HTTP/1.1..Host: 192.168....服务器回复101状态码: HTTP/1.1 101 Switching Protoco...即,PCAP文件实际上包含了完整的双向TCP流量,说明之前的所有错误都是由Zeek对校验和错误的严格处理导致的。当启用ignore_checksums = T后,Zeek能正确处理整个连接加上ignore_checksums=T,再次使用前面的脚本打印所有header:正确打印所有header,说明到http这一步已经能成功识别。最后尝试http_connection_upgrade:推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...