-
-
[原创]2021 KCTF 春季赛 第四题 英雄救美 wp
-
2021-5-14 18:50 4963
-
0x0 初步观察
windows32位控制台程序.
输入错误时没有回显.
输入成功的提示是段shellcode, 使用VirtualAlloc分配地址解密shellcode再执行.
观察验证逻辑, 不难发现是个9*9的经典数独游戏.
0x1 详细分析逻辑
ida F5, main函数逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | int __cdecl main( int argc, const char * * argv, const char * * envp) { int nLen; / / kr00_4 int v4; / / ecx __m128i * v5; / / esi int v6; / / edi void ( * v8)(void); / / [esp + 10h ] [ebp - 2C8h ] int v9[ 22 ]; / / [esp + 14h ] [ebp - 2C4h ] BYREF char v10[ 508 ]; / / [esp + 6Ch ] [ebp - 26Ch ] BYREF __int128 v11; / / [esp + 26Ch ] [ebp - 6Ch ] BYREF char v12[ 88 ]; / / [esp + 27Ch ] [ebp - 5Ch ] BYREF printf( "\t\t\t看雪CTF大赛\r\n" ); printf( "\t\t祝愿看雪CTF大赛越办越好\r\n" ); printf( "Serial: " ); scanf_s( "%s" , (char * )&v11 + 12 ); nLen = strlen((const char * )&v11 + 12 ); if ( nLen < = 64 && fun_convert_input(( int )&v11 + 12 , nLen, &v9[ 21 ]) = = 1 && fun_check_game(( int )v10, nLen - 9 ) = = 1 ) { v11 = 0i64 ; memset(v9, 0 , sizeof(v9)); v9[ 5 ] = 0 ; v9[ 4 ] = 0 ; v9[ 0 ] = 0x67452301 ; v9[ 1 ] = 0xEFCDAB89 ; v9[ 2 ] = 0x98BADCFE ; v9[ 3 ] = 0x10325476 ; sub_4014E0(( int )v12, ( int )v9, nLen); sub_4015B0(( int )&v11, ( int )v9); sub_401ED0(v4, (unsigned __int8 * )&v11); v8 = (void ( * )(void))VirtualAlloc( 0 , 0x620u , 0x1000u , 0x40u ); v5 = (__m128i * )v8; v6 = 0x62 ; do { * v5 = _mm_loadu_si128((__m128i * )((char * )v5 + &sub_4181A0 - (_UNKNOWN * )v8)); ((void (__fastcall * )(char * , __m128i * ))loc_4028B0)(v10, v5 + + ); - - v6; } while ( v6 ); v8(); } return 0 ; } |
输入完serial后, 要执行成功的回显, 必须要满足三个条件:
1.输入length不大于64
2.fun_convert_input(sub_401240)验证通过
3.fun_check_game(sub_401000)验证通过
分析fun_convert_input和fun_check_game两个函数的逻辑.
fun_convert_input(sub_401240)
将输入的serial每个字符进行转换.
遇到非数字字符, 查表的第n行(n从1开始)转换对应下标.
1 2 3 4 5 6 7 8 9 | $BPV:ubfY p}]DtN>aT ^MGmJQ #*H r`O'wjic0 !hdy{oZz - @n + ?& % s_ / g<e[W)XUx RFSLRA;.l = CEkvK - (q |
遇到数字字符, 验证这行转换了的下标个数加上数字字符是不是等于'9'(满足数独一行有9个数字), 并且切换查表的下一行(n++).
fun_check_game(sub_401000)
把转换好的下标(1~9数字)填入游戏格子内.
验证数独是否成立(每行每列每个九宫格都是数字1~9).
0x2 计算serial
先到网页上在线解数独:链接
每行需要填的数字为:
1 2 3 4 5 6 7 8 9 | 5619238 18345 76219 7846925 4539786 6928713 28563 61728 1793452 |
根据fun_convert_input里的字符表进行转换:
1 2 3 4 5 6 7 8 9 | :u$YBPf pa]Dt #QM^H ic'j0`w y{d - Zzo % / n_s@ + <UW)e AR;F. = - qEkvC |
每行最后加上数字(9-要填写的数字个数), 最后得到:
:u$YBPf2pa]Dt4#QM^H4ic'j0`w2y{d-Zzo2%/n_s@+2<UW)e4AR;F.4=-qEkvC2
验证通过!
0x3 成功的回显
拿到flag后处于好奇瞅了一眼成功提示的MessageBox代码, 和之前猜测的一样, 是段shellcode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | void __cdecl fun_start() { void (__stdcall * pfnMessageBoxA)(_DWORD, char * , char * , _DWORD); / / [esp + 0h ] [ebp - ECh] int hUser32; / / [esp + 4h ] [ebp - E8h] int (__stdcall * pfnLoadLibraryA)(char * ); / / [esp + 8h ] [ebp - E4h] int (__stdcall * pfnGetProcAddress)( int , char * ); / / [esp + Ch] [ebp - E0h] int hKernel32; / / [esp + 10h ] [ebp - DCh] char szMsg[ 28 ]; / / [esp + 90h ] [ebp - 5Ch ] BYREF char szGetProcAddress[ 16 ]; / / [esp + ACh] [ebp - 40h ] BYREF char szLoadLibraryA[ 16 ]; / / [esp + BCh] [ebp - 30h ] BYREF char szMessageBoxA[ 12 ]; / / [esp + CCh] [ebp - 20h ] BYREF char szUser32Dll[ 12 ]; / / [esp + D8h] [ebp - 14h ] BYREF char szTitle[ 8 ]; / / [esp + E4h] [ebp - 8h ] BYREF hKernel32 = fun_GetKernel32Address(); strcpy(szGetProcAddress, "GetProcAddress" ); strcpy(szLoadLibraryA, "LoadLibraryA" ); strcpy(szUser32Dll, "user32.dll" ); strcpy(szMessageBoxA, "MessageBoxA" ); pfnGetProcAddress = ( int (__stdcall * )( int , char * ))fun_GetProcAddress(hKernel32, ( int )szGetProcAddress); pfnLoadLibraryA = ( int (__stdcall * )(char * ))pfnGetProcAddress(hKernel32, szLoadLibraryA); hUser32 = pfnLoadLibraryA(szUser32Dll); pfnMessageBoxA = (void (__stdcall * )(_DWORD, char * , char * , _DWORD))pfnGetProcAddress(hUser32, szMessageBoxA); strcpy(szTitle, "message" ); strcpy(szMsg, "You Input The Right Key!" ); pfnMessageBoxA( 0 , szMsg, szTitle, 0 ); } |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
看原图