文章目录
一、前言
两年一届的网络安全"奥运会"也是有幸参加!是一个非常有意思的比赛,其余附件及需要的工具已打包完毕~
123网盘下载:
二、MISC
MISC01
解题思路
附件下载,得到一个zip,解压得到一个MME.cap文件,一开始还没见过后缀以.cap结尾的文件,还是wirshark自动识别出是流量包,也才知道是流量的,那这里既然提到了,我们干脆来简单说说以.cap结尾的文件吧:
简单来说以
.cap
结尾的文件通常是数据包捕获文件,用来存储网络通信数据,记录网络中不同设备之间传输的原始数据包信息。它们可以用于分析网络流量、故障排除、检测安全事件等。(那这里估计就是让我们分析了)
详细分析:
-
数据来源:
.cap
文件一般由网络协议分析工具生成,如Wireshark、tcpdump等,这些工具可以在网络接口上捕获到的每一个数据包,并将其存储在.cap
文件中。 -
文件内容:
.cap
文件记录了传输层、网络层以及应用层协议的信息。通过分析文件可以看到:- 源IP和目标IP地址。
- 传输的协议(如TCP、UDP、HTTP等)。
- 包的内容,包括传输的数据和协议头信息。
- 具体的数据包时间戳、大小等。
通常用Wireshark打开.cap
文件,通过过滤器和视图选项可以深入查看和分析每个数据包的详细信息。
那这里有的师傅可能有疑问了,那这个与普通的.pacp流量包有什么区别嘛?
-
文件格式:
.pcap
(Packet Capture):是由libpcap(Linux/macOS)和WinPcap(Windows)库生成的一种标准格式,广泛用于跨平台的数据包捕获。.pcap
格式被Wireshark、tcpdump等多种分析工具支持,通用性较强。.cap
:是一种数据包捕获文件的通用扩展名,不是特定的格式。.cap
文件可能采用不同的文件格式(如pcap
或其他厂商自定义格式),具体取决于生成它的工具。
-
兼容性:
- **
.pcap
**文件具有较好的跨平台兼容性,几乎所有主流网络分析工具都可以打开和解析。 - **
.cap
**文件可能是不同工具生成的专有格式(如AirPcap、SharkTap等),部分工具可能无法直接打开,需要转换成.pcap
格式以便分析。
- **
-
扩展名的使用场景:
.cap
有时被用于厂商特定的捕获文件,或者用于包含无线数据的文件,可能包含其他格式或加密信息。.pcap
更常用于标准化的网络数据包分析,尤其在开放和跨平台的环境中被广泛使用。
一般来说,通过Wireshark等分析工具将.cap
文件转换为.pcap
格式可以提高兼容性,便于后续分析处理。
那我们就暂时拓展到这里,现在植入主题吧,首先就是打开MME.cap流量包看看它的协议分级情况:
不难看出,没有几个我们熟悉的协议,那能咋办?反正也没断网,我们直接百度一下看看这个diameter是个什么东西:
也是简单看了几篇文章,对diameter也有了个简单的了解,再结合题目描述:某单位网络遭到非法的攻击,安全人员对流量调查取证之后保存了关键证据,发现人员的定位信息存在泄露,哎!捕捉关键词"定位信息"!
那这里提示也是很明显了,就是让我们在流量包中找到可疑的定位信息呗!
那我们这里直接过滤出了diameter协议来进行分析,发现也没多少条记录:
那既然筛选出来了,那到底需要找什么呢?我们也不是很了解这个协议,没关系,通过拷打GPT以及队友的帮助,再加上协议数量也不多,再加上“定位信息”这个提示,我们也是成功把范围锁定在了:Diameter协议中,MME(Mobility Management Entity)-Location-Information,那为什么呢?因为它包含用户的E-UTRAN(Evolved Universal Terrestrial Radio Access Network)小区全局标识(ECGI),这样就可以更为精确定位用户的所在位置。
因为对这个协议也不是很熟悉也是翻啊翻啊翻啊~再经过师傅们不知道多少的MD5加密提交之后,终于也是锁定在了包大小为"410"中!
那可能有的师傅就有疑问了,为什么你们可以锁定呢?有什么依据嘛?是去某鱼一把梭了?
因为在查找中发现这个:64f80099f4既然是小区全球识别码!
64f80099f4
是一种E-UTRAN小区全局标识(ECGI)的十六进制表示,用于标识全球范围内唯一的小区。ECGI是LTE网络中一个关键参数,它结合了移动国家码(MCC)、移动网络码(MNC)和小区标识(Cell ID)来定义小区的唯一性。
简要说明:
-
ECGI组成:
- MCC(移动国家码):代表该小区所属的国家。
- MNC(移动网络码):标识运营商网络。
- Cell ID:特定小区的唯一标识符。
-
64f80099f4
的含义:- 该十六进制数值表示ECGI编码,小区全局标识可能以不同编码格式展示。
- 转换后可得出具体的 MCC、MNC 和 Cell ID 信息,从而了解其所在国家、运营商和具体小区位置。
-
用途:
- 这种唯一的全球标识支持全球漫游、切换等功能,通过该标识,网络可以精准识别和定位用户当前的小区位置,用于流量管理、计费、资源分配等。
简而言之,64f80099f4
提供了全球唯一的小区识别能力,确保网络和用户定位的精确性。
那这时候可能有的师傅就着急了,翻了那么久,直接就拿这个进行MD5加密就去提交了(我就干过),但是答案还不对哈,因为真正的答案还不在这!
我们看到它的上面一条数据:E-UTRAN-Cell-Global-Identity: 802f208f26ae77
E-UTRAN-Cell-Global-Identity
(ECGI,E-UTRAN小区全局标识)用于唯一标识LTE网络中的一个小区,而802f208f26ae77
是其具体的16进制编码形式。
详细解释:
-
ECGI组成:
- 移动国家码(MCC):前3个字节表示国家。
- 移动网络码(MNC):紧随其后的3个字节标识网络运营商。
- 小区标识(Cell ID):最后部分用于标识具体小区。
-
802f208f26ae77
的含义:- 该16进制字符串编码了特定的MCC、MNC和Cell ID,可通过解码提取这些信息。
- 转换成十进制后,可以解析出国家、网络运营商和小区信息。
-
ECGI用途:
- 作为小区的全球唯一标识,ECGI在漫游、切换和用户定位等场景中非常关键,确保了用户的连接在跨越不同小区时的连续性。
简单来说,802f208f26ae77
是一个ECGI值,它通过唯一编码的形式实现对特定小区的全球定位与标识。
最后根据题目要求:flag为用户位置信息进行32位md5哈希值
那我们直接对:802f208f26ae77,进行MD5加密进行提交,答案正确!
至此
wdflag{f9243fa9f18aec525ccb1ea3bd5dd190}
很新型的一种MISC题目类型昂!
MICS02
解题思路
附件下载,得到一个zip,正常解压发现还是需要点解压时间,最后得到一个空白的flag文件,简单看一下文件属性,也难怪要解压一会,发现它的大小既然来到了惊人的2G!
这边我们也是提取拿到了具体的WP,所以话不多说我们直接对flag文件进行foremost的分离:
直接对分离出的PNG图片中最大的那一张进行"zsteg"检测并提取隐藏数据,最大的图片:
得到了部分的密码:Y3p_Ke9_1s_???,但是不难看出它是不全的,毕竟谁家密码后面是几个问号你说对吧?
接着我们返回flag空白文件中,使用010打开来进行分析,你可以发现许多的7z压缩包的痕迹,那到底哪一个才是真正的7z压缩包呢?
010打开flag空白文件发现很多个7z
是最后一个7z才是真正的压缩包,那我们直接定位到最后一个,手动导出一下:
最底下的7z才是真正的压缩包
那我们直接手动选中底下全部的复制粘贴一下,并且新建一个十六进制文本,最后粘贴进去,并且Ctrl+s保存为123.7z压缩包即可:
这时候我们在返回桌面打开我们刚刚导出的7z压缩包,会发现需要密码:
那刚刚找到的密码就派上用场了,但问题又来了,密码不全啊,那怎么办呢?那这里我们有两种方法,这里我都来操作一下:
方法一:
使用hashcat来进行掩码爆破,那我们首先要先使用脚本7z2john.pl
生成123.7z的hash值,脚本如下:(7z2john.pl
已打包至题目附件中)
这里我用的kali来进行操作,命令如下:
perl 7z2john.pl 123.7z
得到;
ATTENTION: the hashes might contain sensitive encrypted data. Be careful when sharing or posting these hashes
123.7z:$7z$1$19$0$$16$ea387fd7406ecd303b35200cf6ced99c$3068667578$112$109$2250e60cb7a8412a864b80158520a200154866fe2668f89a2239e02753b474c5e221b87744deddf0d108f8bb97fc438bf58ffae8e0b1b33581924d6f3994630da6374e9a9021d034e0a0154061414a3d785822a4c9a7ad6c3500c36e711e2cd1be04b4d8001d6fdfed7c1618c84d9467$104$5d00001000
新建一个新的12.hash文本,将123.7z生成的hash值复制进去即可:
接着使用hashcat进行掩码爆破(用的是kali中自带hashcat~)
命令;
hashcat -m 11600 -a 3 12.hash Y3p_Ke9_1s_?d?d?d?d?d -O -w 4 -o 111.txt --force
命令解析:
-
-m 11600
:指定哈希模式,这里11600
表示解密基于PBKDF2-HMAC-SHA1的哈希。 -
-a 3
:选择攻击模式,这里3
表示使用暴力破解(mask attack),即基于掩码的猜测攻击。 -
12.hash
:指定包含待破解哈希值的文件(即12.hash
文件)。 -
Y3p_Ke9_1s_?d?d?d?d?d
:设置掩码,用于猜测密码的格式:Y3p_Ke9_1s_
:指定密码的已知部分;?d?d?d?d?d
:掩码符号?d
表示每个位置尝试数字字符,长度为5,因此Hashcat会尝试组合所有五位数字,以匹配未知部分。
-
-O
:启用优化内核。这会加速破解,但也会限制最大密码长度(通常20字符以内)。 -
-w 4
:设置工作负载模式,这里4
表示最高级别(即极高的资源占用和性能需求)。 -
-o 111.txt
:指定输出文件,将破解结果保存到111.txt
文件。 -
--force
:强制运行Hashcat,即便检测到潜在兼容性或性能问题。
那这里就等它爆破完成就好了,爆破的时间还是蛮久的,而且123.7z压缩包是动态的,也就是每个人爆破出来的密码都是不一样的,不能直接用别人的噢,只能老老实实的等爆破完成~
爆破完成
打开我们的111.txt,里面的这个才是真正的密码:Y3p_Ke9_1s_59418
方法二:
使用ARCHPR来进行爆破(注意版本必须!!4.66往上!要不然不支持.7z爆破!!!没有的师傅也没关系,这里已经打包完毕一并放在附件中!!!)
这边爆破的时间也是蛮久的(每个人的密码都是动态的),我的大概百分之59左右才出!
也是可以爆出:Y3p_Ke9_1s_59418
方法不唯一,仅供参考!!!
那我们打开zip
得到一个flag.txt,打开flag.txt,发现这还并不是flag:
flag.txt
内容如下:
31 226 PUSH_NULL
228 LOAD_NAME 8 (key_encode)
230 LOAD_NAME 7 (key)
232 PRECALL 1
236 CALL 1
246 STORE_NAME 7 (key)
32 248 PUSH_NULL
250 LOAD_NAME 10 (len)
252 LOAD_NAME 7 (key)
254 PRECALL 1
258 CALL 1
268 LOAD_CONST 7 (16)
270 COMPARE_OP 2 (==)
276 POP_JUMP_FORWARD_IF_FALSE 43 (to 364)
33 278 PUSH_NULL
280 LOAD_NAME 9 (sm4_encode)
282 LOAD_NAME 7 (key)
284 LOAD_NAME 5 (flag)
286 PRECALL 2
290 CALL 2
300 LOAD_METHOD 11 (hex)
322 PRECALL 0
326 CALL 0
336 STORE_NAME 12 (encrypted_data)
34 338 PUSH_NULL
340 LOAD_NAME 6 (print)
342 LOAD_NAME 12 (encrypted_data)
344 PRECALL 1
348 CALL 1
358 POP_TOP
360 LOAD_CONST 2 (None)
362 RETURN_VALUE
32 >> 364 LOAD_CONST 2 (None)
366 RETURN_VALUE
Disassembly of <code object key_encode at 0x14e048a00, file "make.py", line 10>:
10 0 RESUME 0
11 2 LOAD_GLOBAL 1 (NULL + list)
14 LOAD_FAST 0 (key)
16 PRECALL 1
20 CALL 1
30 STORE_FAST 1 (magic_key)
12 32 LOAD_GLOBAL 3 (NULL + range)
44 LOAD_CONST 1 (1)
46 LOAD_GLOBAL 5 (NULL + len)
58 LOAD_FAST 1 (magic_key)
60 PRECALL 1
64 CALL 1
74 PRECALL 2
78 CALL 2
88 GET_ITER
>> 90 FOR_ITER 105 (to 302)
92 STORE_FAST 2 (i)
13 94 LOAD_GLOBAL 7 (NULL + str)
106 LOAD_GLOBAL 9 (NULL + hex)
118 LOAD_GLOBAL 11 (NULL + int)
130 LOAD_CONST 2 ('0x')
132 LOAD_FAST 1 (magic_key)
134 LOAD_FAST 2 (i)
136 BINARY_SUBSCR
146 BINARY_OP 0 (+)
150 LOAD_CONST 3 (16)
152 PRECALL 2
156 CALL 2
166 LOAD_GLOBAL 11 (NULL + int)
178 LOAD_CONST 2 ('0x')
180 LOAD_FAST 1 (magic_key)
182 LOAD_FAST 2 (i)
184 LOAD_CONST 1 (1)
186 BINARY_OP 10 (-)
190 BINARY_SUBSCR
200 BINARY_OP 0 (+)
204 LOAD_CONST 3 (16)
206 PRECALL 2
210 CALL 2
220 BINARY_OP 12 (^)
224 PRECALL 1
228 CALL 1
238 PRECALL 1
242 CALL 1
252 LOAD_METHOD 6 (replace)
274 LOAD_CONST 2 ('0x')
276 LOAD_CONST 4 ('')
278 PRECALL 2
282 CALL 2
292 LOAD_FAST 1 (magic_key)
294 LOAD_FAST 2 (i)
296 STORE_SUBSCR
300 JUMP_BACKWARD 106 (to 90)
15 >> 302 LOAD_GLOBAL 3 (NULL + range)
314 LOAD_CONST 5 (0)
316 LOAD_GLOBAL 5 (NULL + len)
328 LOAD_FAST 0 (key)
330 PRECALL 1
334 CALL 1
344 LOAD_CONST 6 (2)
346 PRECALL 3
350 CALL 3
360 GET_ITER
>> 362 FOR_ITER 105 (to 574)
364 STORE_FAST 2 (i)
16 366 LOAD_GLOBAL 7 (NULL + str)
378 LOAD_GLOBAL 9 (NULL + hex)
390 LOAD_GLOBAL 11 (NULL + int)
402 LOAD_CONST 2 ('0x')
404 LOAD_FAST 1 (magic_key)
406 LOAD_FAST 2 (i)
408 BINARY_SUBSCR
418 BINARY_OP 0 (+)
422 LOAD_CONST 3 (16)
424 PRECALL 2
428 CALL 2
438 LOAD_GLOBAL 11 (NULL + int)
450 LOAD_CONST 2 ('0x')
452 LOAD_FAST 1 (magic_key)
454 LOAD_FAST 2 (i)
456 LOAD_CONST 1 (1)
458 BINARY_OP 0 (+)
462 BINARY_SUBSCR
472 BINARY_OP 0 (+)
476 LOAD_CONST 3 (16)
478 PRECALL 2
482 CALL 2
492 BINARY_OP 12 (^)
496 PRECALL 1
500 CALL 1
510 PRECALL 1
514 CALL 1
524 LOAD_METHOD 6 (replace)
546 LOAD_CONST 2 ('0x')
548 LOAD_CONST 4 ('')
550 PRECALL 2
554 CALL 2
564 LOAD_FAST 1 (magic_key)
566 LOAD_FAST 2 (i)
568 STORE_SUBSCR
572 JUMP_BACKWARD 106 (to 362)
18 >> 574 LOAD_CONST 4 ('')
576 LOAD_METHOD 7 (join)
598 LOAD_FAST 1 (magic_key)
600 PRECALL 1
604 CALL 1
614 STORE_FAST 1 (magic_key)
19 616 LOAD_GLOBAL 17 (NULL + print)
628 LOAD_FAST 1 (magic_key)
630 PRECALL 1
634 CALL 1
644 POP_TOP
20 646 LOAD_GLOBAL 7 (NULL + str)
658 LOAD_GLOBAL 9 (NULL + hex)
670 LOAD_GLOBAL 11 (NULL + int)
682 LOAD_CONST 2 ('0x')
684 LOAD_FAST 1 (magic_key)
686 BINARY_OP 0 (+)
690 LOAD_CONST 3 (16)
692 PRECALL 2
696 CALL 2
706 LOAD_GLOBAL 11 (NULL + int)
718 LOAD_CONST 2 ('0x')
720 LOAD_FAST 0 (key)
722 BINARY_OP 0 (+)
726 LOAD_CONST 3 (16)
728 PRECALL 2
732 CALL 2
742 BINARY_OP 12 (^)
746 PRECALL 1
750 CALL 1
760 PRECALL 1
764 CALL 1
774 LOAD_METHOD 6 (replace)
796 LOAD_CONST 2 ('0x')
798 LOAD_CONST 4 ('')
800 PRECALL 2
804 CALL 2
814 STORE_FAST 3 (wdb_key)
21 816 LOAD_GLOBAL 17 (NULL + print)
828 LOAD_FAST 3 (wdb_key)
830 PRECALL 1
834 CALL 1
844 POP_TOP
22 846 LOAD_FAST 3 (wdb_key)
848 RETURN_VALUE
magic_key:b5bbbca902b2b095
encrypted_data:403289c9abb2c98500bff81f16d1ade3ed60639fa41da3443357b20758bf6b66f8b8ee253e9f8576389f1a7c1fc56edf
大致看了一下发现是python的字节码,这段代码的核心作用是使用一个密钥生成函数和加密算法,对 flag
进行加密得到 encrypted_data
,那这里我们去别的师傅那偷一个脚本过来:
脚本如下:
# 原始的16进制字符串密钥
magic_key = "b5bbbca902b2b095"
# 将magic_key的每个字符转换为一个整数列表key_as_ints,其中每个字符都被解释为16进制数字
key_as_ints = list(map(lambda ch: int(ch, 16), magic_key))
# 循环遍历key_as_ints,从0开始,以步长2取值,每次取一个index
for index in range(0, len(key_as_ints), 2):
# 对当前index的值与下一个值做异或运算
key_as_ints[index] ^= key_as_ints[index + 1]
# 循环遍历key_as_ints,从第1个位置开始,对每个值与其前一个值进行异或运算
for index in range(1, len(key_as_ints)):
# 临时存储倒数第二个元素,用于异或操作
temp_value = key_as_ints[len(key_as_ints) - index - 1]
# 对当前index的值与temp_value做异或运算
key_as_ints[len(key_as_ints) - index] ^= temp_value
# 将处理后的整数列表key_as_ints重新转换成16进制字符串,形成新的密钥字符串final_key
final_key = ''.join(format(num, 'x') for num in key_as_ints)
# 计算最终结果,将原始的magic_key和新生成的final_key都转换成整数后做异或操作
final_result = format((int('0x' + magic_key, 16) ^ int('0x' + final_key, 16)), 'x')
# 打印最终结果
print(final_result)
注意!!!每个人的magic_key都是动态的,注意修改自己的脚本!!!
脚本分析!
- 原始密钥转换:
magic_key
作为一个16进制字符串,将其中每个字符转换为整数,形成key_as_ints
列表。 - 第一轮异或操作:将
key_as_ints
列表从头开始,以每两个元素为一组,对第一个元素与第二个元素做异或运算。这样会混淆原始数据的部分信息。 - 第二轮异或操作:从列表的第二个位置开始,将每个元素与其前一个元素(实际上是倒序方向的元素)进行异或运算,进一步混淆数据。
- 生成新的密钥字符串:将处理过的
key_as_ints
列表再次转换为16进制字符串,形成新的密钥final_key
。 - 生成加密结果:将
magic_key
与final_key
逐位进行异或,最终生成一个加密的16进制字符串final_result
。
得到的SM4的密钥为:5ee07753b2092b5c
最后我们直接使用厨子解SM4得到flag:wdbflag{4c7807c127d64edafe23b4307de7f4dc}
至此;
wdbflag{4c7807c127d64edafe23b4307de7f4dc}
因为这里是跟着wp复现嘛,所以就没有那么多做题时思考的思路,详细思路请至:2024 网鼎杯网络安全大赛 Misc Writeup
MISC03
解题思路
附件下载,得到一个zip,解压出来得到c.pcap,不难看出这又是一题流量分析,不过相比于MISC01这里好了很多,毕竟也没给一个陌生的.cap后缀嘛,那我们使用wirshark打开进行分析,仔细观察题目的要求:找出攻击者的攻击IP,话说这题咋那么眼熟呢?流量?攻击者IP?玄机流量分析?
简单看看协议分级,发现存在HTTP的,果断筛选出来进行分析:
哎,发现一筛选,很快就发现了黑客上传了hacher.php
这题其实最笨的办法就是,看见的IP都一个一个提交一遍,当然IP少可以这样做,这里就不是很多,所以随便提交一下就发现正确了,那这样显然不能达到我们学习的效果,那这里我们来说说为什么可以确定黑客的IP就是:39.144.218.183
追踪分析一下hacher.php看看:
URL解码得到:
简单分析一下
-
环境配置:
- 使用
@ini_set("display_errors", "0");
隐藏错误输出,避免暴露信息。 @set_time_limit(0);
设置脚本无时间限制,以便长时间运行。- 检查并修改
open_basedir
配置,以绕过目录限制,获取更多系统访问权限。
- 使用
-
临时目录创建与切换:
- 尝试在多目录位置创建临时目录
/.8896b5
,用于绕过系统目录访问限制。 @chdir($tmdir);
改变工作目录到临时目录,随后用@ini_set("open_basedir", "..");
来进一步尝试突破文件系统限制。- 之后脚本切换回原目录并删除该临时目录,隐蔽自己的痕迹。
- 尝试在多目录位置创建临时目录
-
主要功能:文件管理器:
- 函数
asenc
和asoutput
处理输出内容,分别负责包装和输出数据。 - 通过
base64_decode(substr($_POST["g7823a795e6db"],2));
解码用户请求中的目录路径。 - 以
$F=@opendir($D);
打开该路径,若路径不存在或无权限,则返回相应错误。 - 如果有权限,则遍历该目录下的文件及文件夹,获取文件的修改时间、大小和权限。
- 将这些信息收集整理并输出,最终通过
ob_end_clean(); echo "67897" . "0ae61" . @asenc($output) . "52ca1" . "eae58e";
格式化输出。
- 函数
-
流量解码分析:
g7823a795e6db=9YL3Zhci93d3cvaHRtbC91cGxvYWRzLw==
是发送的一个POST
参数,内容经过base64
编码。解码后得到/var/www/html/uploads/
,这是目标服务器上的路径。- 整个请求目的是访问并遍历这个上传目录,可能用于窃取或删除文件。
总结
这段代码是一种常见的PHP后门,具备绕过目录限制和访问服务器文件的功能。攻击者可以通过发送指定参数,获取服务器文件信息或执行进一步的恶意操作。这类恶意代码通常用于攻击服务器,窃取敏感数据或进行下一步的恶意操作。
蚁剑前两位不需要,所以就是:L3Zhci93d3cvaHRtbC91cGxvYWRzLw==
具体解释就是:其中的 9Y
前两位不解码,是因为这部分可能是伪装用的填充数据,目的是混淆或绕过某些过滤机制,避免被检测为Base64编码。这种做法在恶意流量中比较常见,攻击者通过手动混入字符来使检测机制更难识别这些内容。
很快就发现了攻击者上传的文件路径
所以这题的大致思路就是:39.144.218.183(攻击者的IP)上传了一个hacker.php,用AntSword连接并执行了一句命令,也就是我们俗称的:蚁剑
至此:
wdbflag{39.144.218.183}
MICS04
解题思路
附件下载,得到一个zip,解压出一张图片,图片如下:
感觉有点眼熟,之前有在某个群见过,去看了下,是叫皮亚诺曲线,也是头一回接触,没啥好讲解的,这里就直接上脚本吧!
脚本如下:
from PIL import Image
from tqdm import tqdm
# 递归生成Peano曲线
def peano(n):
if n == 0:
# 基本情况,初始点为(0,0)
return [[0,0]]
else:
# 递归生成上一级曲线,并复制生成列表
in_lst = peano(n - 1)
lst = in_lst.copy()
# 每次递归中,按特定顺序扩展Peano曲线的坐标点
# 将上一级曲线复制并平移到新的位置
px, py = lst[-1] # 获取当前曲线的最后一个点坐标
# 以下步骤根据上一级曲线扩展出新的区域
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px + 1 + i[0], py - i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px - i[0], py - 1 - i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px + i[0], py - 1 - i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px + 1 + i[0], py + i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px - i[0], py + 1 + i[1]] for i in in_lst)
px, py = lst[-1]
lst.extend([px + i[0], py + 1 + i[1]] for i in in_lst)
# 返回生成的曲线点列表
return lst
# 设置曲线的阶数
order = peano(6)
# 打开源图像
img = Image.open(r"./1.png")
# 获取图像的宽度和高度
width, height = img.size
# 初始化新图像的大小
block_width = width
block_height = height
# 创建一个空白的RGB新图像,用于存储重排后的图像
new_image = Image.new("RGB", (width, height))
# 遍历生成的Peano曲线点列表并重新排列像素
for i, (x, y) in tqdm(enumerate(order)):
# 将当前点的索引转换为新图像中的位置
new_x, new_y = i % width, i // width
# 从原图像中获取对应位置的像素
pixel = img.getpixel((x, height - 1 - y))
# 将像素放置在新图像中的相应位置
new_image.putpixel((new_x, new_y), pixel)
# 保存重新排列后的图像
new_image.save("rearranged_image.jpg")
简单分析:
- Peano曲线生成:
- 通过递归调用函数
peano(n)
生成Peano曲线,n
表示曲线的阶数。每一级递归生成的曲线是在前一级曲线上不断延伸,最终得到一个二维坐标列表order
,每个坐标点依次代表在图像中的一个位置。
- 通过递归调用函数
- 图像像素重排列:
- 打开源图像,初始化一个新的空白图像
new_image
,大小与原图像一致。 - 使用
order
中的坐标点,从源图像获取像素值,并将这些像素按顺序放置到新图像new_image
的对应位置上,实现重排。
- 打开源图像,初始化一个新的空白图像
- 保存重排后的图像:
- 将新的图像保存为
"rearranged_image.jpg"
。
- 将新的图像保存为
用途
这个脚本可以用于图像加密或图像数据的空间重排,也常用于图像隐写、图像加密等信息安全领域。通过Peano曲线的特性,像素排列可以被扰乱,同时还可以被还原。
运行得到:
这个彩色的二维码可能不好识别(当然微信扫码还是很强大的,它是可以识别出的),不过没关系,我们可以使用Stegsolve换一下色道:
这样就可以使用QR来进行扫码了
至此:
wdflag{b9367dd6-2d7e-4ef7-ba5c-270a6c6220cd}
最后的最后,有师傅感兴趣的也可以尝试复原一下下面这张图,参考文章:
如图下:
可能比网鼎杯的这题简单一些~
标签:LOAD,CONST,WriteUp,MICS,2024,lst,CALL,key,PRECALL From: https://blog.csdn.net/administratorlws/article/details/143408011