有的题还是比较有意思的
(建议做了再看~)
传送门
Start
没什么好说的,网上随便找个shellcode
exp: https://paste.ubuntu.com/p/4FXP3Drhnd/
orw
也是写shellcode,不过限制了syscall只能open, read, write,提示已经好明显了,就是先open打开文件拿到文件的fd,然后用read和fd把文件内容先读到一块地方(如.bss),最后用write把这个地方的东西打印到stdout。有一点要注意的是read和write时的长度要大一点,不然程序会crash。
网上找不到现成的,只能自己写了。。。
exp: https://paste.ubuntu.com/p/kh69RDfZ98/
(exp上面有个test是本地测试是自己弄的文件夹,因为不想在自己电脑上的home目录里建文件夹)
不过这题比较有意思的一点是限制syscall的方式。在题目的orw_seccomp函数里调用了prctl函数。
![](https://static-img.0xffff.one/DPilepHAlIo8-xG94gZF_53eqK31EeQVWGI3Y7LKF0g/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQzMzgx/LTE2NjI2MS0yMDE5/LTEwLTI5LTE3LTU3/LTEzLnBuZw.jpg)
根据这里说的,prctl函数(好像是关于进程操作的函数?)通过第一个参数指定操作的类型,类型是PR_SET_SECCOMP的话会使线程进入“安全模式”(好像叫seccomp),在这种模式下syscall被限制。
![](https://static-img.0xffff.one/lH2ETuAs4D79TTKMpIha9tfKpBF59dp18dqh38QYZmw/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQzNDg5/LTM5Mjg0OC0yMDE5/LTEwLTI5LTE3LTU5/LTUwLnBuZw.jpg)
calc
这题搞了一个简单而且还会算错的计算器,除了静态编译比较麻烦之外,洞其实还算好找的(吧)。calc的做法基本就是通过get_expr读算式(放到buffer里),然后通过parse_expr把运算结果(和一些中间结果)放到一个pool的结构(第一个int是记录长度,后面100个int是放数)里面。parse_expr的做法大概是遍历buffer,把数字存pool,遇到运算符的话就用一个叫eval的函数做运算。(中间有些优先级的处理没详细看 - -)
漏洞就在eval里,里面有个数组越界(underflow)的漏洞,控制一下buf的length到1的话就会变成"pool->buf[-1] += pool->buf[0]",pool->buf[-1]就是length,在这个时候是0,所以可通过 pool->buf[0]控制pool的length。
![](https://static-img.0xffff.one/7yMPvbic7nmKwC8W74fjJwg3AkaUoaOXVK1SeLRxlwE/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ0MzI5/LTEyMTE0OS0yMDE5/LTEwLTI5LTE4LTE0/LTQ2LnBuZw.jpg)
然后在parse_expr里面有有个这样的东西,count因为length被控制了所以就被控制了,count被控制了就可以往任意的地址写入num。
![](https://static-img.0xffff.one/BikffW9S4O91LROVdz32FG6fIc29oLdgq-TOqAs771k/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ0NDc5/LTk1MDA4Ni0yMDE5/LTEwLTI5LTE4LTE5/LTIwLnBuZw.jpg)
问题是num怎么被控制,后来发现算式如果第一个字符是运算符的话会有点问题,最终就构造了这样"+bias+value"的一个表达式,可以往(base_aderess+bias)的地址上写(value)这个值。最后写个rop就搞定了。不过还有一点要注意的是不能写0,但可以通过写1再dec来绕过。
![](https://static-img.0xffff.one/sgNMs78luYtgyfaY8J5bBkj3gSJO3erVIyEOsNI1Lxc/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ0ODc2/LTk2NjczOC0yMDE5/LTEwLTI5LTE4LTI3/LTQzLnBuZw.jpg)
exp: https://paste.ubuntu.com/p/pPdyCkgsXP/
3x17
题目的3x17是什么意思我到现在都搞不明白 - -。静态编译加stripped看着有点难受,把题逆完之后知道只有一个main函数,可以输入一个地址,然后往这个地址写东西(3 bytes),还有个奇怪的count。
![](https://static-img.0xffff.one/bNAQ9gMzFQrU2hhcfG9PqmJzuUeTlh49wEquI9PBIwA/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ1MzM4/LTc1NTUzMS0yMDE5/LTEwLTI5LTE4LTMy/LTQ0LnBuZw.jpg)
没有信息泄漏,不知道要写什么- -,后来在@Potatso 那里听说了_fini_array这个东西,就是程序在结束前会经过一个叫_libc_csu_fini的函数,这个函数会取_fini_array里面的函数(函数指针)出来执行,然后才让程序结束。
![](https://static-img.0xffff.one/hxPgUIlj5CVAKHFPGe26LF1_fKWiI0gVvzhiTFkvdfs/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ1NTk5/LTU2NTIzOC0yMDE5/LTEwLTI5LTE4LTM3/LTM3LnBuZw.jpg)
所以通过修改_fini_array的函数就可以在main函数后多执行两个函数(因为有个v0限制),本来想如果写个main函数在_fini_array的话就可以返回main实现无数次写了,但main里面有个count限制。后来发现可以第一个函数写main,第二个函数写_libc_csu_fini的话就可以无数次进入main函数,让count溢出变回1。(有一点要注意的是_fini_array里面的函数是逆序执行的)。
所以就实现了无数次的任意位置写,于是就想写个rop,但不知道写哪里。后来发现在执行_fini_array前的rbp就是指向_fini_array(0x4b40f0)的,所以就往这里写了个"leave",然后栈被移到0x4b40e8,在0x4b40e8放个"add rsp,0x18"的rop的话,0x4b4100后面就可以放个rop chain了。后来这么做是成功了,最后的rop:
![](https://static-img.0xffff.one/3-hjHFGOdLoxh6_ILCcHFK9IulWo1VOafVJ5MN5H0aM/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMC0y/OS8xNTcyMzQ2MjMw/LTg4MjkxNS0yMDE5/LTEwLTI5LTE4LTUw/LTEzLnBuZw.jpg)
exp: https://paste.ubuntu.com/p/z43S4C7KnK/
dubblesort
实际上就是个bubble sort,输入排序数字的数量,然后输入数字,输出排序结果。
![](https://static-img.0xffff.one/U5zXijS_hqgrxZM5EJJsArLxL8OxJh7FA7y8McBl56U/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMjc2NDIy/LTg4OTE0NC0yMDE5/LTExLTA5LTEzLTA5/LTMzLnBuZw.jpg)
开局输个名字送libc_base:
![](https://static-img.0xffff.one/s-sZkqutUr4GUSkmjfIniH0W0xq8mGU3Mw7UOk00RJ4/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMjc2NDgw/LTMxMTQ3Mi0yMDE5/LTExLTA5LTEzLTEw/LTMxLnBuZw.jpg)
这题利用技巧在于输入非数字的话会输入不成功,结果是用栈上的东西来排序。加上有栈溢出漏洞,就可以构造特定的ROP了。(由于有排序,会对ROP有一定要求,但是libc里面有大量的ROP,构造还是不太难的)
![](https://static-img.0xffff.one/KaW8dJHakdKJmIFqLaNyUsAO5e_fSUcH6seNFo1NPtw/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMjc2NzQ4/LTkzODMzMy0yMDE5/LTExLTA5LTEzLTE1/LTQyLnBuZw.jpg)
exp: https://paste.ubuntu.com/p/FrRw5c8R2y/
hacknote
堆的题目,思路是fastbin attack。每一条note存放了一个输出函数的指针和数据的指针(空间在栈上分配),那个输出的函数怎么看都是故意放上去的,所以很容易可以想到通过fastbin attack改变这个函数指针到system函数然后getshell。
![](https://static-img.0xffff.one/f7sNv44ft-_0p70YnisSL_4Lx6nTwj0HHszlwp-P4pA/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMjk5Nzky/LTg1NzM5Mi0yMDE5/LTExLTA5LTE5LTQx/LTMxLnBuZw.jpg)
做fastbin attack时要覆盖note块的话就数据块大小就需要跟note块大小一样(0x10,大概是12bytes以下吧),但这样会因为偶数的关系做不了fastbin attack,所以有一个trick就是中间分配一个数据块大于0x10的note隔开变成奇数(这样讲有点迷,看wp吧- -)。heap_base可以容易泄漏出来,libc_base的话可以通过把数据的指针写成.got表地址,然后打印note泄漏。
最后system函数调用时栈上的情况如下,所以第一个参数会是输出函数被覆盖成system函数的那个块的地址,这里有一个trick是可以通过构造一个“或”的表达式来绕过,如“xxxx||/bin/sh”,但这里长度只够输“sh”,虽然最后是成功了,但好像还是有点悬。
![](https://static-img.0xffff.one/XSeCfZt9n3eKpvgU-E71Eh-XVn2s6RmcY66xoEr_9Kk/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAwODIy/LTkzNzAwMi0yMDE5/LTExLTA5LTE5LTUz/LTQ2LnBuZw.jpg)
![](https://static-img.0xffff.one/T_GFAUXcyuj9wjhJtlbb5QqFHvn8MdrtubVjBDt-cq0/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAwODQ3/LTY1Nzg5NS0yMDE5/LTExLTA5LTIwLTAw/LTI5LnBuZw.jpg)
exp: https://paste.ubuntu.com/p/BXZWmmwRQT/
Silver Bullet
这题好像是一个游戏,什么鬼升级子弹然后把狼人射死?
![](https://static-img.0xffff.one/cnyj_35FH6ouT0a9QEdbbO7BjCqjkOiJZswC8b-NLDw/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAxMTk5/LTU0NDkyNS0yMDE5/LTExLTA5LTIwLTA1/LTE5LnBuZw.jpg)
漏洞在于power_up里的strncat函数,根据文档介绍,strncat在复制字符串后会在末尾补\x00,所以就形成了一个字节的溢出。而这里溢出的刚好把power覆盖了(power控制可以继续输入多少的description),然后就可造成更多的溢出。最后写个ROP就搞定了。(因为这里溢出的还是比较少的,所以可以先做个read来写入较长的ROP,然后leave ret到写入的ROP那里。另外,把power覆盖成很大的值可实现一键秒杀)
![](https://static-img.0xffff.one/dBMYjg5iJG8Xi4t5xMzbo2KySaBGw3PVuQAsmEjQjf4/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAxNDUz/LTQ5Nzg3My0yMDE5/LTExLTA5LTIwLTA5/LTI3LnBuZw.jpg)
exp: https://paste.ubuntu.com/p/hjCJK9JqWs/
applestore
这题模拟了一个applestore(大雾),买满7174刀可以用1刀的价格换一台iphone8(而且还是强制的- -)。购物车是个双向链表,结构大概是:
![](https://static-img.0xffff.one/BqpvROtefTMgnrwUeIj50wrqgoRKLrHdkzL-qeQvfOk/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAyMjUz/LTI3Nzc3OC0yMDE5/LTExLTA5LTIwLTIz/LTU3LnBuZw.jpg)
漏洞点还挺明显的,就在checkout时的这个一刀换购里,送的iphone8是在栈里的,所以相当于有了一个可操控(但有些难用)的栈指针。当时找了很久到找不到在哪可以利用,后来瞄了一下别人的wp后才知道在delete的read可以覆盖这个iphone8的位置。
![](https://static-img.0xffff.one/J1NrzuTdp9ZnZl0wevSgXOTunj83RQxB4u7r7KIYTkc/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMS0w/OS8xNTczMzAyOTU0/LTU3OTM4NS0yMDE5/LTExLTA5LTIwLTE4/LTQwLnBuZw.jpg)
所以构造一下假的chunk,通过覆盖name的位置为想读信息的地址可以实现任意读(next和last写成0,不然会crash),本来打算栈地址可以通过tls得到的,但发现服务器的tls地址跟我本地的不一样...,后来才知道libc里面是放着environ的,通过environ可以得到栈的地址。
写的话可以通过delete里的unlink来写,学到了新的方法——通过unlink覆盖ebp。
exp: https://paste.ubuntu.com/p/Dz6DF6ZTk8/
Tcache Tear
给的库是2.27的,根据题目名知道应该是tcache方面的漏洞(堆题),题目逻辑挺简单的就不贴IDA了。
首先贴一下Tcache有关的教程(我认为比较全的)。
这题有个比较难搞的是"Info"那里只能输出开始时输入的"Name"位置的0x20个字符(.bss),这里可以先在填Name时制造一个0x420的fake_chunk(分配到unsorted bin的大小),通过两次free把tcache的fd指到name里,然后free掉刚制造的fake_chunk,因为要解决unlink时错误的问题,还要把free的chunk的邻近的chunk也假造一下(具体看exp)
另外制造假chunk时还要用到一个漏洞,在malloc里的输入大小是"size - 16",存在一个整数溢出,当size小于16时就可以输入任意大小了。free unsorted bin的chunk后可以通过Info泄漏libc的base。
![](https://static-img.0xffff.one/hDvrBCmVtCmvDK504ymEQ3tmMs76qZ-9TuTDtfrEvd4/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0w/OS8xNTc1ODgwMzMz/LTU4OTM1Mi0yMDE5/LTEyLTA5LTE2LTMw/LTUxLnBuZw.jpg)
然后本来是打算覆写atoll的got地址的,后来卡了很久才发现是"Full RELRO" - -,那就只能写malloc_hook或者free_hook了,malloc_hook的话因为one gadget条件不行就弃用了,free_hook刚好有一个符合的,然后就getshell了。
![](https://static-img.0xffff.one/XWSveyYnldeqZCySxq8WAI0R62Ou2WB4DcjLusZYXm4/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0w/OS8xNTc1ODgwNTk3/LTMzMjQwLTIwMTkt/MTItMDktMTYtMTgt/MTEucG5n.jpg)
![](https://static-img.0xffff.one/AxSGUVFeZcKzueZg-EYXqcSO6Rssrxyz6MCbmzKp5gs/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0w/OS8xNTc1ODgwNjA3/LTgwMDExNy0yMDE5/LTEyLTA5LTE2LTE1/LTI1LnBuZw.jpg)
exp: https://paste.ubuntu.com/p/2GfwrZkqQw/
seethefile
我觉得他已经暗示了这是道IO_file的题目了... 可以进行几种文件操作(打开、读取、写到屏幕、关闭),最后的关闭程序时有个溢出漏洞,可以通过name的溢出覆盖文件的fp,伪造假的_IO_FILE_plus结构就可getshell (说是这么简单。。。),这题有几个放水的地方,一个是PIE是关闭的,所以bss段的地址已知,写在bss上的东西就可以直接用了;一个是程序把几个变量都放在了bss段,这样才会有上面说的溢出(好像那两东西不放bss也不行hhhh);一个是可以读处flag(做了某些过滤)以外的文件,读一下/proc/self/maps就可以拿到libc的基址。(反正就是道挺好的IO_file的练手题了)
IO_file网上也有很多教程,这里水过一下就算了(懒 - -),首先上面说了可以伪造一个_IO_FILE_plus结构体,伪造结构体主要造一下_IO_FILE_plus.vtable就行,vtable是一个(const struct _IO_jump_t)类型的结构体(据说跟C++的重载函数差不多),里面记录了一些文件操作相关的函数位址,通过覆盖/伪造里面的函数地址(比如改成system)就可以达到控制程序流的目的。然后据说vtable里面的函数的第一个参数是这个_IO_FILE本身【比如close的定义:JUMP_FIELD(_IO_close_t, __close);】,所以如果改成system的话把'/bin/sh\x00'放在伪造的结构体的头部就好了。
另外,在实践中发现在fclose中执行到【_IO_acquire_lock (fp);】这个地方程序会死掉,后来参考了yuuoniy的wp才知道要造一个假的lock (wtcl- -),然后因为lock的结构跟IO_file的差不多所以可以直接用伪造的结构体放在lock的位置(具体看ta的wp 8)。
最终造出来的_IO_FILE_plus
![](https://static-img.0xffff.one/U2PhaXj2QNzcZtPO2HqReR6JACcaTvH0MmdCQGyxM2U/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0x/Ni8xNTc2NDk1OTc2/LTk0OTAzMy0yMDE5/LTEyLTE2LTE5LTMw/LTU4LnBuZw.jpg)
最终造出来的_IO_jump_t (vtable)
![](https://static-img.0xffff.one/ccurayOxckr5VKuM3IpGRBwAGjX_yoUUMqfmUnrNyyA/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0x/Ni8xNTc2NDk1OTg2/LTg5MzY5LTIwMTkt/MTItMTYtMTktMzEt/MjAucG5n.jpg)
exp: https://paste.ubuntu.com/p/Y4nGyvx7kR/
getshell后好像因为权限问题,要通过里面提供的get_flag程序来拿flag,反正源码都给了,这里就不多说了 (逃
Death Note
Death Note好像是一部挺老的漫画了,好像是把谁的名字写到笔记上去那个人就会死掉(与题目无关hhhh)
题目已经提示了要写shellcode了,checksec发现有rwx的区域,程序的.bss和堆上都是rwx的(最终的选择是写在堆上,因为输入的name放在了堆)。代码中还有个is_printable函数(对,题目很好心的给了符号),提示要alpha_numeric的shellcode,而且长度限制80bytes以下,网上找了一堆不是长度太长就是不能用的,最后还是自己写了(顺便当练习。
写shellcode前首先要找到可以执行shellcode的地方,程序中几个操作里都有一个id的检查,说id不能超过10,但是没有检查下限,所以输入负数的话会触发underflow漏洞。
![](https://static-img.0xffff.one/4EmdqL9FmJ8a-Uucx5hbpjoIOVUZWSgQ9JiGqo3ubaE/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAxOS0xMi0y/My8xNTc3MTA3OTgy/LTU5NTUyNi00ODkx/MDQxNi04NTgxLTQz/MDYtYTgwNy0wN2M5/N2EyMjFiYTUucG5n.jpg)
输入的id是作为note的下标,note有恰好在.bss上,所以选择适当的id就可以覆盖.got表的值,exp选择了free,因为free的参数就是某个堆块的地址,这样方便写shellcode。
接下来就是shellcode部分,alpha_numberic的shellcode写法在winesap的视频中有详细的说明,可以参照一下。这里的大概就是:首先call free时eax是堆上一个块,加0x10后就是shellcode的基址,exp存到了edi中;通过某些神奇的操作可以得到0,方法不唯一,exp存到了esi寄存器中;通过‘xor BYTE PTR [edi+0x37], al’指令可以修改shellcode的值,从而构造出'int 80'指令;通过xor的操作可以构造出不是alpha_numberic的参数,其中通过xor 0xff可以构造比较大的数,因为0xff可以通过'dec esi'即零减一得到,比较方便。然后最终构造出来的shellcode也就56bytes - -
exp: https://paste.ubuntu.com/p/WMDWnSSbPt/
Starbound
startbound好像是一个游戏,题目程序就是个命令行版的(感觉就是个2D版的MC),因为是个真正可以玩的游戏所以要逆向的工作量会有点大,当时花了一段时间逆了大部分后才发现漏洞就在main函数里。
![](https://static-img.0xffff.one/61Itx0h9BeX8cXtbOPXfijFNHhuZ1EldRGcKDpl6iE0/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAyMC0wMS0w/NS8xNTc4MjM0MDAz/LTMzMTY2OS0xZjNj/NmE3NC0yYzIxLTQz/NTQtYjcwNi1jYjJj/YjhiMjE5ZTIucG5n.jpg)
首先要说一下程序的菜单功能,图中的menu_now(名字是我乱取的)其实是个全局的函数指针,会有别的函数设置这个变量来显示不同的菜单;同样menu_option_now(名字也是我乱取的)是当前菜单对应的几个功能的函数指针,这里的index其实有个挺明显(但不知道为什么我一开始没发现)的溢出,输入10以上或负数可以执行别的函数,因为是bss上的,所以负数的话刚好可以执行玩家name的地方,name可以在setting里设置,可设置成ROP或程序自带的函数。
本来这种情况最简单的做法就是one_gadget了,但题目没有给libc,于是想到另一个方法(解法应该不唯一),先执行一个"add esp, 0x1c"的ROP打乱stack,再使nptr造成溢出用常规的栈溢出方法来做。其实写ROP时也需要用到libc的,可以用DynELF泄露,但更简单的方法是查libc_database。
exp: https://paste.ubuntu.com/p/kFgjY66594/
BabyStack
实际上就是个栈溢出,不过覆盖ret value要绕过几个限制。
首先是登录,而且有一个magic copy的功能需要先登录成功才能进入。登录的话会把输入的密码和一串16bytes的随机密钥进行对比,在对比时用了strncmp函数,所以如果输回车的话可以直接登录成功。利用一下strncmp还可以进行密钥的爆破(程序的初始化中alarm了半个小时应该也暗示了要爆破的了),要爆破密钥是因为main退出时会检查密钥有没被更换,有的话会触发__stack_chk_fail,跟canary一样。除了密钥能爆破外,爆破一下16bytes密钥后面的内容可以拿到程序基址,方便写ROP。
然后是magic copy,里面用到了个不怎么安全的strcpy函数,而它的dest是main函数的一个buffer(局部变量,64bytes,栈上),src是这个函数里的局部变量而且有128bytes,所以可以造成main函数的栈溢出。但magic copy函数里只能输入63bytes,所以ROP其实是登录的函数里输的,利用登录函数返回后栈没有被清掉。
然后就是普通的栈溢出做法了。因为main只能溢出3句ROP,23个bytes(而且我自己操作时不知道为什么会被零截断),所以可以利用一下rdi保留的栈地址和程序自带的输入函数(大概是像readn那样的那个)来输入更长的ROP。
exp: https://paste.ubuntu.com/p/62zfW6n2WM/
(这应该是我跑得最久的exp了 - -)
Spirited Away
一道看似是堆题的栈溢出题(而且canary也是关的),是一个写影评的程序(?),只有一个函数,漏洞就是在survey函数中输出已经有多少条评论时用到了sprintf函数,由于buf大小是56bytes,如果cnt是三位数的话会造成buf溢出了一位,刚好会把'n'覆盖到name和comment读入时设定的大小的那个变量(叫它nclen吧- -),所以name和comment的输入大小从60变成了110,造成了栈上的溢出。
![](https://static-img.0xffff.one/mOgqeZ6NJcyGjNItibXBD7hITnZHoNhH2Djr8umxTOo/q:90/w:800/rt:fit/aHR0cHM6Ly9zdGF0/aWMuMHhmZmZmLm9u/ZS9hc3NldHMvZmls/ZXMvMjAyMC0wMS0x/MS8xNTc4NzEwOTEw/LTEyMDcxMy0xMDZi/MTVlMy1jNDRkLTQ1/ODYtODhjNC02Njdi/ZWM0ZDdjMGQucG5n.jpg)
另外,刚开始时由于comment和reason(就是放评论和原因的那两个变量)在栈上,而且输入的时候末尾没有设置0,巧妙利用一下可以泄露栈地址和libc基址。在comment溢出后,可以覆盖到name的指针前泄露heap基址(虽然后来发现这个没什么用- -)。
由于reason的读入大小是由另一个变量控制的,所以不能直接控制reason溢出(reason是最后一个,溢出才能覆盖返回地址),因为栈地址可以泄露出来,所以想到可以在reason里造一个假堆块,然后覆盖name指针到这个假块,最后free-malloc就把name变到reason里了,由于name的读入大小也改了,所以读入name就可以造成stack overflow。给了库其实可以直接one_gadget的,但远程泄露libc_base时被坑了一下,栈上选了几个才能用,还以为是one_gadget出问题了,才搞了普通ROP(- -)。
exp: https://paste.ubuntu.com/p/3KfTFWFpMb/
Secret Garden
堆题。在remove那里free掉name后没有置0,所以可以造成double free,感觉做法应该不唯一,我用的是fastbin attack的做法。
首先fastbin attack可以泄露堆地址,再来一个unsortedbin或smallbin的可以泄露libc基址,用fastbin attack可以改掉堆上某个flower的name指针,造成任意位置的读,可以读libc上environ的stack地址,然后读栈上的内容就可以读到各种东西了。最后在raise函数的栈里找个合适的地方,malloc一个fastbin大小的块就可以造成栈溢出覆盖返回地址,然后one_gadget get shell。
还有,由于用的时libc2.23,找大小时可以错位,由于64位程序的栈地址和libc地址开头都是0x7f,所以选择大小时0x70的块最合适,还有chunk的大小是int类型的,所以看4个bytes就好了,实际操作中发现想写的地方最后一个byte是随机的,需要一点运气才能跑出来,反正就是不知道怎么的就搞出来了 - -
exp: https://paste.ubuntu.com/p/DND5mn586W/
才搞了十五题,还是太菜了啊
流下没技术的眼泪.jpg