Wire - Writeup by AI

题目信息

  • 题目名称: Wire
  • 题目类型: Misc
  • 文件: timu.pcapng (4.8MB)

题目描述

本题提供了一个网络流量捕获文件(pcapng 格式),需要分析其中的数据包,提取出隐藏的 flag。

考点分析

  1. 流量分析: 使用 Wireshark 或 Python 工具分析 pcapng 文件
  2. HTTP 协议理解: 识别 HTTP 请求和响应
  3. SQL 注入攻击检测: 识别 SQL 盲注攻击模式
  4. 盲注原理: 理解基于布尔的 SQL 盲注工作原理
  5. 数据提取: 从大量流量中提取关键信息

解题思路

1. 初步分析

首先查看 pcapng 文件的内容,发现其中包含大量的 HTTP 流量。通过初步分析发现:

  • 总共有 25331 个数据包
  • 包含大量的 HTTP GET 请求
  • 请求目标为 /ctf/Less-5/,这是一个典型的 SQL 注入练习环境

2. 识别 SQL 注入模式

通过分析 HTTP 请求,发现明显的 SQL 盲注攻击特征:

GET /ctf/Less-5/?id=1'%20and%20ascii(substr((select%20flag%20from%20t),1,1))=33--+

URL 解码后:

id=1' and ascii(substr((select flag from t),1,1))=33--+

这是典型的基于布尔的 SQL 盲注

  • 使用 ascii() 函数获取字符的 ASCII 码
  • 使用 substr() 函数逐位截取 flag
  • 通过遍历 ASCII 值(32-126)来猜测每个位置的字符

3. 盲注原理

SQL 盲注的工作原理:

  • 当条件为真时(ASCII 值匹配),页面返回正常内容(长度较短)
  • 当条件为假时(ASCII 值不匹配),页面返回错误内容(长度较长)

通过分析响应包的长度差异,可以判断哪个 ASCII 值是正确的。

4. 解题步骤

步骤 1: 读取 pcapng 文件

使用 Python 的 scapy 库读取流量文件:

from scapy.all import rdpcap, Raw
packets = rdpcap('timu.pcapng')
步骤 2: 提取 HTTP 请求和响应

解析每个数据包,提取:

  • HTTP GET 请求中的 SQL 注入参数(位置和 ASCII 值)
  • HTTP 响应的内容长度
步骤 3: 配对请求和响应

将每个注入请求与其对应的响应配对,用于后续分析。

步骤 4: 分析响应长度差异

对于每个位置(position):

  • 统计所有测试的 ASCII 值对应的响应长度
  • 找到使响应长度特殊的那个 ASCII 值(通常是正确的那个)
步骤 5: 重组 flag

将所有位置的正确字符按顺序组合,得到完整的 flag。

详细步骤

核心代码实现

from scapy.all import rdpcap, Raw
import re
from collections import defaultdict

def analyze_traffic(file_path):
    packets = rdpcap(file_path)
    
    requests = {}
    responses = {}
    
    for i, pkt in enumerate(packets):
        if Raw in pkt:
            data = bytes(pkt[Raw].load)
            try:
                text = data.decode('utf-8', errors='ignore')
                
                # 提取 SQL 注入请求
                if text.startswith('GET'):
                    match = re.search(
                        r'id=1.*?ascii\(substr\(\(select%20flag%20from%20t\),(\d+),1\)\)=(\d+)',
                        text
                    )
                    
                    if match:
                        position = int(match.group(1))
                        ascii_value = int(match.group(2))
                        
                        requests[i] = {
                            'position': position,
                            'ascii': ascii_value,
                            'char': chr(ascii_value)
                        }
                
                # 提取 HTTP 响应
                elif text.startswith('HTTP/'):
                    body_start = text.find('\r\n\r\n')
                    body = text[body_start + 4:] if body_start > 0 else text
                    
                    responses[i] = {
                        'body_length': len(body)
                    }
                    
            except:
                continue
    
    return requests, responses

def extract_flag(requests, responses):
    # 按位置分组
    position_data = defaultdict(list)
    
    for req_idx, req_info in sorted(requests.items()):
        pos = req_info['position']
        
        # 找到对应的响应
        for resp_idx, resp_info in sorted(responses.items()):
            if resp_idx > req_idx:
                position_data[pos].append({
                    'ascii': req_info['ascii'],
                    'char': req_info['char'],
                    'response_length': resp_info['body_length']
                })
                break
    
    # 找出每个位置的正确字符
    flag_chars = {}
    
    for pos in sorted(position_data.keys()):
        items = position_data[pos]
        
        # 统计响应长度
        length_counts = defaultdict(list)
        for item in items:
            length_counts[item['response_length']].append(item)
        
        # 找到出现次数最少的长度(正确的字符)
        lengths = sorted(length_counts.items(), key=lambda x: len(x[1]))
        
        if lengths:
            min_len, matching_items = lengths[0]
            flag_chars[pos] = matching_items[0]['char']
    
    # 重组 flag
    flag = ''.join([flag_chars.get(i, '?') for i in range(1, max(flag_chars.keys()) + 1)])
    
    return flag

执行结果

运行脚本后,成功提取出每个位置的字符:

位置 1: 'f' (ASCII: 102, 响应长度:716)
位置 2: 'l' (ASCII: 108, 响应长度:716)
位置 3: 'a' (ASCII: 97, 响应长度:716)
位置 4: 'g' (ASCII: 103, 响应长度:716)
位置 5: '{' (ASCII: 123, 响应长度:716)
位置 6: 'w' (ASCII: 119, 响应长度:716)
位置 7: '1' (ASCII: 49, 响应长度:716)
位置 8: 'r' (ASCII: 114, 响应长度:716)
位置 9: 'e' (ASCII: 101, 响应长度:716)
位置 10: 's' (ASCII: 115, 响应长度:716)
位置 11: 'h' (ASCII: 104, 响应长度:716)
位置 12: 'A' (ASCII: 65, 响应长度:716)
位置 13: 'R' (ASCII: 82, 响应长度:716)
位置 14: 'K' (ASCII: 75, 响应长度:716)
位置 15: '_' (ASCII: 95, 响应长度:716)
位置 16: 'e' (ASCII: 101, 响应长度:716)
位置 17: 'z' (ASCII: 122, 响应长度:716)
位置 18: '_' (ASCII: 95, 响应长度:716)
位置 19: '1' (ASCII: 49, 响应长度:716)
位置 20: 's' (ASCII: 115, 响应长度:716)
位置 21: 'n' (ASCII: 110, 响应长度:716)
位置 22: 't' (ASCII: 116, 响应长度:716)
位置 23: 'i' (ASCII: 105, 响应长度:716)
位置 24: 't' (ASCII: 116, 响应长度:716)
位置 25: '}' (ASCII: 125, 响应长度:716)

总结

技术要点

  1. SQL 盲注识别:

    • 通过 URL 参数中的 ascii(substr(...)) 模式识别盲注攻击
    • 理解攻击者如何逐字符获取敏感数据
  2. 流量分析技巧:

    • 使用 scapy 库解析 pcapng 文件
    • 提取和配对 HTTP 请求与响应
    • 通过响应长度差异判断注入结果
  3. 数据处理:

    • 从大量流量中筛选关键数据
    • 使用正则表达式提取注入参数
    • 统计分析响应特征

拓展学习

  • SQL 注入类型: 了解不同类型的 SQL 注入(联合查询、盲注、报错注入等)
  • 防御方法: 学习如何防止 SQL 注入(参数化查询、输入验证等)
  • 流量分析工具: 熟悉 Wireshark、tshark 等工具的使用
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐