GDOUCTF 2023 CTF Writeup

警告
本文最后更新于 2023-04-20,文中内容可能已过时。

rank: 27

misc 完整 wp

以及取证题不适用取证软件的手工做法

MISC

pixelart

提取像素点

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


im = Image.open('arcaea.png')
# print(im.height)
img_new = Image.new('RGB',(320,180))

for i in range(0,im.width,12):
    for j in range(0,im.height,12):

        rgb = im.getpixel((i,j))
        img_new.putpixel((i//12,j//12),rgb)

# img_new.show()
img_new.save('fake_flag.png')

fake_flag

然后在 lsb 隐写里

image-20230417083848924

Matryoshka

压缩包套娃,不过密码要解算式

最麻烦的地方是从左向右计算,

然后两个坑是十进制不能以 0 开头和答案是负数要去掉符号

 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
import os
# 去掉十进制数字开头的0
def not_start_zero(tmp):
    while True:
        if tmp.startswith("0"):
            tmp = tmp[1:]
        else:
            break
    return tmp
# 从左到右计算算式
def Calculate(calc:str)->str:
    tmp = ""
    f = []
    for index,i in enumerate(calc):
        if i.isdigit():
            tmp += i
        if i.isdigit()==False:
            f.append(not_start_zero(tmp))
            f.append(i)
            tmp = ""
        if index == (len(calc)-1):
            
            f.append(not_start_zero(tmp))

    result = int("".join(f[:1]))
    for i in range(1,len(f),2):
        result = eval(str(result)+"".join(f[i:i+2]))

    return str(result)
# 循环解压缩
while True:
    zip_name = "".join([i for i in os.listdir() if i.endswith('.zip')])
    if zip_name == "":
        break
    print(zip_name)
    

    pwdtxt="".join([i for i in os.listdir() if i.endswith('txt')])
    passwd =open(pwdtxt,'r').read()
    passwd=passwd.replace('zero','0').replace('one','1').replace('two','2').replace('three','3').replace('four','4').replace('five','5').replace('six','6').replace('seven','7').replace('eight','8').replace('nine','9')
    passwd = passwd.replace('plus','+').replace('times','*').replace('mod','%').replace('minus','-')
    print(passwd)
    # 答案中如果有符号 也要去掉
    cal_passwd = Calculate(passwd).replace('-','')
    print(cal_passwd)
    # zipfile模块太慢了 
    os.system(f'unzip -P {cal_passwd} {zip_name}')

    os.remove(zip_name)
    os.remove(pwdtxt)

zipfile 太慢了,这里用的 unzip 命令解压

getnopwd

明文攻击,攻击流量包

1
bkcrack -C getnopwd.zip -c final.pcapng  -x 8 4D3C2B1A01000000FFFFFFFFFFFFFFFF

解压得到一个流量包,另一个文件是 doc

在 document.xml 中可以看到

image-20230417084850207

流量包根据品牌可以知道是数位板流量

image-20230417084914236

用 knm 提取 USB 流量

然后处理流量,Z 为 0 的时候为没有下笔

参考 B 神脚本

https://byxs20.github.io/posts/43085.html#4-getnopwd

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import binascii
import matplotlib.pyplot as plt


with open("./out.data", "r") as f:
    data = f.read().splitlines()

def big_to_small(data):
    return binascii.hexlify(binascii.unhexlify(data)[::-1]).decode()

x_lis, y_lis = [], []
for line in data:
    x,y,z = int((big_to_small(line[4:8])),16),-int((big_to_small(line[8:12])),16),int((big_to_small(line[12:16])),16)

    if z == 0:
        continue
    
    x_lis.append(x)
    y_lis.append(y)


plt.scatter(x_lis, y_lis)
plt.show()

image-20230417091219935

t3stify

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import scipy.io.wavfile as wav
import numpy as np 
import matplotlib.pyplot as plt

sample_rate, data = wav.read('flagg.wav')

left = data[:, 0::2] 
right = data[:, 1::2]

diff = np.abs(left - right)

plt.plot(diff)
plt.show()

左右声道差分

image-20230417101619718

中间明显有不同,放大细看可以看出摩斯

image-20230417101542770

1
.- .-. -.-. .- . .- .---- ..-. .---- . ...-- ...--
1
ARCAEA1F1E33

是 deepsound 的密码

解得 flag

Ez Forensics

数据库版本 + 字符集格式 + 最长列名 示例:NSSCTF {v0.0.1+GBK+1}

diskgenius 挂载,可以找到 1,2,4 三个 zip,还有个看着是 exe 实际上是 zip 的 MySQL.exe

image-20230417102458510

3.zip 就在里面

image-20230417102607822

1234ZIP 里面三个文件刚好是数据表的数据

image-20230417112711664

file 命令可以发现分别的 frm MYD MYI 文件

50726 对应 MySQL 5.7.26 版本

新建一个数据库导入

image-20230417112805244

image-20230417112825624

这个数据库不是 UTF8 的,但是表数据是 utf8 的默认,所以字符集格式是 UTF8

image-20230417112854424

image-20230417112956374

image-20230417113010100

image-20230417113018688

image-20230417113024507

比较发现 最长的列名是表 2 的 listen

1
NSSCTF{v5.7.26+UTF-8+listen}

image-20230417111556150

crypto

Absolute_Baby_Encrytpion

  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
let messagetoEncrypt = prompt("Enter a string: ").toLowerCase();
let charArray = messagetoEncrypt.split("");
let encryptedString = "";
let hasInvalidCharacter = false;

for (let i = 0; i < charArray.length; i++) {
    switch (charArray[i]) {
        case '!':
            encryptedString = encryptedString.concat('a')
            break;
        case '1':
            encryptedString = encryptedString.concat('b')
            break;
        case ')':
            encryptedString = encryptedString.concat('c')
            break;
        case 'v':
            encryptedString = encryptedString.concat('d')
            break;
        case 'm':
            encryptedString = encryptedString.concat('e')
            break;
        case '+':
            encryptedString = encryptedString.concat('f')
            break;
        case 'q':
            encryptedString = encryptedString.concat('g')
            break;
        case '0':
            encryptedString = encryptedString.concat('h')
            break;
        case 'c':
            encryptedString = encryptedString.concat('i')
            break;
        case ']':
            encryptedString = encryptedString.concat('j')
            break;
        case '(':
            encryptedString = encryptedString.concat('k')
            break;
        case '}':
            encryptedString = encryptedString.concat('l')
            break;
        case '[':
            encryptedString = encryptedString.concat('m')
            break;
        case '8':
            encryptedString = encryptedString.concat('n')
            break;
        case '5':
            encryptedString = encryptedString.concat('o')
            break;
        case '$':
            encryptedString = encryptedString.concat('p')
            break;
        case '*':
            encryptedString = encryptedString.concat('q')
            break;
        case 'i':
            encryptedString = encryptedString.concat('r')
            break;
        case '>':
            encryptedString = encryptedString.concat('s')
            break;
        case '#':
            encryptedString = encryptedString.concat('t')
            break;
        case '<':
            encryptedString = encryptedString.concat('u')
            break;
        case '?':
            encryptedString = encryptedString.concat('v')
            break;
        case 'o':
            encryptedString = encryptedString.concat('w')
            break;
        case '^':
            encryptedString = encryptedString.concat('x')
            break;
        case '-':
            encryptedString = encryptedString.concat('y')
            break;
        case '_':
            encryptedString = encryptedString.concat('z')
            break;
        case 'h':
            encryptedString = encryptedString.concat('0')
            break;
        case 'w':
            encryptedString = encryptedString.concat('1')
            break;
        case 'e':
            encryptedString = encryptedString.concat('2')
            break;
        case '9':
            encryptedString = encryptedString.concat('3')
            break;
        case 'g':
            encryptedString = encryptedString.concat('4')
            break;
        case 'z':
            encryptedString = encryptedString.concat('5')
            break;
        case 'd':
            encryptedString = encryptedString.concat('6')
            break;
        case '~':
            encryptedString = encryptedString.concat('7')
            break;
        case '=':
            encryptedString = encryptedString.concat('8')
            break;
        case 'x':
            encryptedString = encryptedString.concat('9')
            break;
        case 'j':
            encryptedString = encryptedString.concat('!')
            break;
        case ':':
            encryptedString = encryptedString.concat('@')
            break;
        case '4':
            encryptedString = encryptedString.concat('#')
            break;
        case 'b':
            encryptedString = encryptedString.concat('$')
            break;
        case '`':
            encryptedString = encryptedString.concat('~')
            break;
        case 'l':
            encryptedString = encryptedString.concat('^')
            break;
        case '3':
            encryptedString = encryptedString.concat('&')
            break;
        case 't':
            encryptedString = encryptedString.concat('*')
            break;
        case '6':
            encryptedString = encryptedString.concat('(')
            break;
        case 's':
            encryptedString = encryptedString.concat(')')
            break;
        case 'n':
            encryptedString = encryptedString.concat('_')
            break;
        case ';':
            encryptedString = encryptedString.concat('+')
            break;

        case '\'':
            encryptedString = encryptedString.concat('-')
            break;
        case 'r':
            encryptedString = encryptedString.concat('=')
            break;
        case 'k':
            encryptedString = encryptedString.concat('`')
            break;
        case 'p':
            encryptedString = encryptedString.concat('~')
            break;
        case '\"':
            encryptedString = encryptedString.concat('{')
            break;
        case '&':
            encryptedString = encryptedString.concat('}')
            break;
        case '/':
            encryptedString = encryptedString.concat('[')
            break;
        case '\\':
            encryptedString = encryptedString.concat(']')
            break;
        case '2':
            encryptedString = encryptedString.concat('|')
            break;
        case '.':
            encryptedString = encryptedString.concat(':;')
            break;
        case '%':
            encryptedString = encryptedString.concat(';')
            break;
        case '|':
            encryptedString = encryptedString.concat('\"')
            break;
        case ',':
            encryptedString = encryptedString.concat('\'')
            break;
        case '@':
            encryptedString = encryptedString.concat('<')
            break;
        case '{':
            encryptedString = encryptedString.concat('>')
            break;
        case 'u':
            encryptedString = encryptedString.concat(',')
            break;
        case '7':
            encryptedString = encryptedString.concat('.')
            break;
        case 'y':
            encryptedString = encryptedString.concat('?')
            break;
        case 'a':
            encryptedString = encryptedString.concat('/')
            break;

        default:
            hasInvalidCharacter = true;
    }
}

if (hasInvalidCharacter) {
    encryptedString = "Invalid String!";
} else {
    console.log(`Your encoded string is ${encryptedString}`);
}

image-20230417092108350

babylua

这段代码实现了一个简单的加密算法。它的工作流程如下:

  1. 它首先生成一个 4 位的随机种子 seed, 然后使用 md5 算法计算 key = md5 (md5 (seed))。它打印 key 的前 10 位,在例子中是 b5e62abe84。

  2. 它将要加密的字符串 flag 转换为字符的 ASCII 码,存储在 secret 数组中。

  3. 它将 key 中的每个字符的 ASCII 码和 flag 中的对应字符的 ASCII 码相加,得到加密后的 ASCII 码,存储回 secret 数组。

  4. 它将 secret 数组中的值打印出来,这些值就是加密后的字符串。

所以为了解密,我们需要:

  1. 拿到 key 的前 10 位,在例子中是 b5e62abe84。

  2. 拿到加密后的字符串,在例子中是 200 161 198 157 173 169 199 150 105 163 193 175 173 194 135 131 135 225。

  3. 对每个值减去 key 中的对应字符的 ASCII 码,就可以得到 flag 的 ASCII 码。

  4. 将 ASCII 码转换回字符,拼接起来就是 flag。

这是 ai 答的

有一点不对,就是 key 需要通过爆破还原完整的 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
from hashlib import md5 
import itertools
import string

def md5_(string):
    return md5(string.encode()).hexdigest()

for i in itertools.product(string.ascii_letters,repeat=4):
    seed = "%s%s%s%s"%i
    # print(key)
    if "b5e62abe84" in md5_(md5_(seed)):
        print(seed)
        key=md5_(md5_(seed))
        print(key)
        break


encrypted = [200, 161, 198, 157, 173, 169, 199, 150, 105, 163, 193, 175, 173, 194, 135, 131, 135, 225]

flag = ''
for i in range(len(encrypted)):
    ascii_code = encrypted[i] - ord(key[i])
    flag += chr(ascii_code)

print(flag)
# flag{He11o_Lua!!!}

Magic of Encoding

数据清洗

 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 base64
import zipfile
confuse_flags = ["flag{Xd_fake_flag_xD}", "find_me_if_you_can", "flag{not_the_correct_flag_lol}","\nflag{not_the_correct_flag_lol}\nflag{not_the_correct_flag_lol}\n"]
base64_lis = [base64.b64encode(i.encode()).decode() for i in confuse_flags]

data = open("Magic_Of_Encoding.txt", "r").read()

for i in base64_lis:
    data = data.replace(i, "")

print(data)

with open("flag.zip", "wb") as f:
    f.write(base64.b64decode(data))


# zipfile.ZipFile

zip_load = zipfile.ZipFile('flag.zip')
zip_load.extractall()
zip_load.close()
print(open('Magic of Encoding.txt').read())

# flag{h0p3_y0u_lik3d_the_M4gic_7rick}

Math Problem

已知 ecc 的 a,b 和基点的 y 轴

椭圆曲线的 p 就是 rsa 的 p

所以求出 p 的值就可以解出 flag

用 sage 在有理数环上带入椭圆曲线的函数表达式 x3+ax+by2x^3+a*x+b-y^2 求解 x

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *
from sage.all import *

e = 65537
n = 79239019133008902130006198964639844798771408211660544649405418249108104979283858140199725213927656792578582828912684320882248828512464244641351915288069266378046829511827542801945752252863425605946379775869602719406340271702260307900825314967696531175183205977973427572862807386846990514994510850414958255877
c = 45457869965165575324534408050513326739799864850578881475341543330291990558135968254698676312246850389922318827771380881195754151389802803398367341521544667542828862543407738361578535730524976113729406101764290984943061582342991118766322793847422471903811686775249409300301726906738475446634950949059180072008
a = 9303981927028382051386918702900550228062240363697933771286553052631411452412621158116514735706670764224584958899184294505751247393129887316131576567242619
b = 9007779281398842447745292673398186664639261529076471011805234554666556577498532370235883716552696783469143334088312327338274844469338982242193952226631913
y = 970090448249525757357772770885678889252473675418473052487452323704761315577270362842929142427322075233537587085124672615901229826477368779145818623466854

R.<x> = PolynomialRing(Zmod(n))
f = x^3+a*x+b-y^2
ans = f.monic().small_roots(X=2^64, beta=0.4, epsilon=0.02)

p = gcd(n, ZZ(f(ans[0])))
q = n//p
d = inverse(e, (p-1)*(q-1))
m = pow(c, d, n)
print(long_to_bytes(int(m)).decode())

# flag{c4edd6d0-d1b3-cbda-95e3-a323edc35be5}

epsilon 取 0.02 算的比较快

0%