house-of-orange
1.ezstack
简单的格式化字符串漏洞,泄露基地址和canary,泄露rop。
1 | from pwn import* |
2.noleak1
一个很明显的off-by-null,逆向得到密码N0_py_1n_tHe_ct7。
1 | from pwn import * |
3.ezheap
改遍2016年HITCONCTF,正好借这个题来复习一下house-of-orange。
题目存在堆溢出漏洞且没有free等可以释放chunk的函数,因此我们可以利用sysmalloc函数中的_int_free函数,那么该如何调用sysmalloc函数呢?我们来看下malloc的源码
1 | use_top: |
因此可以看到当top_chunk的大小小于我们要申请的大小,且fastbin中没有freechunk,就会调用sysmalloc函数。
sysmalloc()有两种方法分配新的内存,第一种是调用mmap函数分配,第二种就是调用sbrk函数直接扩充top_chunk。判断使用哪种方法的条件如下:
只要我们申请chunk的大小小于mp_.mmap_threshold也就是0x20000,就会调用sbrk函数。那该怎么调用sysmalloc中的_int_free函数呢?我们再次看下sysmalloc函数的源码
1 | old_top = av->top; |
需要top_chunk在减去一个放置fencepost的MINSIZE后,还要大于MINSIZE(0x20);如果是main_arena则需要放置两个fencepost。还需要满足old_size小于nb+MINSIZE,PREV_INUSE标志位为1,以及old_top+old_size页对齐。
因此我们先利用溢出将top_chunk的大小改到小于0x20000的值且需要使old_top+old_size页对齐,然后我们申请一个大于修改过后的top_chunk的size的chunk,这样top_chunk就被放入了unsortedbin中。这时我们就可以泄露出libc_base和heap_base(fd_nextsize)。紧接着我们再次利用溢出将被放入unsortedbin中的old_topchunk的大小改为0x60(后面会说到为什么是0x60)。
同时将其bk指针改为&_IO_list_all-0x10(利用unsortedbin attack将_IO_list_all的值改为main_arena+88)以及布置上其他数据。最后直接申请一个大于0x60大小的chunk即可拿到shell。那么这些数据是什么呢?
在我们申请一个大于0x60大小的chunk这个期间,其实是发生了很多事情
首先old_top从unsortedbin中解链造成unsortedbin attack将_IO_list_all的值改为main_arena+88,然后被放入smallbin[5]中。
然后就会发生内存错误(因为unsortedbin的bk指针被改)调用了_IO_flush_all_lockp()调用流程:
malloc_printerr -> __libc_message -> __GI_abort -> _IO_flush_all_lockp -> __FI__IO_str_overflow
,我们来看看_IO_flush_all_lockp函数的源码: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
53int
_IO_flush_all_lockp (int do_lock)
{
int result = 0;
struct _IO_FILE *fp;
int last_stamp;
#ifdef _IO_MTSAFE_IO
__libc_cleanup_region_start (do_lock, flush_cleanup, NULL);
if (do_lock)
_IO_lock_lock (list_all_lock);
#endif
last_stamp = _IO_list_all_stamp;
fp = (_IO_FILE *) _IO_list_all;
while (fp != NULL)
{
run_fp = fp;
if (do_lock)
_IO_flockfile (fp);
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
#endif
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;
if (do_lock)
_IO_funlockfile (fp);
run_fp = NULL;
if (last_stamp != _IO_list_all_stamp)
{
/* Something was added to the list. Start all over again. */
fp = (_IO_FILE *) _IO_list_all;
last_stamp = _IO_list_all_stamp;
}
else
fp = fp->_chain;
}
#ifdef _IO_MTSAFE_IO
if (do_lock)
_IO_lock_unlock (list_all_lock);
__libc_cleanup_region_end (0);
#endif
return result;
}因为此时_IO_list_all的值是main_arena+88其0x100范围内的值我们不好控制,因此我们需要利用
fp = fp->_chain;
那么我们该如何执行到这条语句呢?要想执行到这条语句,首先就不能让前面出现错误,因为此时的_IO_list_all的值已被更改,所以其vatble的地址也被更改,所以如果执行了_IO_OVERFLOW函数就一定会报错,因此我们得需要&&前的条件不成立,这样就不会执行到_IO_OVERFLOW函数了。1
2
3
4
5if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))绕过执行后这行代码将_IO_list_all的值换成我们可以控制的值。 又因为(main_arena+88)+0x68的值是old_top,这也就是为什么要将old_top的size改为0x60的原因
现在_IO_list_all的值是old_top的地址,因此我们就可以在old_top上布置数据了。首先将vtable的地址改为我们可以控制的地址,然后将system覆盖掉vtable中的_IO_OVERFLOW函数。然后布置使上面&&前的条件能成立的数据,因为有个||因此有两种布置方法。
布置好后我们就可以执行_IO_OVERFLOW(fp,EOF) == EOF,默认此函数的第一个参数是_IO_FILE_plus,因为_IO_list_all的值就是_IO_FILE_plus,因此我们将old_top的prev_size写为/bin/sh\x00。
第一种数据:
第二种数据
exp1:
1 | from pwn import * |
exp2:
1 | from pwn import * |
4.pwnsky
这个题一开始我猜测漏洞在加密算法那,但是一直没有找到越界或是溢出的地方,直到提示有花指令(emmm)
我们先来看一下这个算法,其实也就是将我们输入的值进行异或一下而已
对于登录的密码,我们只需要动态调试一下即可得出key值,然后将后四个字节与该key异或即可得到密码。
对于add函数里藏了一个花指令我是着实没有想到,因为我找到了加密函数里的花指令,但又分析了好长时间也没发现漏洞,并且这个函数感觉那么的完整。。。。。。
通过动态调试,明确函数执行流程,然后patch掉中间跳来跳去的代码比如call,jmp,ret等等,这样花指令就去除了。去除后有一个很明显的off-by-one漏洞,只要我们的输入加密后的前八个字节为0就能多写一个字节的数据。
后面就是off-by-one的常规操作,只不过程序里的堆十分的乱,需要花时间去慢慢调试。拿到libc_base后通过environ打栈,构造rop。
exp:
1 | #coding=utf-8 |