Description
랜덤한 값을 맞혀 get_flag()
함수를 호출하세요! 플래그는 64자리입니다.
플래그 형식은 DH{…} 입니다.
Files
ubuntu@WSL2:~/CTF/dreamhack.io$ tree randzzz randzzz └── chall 0 directories, 1 file ubuntu@WSL2:~/CTF/dreamhack.io$ file randzzz/chall randzzz/chall: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=144322b4d0870425ad69846991455114d0197396, for GNU/Linux 3.2.0, not stripped
64비트 리눅스용 실행 파일 하나.
분석
main
int __cdecl main(int argc, const char **argv, const char **envp) { __int64 v4[3]; // [rsp+0h] [rbp-A0h] __int64 v5[3]; // [rsp+18h] [rbp-88h] BYREF __int64 v6[3]; // [rsp+30h] [rbp-70h] int v7; // [rsp+48h] [rbp-58h] char v8[68]; // [rsp+50h] [rbp-50h] BYREF unsigned int seconds; // [rsp+94h] [rbp-Ch] BYREF int j; // [rsp+98h] [rbp-8h] int i; // [rsp+9Ch] [rbp-4h] puts("----Start----"); sleep(1u); puts("fall asleep from now on."); seconds = rand() + 1; sleep(seconds); rand(); rand(); printf("Can you guess the rand num?: "); __isoc99_scanf("%d", &seconds); if ( rand() % 10 == seconds ) { v6[0] = 0x386C2C39364C396CLL; v6[1] = 0x30383338AC4C4C39LL; v6[2] = 0x353330354CCCCC34LL; v7 = 0xCC6C37AC; for ( i = 0; i <= 27; ++i ) v8[i] = get_flag((unsigned int)*((char *)v6 + i), seconds); } if ( rand() % 10 == seconds ) { v4[0] = 0x1B323838330B1335LL; v4[1] = 0xB332323361B2333LL; v4[2] = 0x23391B0B38370B13LL; qmemcpy(v5, "3539458369+8", 12); for ( j = 0; j <= 35; ++j ) v8[j + 28] = get_flag((unsigned int)*((char *)v4 + j), seconds); } printf("DH{%s}", v8); return 0; }
rand() 함수는 랜덤한 숫자를 반환하는데,
실제로는 프로그램을 매번 처음 실행시킬 때마다 같은 값이 나온다.
그리고, “fall asleep from now on.” 문구를 출력하면서
sleep 함수가 작동되어 거의 무한으로 기다려야된다.
따라서 NOP 패치를 시키거나, 건너뛸 필요가 있다.
rand 함수를 통해 값을 확인하여 get_flag를 호출하는 제어문이 2번 나오는데,
첫번째에서는 rand 값이 1714636915이였고,
두번째에서는 rand 값이 1957747793였다.
따라서 첫 제어문에서는 seconds가 5,
두번째 제어문에서는 seconds가 3이여야 한다.
풀이
sleep 함수를 건너뛴다.
NOP으로 직접 바이너리를 패치해도 되지만, 필자는 rip 레지스터를 그냥 조작해서 건너뛰었다.
ubuntu@WSL2:~/CTF/dreamhack.io/randzzz$ gdb ./chall ... gdb-peda$ b *main+89 Breakpoint 1 at 0x12fe gdb-peda$ disas main Dump of assembler code for function main: ... 0x00005555555552fe <+89>: call 0x5555555550d0 <sleep@plt> 0x0000555555555303 <+94>: call 0x5555555550e0 <rand@plt> ... gdb-peda$ r ... Breakpoint 1, 0x00005555555552fe in main () gdb-peda$ set $rip=*main+94 gdb-peda$ c Continuing. Can you guess the rand num?:
Can you guess the rand num?에서 5를 입력하고,
두번째 제어문에서 seconds를 불러오는 명령어 주소를 breakpoint시켜 값을 수정한다.
... gdb-peda$ set $rip=*main+94 gdb-peda$ b *main+354 Breakpoint 2 at 0x555555555407 gdb-peda$ c Continuing. Can you guess the rand num?: 5 ... => 0x555555555407 <main+354>: mov eax,DWORD PTR [rbp-0xc] 0x55555555540a <main+357>: cmp ecx,eax ... Breakpoint 2, 0x0000555555555407 in main () gdb-peda$ x/wx $rbp-0xc 0x7fffffffdd84: 0x00000005 gdb-peda$ set *0x7fffffffdd84=3 gdb-peda$ x/wx $rbp-0xc 0x7fffffffdd84: 0x00000003 gdb-peda$ c Continuing. DH{c8b48ac08bbe00068ffb6606e2cf6ba0002c0dc4dd0aba20ac8d0608860048e0} [Inferior 1 (process 3449) exited normally]
FLAG
DH{c8b48ac08bbe00068ffb6606e2cf6ba0002c0dc4dd0aba20ac8d0608860048e0}