3-track_hacker Writeup by AI
1. 题目描述
| 项目 | 内容 |
|---|---|
| 题目名称 | track_hacker |
| 题目来源 | BugKu |
| 题目类型 | Crypto / Traffic Analysis / WebShell 流量分析 |
| 附件文件 | shell.pcap (15.0KB) |
题目背景
本题提供了一个网络抓包文件(shell.pcap),要求分析黑客攻击痕迹,从流量中提取 Flag。攻击者通过 WebShell 进行命令执行,并使用编码隐藏响应数据。
2. 考点分析
| 考点 | 分值权重 | 技能要求 |
|---|---|---|
| PCAP 文件分析 | 20% | 使用 scapy/Wireshark 分析网络抓包数据 |
| HTTP 协议理解 | 25% | 识别 HTTP 请求/响应结构,提取关键字段 |
| WebShell 特征识别 | 25% | 识别 POST 请求中的命令执行模式 |
| 编码解码技术 | 20% | Base64 + zlib 双重压缩的识别与解码 |
| 攻击链还原能力 | 10% | 从离散数据包中还原完整攻击流程 |
核心知识点
- WebShell 通信特征 : POST 请求包含
k=密码&c=命令的参数结构 - 数据隐藏技术 : 服务器响应使用
Base64(zlib(数据))双重编码 - Linux 文件隐藏 : 通过在文件名前加点(
.)来隐藏文件 - 流量分析工具: Python scapy 库或 Wireshark 的使用
3. 解题思路
3.1 整体思路
文件检查 → 协议分析 → HTTP 请求提取 → WebShell 识别 → 编码解码 → Flag 提取
3.2 技术路线
| 步骤 | 目标 | 方法 | 预期结果 |
|---|---|---|---|
| 1 | 了解文件基本信息 | file命令、scapy读取 |
确认 PCAP 格式 |
| 2 | 统计协议分布 | 遍历数据包计数 | 发现主要为 HTTP 流量 |
| 3 | 提取 HTTP 请求 | 解析 TCP 负载 | 找到 5 个关键 POST 请求 |
| 4 | 识别攻击行为 | 分析 URL 和参数 | 发现 WebShell 攻击链 |
| 5 | 解码响应数据 | Base64+zlib 解码 | 获取命令执行结果 |
| 6 | 提取 Flag | 匹配 flag{} 模式 | 得到最终答案 |
3.3 关键突破口
- 异常短的响应体: HTTP 响应长度为 10-100 字节,疑似压缩数据
- 重复的 Base64 字符串 :
eJwrLy/XTUksSQQADqgDLQ==多次出现 - POST 参数模式 :
k=cometohackme&c=命令符合 WebShell 特征
4. 详细步骤
步骤 1: 文件基本信息检查
bash
# 检查文件类型
file shell.pcap
# 输出:pcap capture file, microsecond ts (little-endian) - version 2.4
分析: 确认为标准 PCAP 网络抓包文件,可使用 scapy 或 Wireshark 分析。
步骤 2: 读取并统计协议分布
python
from scapy.all import *
packets = rdpcap("shell.pcap")
print(f"总数据包数量:{len(packets)}") # 输出:109
# 协议分布统计
protocol_count = {}
for pkt in packets:
proto = pkt.summary().split()[0]
protocol_count[proto] = protocol_count.get(proto, 0) + 1
# 输出:Loopback: 109 (100%)
分析: 所有 109 个数据包均为 Loopback(本地回环)流量,说明是本地抓包或虚拟机内部流量。
步骤 3: 提取 HTTP 请求详情
python
http_requests = []
for i, pkt in enumerate(packets):
if TCP in pkt and Raw in pkt:
data = pkt[Raw].load.decode('utf-8', errors='ignore')
if data.startswith('POST ') or data.startswith('GET '):
lines = data.split('\r\n')
method_path = lines[0]
# 提取关键信息
host = ""
content_type = ""
post_data = ""
for line in lines[1:]:
if line.startswith('Host:'):
host = line.split(':', 1)[1].strip()
elif line.startswith('Content-Type:'):
content_type = line.split(':', 1)[1].strip()
if '\r\n\r\n' in data:
body = data.split('\r\n\r\n')[1]
post_data = body[:100]
http_requests.append({
'packet_id': i,
'method_path': method_path,
'host': host,
'content_type': content_type,
'post_data': post_data
})
发现的关键请求:
| 包 ID | 请求方法 | 路径 | Content-Type | POST 数据 |
|---|---|---|---|---|
| 4 | POST | /upload.php | text/php | multipart/form-data... |
| 14 | POST | /config.php | application/x-www-form-urlencoded | k=cometohackme&c=whoami |
| 59 | POST | /config.php | application/x-www-form-urlencoded | k=cometohackme&c=mv config.php .config.php |
| 79 | POST | /.config.php | application/x-www-form-urlencoded | k=cometohackme&c=whoami |
| 99 | POST | /.config.php | application/x-www-form-urlencoded | k=cometohackme&c=cat /flag.txt |
步骤 4: 识别 WebShell 攻击特征
WebShell 典型特征:
POST /path/to/shell.php
Content-Type: application/x-www-form-urlencoded
password=your_password&exec=command_to_execute
本题中的模式:
POST /config.php (或 .config.php)
Content-Type: application/x-www-form-urlencoded
k=cometohackme&c=command
↑ ↑
连接密码 执行的命令
判断: 这是典型的 PHP WebShell 使用方式,攻击者通过 POST 请求传递命令并执行。
步骤 5: 提取并解码 HTTP 响应
python
import base64
import zlib
http_responses = []
for i, pkt in enumerate(packets):
if TCP in pkt and Raw in pkt:
data = pkt[Raw].load.decode('utf-8', errors='ignore')
if 'HTTP/1.1 200 OK' in data:
if '\r\n\r\n' in data:
body = data.split('\r\n\r\n')[1].strip()
# 只处理短文本(可能是压缩的命令输出)
if len(body) < 100 and len(body) > 10:
try:
# Base64 解码
decoded_b64 = base64.b64decode(body)
# zlib 解压
decompressed = zlib.decompress(decoded_b64)
result = decompressed.decode('utf-8', errors='ignore')
print(f"包 {i}: {body} -> {result}")
except:
pass
解码结果对照表:
| 包 ID | 原始 Base64 | Base64 解码 (Hex) | zlib 解压结果 | 对应命令 |
|---|---|---|---|---|
| 16 | eJwrLy/XTUksSQQADqgDLQ== | 789c2b2f2fd74d492c4904000ea8032d | www-data | whoami |
| 61 | eJwDAAAAAAE= | 789c030000000001 | (空) | mv 命令 |
| 81 | eJwrLy/XTUksSQQADqgDLQ== | 789c2b2f2fd74d492c4904000ea8032d | www-data | whoami |
| 101 | eJxLy0lMrw6NTzPMS4n3TVWsBQAz4wXi | 789c4bcb494caf0e8d4f33cc4b89f74d55ac050033e305e2 | flag{U_f1nd_Me!} | cat /flag.txt |
步骤 6: 还原完整攻击链
根据时间线整理攻击过程:
时间戳: 1531106404 - 1531106442 (约 38 秒的攻击过程)
┌─────────────────────────────────────────────────────────────┐
│ 阶段 1: 上传 WebShell │
├─────────────────────────────────────────────────────────────┤
│ • 包 ID: 4 │
│ • 请求:POST /upload.php │
│ • Content-Type: multipart/form-data │
│ • 行为:上传了名为 config.php 的 WebShell 文件 │
│ • 目的:获取服务器控制权 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 阶段 2: 测试权限 │
├─────────────────────────────────────────────────────────────┤
│ • 包 ID: 14 │
│ • 请求:POST /config.php │
│ • 参数:k=cometohackme&c=whoami │
│ • 响应:www-data │
│ • 目的:确认 WebShell 可用,当前用户为 www-data │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 阶段 3: 隐藏痕迹 │
├─────────────────────────────────────────────────────────────┤
│ • 包 ID: 59 │
│ • 请求:POST /config.php │
│ • 参数:k=cometohackme&c=mv config.php .config.php │
│ • 响应:(空) 成功 │
│ • 目的:将 WebShell 重命名为隐藏文件(以.开头) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 阶段 4: 验证隐藏后访问 │
├─────────────────────────────────────────────────────────────┤
│ • 包 ID: 79 │
│ • 请求:POST /.config.php │
│ • 参数:k=cometohackme&c=whoami │
│ • 响应:www-data │
│ • 目的:确认隐藏后的 WebShell 仍可正常访问 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 阶段 5: 窃取 Flag │
├─────────────────────────────────────────────────────────────┤
│ • 包 ID: 99 │
│ • 请求:POST /.config.php │
│ • 参数:k=cometohackme&c=cat /flag.txt │
│ • 响应:flag{U_f1nd_Me!} │
│ • 目的:读取根目录下的 flag 文件 │
└─────────────────────────────────────────────────────────────┘
5. 完整代码
5.1 自动化分析脚本
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
BugKu Crypto 6-3-track_hacker - 自动化分析脚本
功能:PCAP 流量分析、WebShell 识别、Base64+zlib 解码、Flag 提取
"""
from scapy.all import *
import base64
import zlib
from collections import defaultdict
def analyze_pcap(filename):
"""主分析函数"""
packets = rdpcap(filename)
# 基础统计
print(f"总数据包:{len(packets)}")
# 协议分布
protocol_count = defaultdict(int)
for pkt in packets:
proto = pkt.summary().split()[0]
protocol_count[proto] += 1
# 提取 HTTP 请求
http_requests = extract_http_requests(packets)
print(f"\n发现 {len(http_requests)} 个 HTTP 请求")
# 提取 HTTP 响应并解码
decoded_results = decode_responses(packets)
# 提取 Flag
extract_flag(decoded_results)
return decoded_results
def extract_http_requests(packets):
"""提取 HTTP 请求"""
requests = []
for i, pkt in enumerate(packets):
if TCP in pkt and Raw in pkt:
data = pkt[Raw].load.decode('utf-8', errors='ignore')
if data.startswith('POST ') or data.startswith('GET '):
# 解析请求头和数据
lines = data.split('\r\n')
requests.append({
'id': i,
'request': lines[0],
'data': data.split('\r\n\r\n')[1][:100] if '\r\n\r\n' in data else ''
})
return requests
def decode_responses(packets):
"""解码 HTTP 响应(Base64+zlib)"""
results = []
for i, pkt in enumerate(packets):
if TCP in pkt and Raw in pkt:
data = pkt[Raw].load.decode('utf-8', errors='ignore')
if 'HTTP/1.1 200 OK' in data and '\r\n\r\n' in data:
body = data.split('\r\n\r\n')[1].strip()
if 10 < len(body) < 100:
try:
decoded = base64.b64decode(body)
decompressed = zlib.decompress(decoded)
result = decompressed.decode('utf-8', errors='ignore')
results.append({'id': i, 'data': result})
print(f"包 {i}: {result}")
except:
pass
return results
def extract_flag(results):
"""从解码结果中提取 Flag"""
for result in results:
if 'flag{' in result['data'].lower():
print(f"\n🎉 Flag found: {result['data']}")
return result['data']
print("\n❌ Flag not found")
return None
if __name__ == "__main__":
analyze_pcap("shell.pcap")
5.2 手动解码验证
python
# 手动解码最后一个响应
import base64
import zlib
encoded = "eJxLy0lMrw6NTzPMS4n3TVWsBQAz4wXi"
# 步骤 1: Base64 解码
decoded_b64 = base64.b64decode(encoded)
print(f"Base64 解码后 (Hex): {decoded_b64.hex()}")
# 输出:789c4bcb494caf0e8d4f33cc4b89f74d55ac050033e305e2
# 步骤 2: zlib 解压
decompressed = zlib.decompress(decoded_b64)
print(f"zlib 解压后:{decompressed}")
# 输出:b'flag{U_f1nd_Me!}'
# 步骤 3: 转换为字符串
flag = decompressed.decode('utf-8')
print(f"Flag: {flag}")
# 输出:flag{U_f1nd_Me!}
6. 总结
6.1 技术要点回顾
| 技术点 | 说明 | 应用场景 |
|---|---|---|
| PCAP 分析 | 使用 scapy 读取和分析网络抓包 | CTF 流量分析题、安全事件调查 |
| WebShell 识别 | POST 请求中 password&command 模式 |
Web 安全、入侵检测 |
| Base64 编码 | 将二进制数据编码为 ASCII 字符串 | 数据传输、简单混淆 |
| zlib 压缩 | DEFLATE 算法压缩数据 | 减少数据体积、隐藏内容 |
| Linux 隐藏文件 | 以点开头的文件名 | 文件隐藏、持久化后门 |
原始问题:请阅读目录下的文件,解出这道CTF题目。