往期看个人主页
checksec
IDA看一下程序结构,一开始看到菜单题还以为是堆题,后来才发现并不是.
main函数没啥,一个alarm函数,可以选择patch掉.初始化了一块内存,这块内存其实就是用来保存双向链表的地方.menu函数打印菜单,接着看handler()函数:
读取输入然后执行相应的函数,这里我们先看看读取输入的my_read函数.
调用read函数读取指定数量的字节,然后末尾用0来截断.
list函数只是打印一些商品信息
如果我们购买一个商品,add函数会调用对应的create函数来生成一个结构体保存商品信息,接着调用insert函数处理结构体,我们先看看create函数:
这个题目采用了双向链表的结构来保存我们的商品信息,具体结构如下:
接着跟进insert函数:
myCart这块内存就是在main函数被初始化的内存,这里用来保存我们的商品数据结构.myCart[0]为链表头,myCart[2]开始才是我们的第一个商品.
delete函数用来将双向链表从链中拆除,操作类似于unlink,即修改前后链表的fd和bk,但是这个并没有任何防护手段.
cart函数打印所有商品信息,可以考虑信息泄露.
checkout函数乍一看好像没什么卵用,并没有后门函数什么的,但是这有一个小彩蛋,如果总价格是7174的话,会附赠你一部一美元的手机.不妨回顾一下create函数,正常add函数添加的商品结构体都是malloc出来的,也就是在堆上,而这个一美元的手机却是保存在栈上的,这个函数结束之后栈地址依然留在我们的双向链表之中,这有点类似于堆题目里面的Use After Free.
利用方法
首先,我们要触发菜单,才能创造出这个堆里面的栈地址,但这个四元一次方程运算量还是有那么一点的,这里我们借助python来计算一下:
也就是说,购买1号商品16个,4号商品10个正好就是7174块钱.
到这可能就卡壳了,虽然我们把一个局部变量的地址放到了堆里面,但是我们并没有办法来控制它的内容,哪怕能修改,那么栈地址在函数返回之后不久没用了么,还怎么操作?这里我们先看一看handler函数的汇编实现:
只截了关键部分,提醒一下,我们将栈地址放到堆上的时候,局部变量保存在ebp-22的地址.通过汇编代码可以看到,每个函数在结束调用之后,并没有对栈进行操作,所以这几个函数用的栈都是同一个地址的内存空间,也就是说,如果checkout函数之外的函数对ebp-22进行了操作,也就是对堆上的第27个结构进行了操作.回顾一下add,delete,cart三个函数,我们就可以发现我们是可以对ebp-22这块地址进行操作的,只不过我们需要用\x00来隔断函数需要的选项和我们构造的结构体.比如cart函数,我们可以先输入一个'y\x00'来进行下一步,接着在字符串后面跟上我们构造的结构体,这样cart就会打印任意地址的数据.
根据这个特点,我们泄露libc基地址,堆地址和栈地址.
光泄露了信息还不行,我们还需要进一步的操作才能getshell.程序还有一个漏洞,就是delete函数.这个函数的操作非常类似unlink,而且还没有任何防护,所以我们可以通过这个函数进行任意地址写,delete函数关键部分如下:
简化一下就成了
这样,我们只要把bk修改为目标地址-8,就可以将任意的fd写入目标地址了.这里我们可以劫持ebp,从而控制handler函数的栈.只要把栈迁移到GOT底部-22的位置,我们就可以用handler中的my_read函数改写GOT表中的值,从而getshell.
exp
博客有我的联系方式,欢迎大家来玩,地址:https://www.0x2l.cn
[0] % checksec applestore
[*] '/home/dylan/ctfs/pwnable_tw/applestore/applestore'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
int __cdecl main(int argc, const char **argv, const char **envp)
{
signal(14, timeout);
alarm(0x3Cu);
memset(&myCart, 0, 0x10u);
menu();
return handler();
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-8-12 20:24
被0x2l编辑
,原因: 修改