首页
社区
课程
招聘
[原创] JustCTF 6pack wp
发表于: 2025-8-11 00:09 2807

[原创] JustCTF 6pack wp

2025-8-11 00:09
2807

1:init 若收到包则状态转到3,若接收到cmd,则转到2

2:send_wait ack确认 然后发送队列中下一个直到发送完毕

3:recive 发送ack,接收同时,若不是0x10001保存队列,是0x10001则解码 输出队列中字符

Header字段随机 除了flowlabel

解码:

编码逻辑:sub_4CA200

开始包 0x10000

编码包

编码和解码能对上,感觉就是压缩后然后2字节打包装进flowlabel

在wireshark中查看,时间排序:

下面就是写脚本解码了

在对话中存在exe, upx加壳了,upx 解壳

flag_check程序在加密的shelcode,

shellcode加密的,提取脚本:

需要找RC4密钥,解密shellcode,爆破:

只有31337的时候,可以成功反汇编10条以上

先翻转flag + smc,smc后:

这里就是加载bcrypt.dll,调用sha-256,每三个字符check一次

爆破脚本:

flag:

justCTF{ipv6_ch4t___h0w_b4d_1s_th4t}

IPv6Header *__golang sub_4C8820(uint8 *a1, size_t a2, size_t a3, uint8 *a4, size_t a5, size_t a6, uint32 a7)
{
  IPv6Header *result; // rax
  uint8 *v8; // rcx
  uint8 *v9; // rdx
  uint8 **v10; // r11
  uint8 v11; // [rsp+0h] [rbp-28h]
  char v12; // [rsp+8h] [rbp-20h]
  uint8 v13; // [rsp+10h] [rbp-18h]
  uint16 v14; // [rsp+18h] [rbp-10h]
 
  v11 = math_rand_v2_UintN(32LL);
  v14 = math_rand_v2_UintN(4096LL);
  v13 = math_rand_v2_UintN(64LL);
  v12 = math_rand_v2_UintN(32LL);
  result = (IPv6Header *)runtime_newobject(&RTYPE_IPv6Header);
  result->Version = 6;
  result->TrafficClass = v11;
  result->FlowLabel = a7;
  result->PayloadLength = v14;
  result->NextHeader = v13;
  result->HopLimit = v12 + 10;
  result->SourceIP.len = a2;
  result->SourceIP.cap = a3;
  if ( dword_5F9810 )
  {
    result = (IPv6Header *)runtime_gcWriteBarrier2(result);
    v8 = a1;
    *v10 = a1;
    v9 = a4;
    v10[1] = a4;
  }
  else
  {
    v8 = a1;
    v9 = a4;
  }
  result->SourceIP.ptr = v8;
  result->DestinationIP.len = a5;
  result->DestinationIP.cap = a6;
  result->DestinationIP.ptr = v9;
  return result;
}
IPv6Header *__golang sub_4C8820(uint8 *a1, size_t a2, size_t a3, uint8 *a4, size_t a5, size_t a6, uint32 a7)
{
  IPv6Header *result; // rax
  uint8 *v8; // rcx
  uint8 *v9; // rdx
  uint8 **v10; // r11
  uint8 v11; // [rsp+0h] [rbp-28h]
  char v12; // [rsp+8h] [rbp-20h]
  uint8 v13; // [rsp+10h] [rbp-18h]
  uint16 v14; // [rsp+18h] [rbp-10h]
 
  v11 = math_rand_v2_UintN(32LL);
  v14 = math_rand_v2_UintN(4096LL);
  v13 = math_rand_v2_UintN(64LL);
  v12 = math_rand_v2_UintN(32LL);
  result = (IPv6Header *)runtime_newobject(&RTYPE_IPv6Header);
  result->Version = 6;
  result->TrafficClass = v11;
  result->FlowLabel = a7;
  result->PayloadLength = v14;
  result->NextHeader = v13;
  result->HopLimit = v12 + 10;
  result->SourceIP.len = a2;
  result->SourceIP.cap = a3;
  if ( dword_5F9810 )
  {
    result = (IPv6Header *)runtime_gcWriteBarrier2(result);
    v8 = a1;
    *v10 = a1;
    v9 = a4;
    v10[1] = a4;
  }
  else
  {
    v8 = a1;
    v9 = a4;
  }
  result->SourceIP.ptr = v8;
  result->DestinationIP.len = a5;
  result->DestinationIP.cap = a6;
  result->DestinationIP.ptr = v9;
  return result;
}
// local variable allocation has failed, the output may be wrong!
__int64 __golang sub_4CB100(int a1, int a2, int a3, signed __int64 a4, __int64 a5, int a6, int a7, int a8)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]
 
  v10 = sub_4CAFE0(a1, a2, a3, a4, *((_DWORD *)&a4 + 2), a6, a7, a8, v8);
  v16 = sub_4CABA0(v10, a2, v11, a4, *((_DWORD *)&a4 + 2), v12, v13, v14, v15);
  if ( !a4 )
    return runtime_slicebytetostring(0, v16, a2, 0, *((_DWORD *)&a4 + 2), v17, v18, v19, v20);
  v22 = v9;
  a4 = *(_QWORD *)(a4 + 8);
  v22 = *(_OWORD *)&a4;
  fmt_Errorf((unsigned int)"unable to decode message: %w", 28, (unsigned int)&v22, 1, 1, v17, v18, v19, v20);
  return 0LL;
}
// local variable allocation has failed, the output may be wrong!
__int64 __golang sub_4CB100(int a1, int a2, int a3, signed __int64 a4, __int64 a5, int a6, int a7, int a8)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]
 
  v10 = sub_4CAFE0(a1, a2, a3, a4, *((_DWORD *)&a4 + 2), a6, a7, a8, v8);
  v16 = sub_4CABA0(v10, a2, v11, a4, *((_DWORD *)&a4 + 2), v12, v13, v14, v15);
  if ( !a4 )
    return runtime_slicebytetostring(0, v16, a2, 0, *((_DWORD *)&a4 + 2), v17, v18, v19, v20);
  v22 = v9;
  a4 = *(_QWORD *)(a4 + 8);
  v22 = *(_OWORD *)&a4;
  fmt_Errorf((unsigned int)"unable to decode message: %w", 28, (unsigned int)&v22, 1, 1, v17, v18, v19, v20);
  return 0LL;
}
import csv
import struct
import zlib
from collections import defaultdict
 
# --- 配置 ---
# 脚本将直接处理这个文件
CSV_FILENAME = "1.csv"
 
# CSV文件中各列的索引 (从0开始)
# 请根据你的Wireshark导出格式进行调整
SOURCE_IP_COL = 2
DEST_IP_COL = 3
FLOW_LABEL_COL = 6
 
# 隐蔽信道协议的控制信号
START_SIGNAL = 0x10000
END_SIGNAL = 0x10001
 
def decode_chunks_to_data(data_chunks, session_info):
    """
    将整数块列表解码为最终数据。
    此函数处理字节打包和解压缩。
    """
    if not data_chunks:
        return b''
 
    # 步骤 1: 将uint16整数块重组为字节流 (小端序)
    compressed_bytestream = bytearray()
    for chunk in data_chunks:
        # '<' = 小端序, 'H' = 无符号短整型 (2字节)
        try:
            compressed_bytestream.extend(struct.pack('<H', chunk))
        except struct.error:
            # 如果某个值超过65535,说明数据块损坏或混入了无关包
            print(f"[!] 警告: 在会话 {session_info} 中跳过无效数据块值 {chunk} (0x{chunk:x})")
            continue
 
    # 步骤 2: 解压缩原始DEFLATE流
    try:
        # wbits=-zlib.MAX_WBITS 这个参数是处理无zlib头的原始DEFLATE流的关键
        decompressor = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
        decompressed_data = decompressor.decompress(compressed_bytestream)
        decompressed_data += decompressor.flush()
        return decompressed_data
    except zlib.error as e:
        print(f"[-] 错误: 会话 {session_info} 的数据解压缩失败: {e}")
        return None
 
def process_capture_file(csv_filepath):
    """
    解析一个CSV捕获文件,管理多个C2会话,并解码隐藏数据。
    """
    # `active_sessions` 存储已开始但尚未结束的会话数据。
    # 键: (源IP, 目的IP)元组
    # 值: 整数数据块列表
    active_sessions = {}
    completed_payloads = []
 
    print(f"[*] 开始分析文件: '{csv_filepath}'...")
 
    try:
        with open(csv_filepath, 'r', newline='', encoding='utf-8') as f:
            reader = csv.reader(f)
            header = next(reader, None# 跳过标题行
            print(f"[*] CSV 标题: {header}")
 
            for i, row in enumerate(reader, start=2):
                try:
                    # ---- 过滤与解析 ----
                    combined_label_str = row[FLOW_LABEL_COL]
 
                    # 关键过滤步骤: 只处理Flow Label包含逗号的行。
                    # 这是筛选我们目标流量的最高效方式。
                    if ',' not in combined_label_str:
                        continue
 
                    src_ip = row[SOURCE_IP_COL]
                    dst_ip = row[DEST_IP_COL]
                    data_hex_str = combined_label_str.split(',')[-1]
                    flow_label_int = int(data_hex_str, 16)
 
                    session_key = (src_ip, dst_ip)
 
                    # ---- 状态机逻辑 ----
                    if flow_label_int == START_SIGNAL:
                        if session_key in active_sessions:
                            print(f"[!] 警告 (第 {i} 行): 在会话 {session_key} 结束前收到新的START信号。会话已重置。")
                        print(f"[+] 第 {i} 行: 检测到会话开始: {src_ip} -> {dst_ip}")
                        active_sessions[session_key] = []  # 开始或重置会话
 
                    elif flow_label_int == END_SIGNAL:
                        if session_key in active_sessions:
                            print(f"[+] 第 {i} 行: 检测到会话结束: {src_ip} -> {dst_ip}")
                            chunks = active_sessions.pop(session_key) # 弹出该会话的数据
                            print(f"    -> 会话捕获了 {len(chunks)} 个数据块。正在解码...")
 
                            payload = decode_chunks_to_data(chunks, session_key)
                            if payload is not None:
                                completed_payloads.append({'data': payload, 'session': session_key})
                                print(f"    -> 成功: 已解码 {len(payload)} 字节。")
                            else:
                                print(f"    -> 失败: 无法解压此会话的数据。")
                        else:
                            # 收到一个我们未追踪的会话的END信号,忽略。
                            pass
 
                    # 如果是数据包
                    else:
                        if session_key in active_sessions:
                            active_sessions[session_key].append(flow_label_int)
 
                except (IndexError, ValueError) as e:
                    # 忽略格式错误或缺少所需数据的行。
                    print(f"[!] 警告: 无法解析第 {i} 行。错误: {e}。已跳过。")
                    continue
 
    except FileNotFoundError:
        print(f"[!] 致命错误: 未找到文件 '{csv_filepath}'")
        return []
 
    # 文件解析完毕后,检查是否有未关闭的会话
    if active_sessions:
        print("\n" + "-"*30)
        print("[!] 警告: 以下会话已开始但从未正常结束:")
        for key, chunks in active_sessions.items():
            print(f"    -> 会话: {key[0]} -> {key[1]} (捕获了 {len(chunks)} 个数据块)")
        print("-" * 30)
 
    return completed_payloads
 
if __name__ == '__main__':
    # 直接处理硬编码的文件名
    decoded_results = process_capture_file(CSV_FILENAME)
 
    print("\n" + "="*60)
    if not decoded_results:
        print("                分析完成,未解码任何完整的会话。")
    else:
        print(f"            成功: 共解码 {len(decoded_results)} 个完整的会话")
        for i, result in enumerate(decoded_results, 1):
            session_info = f"{result['session'][0]} -> {result['session'][1]}"
            data = result['data']
            print("\n" + "="*20 + f" 解码结果 {i} (来自: {session_info}) " + "="*20)
            try:
                # 尝试以UTF-8文本形式打印
                print(data.decode('utf-8'))
            except UnicodeDecodeError:
                # 如果是二进制数据,则回退到打印原始字节
                print("[*] 载荷为二进制数据,将以原始字节形式显示:")
                print(data)
    print("="*60)
import csv
import struct
import zlib
from collections import defaultdict
 
# --- 配置 ---
# 脚本将直接处理这个文件
CSV_FILENAME = "1.csv"
 
# CSV文件中各列的索引 (从0开始)
# 请根据你的Wireshark导出格式进行调整
SOURCE_IP_COL = 2
DEST_IP_COL = 3
FLOW_LABEL_COL = 6
 
# 隐蔽信道协议的控制信号
START_SIGNAL = 0x10000
END_SIGNAL = 0x10001
 
def decode_chunks_to_data(data_chunks, session_info):
    """
    将整数块列表解码为最终数据。
    此函数处理字节打包和解压缩。
    """
    if not data_chunks:
        return b''
 
    # 步骤 1: 将uint16整数块重组为字节流 (小端序)
    compressed_bytestream = bytearray()
    for chunk in data_chunks:
        # '<' = 小端序, 'H' = 无符号短整型 (2字节)
        try:
            compressed_bytestream.extend(struct.pack('<H', chunk))
        except struct.error:
            # 如果某个值超过65535,说明数据块损坏或混入了无关包
            print(f"[!] 警告: 在会话 {session_info} 中跳过无效数据块值 {chunk} (0x{chunk:x})")
            continue
 
    # 步骤 2: 解压缩原始DEFLATE流
    try:
        # wbits=-zlib.MAX_WBITS 这个参数是处理无zlib头的原始DEFLATE流的关键
        decompressor = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
        decompressed_data = decompressor.decompress(compressed_bytestream)
        decompressed_data += decompressor.flush()
        return decompressed_data
    except zlib.error as e:
        print(f"[-] 错误: 会话 {session_info} 的数据解压缩失败: {e}")
        return None
 
def process_capture_file(csv_filepath):
    """
    解析一个CSV捕获文件,管理多个C2会话,并解码隐藏数据。
    """
    # `active_sessions` 存储已开始但尚未结束的会话数据。
    # 键: (源IP, 目的IP)元组
    # 值: 整数数据块列表
    active_sessions = {}
    completed_payloads = []
 
    print(f"[*] 开始分析文件: '{csv_filepath}'...")
 
    try:
        with open(csv_filepath, 'r', newline='', encoding='utf-8') as f:
            reader = csv.reader(f)
            header = next(reader, None# 跳过标题行
            print(f"[*] CSV 标题: {header}")
 
            for i, row in enumerate(reader, start=2):
                try:
                    # ---- 过滤与解析 ----
                    combined_label_str = row[FLOW_LABEL_COL]
 
                    # 关键过滤步骤: 只处理Flow Label包含逗号的行。
                    # 这是筛选我们目标流量的最高效方式。
                    if ',' not in combined_label_str:
                        continue
 
                    src_ip = row[SOURCE_IP_COL]
                    dst_ip = row[DEST_IP_COL]
                    data_hex_str = combined_label_str.split(',')[-1]
                    flow_label_int = int(data_hex_str, 16)
 
                    session_key = (src_ip, dst_ip)
 
                    # ---- 状态机逻辑 ----
                    if flow_label_int == START_SIGNAL:
                        if session_key in active_sessions:
                            print(f"[!] 警告 (第 {i} 行): 在会话 {session_key} 结束前收到新的START信号。会话已重置。")
                        print(f"[+] 第 {i} 行: 检测到会话开始: {src_ip} -> {dst_ip}")
                        active_sessions[session_key] = []  # 开始或重置会话
 
                    elif flow_label_int == END_SIGNAL:
                        if session_key in active_sessions:
                            print(f"[+] 第 {i} 行: 检测到会话结束: {src_ip} -> {dst_ip}")
                            chunks = active_sessions.pop(session_key) # 弹出该会话的数据
                            print(f"    -> 会话捕获了 {len(chunks)} 个数据块。正在解码...")
 
                            payload = decode_chunks_to_data(chunks, session_key)
                            if payload is not None:
                                completed_payloads.append({'data': payload, 'session': session_key})
                                print(f"    -> 成功: 已解码 {len(payload)} 字节。")
                            else:
                                print(f"    -> 失败: 无法解压此会话的数据。")
                        else:
                            # 收到一个我们未追踪的会话的END信号,忽略。
                            pass
 
                    # 如果是数据包
                    else:
                        if session_key in active_sessions:
                            active_sessions[session_key].append(flow_label_int)
 
                except (IndexError, ValueError) as e:
                    # 忽略格式错误或缺少所需数据的行。
                    print(f"[!] 警告: 无法解析第 {i} 行。错误: {e}。已跳过。")
                    continue
 
    except FileNotFoundError:
        print(f"[!] 致命错误: 未找到文件 '{csv_filepath}'")
        return []
 
    # 文件解析完毕后,检查是否有未关闭的会话
    if active_sessions:
        print("\n" + "-"*30)
        print("[!] 警告: 以下会话已开始但从未正常结束:")
        for key, chunks in active_sessions.items():
            print(f"    -> 会话: {key[0]} -> {key[1]} (捕获了 {len(chunks)} 个数据块)")
        print("-" * 30)
 
    return completed_payloads
 
if __name__ == '__main__':
    # 直接处理硬编码的文件名
    decoded_results = process_capture_file(CSV_FILENAME)
 
    print("\n" + "="*60)
    if not decoded_results:
        print("                分析完成,未解码任何完整的会话。")
    else:
        print(f"            成功: 共解码 {len(decoded_results)} 个完整的会话")
        for i, result in enumerate(decoded_results, 1):
            session_info = f"{result['session'][0]} -> {result['session'][1]}"
            data = result['data']
            print("\n" + "="*20 + f" 解码结果 {i} (来自: {session_info}) " + "="*20)
            try:
                # 尝试以UTF-8文本形式打印
                print(data.decode('utf-8'))
            except UnicodeDecodeError:
                # 如果是二进制数据,则回退到打印原始字节
                print("[*] 载荷为二进制数据,将以原始字节形式显示:")
                print(data)
    print("="*60)
import os
import sys
# 导入 pyelftools 库中的核心类
from elftools.elf.elffile import ELFFile
from elftools.common.exceptions import ELFError
 
def extract_go_payload_elf(file_path: str, target_section: str = ".go.runtimeinfo") -> bytes | None:
    """
    解析一个 ELF (Executable and Linkable Format) 文件,寻找一个特定的节区,并提取其原始数据。
 
    这个函数模拟了 C 代码在 Go 语言编译的 Linux/Unix 可执行文件中查找
    .go.runtimeinfo 节以提取隐藏载荷的行为。
 
    Args:
        file_path (str): Go 可执行文件的路径 (ELF 格式)。
        target_section (str): 要查找的目标节区的名称。
 
    Returns:
        bytes: 如果找到目标节区,则返回其包含的二进制数据。
        None: 如果文件不存在、不是有效的 ELF 文件或找不到目标节区。
    """
    if not os.path.exists(file_path):
        print(f"[!] 错误: 文件 '{file_path}' 不存在。")
        return None
 
    print(f"[*] 正在打开并解析文件: {file_path}")
 
    try:
        # 使用 'with' 语句确保文件能被正确关闭
        with open(file_path, 'rb') as f:
            # 使用 pyelftools 库加载 ELF 文件,它会自动解析所有结构
            elf = ELFFile(f)
 
            # 遍历所有节区 (sections)
            # 这等同于 C 代码中的 while 循环遍历节头表
            print(f"[*] 正在搜索目标节区: '{target_section}'...")
            for section in elf.iter_sections():
                # section.name 在 pyelftools 中已经是解码后的字符串
                print(section.name)
                if section.name == target_section:
                    print(f"[+] 成功找到节区 '{section.name}'!")
                     
                    # 获取该节区的原始数据
                    # .data() 方法会返回该节区的完整二进制内容
                    # 这等同于 C 代码中 fseek 和 fread 的组合操作
                    payload_data = section.data()
                     
                    print(f"[+] 成功提取载荷数据,大小: {len(payload_data)} 字节。")
                    return payload_data
 
            # 如果循环结束仍未找到
            print(f"[!] 警告: 在文件中未找到目标节区 '{target_section}'。")
            return None
 
    except ELFError:
        print(f"[!] 错误: '{file_path}' 不是一个有效的 ELF 文件格式或已损坏。")
        return None
    except IOError as e:
        print(f"[!] 错误: 读取文件 '{file_path}' 失败: {e}")
        return None
 
# --- 主程序执行部分 ---
if __name__ == "__main__":
    # 检查命令行参数,如果没有则使用默认文件名 '6-pack'
    if len(sys.argv) > 1:
        input_file = sys.argv[1]
    else:
        # 在Linux环境下,可执行文件通常没有扩展名
        input_file = "6-pack"
 
    # 调用核心函数提取载荷
    payload = extract_go_payload_elf(input_file)
 
    if payload:
        output_filename = "extracted_payload.bin"
        try:
            with open(output_filename, "wb") as f:
                f.write(payload)
            print(f"\n[*] 载荷已成功保存到文件: '{output_filename}'")
            print("[*] 下一步,你可以使用解密密钥对此文件进行 RC4 解密。")
        except IOError as e:
            print(f"[!] 错误: 写入文件 '{output_filename}' 失败: {e}")
import os
import sys
# 导入 pyelftools 库中的核心类
from elftools.elf.elffile import ELFFile
from elftools.common.exceptions import ELFError
 
def extract_go_payload_elf(file_path: str, target_section: str = ".go.runtimeinfo") -> bytes | None:
    """
    解析一个 ELF (Executable and Linkable Format) 文件,寻找一个特定的节区,并提取其原始数据。
 
    这个函数模拟了 C 代码在 Go 语言编译的 Linux/Unix 可执行文件中查找
    .go.runtimeinfo 节以提取隐藏载荷的行为。
 
    Args:
        file_path (str): Go 可执行文件的路径 (ELF 格式)。
        target_section (str): 要查找的目标节区的名称。
 
    Returns:
        bytes: 如果找到目标节区,则返回其包含的二进制数据。
        None: 如果文件不存在、不是有效的 ELF 文件或找不到目标节区。
    """
    if not os.path.exists(file_path):
        print(f"[!] 错误: 文件 '{file_path}' 不存在。")
        return None
 
    print(f"[*] 正在打开并解析文件: {file_path}")
 
    try:
        # 使用 'with' 语句确保文件能被正确关闭
        with open(file_path, 'rb') as f:
            # 使用 pyelftools 库加载 ELF 文件,它会自动解析所有结构
            elf = ELFFile(f)
 
            # 遍历所有节区 (sections)
            # 这等同于 C 代码中的 while 循环遍历节头表
            print(f"[*] 正在搜索目标节区: '{target_section}'...")
            for section in elf.iter_sections():
                # section.name 在 pyelftools 中已经是解码后的字符串
                print(section.name)
                if section.name == target_section:
                    print(f"[+] 成功找到节区 '{section.name}'!")
                     
                    # 获取该节区的原始数据
                    # .data() 方法会返回该节区的完整二进制内容
                    # 这等同于 C 代码中 fseek 和 fread 的组合操作
                    payload_data = section.data()
                     
                    print(f"[+] 成功提取载荷数据,大小: {len(payload_data)} 字节。")
                    return payload_data
 
            # 如果循环结束仍未找到
            print(f"[!] 警告: 在文件中未找到目标节区 '{target_section}'。")
            return None
 
    except ELFError:
        print(f"[!] 错误: '{file_path}' 不是一个有效的 ELF 文件格式或已损坏。")
        return None
    except IOError as e:
        print(f"[!] 错误: 读取文件 '{file_path}' 失败: {e}")
        return None
 
# --- 主程序执行部分 ---
if __name__ == "__main__":
    # 检查命令行参数,如果没有则使用默认文件名 '6-pack'
    if len(sys.argv) > 1:
        input_file = sys.argv[1]
    else:
        # 在Linux环境下,可执行文件通常没有扩展名

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回