前言

之前不喜欢打CTF,觉得CTF和实际工作中的环境操作差距过大,但本次集团的CTF中有一道流量解析的题目感觉和实际稍微贴近一些,就记录下来

题目概述

冰蝎是一款基于Java开发的动态加密通信流量的新型Webshell客户端,其最大特点就是对交互流量进行对称加密,且加密秘钥是由随机数函数动态生成,因此该客户端的流量几乎无法检测。冰蝎的主要通信过程就是密钥协商和加密传输(一般是aes或xor),可从寻找密钥再破解加密算法入手获取流量中的信息。本题需从流量中找出flag。

本题格式:flag{……}

解题

冰蝎每一代都有不同的特征,可以到https://github.com/rebeyond/Behinder/releases查看源码。

冰蝎1使用一次GET请求协商密钥;冰蝎2使用两次get并将第二次GET的响应作为密钥;冰蝎3使用预共享密钥,默认密钥是作者的ID:rebeyond的MD5加密值的前16位“e45e329feb5d925b”,默认链接密码是“rebeyond”;冰蝎4可以完全自定义加密方式。

本题的流量是冰蝎2,主要加密方式是aes和xor,使用第二次get请求得到的响应内容“be17e2d70c23ddcd”作为协商密钥。

image

将第一个POST请求的内容使用base64+aes解密,IV向量默认是HEX的0X00,可以填32个0。

image

再次base64解密可以得到一段PHP代码

image

正常情况下服务器应该使用密钥加密content的内容返回给客户端,如果客户端解密后得到的内容一致则完成协商,但是本题流量并没有。

后续POST请求和响应的内容均无法使用base64+aes解密,从上述代码可以看到在没有加载openssl的情况下会使用XOR加密,本题流量是HTTP,所以后续应该使用XOR解密。对上述XOR进行解密,写成python解密脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import base64
from Crypto.Cipher import AES

def XOR(K, D):
result = []
for i in range(len(D)):
c = K[i + 1 & 15]
if not isinstance(D[i], int):
d = ord(D[i])
else:
d = D[i]
result.append(d ^ ord(c))
return b''.join([i.to_bytes(1, byteorder='big') for i in result])
def decrypt(key, content):
encrypted_text = base64.b64decode(content)
try:
cipher = AES.new(key=key.encode(), mode=AES.MODE_CBC, iv=b'\x00' * 16)
decrypted_text = cipher.decrypt(encrypted_text)
except:
decrypted_text = XOR(key, base64.b64decode(content))
return decrypted_text

if __name__ == '__main__':
key='be17e2d70c23ddcd'
content = b''
data = decrypt(key, content)
print(data)

image

继续进行base64解码,在第二个流中的第一个POST请求可以看到有以下内容

1
$path="C:/WWW/DVWAmaster/hackable/uploads/flag.zip";$content="UEsDBBQAAAAIAKODRk86sSi8DgAAABEAAAAKAAAAU2VjcjR0LnR4dEvLSUyvzkgEQjCRWAsAUEsBAh8AFAAAAAgAo4NGTzqxKLwOAAAAEQAAAAoAJAAAAAAAAAAgAAAAAAAAAFNlY3I0dC50eHQKACAAAAAAAAEAGADn/xoeIHzVAef/Gh4gfNUBqq1JAyB81QFQSwUGAAAAAAEAAQBcAAAANgAAAAAA";

image

复制出content值后解码得到flag:flag{hahahhahaha}

image