-
-
[原创] 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 csvimport structimport zlibfrom collections import defaultdict# --- 配置 ---# 脚本将直接处理这个文件CSV_FILENAME = "1.csv"# CSV文件中各列的索引 (从0开始)# 请根据你的Wireshark导出格式进行调整SOURCE_IP_COL = 2DEST_IP_COL = 3FLOW_LABEL_COL = 6# 隐蔽信道协议的控制信号START_SIGNAL = 0x10000END_SIGNAL = 0x10001def 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 Nonedef 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_payloadsif __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 csvimport structimport zlibfrom collections import defaultdict# --- 配置 ---# 脚本将直接处理这个文件CSV_FILENAME = "1.csv"# CSV文件中各列的索引 (从0开始)# 请根据你的Wireshark导出格式进行调整SOURCE_IP_COL = 2DEST_IP_COL = 3FLOW_LABEL_COL = 6# 隐蔽信道协议的控制信号START_SIGNAL = 0x10000END_SIGNAL = 0x10001def 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 Nonedef 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_payloadsif __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 osimport sys# 导入 pyelftools 库中的核心类from elftools.elf.elffile import ELFFilefrom elftools.common.exceptions import ELFErrordef 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 osimport sys# 导入 pyelftools 库中的核心类from elftools.elf.elffile import ELFFilefrom elftools.common.exceptions import ELFErrordef 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环境下,可执行文件通常没有扩展名赞赏
他的文章
赞赏
雪币:
留言: