第七届"湖湘杯"网络安全大赛 - 初赛writeup
Web
easywill
解题思路
http://eci-2zej1goyn9jh8hty6ton.cloudeci1.ichunqiu.com/?name=cfile&value=/etc/passwd
P神博客最近的文章利用pearcmd:https://tttang.com/archive/1312/
Pentest in Autumn
解题思路
http://eci-2ze40jm526y24nv2lkl3.cloudeci1.ichunqiu.com:8888/ 权限绕过 /;/actuator/env /;/actuator/heapdump
解密脚本
import base64
import struct
print(base64.b64encode(struct.pack('<bbbbbbbbbbbbbbbb', -126,-67,24,-71,-62,-122,61,-52,91,77,-110,115,-43,100,-88,103)))
#gr0YucKGPcxbTZJz1WSoZw==
flag{3fa31850-8ee6-40f2-9b18-9ecf6cac176c}
Reverse
Hideit
解题思路
打开之后发现有SMC,单步调试总是找不到主函数
发现输出字符串,在puts下断点,第二次断下后回溯到主函数处,向上一直到函数头,反编译
__int64 __fastcall sub_24D61161BB0(__int64 a1)
{
//...
if ( !(unsigned int)off_24D61163000(-2147483646i64, aSoftwareClasse, &v24) )
{
v23 = 0;
((void (__fastcall *)(char *, _QWORD, __int64))unk_24D61162A0C)(v21, 0i64, 520i64);
v22 = 66;
if ( !(unsigned int)off_24D61163008(v24, aKeysSecret, 0i64, &v23, v21, &v22) )
off_24D61163020(0i64, 0i64, v21, 0xFFFFFFFFi64, v14, 260, 0i64, 0i64);
}
off_24D611630F8(aFirstSecretHer);
v10 = 0i64;
v11 = 0;
((void (__fastcall *)(void *, __int64 *))unk_24D61161B50)(&unk_24D6116324C, &v10);
v12 = 0i64;
strcpy((char *)&v12, (const char *)&v10);
v13[0] = 114;
v13[1] = 514;
v13[2] = 19;
v13[3] = 19;
((void (__fastcall *)(char *, _QWORD, __int64))unk_24D61162A0C)(v20, 0i64, 512i64);
v3 = HIDWORD(v12);
v4 = 32;
v5 = v12;
v6 = HIDWORD(v12);
v7 = 0;
do
{
v7 -= 1640531527;
v8 = (v7 >> 2) & 3;
v5 += ((v7 ^ v3) + (v6 ^ v13[v8])) ^ (((16 * v6) ^ (v3 >> 3)) + ((v6 >> 5) ^ (4 * v3)));
v3 += ((v7 ^ v5) + (v5 ^ v13[v8 ^ 1])) ^ (((16 * v5) ^ (v5 >> 3)) + ((v5 >> 5) ^ (4 * v5)));
v6 = v3;
--v4;
}
while ( v4 );
if ( v5 == 288407067 && v3 == 1668576323 )
{
v17 = 0i64;
v18 = (unsigned __int8)v10 | ((BYTE1(v10) | (WORD1(v10) << 8)) << 8);
v19 = BYTE4(v10) | ((BYTE5(v10) | (HIWORD(v10) << 8)) << 8);
((void (__fastcall *)(_DWORD *, __int64))unk_24D61161000)(v16, a1);// 密钥扩展
sub_24D61161150(v16, v14, v20); // 第二步加密
while ( byte_24D611631D0[v2] == v20[v2] )
{
if ( ++v2 >= 32 )
return off_24D611630F8(aYouFindLastSec);
}
}
return 0i64;
}
先进行类tea加密,这8个字符合法的话再进行第二步加密 类tea加密解出字符串dotitsit,第二段加密如下
_DWORD *__fastcall sub_24D61161150(_DWORD *a1, __int128 *a2, _BYTE *a3)
{
// ...
if ( a2 )
{
v13 = (char *)a2 - (char *)&v122;
v14 = &v122;
do
{
*(_BYTE *)v14 = *((_BYTE *)v14 + v13);
v14 = (__int128 *)((char *)v14 + 1);
--v11;
}
while ( v11 );
v127 = &v122;
}
// key operation
while ( 1 )
{
// key operation
}
//...
if ( v127 )
{
// 在这里下断点,查看v76...的值就可以异或处原始数据
v76 ^= *(unsigned __int8 *)v127 | ((*((unsigned __int8 *)v127 + 1) | (*((unsigned __int16 *)v127 + 1) << 8)) << 8);
v77 ^= *((unsigned __int8 *)v127 + 4) | ((*((unsigned __int8 *)v127 + 5) | (*((unsigned __int16 *)v127 + 3) << 8)) << 8);
v78 ^= *((unsigned __int8 *)v127 + 8) | ((*((unsigned __int8 *)v127 + 9) | (*((unsigned __int16 *)v127 + 5) << 8)) << 8);
v79 ^= *((unsigned __int8 *)v127 + 12) | ((*((unsigned __int8 *)v127 + 13) | (*((unsigned __int16 *)v127 + 7) << 8)) << 8);
v80 ^= *((unsigned __int8 *)v127 + 16) | ((*((unsigned __int8 *)v127 + 17) | (*((unsigned __int16 *)v127 + 9) << 8)) << 8);
v129 ^= *((unsigned __int8 *)v127 + 20) | ((*((unsigned __int8 *)v127 + 21) | (*((unsigned __int16 *)v127 + 11) << 8)) << 8);
LODWORD(v97) = (*((unsigned __int8 *)v127 + 24) | ((*((unsigned __int8 *)v127 + 25) | (*((unsigned __int16 *)v127
+ 13) << 8)) << 8)) ^ v97;
HIDWORD(v97) ^= *((unsigned __int8 *)v127 + 28) | ((*((unsigned __int8 *)v127 + 29) | (*((unsigned __int16 *)v127
+ 15) << 8)) << 8);
v81 ^= *((unsigned __int8 *)v127 + 32) | ((*((unsigned __int8 *)v127 + 33) | (*((unsigned __int16 *)v127 + 17) << 8)) << 8);
v86 ^= *((unsigned __int8 *)v127 + 36) | ((*((unsigned __int8 *)v127 + 37) | (*((unsigned __int16 *)v127 + 19) << 8)) << 8);
v87 ^= *((unsigned __int8 *)v127 + 44) | ((*((unsigned __int8 *)v127 + 45) | (*((unsigned __int16 *)v127 + 23) << 8)) << 8);
v82 ^= *((unsigned __int8 *)v127 + 48) | ((*((unsigned __int8 *)v127 + 49) | (*((unsigned __int16 *)v127 + 25) << 8)) << 8);
v83 ^= *((unsigned __int8 *)v127 + 52) | ((*((unsigned __int8 *)v127 + 53) | (*((unsigned __int16 *)v127 + 27) << 8)) << 8);
v84 ^= *((unsigned __int8 *)v127 + 56) | ((*((unsigned __int8 *)v127 + 57) | (*((unsigned __int16 *)v127 + 29) << 8)) << 8);
v85 ^= *((unsigned __int8 *)v127 + 60) | ((*((unsigned __int8 *)v127 + 61) | (*((unsigned __int16 *)v127 + 31) << 8)) << 8);
v75 ^= *((unsigned __int8 *)v127 + 40) | ((*((unsigned __int8 *)v127 + 41) | (*((unsigned __int16 *)v127 + 21) << 8)) << 8);
}
// data copy
do
{
*v90 = v90[(char *)&v122 - a3];
++v90;
--v91;
}
while ( v91 );
result = a1;
a1[12] = v105;
a1[13] = v100;
return result;
}
这个函数看起来复杂,实际上就是把key进行很复杂的操作之后,和输入进行异或,所以只需要dump出key就可以得到flag
exp
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "defs.h"
#include <stdint.h>
void decrypt(uint32_t *v)
{
uint32_t v7, v8, v6, v5, v4, v3;
v4 = 32;
uint32_t v11[] = {114, 514, 19, 19};
v7 = 0x9e3779b9 * 32;
v5 = 0x1130BE1B;
v3 = 0x63747443;
do
{
v8 = (v7 >> 2) & 3;
v3 -= ((v7 ^ v5) + (v5 ^ v11[v8 ^ 1])) ^ (((16 * v5) ^ (v5 >> 3)) + ((v5 >> 5) ^ (4 * v5)));
v6 = v3;
v5 -= ((v7 ^ v3) + (v6 ^ v11[v8])) ^ (((16 * v6) ^ (v3 >> 3)) + ((v6 >> 5) ^ (4 * v3)));
--v4;
v7 -= 0x9e3779b9;
} while (v4);
v[0] = v5;
v[1] = v3;
}
int main()
{
uint32_t k[] = {114, 514, 19, 19};
uint8_t p[] = "12345678";
uint32_t c[] = {288407067, 1668576323};
decrypt(c);
printf("%sn", c);
for (size_t i = 0; i < 8; i++)
{
printf("0x%02x,", *(uint8_t *)&c[i]);
}
printf("n");
char key[] = "expand 32-byte k0N3@aYI_M3l0dy_KurOm1_W_Suk1dqy0x01x00x00x00x00x00x00x00dotitsit";
uint8_t data[] = {0xeb, 0x8e, 0x5c, 0xa5, 0x62, 0xb4, 0x1c, 0x84, 0x5c, 0x59, 0xfc, 0xd, 0x43, 0x3c, 0xab, 0x20, 0xd8, 0x93, 0x33, 0x13, 0xa1, 0x9e, 0x39, 0x0, 0x76, 0x14, 0xb5, 0x4, 0x58, 0x9d, 0x6, 0xb8};
uint8_t res[128] = {0};
uint32_t k0=0xC23DE28D;
uint32_t *d=(uint32_t*)data;
d[0]^=k0;
d[1]^=0xca2df219;
d[2]^=0x52cf1418;
d[3]^=0x139c5a77;
d[4]^=0x5b04ccaa;
d[5]^=0x680cc192;
d[6]^=0x47F95845;
d[7]^=0xC535D968;
printf("%sn",d);
}
shell
解题思路
main中创建了子进程,父子进程反调试 找到个dump 子进程的程序 https://github.com/glmcdona/Process-Dump
pd -pid <子进程pid>
其中子进程pid可以调试获得 ida打开dump后的子进程如下
.text:000001FA6C311160 push rsi
.text:000001FA6C311161 push rdi
.text:000001FA6C311162 sub rsp, 28h
.text:000001FA6C311166 lea rcx, Format ; "plz input your flagn"
.text:000001FA6C31116D call sub_1FA6C3112B0
.text:000001FA6C311172 lea rcx, a42s ; "%42s"
.text:000001FA6C311179 lea rsi, known_string ; 这个就是0x40a0
.text:000001FA6C311180 mov rdx, rsi
.text:000001FA6C311183 call scanf
.text:000001FA6C311188 int 3 ; Trap to Debugger
.text:000001FA6C311189 ; ---------------------------------------------------------------------------
.text:000001FA6C311189 mov rcx, rsi ; Str
.text:000001FA6C31118C call strlen
.text:000001FA6C311191 cmp rax, 0C9h
.text:000001FA6C311197 jb short near ptr unk_1FA6C31119E
.text:000001FA6C311199 call sub_1FA6C311020
.text:000001FA6C311199 ; ---------------------------------------------------------------------------
.text:000001FA6C31119E unk_1FA6C31119E db 0C4h ; CODE XREF: main+37↑j
.text:000001FA6C31119F db 12h
结合主进程中的调试函数
int __fastcall sub_7FF6C56B1560(_DWORD *a1)
{
// ...
if ( *a1 == 0x80000003 )
{
v5 = qword_7FF6C56B5630;
if ( qword_7FF6C56B5630 )
{
Context.ContextFlags = 1048587;
if ( !GetThreadContext(hThread, &Context) )
{
v6 = GetLastError();
printf("GetThreadContext failed: %llxn", v6);
}
ReadProcessMemory(hProcess, (LPCVOID)(qword_7FF6C56B5638 + 0x40A0), v13, 0x2Aui64, &NumberOfBytesRead);
v7 = _mm_load_si128((const __m128i *)&xmmword_7FF6C56B3420);
for ( i = 0i64; i < 32; i += 16i64 )
*(__m128i *)&v13[i] = _mm_xor_si128(_mm_loadu_si128((const __m128i *)&v13[i]), v7);
for ( j = 32i64; j < 42; ++j )
v13[j] ^= 0x78u;
WriteProcessMemory(hProcess, (LPVOID)(qword_7FF6C56B5638 + 0x40A0), v13, 0x2Aui64, &NumberOfBytesRead);
v5 = qword_7FF6C56B5630;
}
v1 = v5 + 1;
qword_7FF6C56B5630 = v1;
goto LABEL_21;
}
if ( (_DWORD)v1 == 0xC000001D )
{
Context.ContextFlags = 1048587;
if ( !GetThreadContext(hThread, &Context) )
{
v2 = GetLastError();
printf("GetThreadContext failed: %llxn", v2);
}
ReadProcessMemory(hProcess, (LPCVOID)(Context.Rip + 1), Buffer, 1ui64, &NumberOfBytesRead);
if ( Buffer[0] == 0x12 )
{
v3 = qword_7FF6C56B5638 + 0x1035;
}
else
{
if ( Buffer[0] != 0x48 )
goto LABEL_10;
v3 = qword_7FF6C56B5638 + 0x1242;
}
Context.Rip = v3;
LABEL_10:
LODWORD(v1) = SetThreadContext(hThread, &Context);
if ( !(_DWORD)v1 )
{
v4 = GetLastError();
LODWORD(v1) = printf("SetThreadContext failed: %llxn", v4);
}
LABEL_21:
*(_QWORD *)&dwContinueStatus = 65537i64;
}
return v1;
}
可以得到替换了三处 1.用户的输入都异或0x78 2.如果遇到未知指令且指令下一个字节是0x12就去0x1035文件偏移 3.如果遇到未知指令且指令下一字节是0x48就去0x1242文件偏移 此时再看子进程主函数,可以得到流程
0x1035文件偏移为
.text:000001FA6C311035 mov [rsp+arg_28], 0
.text:000001FA6C31103D
.text:000001FA6C31103D loc_1FA6C31103D: ; CODE XREF: sub_1FA6C311020+A5↓j
.text:000001FA6C31103D cmp [rsp+arg_28], 2Ah ; '*'
.text:000001FA6C311042 jge near ptr unk_1FA6C3110CA
.text:000001FA6C311048 movsxd rax, [rsp+arg_28]
.text:000001FA6C31104D lea rcx, known_string
.text:000001FA6C311054 mov al, [rcx+rax]
.text:000001FA6C311057 mov [rsp+arg_25], al
.text:000001FA6C31105B mov eax, [rsp+arg_28]
.text:000001FA6C31105F mov [rsp+arg_24], al
.text:000001FA6C311063 movzx eax, [rsp+arg_25]
.text:000001FA6C311068 movzx ecx, [rsp+arg_24]
.text:000001FA6C31106D and eax, ecx
.text:000001FA6C31106F xor eax, 0FFFFFFFFh
.text:000001FA6C311072 mov [rsp+arg_23], al
.text:000001FA6C311076 movzx eax, [rsp+arg_23]
.text:000001FA6C31107B movzx ecx, [rsp+arg_25]
.text:000001FA6C311080 and eax, ecx
.text:000001FA6C311082 xor eax, 0FFFFFFFFh
.text:000001FA6C311085 mov [rsp+arg_27], al
.text:000001FA6C311089 movzx eax, [rsp+arg_23]
.text:000001FA6C31108E movzx ecx, [rsp+arg_24]
.text:000001FA6C311093 and eax, ecx
.text:000001FA6C311095 xor eax, 0FFFFFFFFh
.text:000001FA6C311098 mov [rsp+arg_26], al
.text:000001FA6C31109C movzx eax, [rsp+arg_27]
.text:000001FA6C3110A1 movzx ecx, [rsp+arg_26]
.text:000001FA6C3110A6 and eax, ecx
.text:000001FA6C3110A8 xor eax, 0FFFFFFFFh
.text:000001FA6C3110AB movsxd rcx, [rsp+arg_28]
.text:000001FA6C3110B0 lea rdx, known_string
.text:000001FA6C3110B7 mov [rdx+rcx], al
.text:000001FA6C3110BA mov eax, [rsp+arg_28]
.text:000001FA6C3110BE add eax, 1
.text:000001FA6C3110C1 mov [rsp+arg_28], eax
.text:000001FA6C3110C5 jmp loc_1FA6C31103D
.text:000001FA6C3110C5 ; ---------------------------------------------------------------------------
.text:000001FA6C3110CA unk_1FA6C3110CA db 0C4h ; CODE XREF: sub_1FA6C311020+22↑j
.text:000001FA6C3110CB db 48h
接下来是0x1242
.text:000001FA6C311242 mov eax, 2
.text:000001FA6C311247 lea rcx, unk_1FA6C314030
.text:000001FA6C31124E xchg ax, ax
.text:000001FA6C311250
.text:000001FA6C311250 loc_1FA6C311250: ; CODE XREF: main+117↓j
.text:000001FA6C311250 movzx edx, byte ptr [rax+rsi-2]
.text:000001FA6C311255 cmp dl, [rax+rcx-2]
.text:000001FA6C311259 jnz short loc_1FA6C31128E
.text:000001FA6C31125B movzx edx, byte ptr [rax+rsi-1]
.text:000001FA6C311260 cmp dl, [rax+rcx-1]
.text:000001FA6C311264 jnz short loc_1FA6C31128E
.text:000001FA6C311266 movzx edx, byte ptr [rax+rsi]
.text:000001FA6C31126A cmp dl, [rax+rcx]
.text:000001FA6C31126D jnz short loc_1FA6C31128E
.text:000001FA6C31126F add rax, 3
.text:000001FA6C311273 cmp rax, 2Ch ; ','
.text:000001FA6C311277 jnz short loc_1FA6C311250
.text:000001FA6C311279 lea rcx, aWin ; "winn"
.text:000001FA6C311280 call sub_1FA6C3112B0
.text:000001FA6C311285 xor eax, eax
.text:000001FA6C311287 add rsp, 28h
.text:000001FA6C31128B pop rdi
.text:000001FA6C31128C pop rsi
.text:000001FA6C31128D retn
.text:000001FA6C31128E ; ---------------------------------------------------------------------------
.text:000001FA6C31128E
.text:000001FA6C31128E loc_1FA6C31128E: ; CODE XREF: main+F9↑j
.text:000001FA6C31128E ; main+104↑j ...
.text:000001FA6C31128E lea rcx, aWrong ; "wrongn"
.text:000001FA6C311295 call sub_1FA6C3112B0
.text:000001FA6C31129A call cs:getchar
.text:000001FA6C3112A0 xor ecx, ecx ; Code
.text:000001FA6C3112A2 call cs:__imp_exit
.text:000001FA6C3112A2 ; ---------------------------------------------------------------------------
.text:000001FA6C3112A8 db 0CCh
.text:000001FA6C3112A8 main endp
用户输入先异或0x78,再异或数组下标 exp
a=[0x1e, 0x15, 0x1b, 0x1c, 0x7, 0x4d, 0x1f, 0x1b, 0x12, 0x17, 0x4b, 0x44, 0x47, 0x58, 0x12, 0x47, 0x58, 0x58, 0x47, 0x5f, 0x54, 0x54, 0x5
8, 0x42, 0x59, 0x57, 0x50, 0x1, 0x49, 0x51, 0x53, 0x57, 0x3d, 0x6b, 0x3e, 0x6f, 0x3d, 0x6d, 0x6c, 0x3e, 0x69, 0x2c, 0x0, 0x0, 0x0]
b=[a[i]^i^0x78 for i in range(len(a))]
print(bytes(b))