Robbing the Rich Using Bitcoin

by 0rbytal (0rbytal@burntmail.com

This is a concept article (i.e., I haven't actually tried this), and is for educational/informational purposes only.

You should not steal from other people, no matter how much they have, or how little you have.  You have no right to someone else's property or the fruits of their labor.  In this article, I am simply illustrating how thieves could exploit a couple of the most common security flaws still found in practice today.  I love the concept behind Bitcoin, and I also enjoy the convenience of online banking... so, please don't abuse them by using this methodology to your financial advantage!

This "attack" is founded upon the classic, yet prevalent, blunder of using weak passwords, and the all too common habit of repeatedly using the email address for personal activity.  If you have a very strong (i.e., difficult-to-guess or brute-force) password, or you don't do any online banking, you should not be vulnerable to this sort of attack.  I'm merely speculating on a new method by which a technologically-savvy thief could rob an online banker, and likely avoid getting caught.

Online banking has been around since the early-1980s, and millions of people around the world take advantage of its convenience every day.  Unfortunately, many of these online bankers do not like having to remember complex passwords to access their account, and therefore resort to using a weak password that could be guessed or brute-forced will relative ease.  These security-ignorant online bankers also tend to be the kind of people who use the same email address to register/access all of their important accounts.  So, once an attacker discovers someone's email address, it is not a stretch to assume the attacker can use that email address to access the associated bank account - if they know the password.

By learning more about the target, the thief could use a script that scrapes certain websites to generate a list of potential passwords.  (Note:  Such a script can be found following this article.)  If a thief discovered a bank that allowed unlimited login attempts, or the thief was patient enough to try logging in three times per day until they succeeded, she could execute a custom, brute-force login script that tried commonly-used and "relevant" passwords (from the generated list) until she gained access to the targeted account.  Now, once a thief has attained access to her target's online bank account, if she started transferring the target's funds to her bank accounts, she would surely get busted from the logs and audit trail.  Enter Bitcoin...

Bitcoin allows relatively anonymous transfer of funds with almost no way to determine the sender or recipient.  There is a long hash value associated with each Bitcoin user's account, and unless the user has associated their email address with their hash value somewhere on the Internet (e.g. forums, blogs, personal website, online vendor, etc.), the Bitcoin user can be fairly confident that nobody will discover their identity based solely upon the hash value (i.e., their Bitcoin "account number").

So, back to our thief... if the target already has a Bitcoin wallet, there's a possibility the email address and password used to access the target's bank account will also work to access the target's Bitcoin wallet (assuming it's online, and not a local client).  Otherwise, the thief could create a Bitcoin wallet for the target, just to convert transferred funds from the bank account into Bitcoins.  Once the funds are converted to Bitcoins and sitting in the target's new Bitcoin wallet, the thief can send all of those Bitcoins to his own Bitcoin wallet, and then convert those stolen Bitcoins back into another fiat currency stored in his own bank account.

Using this technique, a thief could steal money from one person's bank account, and put it in her own bank account, without anyone being able to track it.  The lessons I hope everyone learned from this article:

1.)  Use difficult-to-guess/brute-force passwords.  I suggest using a password safe (e.g. KeePass), generating a 20+ random character password for each account you have, and storing the encrypted database in your favorite cloud storage.  This way, all of your passwords are different, random, and accessible anywhere you go (via the KeePassDroid app), so you don't even have to remember them!  Plus, if one of your passwords is compromised, all of your other accounts aren't automatically compromised as well.

2.)  Use different email accounts.  By using different email accounts to register for services, if someone gets one of your email accounts, they don't automatically know your username for every other account you have.

3.)  Beware what you share.  If you have a Bitcoin wallet, and you share your hash on the Internet, you've just associated your identity to that account.

4.)  Use two-factor authentication.  If you use a service that offers two-factor authentication (e.g. DropBox, Gmail, etc.), you really should enable this feature so you are notified if someone is trying to access your account without your permission.

Stay curious, stay safe, and Hack All The Things!

### wordlistgenerator.py by blerbl
import re, sys, os, urllib
#### custom useragent
class AppURLopener(urllib.FancyURLopener):
    version = "Mozilla/5.0(compatable;MSIE 9.0; Windows NT 6.1; Trident/5.0)"

urllib._urlopener = AppURLopener()
uopen = urllib.urlopen
uencode = urllib.urlencode

###############################################################
###
### Helper Function
###

def ls(file):
    print(open(file,'rb').read())

def google(query,numget=10,verbose=0):
    numget = int(numget)
    start = 0
    results = []
    if verbose == 2:
        print("[+]Getting " + str(numget) + " results")
    while len(results) < numget:
        print("[+]"+str(len(results)) + " so far...")
        data = uopen("https://www.google.com/search?q="+query+"&start="+str(start))
        if data.code != 200:
            print("Error " + str(data.code))
            break
        results.extend(re.findall("<a href=\"([^\"]*)\"class=(?:l|s)",data.read()))
        start += 10
    if verbose == 2: print("[+] Got " + str(numget) + " results")
    return results[:numget]

def genWordlist(targetlist,word_reg,outfile,verbose=0,quotes=True):
    quote_reg = re.compile("\"([^\"]{2,35})\"")
    ###
    ### Initialize Engine
    ###
    words = []
    append = False
    total_wb = 0
    dircount = 0
    totalcount = 0
    ###
    ### Read the old list
    ###
    if outfile.startswith("+"):
        outfile = outfile[1:]
        words = open(outfile).readlines()
        append = True
        total_wb = len(words)
    ###
    ### Hit the sources
    ###

    for target in targetlist:

        data = None
        ###
        ### Get the data
        ###
        if os.path.isfile(target):
            data = open(target).read()
        elif os.path.isdir(target):
            dircount += 1 # for stats in end
            subtargets = os.listdir(target)
            for subtarget in subtargets:
                if os.path.isfile(subtarget):
                    data = "\n\n" + os.read(subtarget)
                else:
                    targetlist.append(subtarget)
                    # We will get it the next time around
        else:
            try:
                res = uopen(target)
                if res.code != 200:
                    print("[!]Error: " + str(res.code))
                else:
                    data = res.read()
            except Exception as e:
                print("[!]"+str(e))

        totalcount += 1
        if not data:
            if verbose: print("[-]No data from source: " + str(target))
            continue
        else:
            if verbose:
                sys.stdout.write(str(totalcount) + " of ~" + str(len(targetlist)) + " sources complete\r")
                sys.stdout.flush()
            else:
                pass
        ###
        ### Format the data
        ###
        data = re.sub("(<!--|-->)"," ",data) # keep comments as normal text
        data = re.sub("</?[^>]+>"," ",data) # remove the html tags
        data = re.sub("\r|\n"," ",data) # make it a strait file

        ###
        ### Add the new words
        ###
        allwords = word_reg.findall(data)
        allquotes = quote_reg.findall(data)
        for quote in allquotes:
            allwords.append(quote)
            allwords.append(quote.replace(" ",""))
            #flw = ''
            #for each in quote.split(' '):
            # if len(each) > 0: flw += each[0]
            #if flw: allwords.append(flw)

        for word in allwords:
            ###
            ### Mangle
            ###

            if( word.endswith('.') or
                word.endswith(',') or
                word.endswith('!') or
                word.endswith('?') or
                word.endswith(';') or
                word.endswith('"') or
                word.endswith('\'')):
                allwords.append(word.strip('.,!?;"\''))
            if re.match("\A.*\.(jpg|png|txt|com|html)\Z",word):
                allwords.append(word.rsplit('.',1)[0])

            ###
            ### Add
            ###
            if not word in words:
                words.append(word)

    total_wa = len(words)
    total_s = len(targetlist)
    words.sort()
    of = open(outfile,'w')
    for word in words:
        of.write(word+"\n")
    of.close()
    if verbose:
        print("[+]Complete!")
        print("[+]"+ str(total_wa) + " words in the list.")
        if append: print("[+]"+str(total_wa - total_wb)+" are new.")
        print("[+]Collected from " + str(total_s - dircount) + " sources.")

if __name__ == "__main__":
    ###
    ### User input
    ###

    verbose = 2
    minlen = 6
    maxlen = None
    find_quotes = True

    wordrules = ["A-z","A-z0-9","A-z0-9*-.!$#@%"]

    wordrule = None
    while not wordrule:
        print("Select a word rule:")
        for i,rule in enumerate(wordrules):
            print(str(i + 1) + " -- " + wordrules[i])
        print(str(i+2) + " Custom (WARNING: ADVANCED!! not validation)")
        que = raw_input("Rule[1-"+str(i+2)+"]:")
        try: que = int(que.strip())
        except: que = -1
        if que == i+2:
            wordrule = raw_input("Wordrule:").strip()
        elif que < 1 or que > i+2:
            print("Not a valid selection")
        else:
            wordrule = wordrules[i-1]

    if not minlen: minlen = 3
    outfile = raw_input("Filename:")
    if os.path.exists(outfile) and not outfile.startswith("+"):
        que = raw_input("[?]This file exists! Overwrite[y|N]:")
        if not 'y' in que.lower():
            exit(0)
    targetlist = raw_input("Input target list, separate by ';' no space or quote\n"+
                            "Use %g<query>%<numresults> to use google query sites\n"+
                            "Targets:")
    targetlist = targetlist.split(';')
    for target in targetlist:
        if re.match("%g[^%]+%[0-9]+",target):
            if verbose == 2: print("[+]Google sources: " + target[2:].split('%')[0])
            new_targets = google(target[2:].split("%")[0],target[2:].split("%")[1],verbose)
            targetlist.remove(target)
            targetlist.extend(new_targets)
    if verbose == 2:
        print("[+]Gathering data from the following targets:")
        for target in targetlist: print("[+]"+target)
        print("=============================================")
    ###
    ### Prepare and call
    ###
    word_reg = re.compile("(["+wordrule+"]{"+str(minlen)+","+str(maxlen)+"})")
    genWordlist(targetlist,word_reg,outfile,verbose)

Code: wordlistgenerator.py

Return to $2600 Index