Auditing Default Credentials with a Custom Python Script

From time to time I need to audit a large number of devices for default credentials. I typically use Metasploits http_login module for this. You can pass it a file of rhosts with something like set rhosts file://root/hosts.txt and have it check numerous systems quickly. However, a recent system wasn't playing well with this module for reasons I still don't understand. No matter what settings I tried, I kept getting No URI found that asks for HTTP authentication. Fine, I'll go make my own toys.

Setting up Burp and proxies (potentially optional)

A lot of times when I'm looking for default credentials across numerous devices, I'm looking at crappy, old, and unpatched IoT "stuff". A lot of these, if they offer encryption at all, are using old versions of SSL (and I do mean SSL, as in v2 or v3), old cipher suites, self-signed/untrusted certificates, etc. In this case, I get the following error from Metasploit (ruby) OpenSSL::SSL::SSLError SSL_connect returned=1 errno=0 state=error: sslv3 alert handshake failure. In the case of the python script we're getting to, I would get OpenSSL.SSL.Error: [('SSL routines', 'ssl_choose_client_version', 'unsupported protocol')].

The easiest solution is to force everything through Burp honeybadger, Burp honeybadger don't care. You can find more information on this here: https://www.th3r3p0.com/random/python-requests-and-burp-suite.html

Grab the Burp certificate from http://burp/ and convert it to a .pem format
openssl x509 -inform der -in cacert.der -out certificate.pem

Use the following environment variables to force everything through Burp and ignore Python warnings (adjust for your path and ports).

export REQUESTS_CA_BUNDLE="/root/Downloads/certificate.pem"
export HTTP_PROXY="http://127.0.0.1:8082"
export HTTPS_PROXY="http://127.0.0.1:8082"
export PYTHONWARNINGS="ignore:Unverified HTTPS request"

Undo later with

unset REQUESTS_CA_BUNDLE
unset HTTP_PROXY
unset HTTPS_PROXY
unset PYTHONWARNINGS

Building Our Request

We are going to need Burp anyway. Find a device you want to test and, while the proxy is running (you don't have to be actively intercepting something), make a valid login attempt. In Burp, go into Proxy -> HTTP history, and find this request. Right click on it and choose "Copy as curl command".

curl -i -s -k -X $'POST' \
    -H $'Host: 192.168.2.3' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Referer: https://192.168.2.3/logon.shtm' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 32' -H $'Connection: close' -H $'Upgrade-Insecure-Requests: 1' \
    --data-binary $'UserName=administrator&PassWord=123456' \
    $'https://192.168.2.3/~logon_controller'

Using https://curl.trillworks.com/# or https://github.com/NickCarneiro/curlconverter, convert this cURL command into a Python request:

import requests

headers = {
    '$Host': '192.168.2.3',
    '$User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0',
    '$Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    '$Accept-Language': 'en-US,en;q=0.5',
    '$Accept-Encoding': 'gzip, deflate',
    '$Referer': 'https://192.168.2.3/logon.shtm',
    '$Content-Type': 'application/x-www-form-urlencoded',
    '$Content-Length': '32',
    '$Connection': 'close',
    '$Upgrade-Insecure-Requests': '1',
}

data = '$UserName=administrator&PassWord=123456'

response = requests.post('http://$https://192.168.2.3/~logon_controller', headers=headers, data=data, verify=False)

Something a bit weird here. Note the $ before each header field and the data field, remove those. Also remove the http://$ at the beginning of the request.post function.

Wrap it all in a Python script

Things to do:

  1. Store a list of IP addresses in hosts.txt
  2. Get string values that can be used to detect successful or failed logins
    1. Set these in the if, elif, else statements at the bottom
  3. Replace the actual IP addresses in our request above with our ip variable from the hosts.txt file (3 places)
#!/usr/bin/python3
import requests

with open('hosts.txt') as file:
    for ip in file:
        ip = ip.strip()

        headers = {
        'Host': ip,
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.5',
        'Accept-Encoding': 'gzip, deflate',
        'Referer': 'https://'+ip+'/logon.shtm',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': '32',
        'Connection': 'close',
        'Upgrade-Insecure-Requests': '1',
    }

    data = 'UserName=administrator&PassWord=123456'

    response = requests.post('https://'+ip'+/~logon_controller', headers=headers, data=data, verify=False)

        if 'User already logged in' in str(response.content):
            print(ip+" - Login Successful (already logged in)")
        elif 'Welcome administrator' in str(response.content):
            print(ip+" - Login Successful")
        elif 'Incorrect username or password.' in str(response.content):
            print(ip+" - Incorrect username or password")
        else:
            print(ip+" - Something wrong")

Edit: Missed the ip variables, corrected now.