unctf2019 pwn部分题解
babyheap
easy,不讲了
1 | #!/usr/bin/env python2 |
babyrop
简单
1 | #!/usr/bin/env python2 |
soeasypwn
1 | #!/usr/bin/env python2 |
这里有个小细节,r()两次,
Box
漏洞点
数组index是可以输入负数的,就是不会利用,后面看了萝卜师傅的wp才知道可以直接改IO_stdout
我是傻逼!这都想不到
然后有个double free,新点记录下
- size == 0 ,这个时候等同于free
- realloc_ptr == 0 && size > 0 , 这个时候等同于malloc
- malloc_usable_size(realloc_ptr) >= size, 这个时候等同于edit
- malloc_usable_size(realloc_ptr) < szie, 这个时候才是malloc一块更大的内存,将原来的内容复制过去,再将原来的chunk给free掉
所以利用这个点第一次可以用普通的
- free(ptr)
- realloc(ptr,0)
这就是double free
漏洞利用
- 利用IO_stdout泄露libc地址
- 利用double free改realloc为one_gadget
准备工作
1 |
|
泄露libc地址
1 | payload = p64(0xfbad1800)+ p64(0)*3 + '\x00' |
这里就是IO_FILE攻击,不清楚的可以自己学下,这里我学到个新操作。。我调试的时候要生要死的,没想到抛出异常,多亏大佬博客了,还有自己复现的时候用ida把前面一段打开文件那部分patch掉吧,不然感觉效率太慢了。。。
double free
这里还有个uaf
1 | new(0, 0x68) |
这里常规操作,接下来的才是重头戏
one_gadget失败
1 | payload = "a"*0xb + p64(0xAAAAAAAA) |
这里你用payload = “a”*0xb + p64(one_gadget)你会发觉成功不了,
而malloc_hook和realloc_hook通常是一起的,所以我们可以利用这个组合达到一个目的,调整栈过后在one_gadget,具体如何往下看
1 | 0x45216 execve("/bin/sh", rsp+0x30, environ) |
原因就是环境对不上,接下来讲下如何让环境对的上这个
首先将realloc_hook覆盖成随便一个无法正常运行的地址
例如这种 payload = “a”*0xb + p64(0xAAAAAAAA)
成功断下查看现在栈环境,跟上面的差别是什么
1 | gdb-peda$ x/10gx $rsp+0x30-0x20 |
看,上述环境没有一个符合了,那么现在该如何做呢,发觉0x10可以,0x40可以,还有0x60可以,
栈是往低地址生长的
也就是说我们只要将rsp提高0x10,就变成rsp+0x10+0x30了就可以了
调用一个函数过后通常来说栈是平衡的
只要我们稍微改动一下我们调用的位置就行了,比如函数头地址+4,从这里开始执行,假设绕过一个push,这样的话,就相当于pop多一个,pop多一个的话,esp会提高一个寄存器大小的位置,也就是rsp=rsp+0x8
利用这个特性,我们也就是说可以调整栈,让其指定位置为0
我们调整第2个one_gadget吧,让其提高0x10就可以了,怎么让其提高呢,我们可以利用malloc这个函数,因为他会调用malloc_hook,组合调用
我就选了malloc
1 | gdb-peda$ p __libc_malloc |
1 | gdb-peda$ disassemble 0x7f4137102130 |
看函数头,我们发觉有两个push,一个sub rsp,0x8,
计算下我们有0x18可控,所以我们提高0x10的话,就从+2开始就行了,
所以payload = “a”*0xb + p64(malloc+0x2) + p64(libc.address + one_gadget[1])
前面的a填充过后就是realloc_hook,覆盖成malloc+0x2,所以这样让栈提高0x10,接下来是malloc函数,
具体个执行过程呢就是realloc_hook被覆盖成malloc+2了,malloc_hook被覆盖成one_gadget了,
所以先执行的是malloc+2,然后执行malloc_hook
1 | 0x7f1e223d2132 <malloc+2> sub rsp, 8 |
1 | void * |
malloc调用前会查看mallo_hook是否存在,存在就调用malloc_hook
1 | 0x00007f1e223d2130 <+0>: push rbp |
这里就是查看malloc_hook部分,若有调到+360处
看,成功迁移位置
exp
1 | #!/usr/bin/env python2 |
driver
开头没想到怎么利用,他利用了top_chunk合并将unsortbin合并了,以前只是防止合并,利用合并也是个知识盲点
1 | /* |
House Of Spirit¶
介绍
House of Spirit 是 the Malloc Maleficarum 中的一种技术。
该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。
要想构造 fastbin fake chunk,并且将其释放时,可以将其放入到对应的 fastbin 链表中,需要绕过一些必要的检测,即
fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理。
fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐。
fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem 。
fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况。
又补充了知识盲区,要将chunk放入fastbin,得过掉检查,其中一个便是下一个chunk的size检查,不能小于两倍的size_s,并且不能大于sysstem_mem
1 | /* |
还用到了unsortbin攻击,强,各种组合,多次house of sprit加unsortbin攻击
整体流程,unlink造成可以house of sprit攻击,然后通过多次house of sprit攻击,后门用unsortedbin攻击,最后getshell,流程复杂,原理简单
我本来想用chunk extends加fastbin attack,发觉他给了这么多功能好像没用上,应该不是这个攻击方法。。。然后就去看wp了,发觉他的wp攻击流程那些点全用上了,不过复杂起来了,赛后还看到另外师傅的wp,就是说这个有非预期,就是说这个有非预期,就是用chunk extends加fastbin attack
exp
1 | #!/usr/bin/env python2 |
orwpwn
1 | [+] libc.addressess-->0x7f4fabd43000 |
先放上成功结果
新点
mprotect改内存页权限
以前不知道这个姿势,知道后感觉挺骚的,挺强的一个方法
mprotect传入参数后,能让指定内存页变成可执行,所以利用方式
mprotect改内存页权限
- 知道一个内存页的地址
- 这个内存页内容可控
x64系统调用表
shellcode编写
这个我以前也很怕的,这次自己写了下好像也就那样嘛,不会很复杂的,通常来说,你只要自己调试下就行了
1 | from pwn import * |
可以通过context设置平台,context.arch=’amd64’
我这里没设置,所以就用每次加个amd64
打开flag文件部分,大概就是
- 设置rax=2
- rdi = filename
- rsi = 0 #标志只读方式
- rdx = 0 # mode其实可以不填,所以,不用设置也可以
- rax=2 # 系统中断号
- 调用syscall
后面几个流程差不多,看下中断表就行
自己写的话
1 | push 0x67616c66 |
然后我为了省事,直接用shellcraft.amd64.open(‘flag’)生成了
接下来读取函数,因为返回了fd,存在rax里,所以第一步要保存rax值到rdi里
1 | mov rdi,rax |
在接下来写函数
1 | mov edi,1 |
最后推荐篇文章
shellcode编写
感觉总结得挺好的
SROP
这部分可以去看下ctf-wiki吧
SROP攻击
漏洞利用过程
准备部分
1 | def choice(idx): |
IO_file攻击
这部分就是通过溢出,修改size,然后free掉一个fake的,最后通过IO_file攻击泄露地址,
这部分我是拿的ex师傅的部分的,我自己也写了个这部分的,利用chunk extends,搞复杂了,那会,感觉这个简洁些
1 | new(0x68, '1') #0 |
unsortedbin攻击
1 | edit(1, 'b'*0x70 + p64(0) + p64(0x91)) |
fastbin attack
这里有个点点一下,就是srop部分,因为setcontext最后一句xor eax,eax,再加上syscall就是相当于调用read,
rdi 第一个参数 fd
rsi 第二个参数 buf
rdx 第三个参数 count 大小
rsp 执行完后的rsp
rip 就是 执行syscall加ret
1 | edit(1, 'b'*0x70 + p64(0) + p64(0x71)) |
mprotect修改内存页权限
1 | delete(1) |
shellcode jmp rsp
第一份shellcode ex师傅的
第二份用pwntools加自己编写一些
第三份纯自己写一遍
1 | shellcode = asm(''' |
getshell走起
1 | s(flat(layout) + shellcode) |
。。。好像不能啊,只能特么的读flag,没意思
exp
1 | #!/usr/bin/env python2 |
easyshellcode
这个没啥好说的,用工具或者别的都行
exp
1 | #!/usr/bin/env python2 |
easystack
看着很简单。。。然后不会做,后面发觉了,利用数学就可以计算出来,计算有几个比canary大的,逐字节爆破,
假如你输入了0x00000000-0xff000000,这么多个数,再加上填充到canary处的数,总共为301个
那计算出来的就是比canary大的数有几个,假设有70个,那在减掉填充的45个,就还有35个,意思从255开始往前数35个就是canary那一位的大小,
难点在封装成函数。。我觉得。。通常从低位往高位爆破,高往低,第一次写,太菜了
exp
1 | #!/usr/bin/env python2 |
总结
- 这几道题学到了好多东西
- shellcode编写,mprotect改内存页权限,SROP
本文作者:NoOne
本文地址: https://noonegroup.xyz/posts/89fa1574/
版权声明:转载请注明出处!