前言
?ctf的week1过于简单,所以从week2开始写起,查漏补缺。
Week2
Look at the picture
知识点:文件包含、php:filter伪协议字符编码绕过
www.zip获取源码
|
|
打的时候一直觉得是ssrf,但是ssrf又读不到什么东西。原来关键点是file_get_contents这tm就是个文件包含啊!
文件包含一直不太行,感觉还是得找时间练练。
这里可以用php伪协议,php://filter但是常用的base64、rot13啥的都被ban了。
这里就可以用UTF编码绕过了
|
|

好像也不需要解码,直接是对的,不过真要写的话还是建议用UTF-16,这样也好找转换工具
Only Picture Up
知识点:文件上传
直接传图片马,这靶场里面直接有配置文件可以将png当作php运行,直接打了。

留言板
知识点:过滤ssti request
过滤了单双引号,发现request没有被过滤,这里直接用requset,这里是从cookie获取x和y

登录和查询
知识点:爆破和奇奇怪怪的sql

下面还有个网盘,给了字典,不看也行反正爆破出来admin123

然后重定向到flag.php,直接给了个查询结果,连个注入点都没有wok
后面看wp才知道注入点是id。
|
|

字段数为3,然后题目有提示说flag在flags表里,直接试出来了
|
|
这是什么函数?
知识点:原型链污染
首先是只能传json数据的,发给pollute这个路由,不出意外就是原型链污染了

有提示说flag在/flag

扫盘可以扫出源码,审计一下

|
|
简单审计一下,我们需要在/flag中将cat和dog的值相等
如何做到呢?
|
|
__init__:这是类的构造函数,当我们访问instance.__init__时,实际上访问的是cls.__init__方法。__globals__:Python 函数的__globals__属性是一个字典,包含函数定义时所在模块的全局变量。通过修改__init__方法的__globals__,我们可以影响包含cat和dog全局变量的模块命名空间。- 设置相同值:我们将
__globals__中的cat和dog都设置为相同的值(如"same_value"),这样在/flag路由中比较时,它们就会相等。
merge函数处理逻辑
merge函数处理 JSON 数据时,会递归设置属性。- 当遇到
__init__键时,它会设置instance.__init__属性,但由于instance已经有__init__方法,所以会进入hasattr(dst, k) and type(v) == dict分支。 - 然后递归调用
merge(v, getattr(dst, k)),即merge({"__globals__": {...}}, instance.__init__)。 - 接下来处理
__globals__键,它会访问instance.__init__.__globals__,这是包含模块全局变量的字典。 - 最后,
merge函数会设置__globals__字典中的cat和dog键为相同的值。 - 当访问
/flag路由时,全局变量cat和dog已经相等,因此返回 flag。

Regular Expression
知识点:正则表达式
|
|
第一个要匹配正则,并且要满足长度为40
我们来看看
preg_match('/^-(ctf|CTF)<\n>{5}[h-l]\d\d\W+@email\.com flag.\b$/', $_?)
^-:必须以-开头
(ctf|CTF):可以是ctf也可以是CTF
<\n>{5}:字符<、\n是换行,用%0a代替、>{5}连续五个字符 >
[h-l]:通配符,再h到l之间的字母仍选
\d\d:\d表示任意数字,两个就是两个数字
\W+:带加号的就是贪婪匹配模式,可以一次或多次的匹配。然后w模式是与除 A-Z、a-z、0-9 和下划线以外的任意字符匹配,⽐如!、@、~、#、空格、等等。
@email\.com flag:\.就是.、然后其他就是一样的,空格记得url编码
.\b$:任意匹配一个字符结尾
得到payload:
|
|
然后第二关
我们需要自己写可以匹配
Please\ 777give+. !me?<=-=>(.*)Flaggg0
的正则表达式,但是要超过77个字符
简单构造
|
|

Week3
VIP
知识点:go语言ssti、Go build 环境变量注入
|
|

之前考过一次go语言的ssti,那次的{{.}}有base64模块和exec,可以直接执行命令,这一次是Getenv和Utils模块,结合后面要去的vip专区中需要密钥,推测这里需要通过ssti读取密钥
观察附件源码,发现Utils中有GetReader和Readall方法

|
|
注意空格格式

看到Secretkey的路径了直接读
|
|

获得密钥之后就可以用vip的功能了
vip功能为一个go语言的编译器,这里存在环境变量注入的漏洞,以后遇到go编辑器几乎就是这种题了
在Go环境变量中有一个CC变量,这是个指令。
原本是CC=gcc,可以编译C。这里就是一个命令注入的点位
怎么注入?需要构造怎样的payload呢?
首先看看怎么定义CC
|
|
/bin/sh -c '...' 表示执行单引号内的字符串作为 shell 命令。
gcc "$@": 这是为了伪装和欺骗。在恶意命令执行完毕后,脚本会继续执行 gcc。"$@" 是一个特殊的 shell 变量,它会把传递给这个脚本的所有参数原封不动地传给 gcc。这样,Go 编译过程就能正常完成,不会因为找不到 C 编译器而报错。(可有可无,顺带一提, gcc-shim也可有可无)
好,到这一步已经准备好命令注入了。但是要调用CC这个指令,我们需要导入import “C”。
所以接下来给出完整的payload
payload是json形式
|
|
import "C" 会强制 Go 编译器启用 Cgo,即调用 C 语言工具链(编译器、链接器)来处理 C 代码部分。这是触发漏洞的关键。 如果没有 import "C",Go 编译器会忽略 CC 和 CGO_* 相关的环境变量。
环境变量还定义了"CGO_ENABLED": "1" 和 "GOOS": "linux": 这两个是辅助,确保 Cgo 被启用并指定目标系统。
好,到这里,如果是出网的题目,我们已经可以用反弹shell解决了。但问题这里的环境是不出网的。又该如何解决呢?
可以用>&2重定向解决回显的问题
|
|

flag.txt,直接读,显示我们没有权限

那就要提权了
找一下所有具有特殊权限suid的文件,返回前五个
|
|

直接执行

这个方法就和正常拿shell一样,执行命令,但是官方的脚本wp不是这样的,是用go embed
还有一种方法是用go embed
Go embed 特性 : Go 语言在编译的时候会将被 embed 的文件一起打包到二进制程序内部
也就是说,我们通过命令注入可以将flag写入一个被embed的文件,然后一起打包出来。
embed 特性通过 //go:embed 指令来实现。以下是一些常见的用法:
嵌入单个文件demo
|
|
-
//go:embed hello.txt:表示将hello.txt文件的内容嵌入到变量s中。 -
s是一个字符串变量,存储了hello.txt文件的内容。 -
运行程序时,
s的值就是hello.txt文件的内容。
所以我们第一个命令将命令结果写入一个文件,然后用embed打包一起调出来
构建第二个payload
|
|
env部分相同
code部分:
import (_ "embed"; "os"): 导入了两个关键包。embed 用于在编译时嵌入文件,os 用于将内容输出到标准输出。
//go:embed s.txt: 这是 Go 1.16+ 引入的编译器指令。它告诉编译器,在编译时查找 s.txt 文件,并将其内容嵌入到下面的变量中。
var f []byte: 这个变量 f 将在编译后包含 s.txt 的所有内容。
func main() { os.Stdout.Write(f) }: 程序运行时,它只做一件事:将 f 变量(即 s.txt 的内容)印到屏幕上。
但是我自己做实验的时候,并没有一起打包出来,也不知道是什么原因,这里暂时就这样吧。
ez_php
知识点:非法变量名传参、无字母数字 + 四字符rce

考过很多次了,就是+和&要传进去的话需要url编码一下
|
|

然后是限长的无字母数字rce,给了flag的位置
网上有很多相关文章,可以发现取反是字符数最少的
|
|
然后用 `` 执行系统命令,也可以缩短
|
|
但是还是太长了,我们在去除echo的情况下需要用到总计10个字符
|
|
限长十四,所以我们只能传四个字符。常规执行肯定是不行了,这里应该可以想到四字符rce
预备知识:
1.输入统配符* ,Linux会把第一个列出的文件名当作命令,剩下的文件名当作参数
|
|
这里巧妙的地方就来了
>cat:在当前目录下写入文件名为cat的文件
此时ls可以得到:cat flag.php index.php
然后我们用 *>=就可以将flag.php写入=文件中
|
|
最后访问=

mysql管理工具
知识点:JWT爆破、Mysql任意文件读取、yaml反序列化
源代码获取登录账号user/pass
跳转到mysql测试链接,但是需要admin权限才能获取连接

抓包看到JWT,没有其他信息点时优先考虑爆破。


那么接下来就是伪造了

之后再次连接显示连接失败
查看wp得知这里考的是mysql任意文件读取漏洞
脚本:Rogue-MySql-Server/rogue_mysql_server.py at master · allyshka/Rogue-MySql-Server
首先在自己的虚拟机上运行上述脚本,然后通过Cpolar穿透到外网,然后查域名可以获取ip,最后调整参数发包,虚拟机这里自己会生成mysql.log,那个就是返回值了

然后就是修改filelist

这里改错了,直接app.py就行,成功获取源码

让ai整合一下代码,去除一下没必要的html代码
|
|
除了看到过的mysql相关和根,还有一个/uneed1t路由。这里藏了一个yaml反序列化
|
|
有一点黑名单的yaml反序列化
塞一篇文章浅谈PyYAML反序列化漏洞-先知社区
弹个shell
|
|

这又是什么函数?
知识点:python内存马 || 盲注
/src可以看到源码
|
|
简单源码,大大地难
重点是eval,但是此eval非彼eval,php的eval利用起来很舒服,但这是python
本来可以反弹shell的,但这里不出网。
不出网就两个方法,盲注和内存马。这里详细讲一下内存马,因为我自己写的时候也想过内存马,但是失败了
后来发现就是payload有问题,这里直接就是打pickle反序列化的内容就好,因为直接是eval包裹的

|
|
这个内存马就是当你不传shell这个参数时,doti路由就是正常的,但是当你传了shell参数,那么doit这个路由就会变成你的后门

盲注就不多说了,贴一个payload
|
|
查查忆
知识点:外部注入dtd+waf XXE
正好xee博客里无回显xxe没写完,这里就当补充了
源代码里
首先是过滤了一些伪协议的字符,还ban了<!ENTITY,前面可以通过外部注入dtd绕过,也就是无回显的打法,后面这个可以用编码绕过。因为xml是可以指定编码格式的,我们改成UTF-7,就可以绕过了
但是我们如何才能通过外部注入dtd呢?
浅析无回显的XXE(Blind XXE) - FreeBuf网络安全行业门户
首先我们在虚拟机上搞一个xxe.dtd文件,内容为
|
|
因为是外部dtd访问,你需要让你的dtd能够被公网访问到,也很简单,掏出我们的老朋友Cpolar
|
|
这里需要开两个穿透,第一个是用python启一个http服务,然后内网穿透传出去。然后是你的dtd文件里第二行的网址,也需要你内网穿透出去
然后需要搞payload了
|
|
用虚拟机改成UTF-7
|
|
改后的
|
|
发包就能抓到了,用vps的话会方便一点但是,内网穿透反弹shell毕竟是自己研究出来的,每次用都觉得自己很厉害QAQ

魔术大杂烩
知识点:php反序列化
week3最简单的一集
直接贴exp了,因为是写出了的题目
|
|
Week4
Path to Hero
知识点:php反序列化、md5、rce
|
|
链子简单,绕过简单,就是最后执行命令的的时候如何处理$arg[0]有些问题。
这里arg就是数组,指向的就是Mon3tr的end参数,waf不多再传个eval就行
|
|

android or apple
知识点:代码审计、ssrf打sql注入
www.zip获取源码,目录结构如下

正好要训练代码审计的能力,这里练一下
首先是功能,首先是一个登入框,有安卓登入和苹果登入之分,仔细看看就能发现安卓登入的login.php根本没有成功登入的判断逻辑,也就是说安卓登入就是没用任何作用的。
然后是苹果登入,苹果登入会获取一个验证码,这个验证码是通过verify.php实现的。而这个文件中又有其他类的对象,所以这道题有点像java反序列化的链子。
继续看这个verify.php,可以发现他调用了generateAndDisplayCode这个函数,我们跟进看看

可以看到在这个函数中创建了新的\Util\ImageProcessor()对象,并调用了fetch函数,另外还调用了saveAndDisplay()

我们先直接跟进fetch函数,可以看到首先是url参数调用了getSourceUrl,然后是调用了$this->securityCheck($url);最后调用Request($url)返回值赋给$img,如果img不为空,就可以return这个参数

然后逐一审计一下这几个代码,看到了个ssrf,然后后面Request里确实有关于ssrf的漏洞代码。这里就可以看到我们可以通过 $_SERVER['HTTP_X_VERIFY_CODE_URL']传入数据,这里就是入口可以控制url数据

我们在http头里添加X_VERIFY_CODE_URL数据,就可以控制url了
我们尝试用dick协议探测一下,探测到了数据库
|
|


所以,这里就可以用gopher协议打mysql(还没打过)用户是root,然后是你要执行的sql语句

这里还需要去掉_就行,原因如下,也是一个小知识点,用curl的gopher协议需要_但是fsockopen不需要


|
|
看到表名flags

|
|
列名也是flag,其实这一步都不需要,直接就行
|
|
waf?waf!
知识点:http请求走私
源码很长,慢慢审计。
|
|
waf很多,漏洞点在/calc路由里有一个eval,但是wa非常非常多。不是一般人绕的
预期解也不是死命绕waf
首先我们看到源码里实现了一个代理服务器,只有为post的时候才会往后走,然后是处理CL和TE两个头

关于content-length和transfer-encoding的作用都是告诉服务器请求到哪里结束用的,可以看到这里代理服务器会对TE特殊处理,就算有问题也会转发出去。但是flask并不知道TE,这里就产生了http请求走私

可以知道只要存在transfer-encoding这个http头,中间转发时都会body的数据当初transfer-encoding:chunked 处理,而flask只有transfer-encoding:chunked时才会做chunked的解析,其他情况都会当初普通的POST处理
所以我们设置TE=1,而且这个代码解析TE时会把0视作数据停止的标志,所以这里直接加个0,然后后面加个&用于传递其他参数
|
|
可能是环境的原因,我尝试了很多次都是错的,这里就先跳过了。
好像什么都能读
知识点:flask pin码计算
可以直接读到/etc/passwd,尝试能不能读源码
直接读/app/app.py是没用的会报错,但是观察报错,是可以发现有源码路径的

尝试读取成功
|
|

|
|
源码非常的简洁,没有什么其他的功能逻辑,结合提示说要我们计算什么东西
经过搜索可以发现在Flask的调试模式下,Werkzeug会生成一个PIN码,用于保护调试控制台的访问。
生成pin码需要
|
|
都可以通过文件读取读出来
moddir

uuidnode


machine_id

计算pin码的脚本
|
|
有这个cookie我们还需要两个参数,frm和s,这里frm直接置为0,s的话需要获取一下

获取s通过访问/console,这里需要用回环地址
yakit这么发就发不到,还是bp厉害

最后带上cookie执行命令
|
|

来getshell 速度!
知识点:include解析恶意phar,sudo提权汇总
源码
|
|
只能传压缩包文件,很自然的就想到了phar压缩gz绕过,然后include解析时写马
|
|
|
|
写马成功

但是读不到flag,估计是权限问题,尝试sudo提权
Linux提权-利用sudo提权超级无敌大汇总 - Jimi’s blog
先看看自己的权限,其实前面也有

非常低,接下来看看sudo的版本

1.8.23存在漏洞
- 漏洞名称:Linux sudo host 权限提升漏洞
- CVE 编号:CVE-2025-32462
- 影响版本:sudo 版本 1.9.0 <= sudo <= 1.9.17 或 1.8.8 <= sudo <= 1.8.32
- 漏洞原理:sudo 的 - h(–host)选项错误地将远程主机的权限规则应用到本地系统,导致本地低权限用户可通过指定允许的远程主机名,绕过本地权限限制,以 root 身份执行命令。
还需要知道远程主机是啥
|
|

所以我们用-h参数指定asd.asd.asd远程虚拟主机执行我们的命令。这里还需要保证www-data asd.asd.asd = NOPASSWD:ALL
最后
|
|

这又又是什么函数
知识点:pickle内存马
/src获取源码
|
|
一眼pickle反序列化,直接打内存马,os被waf了,绕过一下
|
|

一开始我还纳闷怎么反弹shell弹不上去,才意识到是不出网的环境
ez_Ref
知识点:java反序列化、FastJson绕过resolveclass
java题,有源码直接用idea反编译一手,定位到serlvet,看看主要逻辑

|
|
两个路由,一个是根目录,输出hello,ctfer,另一个/ez_Ref会获取data并把它反序列化,还要满足
if (name.equals("Ref") && year == 2025)
我们继续看到ctfer类,ctfer类里面重写了toString方法,通过调用templatesImpl类的.getOutputProperties()方法

这个就是一些cc链反序列化的终点,也就是可以执行代码的方法。
只是我们发现这里的readObject类里面嵌套了一层waf。

所以继续跟进看到这个类,就是一个白名单绕过。这个白名单resolveClass

然后wp说要分析依赖,发现并不存在其它依赖可以构造新链子绕过调用ctfer#toString方法进行调用,就是绕过waf
不能新链子,那就绕过白名单
因为要绕过,所有首先我们先把waf拿过来,还原一下

然后就是构造链子绕过了
首先需要一些前置知识
Java 反序列化绕过 resolvClass | DummyKitty’s Blog
在 fastjson <= 1.2.48 版本中,存在这样的一个 gadget:通过触发 JSONArray 和 JSONObject 这两个类的 toString 方法来调用任意的 getter 方法,由于该版本下,JSONArray 和 JSONObject 并没有 readObject 方法,因此需要通过 BadAttributeValueExpException 来触发 toString,具体的利用链如下:
BadAttributeValueExpException -> JSONArray/JSONObject.toString -> toJSONString -> TemplateImpl.getOutputProperties
算了这里确实有些难以理解,先放放