2024第七届浙江省大学生网络与信息安全竞赛决赛部分题解
城南花已开 Lv5

Misc

FinalSign

打开附件发现有空白字符,猜测是Snow雪花隐写,工具下载:https://www.darkside.com.au/snow/

image

直接下载32位的执行文件就行,-c提取出关键信息,异或的密钥为helloworld

1
2
C:\Users\maple\Desktop\Misc\浙江大学生信息安全决赛\Misc1-FinalSign\tempdir\MISC附件>snow -C FinalSign.txt
xorkey:helloworld

-C 如果隐藏数据,则压缩数据,如果提取,则解压缩数据。

-Q 安静模式。如果未设置,程序将报告 压缩百分比和已用可用存储空间量。

-S 报告可用于隐藏的大致空间量 消息。行长度被考虑在内,但 其他选项将被忽略。

-p 密码 如果已设置此项,则在 隐藏,或在提取过程中解密。

-l 行长度 当附加空格时,snow 将始终产生线条 短于此值。默认情况下,它设置为 80。

-f 消息文件 此文件的内容将隐藏在输入文本文件中。

-m 消息字符串 此字符串的内容将隐藏在输入文本文件中。 请注意,除非字符串中以某种方式包含换行符,否则 提取消息时,将不会打印 newline。

那就利用python将文本中的密文和helloworld异或一下

1
2
3
4
5
6
7
8
a=bytes.fromhex("2c243f2f3b3114345d0a0909333f06100143023b2c55020912")
key=b"helloworld"
e=[]
for i in range(0,len(a)):
e.append(a[i]^key[i%len(key)])
print(bytes(e))
#########
DASCTF{F1nal_Sign1n_D0ne}

或者利用CyberChef也能出,每个人习惯不同

image

天命人

附件是个压缩包解压之后得到一些以中华词语命名的文件

image

夜生白露 日落红尘 曲度紫鸳 未竟 火照黑云 风起黄昏

结合压缩包注释想起黑神话悟空里面的章节,将文件排序下得到

image

1
2
3
4
5
6
火照黑云
风起黄昏
夜生白露
曲度紫鸳
日落红尘
未竟

加之RAID 0磁盘阵列猜测将文件组合形成一个新的文件,放到010Editor看一下,没错就是把单个文件拆成了六个文件,按章节拼接组合即可,但可不是直接将文件整体拼接那么简单的

而是每个文件每一位按章节顺序拼接,简单查看了解到开头是504B0304典型的压缩包

1
2
3
4
5
6
7
8
9
10
11
12
13
file_list = ['火照黑云', '风起黄昏', '夜生白露', '曲度紫鸳', '日落红尘', '未竟']
sources = [open(file_name, 'rb') for file_name in file_list]
n = 0
with open('1.zip', 'wb') as target:
while n < 0x5ead4:
bytes_read = [source.read(1) for source in sources]
# for byte in bytes_read:
# if byte: # 如果读取的字节不为空
# print(hex(ord((byte))), end=' ') # 打印读取的十六进制
for byte in bytes_read:
if byte:
target.write(byte)
n += 1

利用python拼接文件得到zip文件,解压得到两个加密的zip文件

其中一个压缩包中有很多大小相等的文本文件,显然采用CRC32爆破

image

Github找到CRC32爆破的项目AabyssZG/CRC32-Tools: Easy CRC32 Tools,so easy!!!,git clone一下

image

尝试用这个解密根器压缩包,结果死活不行,卡了半天,结果这个密码是未竟的压缩包密码😒

是自己太蠢了,回头一想这几个文本文件也就4字节,一般也没啥有用的信息

得到一张金箍棒.png图片和一个紧箍咒未知文件,仔细观察图片文件发现怎么布满了彩点,图片大概率隐藏了某些信息

我发现我眼镜拿掉站远点看隐隐约约能发现图片中间有一行字🤣

image

虽然不知道是啥隐写方法,看了其他师傅的WP得知是等距像素隐写,每个像素间隔10像素,将这些彩色的像素拼起来

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

a = Image.open("金箍棒.png")
x, y = 5, 5
x_, y_ = 0, 0
w, h = a.size
b = Image.new(a.mode, (w // 10, h // 10))

for x in range(5, w, 10):
for y in range(5, h, 10):
print(x, y, x_, y_)
b.putpixel((x_, y_), a.getpixel((x, y)))
y_ += 1
x_ += 1
y_ = 0
b.save('1.png')

得到VeraCrypt的密码为jinggubang

image

安装下VeraCrypt,打开后加载另一个紧箍咒未知文件,这里不仅要输入上面的密码还要将金箍棒.png作为密钥文件才能成功挂载,踩坑了好久

image

最终得到DASCTF{T1m3_t0_F4Ce_De5t1nY}

非黑即白

拿到附件用file检查一下,没发现什么东西,利用010Editor打开文件看一下,发现是个反的GIF文件

image

利用CyberChef反转一下,下载得到GIF文件

image

发现是一张不断闪烁黑白的动图,频率还不规律

尝试利用convert分离下GIF动图发现有1536张,我勒个豆image

这肯定需要脚本进行批量操作了

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

# 定义一个空字符串,用于存储二进制数据
data_list = ""
# 循环1536次,每次读取一个图片文件
for i in range(1536):
# 定义图片文件名
filename = f"flag-{i}.png"
# 打开图片文件
tmp_img = Image.open(filename)
# 利用PIL的getpixel方法获取坐标(x,y)的红色通道的值
tmp_pixel = tmp_img.getpixel((0,0))
# 打印像素值
print(tmp_pixel)
# 如果像素值小于200,则将0添加到data_list中,否则将1添加到data_list中
# 一般小于200图片颜色会偏暗,接近黑色或深灰色
if tmp_pixel < 200:
data_list += '0'
else:
data_list += '1'

# 打印最终的二进制数据
print(data_list)

发现可以利用CyberChef将得到的二进制文本转为ZIP

image

显然压缩包是加密的,这样就需要另寻方法找到压缩包的密码了

查看压缩包信息无果后回头检查GIF文件,发现在CTF-Wiki中有一个关于GIF间隔隐藏信息的内容

image

尝试一下,发现果然有,前15张图片的间隔时间都不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ identify -format "%s %T \n" MISC附件/download.gif
0 118
1 106
2 69
3 74
4 48
5 98
6 83
7 117
8 77
9 79
10 86
11 65
12 90
13 103
14 101
##写入1.txt中
❯ vim 1.txt
cat 1.txt |cut -d " " -f 2|tr '\n' " "
118 106 69 74 48 98 83 117 77 79 86 65 90 103 101 %

identify参数参考:ImageMagick:格式化和打印图像属性 — ImageMagick: Format and Print Image Properties

image

得到密码解压压缩包即可拿到flag,得到DASCTF{H3r3_1s_C0L0rful_W0rld}

数据安全

datasecurity_classifyl

现在有一份未归档的数据文件,里面有〖姓名、身份证号、手机号〗三种类型的数据,每一行一个数据值。请你对其进行数据分析并分类,类别有〖姓名、身份证号、手机号〗这三种类型。将其归档到 csv 文件中(文件列名为 “类型,数据值”,文件编码为 utf-8),最终将该文件上传至该题的校验平台(在该校验平台里可以下载示例文件 example.csv, 可作为格式参考),校验达标即可拿到 flag 。

附件是个csv文件,利用python对着数据校验规范进行编写即可

image

image

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
from string import ascii_letters
import os
# 定义手机号前缀列表
phone_prefix = [
734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772,
778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755,
756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777,
780, 781, 789, 790, 791, 793, 799
]
# 定义身份证号校验系数
id_card_xishu = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
# 定义身份证号校验码
odd = "1 0 X 9 8 7 6 5 4 3 2".split(' ')
# 判断result.csv文件是否存在,如果存在则删除
if os.path.exists('result.csv'):
os.remove('result.csv')
# 打开result.csv文件,以追加模式写入数据
with open('result.csv','+a') as result:
result.write('类型,数据值\n');
# 打开data.csv文件,以二进制模式读取数据
with open('data.csv','rb') as data:
for line in data.readlines():
line = line.decode().strip();
# 判断数据长度是否为18,如果是则认为是身份证号
if len(line) == 18:
# id card
qian_17 = line[:17]
sums = 0;
# 计算身份证号校验码
for i,e in enumerate(qian_17):
e = int(e) * id_card_xishu[i];
sums += e;
# 判断身份证号校验码是否正确
if line[-1] != odd[sums % 11]:
continue;
# 将身份证号写入result.csv文件
result.write('身份证号,'+line + '\n');
# 判断数据长度是否为11,如果是则认为是手机号
elif len(line) == 11:
for prefix in phone_prefix:
# 判断手机号前缀是否在手机号前缀列表中
if line.startswith(str(prefix)):
# 将手机号写入result.csv文件
result.write('手机号,'+line + '\n');
break;
# 如果数据长度既不是18也不是11,则认为是姓名
else:
# 判断数据中是否包含'数据值',如果是则跳过
if '数据值' in line:
continue;
# name
sign = False;
# 判断数据中是否包含字母
for i in ascii_letters:
if i in line:
sign = True;
break;
# 如果数据中包含字母,则认为是姓名
if not sign:
# 将姓名写入result.csv文件
result.write('姓名,'+line + '\n');

result.csv,上传即可获得flag

datasecurity_classify2

现有一个流量文件,需要作为数据分析人员的你对该流量文件进行分析,并进行数据识别与分类。请你参考附件中《个人信息数据规范文档.pdf》中“数据上传规范”说明的要求识别出 身份证号(idcard)、手机号(phone)、IP地址 (ip)这三类敏感数据,并对其进行数据分类。最终将进行了数据识别与分类后的内容保存到 csv 文件中(文件编码 utf-8,并将该文件上传至该题的校验平台(在该校验平台里可以下载该题示例文件 example.csv, 可作为该题的格式参考),校验达标即可拿到 flag。

附件解压有个流量包,大多都说HTTP流量,跟踪流,详细看看,发现每个流都有一大段话,每段话之间藏了idcard

phone ip等信息

image

image

可以利用tshark命令导出data的十六进制文件

1
2
❯ tshark -r data.pcapng -T fields -Y "http.request.method==POST" -e data >data.txt
Warning: program compiled against libxml 212 using older 209
  • -r data.pcapng:读取名为 data.pcapng 的数据包捕获文件。
  • -T fields:指定输出格式为字段值,这些字段值由 -e 选项指定。
  • -Y "http.request.method==POST":使用显示过滤器来只显示 HTTP 请求方法为 POST 的数据包。
  • -e data:指定要显示的字段是 data 字段
  • > 是重定向操作符,它会将命令的输出重定向到指定的文件中。如果文件已经存在,它会被覆盖;如果文件不存在,它会被创建。
  • data.txtdata.dat 是你保存输出的文件名,你可以根据需要更改这些文件名。

再根据数据规范文档,利用python筛选

image

image

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

# 定义一个函数,用于验证身份证号码
def veryifyIdCard(idcard):
# 如果身份证号码长度不等于18,返回False
if len(idcard) != 18:
return False;
# 将身份证号码添加到idcardList列表中
idcardList.append(idcard)
# 将身份证号码转换为大写
idcard = idcard.upper()
# 定义身份证号码的加权因子
id_card_xishu = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
# 定义身份证号码的校验码
odd = "1 0 X 9 8 7 6 5 4 3 2".split(' ')
# 初始化校验和
sums = 0;
# 获取身份证号码的前17位
qian_17 = idcard[:17]
# 遍历前17位,计算校验和
for i,e in enumerate(qian_17):
e = int(e) * id_card_xishu[i];
sums += e;

# 返回校验码是否与身份证号码的最后一位相等
return idcard[-1] == odd[sums % 11];

# 定义一个函数,用于验证电话号码
def verifyPhone(phone):
# 定义电话号码的前缀
phone_prefix = [
734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772, 778,
782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755, 756, 766,
767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777, 780, 781, 789,
790, 791, 793, 799
]
# 如果电话号码长度不等于11或者最后一位是'X',返回False
if len(phone) != 11 or phone[-1] == 'X':
return False;
# 遍历电话号码的前缀,如果匹配,返回True
for prefix in phone_prefix:
if phone.startswith(str(prefix)):
return True;
# 否则返回False
return False;

# 定义一个函数,用于验证IP地址
def verifyIp(ip):
# 遍历IP地址的每一部分,如果大于255,返回False
for i in ip.split('.'):
if int(i) > 255:
return False;
# 否则返回True
return True;

# 定义一个函数,用于清理数据
def cleanData(data):
# 如果数据中包含'-',将删除'-'
if '-' in data:
data = ''.join(data.split('-'))
# 如果数据中包含空格,将删除空格
elif ' ' in data:
data = ''.join(data.split(' '))

# 返回清理后的数据
return data;



# 导入正则表达式和os模块
import re,os
# 定义一个正则表达式,用于匹配IP地址
ipMatch = re.compile(r'[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}');
# 定义一个正则表达式,用于匹配电话号码
phoneMatch = re.compile('(\d{11}|\d{3}\ \d{4}\ \d{4}|\d{3}\-\d{4}\-\d{4})');
# 定义一个正则表达式,用于匹配身份证号码
idcardMatch = re.compile('(\d{18}|\d{6}\ \d{8}\ \d{4}|\d{6}\-\d{8}\-\d{4})');
# 定义一个正则表达式,用于匹配带'X'的身份证号码
idcardMatch_with_x = re.compile('(\d{17}X|\d{6}\ \d{8}\ \d{3}X|\d{6}\-\d{8}\-\d{3}X)');

# 定义一个空列表,用于存储身份证号码
idcardList = []

# 如果result2.csv文件存在,删除它
if os.path.exists('result2.csv'):
os.remove('result2.csv')
# 打开result2.csv文件,以追加模式写入
with open('result2.csv','+a') as result:
# 写入表头
result.write('category,value\n');
# 打开data.txt文件,以只读模式读取
with open('data.txt', 'r') as data:
# 将data.txt文件中的十六进制数据转换为字符串
data2 = bytes.fromhex(data.read()).decode();
# 遍历字符串中的每一行
for line in data2.split(','):
# 如果行中包含身份证号码
if idcardMatch.findall(line):
# 遍历行中的每一个身份证号码
for e in idcardMatch.findall(line):
# 清理身份证号码
e = cleanData(e);
# 验证身份证号码
if veryifyIdCard(e):
# 将身份证号码写入result2.csv文件
result.write('idcard,' + e + '\n');
# 如果行中包含带'X'的身份证号码
if idcardMatch_with_x.findall(line):
# 遍历行中的每一个带'X'的身份证号码
for e in idcardMatch_with_x.findall(line):
# 清理带'X'的身份证号码
e = cleanData(e);
# 验证带'X'的身份证号码
if veryifyIdCard(e):
# 将带'X'的身份证号码写入result2.csv文件
result.write('idcard,' + e + '\n');
# 如果line中存在手机号
if phoneMatch.findall(line):
# 遍历line中的所有手机号
for e in phoneMatch.findall(line):
# 清洗手机号数据
e = cleanData(e);
# 标记为True
sign = True;
# 遍历idcardList中的所有身份证号
for card in idcardList:
# 如果手机号在身份证号中
if e in card:
# 标记为False
sign = False;
# 跳出循环
break;
# 如果标记为False
if not sign:
# 跳过本次循环
continue;
# 如果手机号验证通过
if verifyPhone(e):
# 将手机号写入result文件
result.write('phone,' + e + '\n');
# 如果line中存在IP地址
if ipMatch.findall(line):
# 遍历line中的所有IP地址
for e in ipMatch.findall(line):
# 如果IP地址验证通过
if verifyIp(e):
# 将IP地址写入result文件
result.write('ip,' + e + '\n');

image

csv文件上传即可获得flag

参考自:第七届浙江省大学生网络与信息安全竞赛决赛-WP - Dr0n’s blog

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 258.9k