babyheap
题目
整体代码风格与2017 的babyheap基本一致,不过漏洞点变得隐蔽且利用变得困难了
功能: allocate, resize, show, delete 四个功能
指针仍然是通过一通操作被隐蔽了,所以不能打存放指针地址的主意
allocate时:1
2
3
4
5
6printf("Size: ");
size = read_int_8();
if ( size > 0 )
{
if ( size > 0x58 )
size = 0x58;
而resize时:1
2
3
4
5
6
7printf("Size: ");
LODWORD(v1) = read_int_8();
newsize = v1;
if ( (signed int)v1 > 0 )
{
v1 = *(_QWORD *)(24LL * idx + a1 + 8) + 1LL; // new size+1
if ( newsize <= v1 )
存在off-by-one漏洞,其他函数基本没有问题
分析
利用off-by-one,修改chunk size,释放chunk,再申请,将未释放的chunk被系统标记为释放,以此泄露libc
之后的思路便是复写malloc_hook ,但是这里需要注意一点:
申请的chunk最终大小最大为0x60,也就是即使千辛万苦将某个chunk的size改为0x70并将其置入链表,也没有任何用,尝试了好久之前却没动脑子,哭了;
所以需要换个思路,可以使用之前三级头招新时出题人的思路:将size放入fastbinY中,然后将chunk分配到main_arena处,修改topchunk为malloc_hook之上,然后再次分配chunk复写malloc_hook即可
这里就没有必要想free_hook了,毕竟free_hook周围全都是’\x00’,topchunk地址改过去,分配时size检查就没法过了
这种不能查看指针的,需要搞清楚自己申请的释放的都是哪些chunk,不然很容易乱。
exp
1 | from pwn import * |
babystack
题目
ret2_resolve_runtime , 直接给了溢出,但是无处下手,实际就是ret2resolve 的标准情况,这里也学习一下
https://www.siriuswhiter.tk/2019/03/20/introduction-to-pwn1-4-ret2dl_runtime_resolve/
1 | ssize_t read__() |
分析
溢出在bss段伪造resolve结构体,覆盖write为system调用’/bin/sh’ getshell
exp
1 | import sys |
heapstorm2
题目
刚开始进行了一通设置,这里需要关注一下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
33signed __int64 setup()
{
signed int i; // [rsp+8h] [rbp-18h]
int fd; // [rsp+Ch] [rbp-14h]
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
alarm(0x3Cu);
puts(
" __ __ _____________ __ __ ___ ____\n"
" / //_// ____/ ____/ | / / / / / | / __ )\n"
" / ,< / __/ / __/ / |/ / / / / /| | / __ |\n"
" / /| |/ /___/ /___/ /| / / /___/ ___ |/ /_/ /\n"
"/_/ |_/_____/_____/_/ |_/ /_____/_/ |_/_____/\n");
puts("===== HEAP STORM II =====");
if ( !mallopt(1, 0) ) // ban fastbin
exit(-1);
if ( mmap((void *)0x13370000, 0x1000uLL, 3, 34, -1, 0LL) != (void *)0x13370000 )
exit(-1);
fd = open("/dev/urandom", 0);
if ( fd < 0 )
exit(-1);
if ( read(fd, (void *)0x13370800, 0x18uLL) != 24 )
exit(-1);
close(fd);
MEMORY[0x13370818] = MEMORY[0x13370810];
for ( i = 0; i <= 15; ++i )
{
*(_QWORD *)(16 * (i + 2LL) + 0x13370800) = set_ptr((_QWORD *)0x13370800, 0LL);
*(_QWORD *)(16 * (i + 2LL) + 0x13370808) = set_size(0x13370800LL, 0LL);
}
return 0x13370800LL;
}
可以注意到
- 使用mallopt取消了fastbin,
- 在0x1337000 处 分配了0x1000大小的空间,可读可写,然后从0x13370800处 写入了0x20的随机数据,后面是存储结构,指针+size
可以在调试时看这块区域的情况:
1 | x/20gx 0x13370800 |
后面会将指针,size经过处理存储到这里,指针会与0x13370800处的随机数xor,size会与0x13370808处的随机数xor
主功能有
- alloc , 使用了calloc ,最多16个chunk且会将输入的chunk 的指针与size存储到上面说的地方 size要大于12小于0x01000;
update , 再输入的大小要不大于原size-12,输入数据后会将其最后12字节进行填充,很明显有off-by-null;
1
2
3
4
5
6
7
8
9
10
11
12printf("Size: ");
size_ = get_num();
if ( size_ <= 0 || size_ > (unsigned __int64)(set_size((__int64)a1, a1[idx + 2].size) - 12) )
return puts("Invalid Size");
printf("Content: ");
v2 = set_ptr(a1, a1[idx + 2LL].ptr);
get_str(v2, size_);
v3 = size_ + v2;
*(_QWORD *)v3 = 'ROTSPAEH';
*(_DWORD *)(v3 + 8) = 'II_M';
*(_BYTE *)(v3 + 12) = 0; // off-by-null
return printf("Chunk %d Updated\n", (unsigned int)idx);delete 检查存储的size,之后删除, 可以看到处理的比较干净;
1 | if ( idx < 0 || idx > 15 || !set_size((__int64)a1, a1[idx + 2].size) ) |
- view 会检查之前的0x10处的随机数,满足条件才可以view;
1
2if ( (a1[3] ^ a1[2]) != 0x13377331LL )
return puts("Permission denied");
分析
思路还是比较清晰的,首先利用off-by-null的漏洞通过chunk overlapping 将chunk分配到0x13370800处,修改原先存储的随机数,使得可以使用view函数,同时覆盖下面存储的指针与size,调用view泄露libc基址,再将指针指向malloc_hook处或free_hook处直接getshell;
主要的问题就是利用overlapping做到条件地址写,过程应该会较为复杂;
exp
1 |