巅峰极客2023初赛部分wp

j1ya Lv5

Crypto

数学但高中

本来想手搓的,在队友提醒下直接GPT4生成脚本

生成的脚本有点问题,把整个椭圆都画出来了,显然看原函数只有半个椭圆

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
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches

fig, ax = plt.subplots()

# Creating a large enough plot
ax.set_xlim([0, 52])
ax.set_ylim([0, 10])

# Add lines
ax.plot([4, 4], [0, 6])
ax.plot([7, 7], [0, 6])
ax.plot([10, 10], [2, 3])
ax.plot([13, 13], [0, 3])
ax.plot([15, 15], [0, 2])
ax.plot([15, 15], [4, 6])
ax.plot([17, 17], [1, 6])
ax.plot([19, 19], [3, 4])
ax.plot([21, 21], [3, 4])
ax.plot([22, 22], [2, 3])
ax.plot([24, 24], [2, 3])
ax.plot([29, 29], [2, 5])
ax.plot([32, 32], [2, 5])
ax.plot([36, 36], [2, 3])
ax.plot([38, 38], [2, 3])
ax.plot([41, 41], [2, 6])
ax.plot([44, 44], [3, 4])
ax.plot([46, 46], [3, 4])
ax.plot([47, 47], [2, 3])
ax.plot([49, 49], [2, 3])
ax.plot([51, 51], [0, 2])
ax.plot([51, 51], [4, 6])

# Add horizontal lines
ax.hlines(4, 2, 6)
ax.hlines(4, 17, 18)
ax.hlines(4, 28, 30)
ax.hlines(4, 41, 42)
ax.hlines(6, 4, 6)
ax.hlines(6, 15, 16)
ax.hlines(6, 17, 19)
ax.hlines(6, 41, 43)
ax.hlines(2, 29, 30)
ax.hlines(2, 31, 33)
ax.hlines(2, 39, 40)

# Add circles
circle1 = plt.Circle((9, 3), 1, color='b', fill=False)
circle2 = plt.Circle((12, 3), 1, color='b', fill=False)
circle3 = plt.Circle((20, 3), 1, color='b', fill=False)
circle4 = plt.Circle((23, 3), 1, color='b', fill=False)
circle5 = plt.Circle((26, 3), 1, color='b', fill=False)
circle6 = plt.Circle((37, 3), 1, color='b', fill=False)
circle7 = plt.Circle((45, 3), 1, color='b', fill=False)
circle8 = plt.Circle((48, 3), 1, color='b', fill=False)
ax.add_artist(circle1)
ax.add_artist(circle2)
ax.add_artist(circle3)
ax.add_artist(circle4)
ax.add_artist(circle5)
ax.add_artist(circle6)
ax.add_artist(circle7)
ax.add_artist(circle8)

# Add ellipse
ellipse = patches.Ellipse((34, 3.5), 2, 1.5, color='b', fill=False)
ax.add_artist(ellipse)

# Add diagonal lines
x = np.linspace(14, 15, 100)
y1 = -x + 17
y2 = x - 11
ax.plot(x, y1, 'b')
ax.plot(x, y2, 'b')

x = np.linspace(26, 27, 100)
y1 = 0.5*x - 11
y2 = -0.5*x + 17
ax.plot(x, y1, 'b')
ax.plot(x, y2, 'b')

x = np.linspace(51, 52, 100)
y1 = x - 49
y2 = -x + 55
ax.plot(x, y1, 'b')
ax.plot(x, y2, 'b')

x = np.linspace(31, 32, 100)
y = x - 27
ax.plot(x, y, 'b')

# Show the plot
plt.show()

另外,一些字母也可以直接看出来,因为题目说没有字母,所以不可能是O

1
flag{Funct10n_Fun}

还可以在线画,记得给函数加花括号

https://www.desmos.com/calculator?lang=zh-CN

Reverse

gore

先脱壳

搜索main_main函数一个是AES,一个是Base64,主函数后半部分还有一个^0x1A

查字符串能明显看到base64变表:456789}#IJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123ABCDEFGH

接下来需要动调拿到AES的key,解密函数处下一个断点,接下来去找动调后的串

多了123是因为一开始输的就是123,拿到key:wvgitbygwbk2b46d

先异或0x1A再加v57[i%16],最后和密文v53比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enc=[0xE6, 0xCE, 0x89, 0xC8, 0xCF, 0xC5, 0xF5, 0xC9, 0xD2, 0xD9,
0xC0, 0x91, 0xCE, 0x7F, 0xAC, 0xCC, 0xE9, 0xCF, 0xB7, 0xC0,
0x96, 0xD4, 0xEA, 0x92, 0xE2, 0xD7, 0xDF, 0x84, 0xCB, 0xA5,
0xAE, 0x93, 0xA6, 0xCA, 0xBE, 0x97, 0xDF, 0xCE, 0xF0, 0xC9,
0xB7, 0xE1, 0xAE, 0x6B, 0xC4, 0xB1, 0x65, 0xDB, 0xCE, 0xED,
0x92, 0x93, 0xD6, 0x8C, 0xED, 0xC3, 0xA3, 0xDA, 0x94, 0xA5,
0xAA, 0xB2, 0xB5, 0xA7]
key=b"wvgitbygwbk2b46d"

base = ""
for i in range(len(enc)):
base+=chr(((enc[i]-key[i%16])^0x1a)&0xff)
print(base)
#uB8EAyfxAmOEvQlrhCJM8hk1qonHskb55NM4qvmxZeY#xg5mMm10x0nF6b3iRdeY

m1_read

识别出来是AES

白盒加密是一种特殊的加密方法,它允许加密数据在不受信任的环境中被解密而无需暴露密钥本身,不会直接保存一个明显的16字节 key,而是把密钥融合进大量查表、轮函数和编码变换中

针对白盒加密可以使用DFA攻击,在倒数第一轮列混淆和倒数第二轮列混淆之间修改中间结果的一个字节,会导致最终密文和正确密文有4个字节不同。通过多次的修改,得到多组错误的密文,然后通过正确密文和这些错误密文推算出加密模式下第10轮的密钥,从而能推算出原始密钥

aes+异或0x66

1
2
3
4
5
6
7
8
9
10
11
12
13
import frida
import os

currentDir = os.getcwd()
print(currentDir)
session = frida.attach("m1_read.exe")

def getJsScript(fileName="getKey.js", fileDir=currentDir) -> str:
with open(fileDir + "\\" + fileName, encoding='utf-8') as jsScript:
return jsScript.read().rstrip()

script = session.create_script(getJsScript())
script.load()

用frida hook key,在第9轮时随机修改中间状态的1个字节,第一次输出可以看作正确密文,后面多次输出是在第9轮随机改一个字节后的错误密文

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
var baseAddr = Module.findBaseAddress("m1_read.exe")
// AES函数偏移:0x4BF0
var whiteAES = new NativeFunction(baseAddr.add(0x4BF0), 'pointer', ['pointer','pointer'])
// count = 9,这样第一轮数据恰好对应正确的数据
var count = 9

Interceptor.attach(baseAddr.add(0x4C2C),{
// 列混淆处的偏移:0x4C2C
// 若不确定,可以通过测试来看:
// 1. 若正确数据与异常数据全部不同,则修改太早
// 2. 若正确数据与异常数据仅一个不同,则修改太晚
onEnter: function(args){
count++;
// 第九轮修改中间结果
if(count == 9){
// 参考汇编代码可以发现rdi记录第九轮中间数据的地址
// "add(Math.floor(Math.random()*16))":
// 表示对操作的地址进行偏移,值属于{1,2,3}
// (0,4的可能性基本为0)
//
// "writeU8(Math.floor(Math.random()*256))":
// 表示对写入的值进行修改,值属于{1,2,3,...,E,F}
// (0,16的可能性基本为0)
// 综上,这实现了对中间数据的单个Byte修改,取消注释下述代码即可看出
var p = ptr(this.context.rdi)
// console.log("ori: ", p.readByteArray(16), "\n")
p.add(Math.floor(Math.random()*16)).writeU8(Math.floor(Math.random()*256))
// console.log("out: ", p.readByteArray(16), "\n")
}
},
onLeave:(retval) =>{
}
})

for (let index = 0; index < 33; index++){
// 取消注释下述代码即可得到每一轮的数据
var l = Memory.allocAnsiString("0123456789abcdef");
var b = Memory.alloc(16);
whiteAES(l, b);
// console.log(b.readByteArray(16));
count = 0;
}
# 异或处理脚本
data = """
00000000 72 93 98 12 0f 00 94 f4 03 7a 44 ee dd 99 20 6f r........zD... o
00000000 72 93 98 5a 0f 00 aa f4 03 2d 44 ee 16 99 20 6f r..Z.....-D... o
00000000 72 56 98 12 b6 00 94 f4 03 7a 44 30 dd 99 b9 6f rV.......zD0...o
00000000 1c 93 98 12 0f 00 94 89 03 7a ce ee dd c8 20 6f .........z.... o
00000000 72 93 98 c4 0f 00 d4 f4 03 d4 44 ee ac 99 20 6f r.........D... o
00000000 72 17 98 12 c1 00 94 f4 03 7a 44 7a dd 99 02 6f r........zDz...o
00000000 72 93 98 e5 0f 00 ad f4 03 0a 44 ee 08 99 20 6f r.........D... o
00000000 72 84 98 12 6a 00 94 f4 03 7a 44 68 dd 99 a6 6f r...j....zDh...o
00000000 d2 93 98 12 0f 00 94 57 03 7a 31 ee dd 36 20 6f .......W.z1..6 o
00000000 72 93 65 12 0f 7c 94 f4 90 7a 44 ee dd 99 20 0f r.e..|...zD... .
00000000 72 93 98 3a 0f 00 e8 f4 03 aa 44 ee 98 99 20 6f r..:......D... o
00000000 72 93 98 44 0f 00 49 f4 03 b2 44 ee 24 99 20 6f r..D..I...D.$. o
00000000 73 93 98 12 0f 00 94 1f 03 7a 76 ee dd 6b 20 6f s........zv..k o
00000000 72 7d 98 12 7e 00 94 f4 03 7a 44 bf dd 99 23 6f r}..~....zD...#o
00000000 72 20 98 12 8a 00 94 f4 03 7a 44 c4 dd 99 35 6f r .......zD...5o
00000000 5e 93 98 12 0f 00 94 35 03 7a 1a ee dd 20 20 6f ^......5.z... o
00000000 72 93 98 a5 0f 00 1e f4 03 77 44 ee 15 99 20 6f r........wD... o
00000000 72 93 98 5c 0f 00 07 f4 03 a5 44 ee 06 99 20 6f r..\......D... o
00000000 72 93 eb 12 0f e0 94 f4 13 7a 44 ee dd 99 20 2a r........zD... *
00000000 72 93 98 97 0f 00 f8 f4 03 c5 44 ee 2a 99 20 6f r.........D.*. o
00000000 72 93 98 ab 0f 00 99 f4 03 d0 44 ee 5b 99 20 6f r.........D.[. o
00000000 72 93 98 cb 0f 00 bd f4 03 9d 44 ee 58 99 20 6f r.........D.X. o
00000000 72 73 98 12 05 00 94 f4 03 7a 44 c5 dd 99 3d 6f rs.......zD...=o
00000000 72 dd 98 12 bf 00 94 f4 03 7a 44 b9 dd 99 45 6f r........zD...Eo
00000000 72 93 67 12 0f 5c 94 f4 d6 7a 44 ee dd 99 20 bb r.g..\...zD... .
00000000 72 84 98 12 42 00 94 f4 03 7a 44 da dd 99 f3 6f r...B....zD....o
00000000 72 68 98 12 b2 00 94 f4 03 7a 44 5c dd 99 b1 6f rh.......zD\...o
00000000 72 e5 98 12 66 00 94 f4 03 7a 44 27 dd 99 42 6f r...f....zD'..Bo
00000000 72 fb 98 12 6e 00 94 f4 03 7a 44 7c dd 99 cc 6f r...n....zD|...o
00000000 72 93 98 86 0f 00 27 f4 03 18 44 ee e0 99 20 6f r.....'...D... o
00000000 72 93 63 12 0f 29 94 f4 8a 7a 44 ee dd 99 20 b0 r.c..)...zD... .
00000000 72 93 98 10 0f 00 cc f4 03 01 44 ee cd 99 20 6f r.........D... o
00000000 72 e0 98 12 77 00 94 f4 03 7a 44 ba dd 99 9a 6f r...w....zD....o
"""

def cut(obj, sec):
return [obj[i:i+sec] for i in range(0, len(obj), sec)]

data = cut(data, 76)
data = data[:-1]
for i in range(len(data)):
data[i] = data[i][11:-18]
for i in data:
tmp = i.split(' ')
res = ''
for j in tmp:
res += '{:02x}'.format(int(j, 16) ^ int("66", 16))
print(res)

利用phoenixAES实现DFA

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
# s1是前文中的33轮数据,以字符串形式存储(包含换行,否则无法识别)
import phoenixAES
s1 = """
14f5fe746966f292651c2288bbff4609
14f5fe3c6966cc92654b228870ff4609
1430fe74d066f292651c2256bbffdf09
7af5fe746966f2ef651ca888bbae4609
14f5fea26966b29265b22288caff4609
1471fe74a766f292651c221cbbff6409
14f5fe836966cb92656c22886eff4609
14e2fe740c66f292651c220ebbffc009
b4f5fe746966f231651c5788bb504609
14f50374691af292f61c2288bbff4669
14f5fe5c69668e9265cc2288feff4609
14f5fe2269662f9265d4228842ff4609
15f5fe746966f279651c1088bb0d4609
141bfe741866f292651c22d9bbff4509
1446fe74ec66f292651c22a2bbff5309
38f5fe746966f253651c7c88bb464609
14f5fec3696678926511228873ff4609
14f5fe3a6966619265c3228860ff4609
14f58d746986f292751c2288bbff464c
14f5fef169669e9265a322884cff4609
14f5fecd6966ff9265b622883dff4609
14f5fead6966db9265fb22883eff4609
1415fe746366f292651c22a3bbff5b09
14bbfe74d966f292651c22dfbbff2309
14f50174693af292b01c2288bbff46dd
14e2fe742466f292651c22bcbbff9509
140efe74d466f292651c223abbffd709
1483fe740066f292651c2241bbff2409
149dfe740866f292651c221abbffaa09
14f5fee069664192657e228886ff4609
14f50574694ff292ec1c2288bbff46d6
14f5fe766966aa9265672288abff4609
1486fe741166f292651c22dcbbfffc09
"""
with open("testfile", "wb") as t:
t.write(s1.encode())

phoenixAES.crack_file("testfile")
# 自动打印出来第10轮的密钥
# 结果如下
# B4EF5BCB3E92E21123E951CF6F8F188E

用Stark提供一轮密钥暴力破解AES-128

在提供的out.bin里根据M1卡的存储格式,第一行中,4B为校验位,剩下为厂商信息,需要解密的部分为第三行

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Cipher import AES

s = "0B987EF5D94DD679592C4D2FADD4EB89"

key = bytes.fromhex("00000000000000000000000000000000")
enc = bytearray(bytes.fromhex(s))
cipher = AES.new(key, AES.MODE_ECB)

for i in range(16):
enc[i] ^= 0x66

flag = cipher.decrypt(enc)
print(flag)

flag{cddc8d28dabb4ea9}

  • 标题: 巅峰极客2023初赛部分wp
  • 作者: j1ya
  • 创建于 : 2023-07-24 20:09:19
  • 更新于 : 2026-10-02 16:17:02
  • 链接: https://j1ya-22.github.io/2023/07/24/2023巅峰极客初赛部分wp/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
巅峰极客2023初赛部分wp