最近打了N1CTF和强网杯两场比赛,应该都算是国内大型比赛了.题目的难度都很高,质量上乘,菜的我泣不成声….
以下是当时我做出来的一些题目.
N1CTF
pwn - beeper
这题其实就两个点
第一个点是他通过时间种子去随机出一个mmap page出来,因此我们可以同步时间,从而得到远程mmap的地址
其次是我们的操作需要先login,而login需要执行他的vmcode得到密文,很麻烦.
但是这里有一个明显的栈溢出,能够覆盖他的vmcode,从而对我们的输入不进行变换
过了验证以后发现,我们还有relogin的功能,由于vmfunc在操作内存的时候,是根据reg1即pwd的地址来寻址的,而reg1我们也能够覆盖到,因此我们可以把reg1改到mmap page上,而page上有shellcode,且page有write权限,因此我们去修改它的shellcode为我们getshell的shellcode.这里就必须用到他的那一套解释器了.
大概这样
1 | y='686F6420018134240101010148B8757920612070686F5048B8616E206E6F7420625048B865722C796F7520635048B842'.decode('hex')#write |
利用他的buy
功能,去执行shellcode,getshell
1 | #coding=utf8 |
pwn - null
首先这题是一道多线程题,程序的主体都在多线程中.
程序提供了system函数,还在bss上放了一个函数指针,因此我们只要能够把heap改到bss上就利用成功了.
漏洞点很好发现,在read_n
中
差不多能够溢出一个size的大小.
很关键的一点是这题没有free
本来设想过利用house of force,但这题是64位的,read_int
读取不了那么大的大小.
后来又想用house of orange,但是调试后发现,多线程的sys_malloc
逻辑和单线程的完全不同,你去修改topchunk大小他根本不理你,人家从arena中取.
后来在测试中发现,当heap大小达到一定程度时,系统会mmap,而新mmap出的page比原heap低,而线程的arena是在heap段的段首的.
大小合适的时候,有一个chunk会刚好分配在arena的上面,且两个段间没有间隔
这个时候我们就可以去改arena的fastbinsY,构造一个假的chunk出来,然后 house of sprit
1 | #coding=utf8 |
re - baby-unity3d
这题真的是强行学习了一波orz,从来没搞过unity
首先网上随便找日安卓unity的教程,说有个Assembly-CSharp.dll
可以直接看反编译代码,我找来找去没有这个文件…
然后又看到有说有一种Il2Cpp
的方法,让cs代码变成c代码….然后说代码在libil2cpp.so
,符号和字符串在global-metadata.dat
里,我又按着教程做,但是在从global-metadata.dat
提取信息的时候又报错了,我看了一下工具的代码,在判断文件魔数的时候就已经炸了,这个时候我拿hex工具看了一下global-metadata.dat
,感觉太tm丑了,肯定是被加密了.
通过大佬的帮助,用源码先一点点还原,找到这里
上面连global-metadata.dat
这个字符串都加密了
再通过字符串,以及fopen之类的跟到这里
这段代码将global-metadata.dat
解密
写出解密脚本
1 | key=[0xF83DA249, 0x15D12772, 0x40C50697, 0x984E2B6B, 0x14EC5FF8, |
再用Il2CppDumper去dump信息就成功了.
通过符号找到主函数
发现里面其实是一个aes,具体代码就不贴了
1 | # encoding:utf-8 |
这里还是要膜一下p姐姐,太强了.orz
强网杯
pwn - GameBox
ummmm,fmt
但是信息是存在heap上的,考虑用ebp chain去改freehook好了.不是什么新鲜套路了,不想多说
考虑到限制了rank数量,测试了一下,删除rank后构造的ebp chain没有被破坏 开心
1 | #coding=utf8 |
pwn - opm
两个明显的overflow,但是除了got表可写,其他保护全开
由于是gets,所以会末尾写0,但是,这题有个特点,程序运行时在heap上会有个很大的chunk,以至于你只覆盖struct指针的最低位,第二位写0,但没有关系,第二位写0后这个指针依然在heap上,因为那个大chunk,因此我们有了一个相对固定的地址,让chunk相互重叠,最后leak出code段地址,libc基址,通过改heap上的函数指针到onegadget从而getshell
1 | #coding=utf8 |
pwn - raise_pig
eat的时候没有考虑inuse,所以可以fastbin dup,再通过打印函数能轻易leak出heap和libc.
之后在heap上伪造好onegadget的vtable,利用house of sprit 就能去改stdout的vtable,getshell.
1 | #coding=utf8 |
pwn - silent
非常传统的house of sprit改got
1 | #coding=utf8 |
pwn - silent2
和silent唯一不同就是malloc的时候加了大小限制
可以用heap overlap来做unlink
先分配3个0x80,free掉前两个,然后malloc一个0x110就能overlap
然后通过unlink去改chunklist指针,再通过构造的任意指针去改got
1 | #coding=utf8 |
pwn - xx_game
我和大佬一起做的这题,这题的前半部分是自动化逆向,是大佬做的,我就说说后半部分pwn的部分吧
先是限制了一堆的syscall调用,execve是不存在的了.只能用open,read,write去打印flag了
其中这里ida参数分析错了,seccomp_rule_add_exact
的参数不止这些,其中第二句
后面这个是一个结构体,结构我不是很清楚,但是通过那个602100h大概是猜到write的地址只能在602100h了.
不考虑去用syscall,太鸡儿麻烦了,我用linkmap来完成.
具体的我在这篇中已经说的很清楚了.
1 | #coding=utf8 |
re - hide
查字符串显示upx,但是完全脱不掉(失望
用gdb调试发现还用ptrace trace_me加了反调,
我当时怕他ptrace是用来smc的,没敢直接改(当然后来发现他真的只是反调试一下
gdb下catch syscall ptrace
断下后dump memory
,从而得到脱壳后的文件
程序主体如下
func1和func2都比较简单,直接逆着写就好了
1 | from struct import * |