Bullying phishers with Python

Today is my lucky day. I’ve been wondering what to post in my blog, as most of my projects are very unfinished, due to me working like a sprint relay team that is missing the anchor who brings the stick over the finish line.

However, possibly exactly due to this, Mr or Mrs. Phisher decided to intervene and flood our school’s emails today:

Photo credits: Jaakko Paikkala, http://jaakko.in

Basically, the message said that a phishing email has gone through our school’s emails and exposed most of the accounts of employees and students. Due to this, the recipient is asked to ‘verify’ and ‘activate’ their account by logging into our School’s site that is linked in the email. The message is written in pretty much excellent grammar and is pretty clever in creating a feeling of trust between the sender and the recipient. I myself would’ve probably clicked on the link if I had just woken up and decided to take a look in my inbox.

Clicking the link

What was behind the link? A copy of our school’s homepage. Sort of. What they had done was take a screenshot of the header of our school’s webpage and inserted it on top of their page, to make it look as if there were links. In addition to that, they had added a simple form that asked for userid, email and something called Email PasswOrd.

The site was hosted on weebly.com

Upon submitting, the site directed the user to our school’s actual page.
The phishing site itself wasn’t as well made as the email, as we can see with the inputted password not getting hidden, despite it being a relatively simple thing to do. Still, rumors say many were tricked into posting their password there, and the actual IT-department had to address everyone not to post their personal data on external sites.

I also received another email from a student telling me to do pretty much the same thing as in the original email, but on a different site. This also came from an actual student, which means that they probably fell for this scam and now served as a spambot.

Using Python to bully phishers

The idea

I have been crawling websites in my previous projects and learned a thing or two about using libraries related to it, such as BeautifulSoup and requests, so I thought, “Why not send some data to their form?”.

The idea was to send a combination of random userids, emails and passwords to cause at least some minor confusion when the phisher(s) process their gathered emails and passwords. In the best case scenario, the script they’re running is very basic and doesn’t have basic things such as logging of time of each form submit, so separating just my submissions from the real ones would be too much of a PITA to do. My motivation, however, is not to become a hero who slays the evil phisher. No, I’m just doing this for fun and to maybe learn something new.

The problems

The phishing emails were sent early in the morning, and I started working on this late in the day, so I knew it would be just a matter of time before they either pull the plug or Weebly takes their sites down. This also made the project more interesting, as I would have to work against the clock and write logical code straight from the top of my dome, as opposed to having time to rewrite things if they don’t look good.

Another problem was the question if it’s a good idea to randomly generate email addresses that contain an userid that might actually belong to a student. But I decided to go for it anyway. For science.


Tracking what gets POSTed in Chrome

Before I decided to do anything else, I wanted to find out just how easy it would be to send data to their form. In general, this is an easy thing to do as a POST request, but I wanted to see if they have taken precautions and, for example, given each password-giving user an unique id or something. So I opened up the phishing site, opened up my Chrome developer console with CTRL + SHIFT + I. From there, one has to browse to ‘Network‘ and then refresh the page or do an action in the site to see their network activity. This particular site does a redirect upon submitting the form, so I had to tick ‘Preserve log‘ before inputting fake data into the form and pressing ‘submit’. After this, the network activity tab filled up with different things and after a while, I found the one that was of the most interest to me: the script that handles form submitting.

From here I looked for two things: the url where the form-submit page was and what data it takes. The first mentioned can be found from the top of the page, whereas finding the POST parameters is in the bottom of the Headers tab under ‘Form Data‘.

My apologies for not using actual form data instead of gibberish

Here we can basically see the parameters that are sent with the post request:

  • The User ID, which I sent as ‘dfdfdf’ is sent as an attribute to _u697771171613901263
  • The email ‘fddffdg’ is sent using _u770510225617309293 parameter
  • The password ‘dssfdsdffd’ is sent with _u347286333246038102

Also, the form contains some sort of ID which is sent with the ‘ucfid’ -parameter.
I then took these parameters and made a simple Python-script that sent data to their form to test if I was on the right track.

import requests

def phising_bully():
    url = 'https://my-haga-helia-fi.weebly.com//ajax/apps/formSubmitAjax.php'
    data = {"_u697771171613901263": "Phi Sher", "_u770510225617309293": "Phi.Sher@haaga-helia.fi", "_u347286333246038102": "Dishwasher12",
            "ucfid": 361303802676750299}
    r = requests.post(url, data=data)


It did work, which basically gave the final green light on this project.

This article is written after both phishing sites were taken down, so I cannot show any response here.

Getting to work

Assigning prison codes

The site asked for school’s userid and these userids (or prison numbers as a certain teacher calls them) are always a 7-digit sequence preceded by ‘a’, for example, a1712345. This also easily done with random’s randomrange-function.

import random

def get_username():
    return first_letter + "1" + random.choice(["6","7"]) + str(random.randrange(10000,20000)) # First it adds either 6 or 7 and the 
    # string becomes a17, then it adds a random sequence of numbers between 10000 and 20000, for example 12345, thus the final string becomes a1712345

def main():
    username = get_username()

This was then combined with ‘@myy.haaga-helia.fi’ to generate a fully fake email.

Generating random, human-like passwords

This one required a bit more work as I didn’t want the passwords to look like a human has came up with them. Also, one thing to take into account was that passwords are usually required to contain numbers and also possibly symbols. I also didn’t have too much time to get this done so I couldn’t put too much effort into this.

I decided to do some quick googling and ended up creating

  • A list of 7070 Finnish words
  • A list of 100 of the most common and weak passwords
  • A list of 10000 most common American English words
  • A list of Finnish first names

However, I simply didn’t just want to randomly choose a word from each of these lists, I wanted to make certain words be used most commonly. This brings us to the next chapter in this blog.

The human factor – Making everything look more real

Humans tend to mistype certain things, some like to type their email address with first initial capitalized, etc. Therefore, I wanted to make everything seem a bit more real, as if a human had actually done them.

Humanizing emails
Even though school’s emails are all using the same
userid@myy.haaga-helia.fi format, one might accidentally type haaga-helia.fi or just typo the word entirely. Someone might spell their username with a capital A instead of a. I wanted to take this into account at least in some form.

def get_username():
    first_letter = random.choice(["a","A"])
    return first_letter + "1" + random.choice(["6","7"]) + str(random.randrange(10000,20000))

In the above code, there is a 50% chance that the user will type the A with capitals. Also, school user ids mostly begin with either 17 or 16, hence the other random.choice.

def get_domain():
    return random.choices(population=['myy.haaga-helia.fi', 'haaga-helia.fi', 'haag-helia.fi', 'haagahelia.fi',
                                      'myyhaaga-helia.fi', 'haaga-helia.net', 'myy.haaga-helia.net', 'gmail.com', 'live.fi'], weights=[60, 20, 5, 5, 5, 5, 10,10,5],k=1)[0]

Here I use randoms’ choices function, which takes a weights parameter. Basically with it, you can define which options have the highest chance of being chosen. In this instance, getting myy.haaga-helia.fi is most likely going to happen, followed by haaga-helia.fi, etc.


At this point, I decided to do a little test with one of the urls with my newly generated fake emails. To my horror I noticed that it had been taken down. The clock had started ticking faster.

import string
def get_password():
    random_number = random.randrange(0,15)
    random_symbols = ["!","?","@", "§", "$"]
    if random_number == 1:
        return ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(random.randrange(8,20))) # Generates a password with random letters, varying between 9 and 19 characters in length
    elif random_number in [2,3]: #Generates a password that is firstname with first letter capitalized + random sequence of 2 digits + a random symbol
        firstname = get_firstname()
        return firstname.title() + str(random.randrange(10,20)) + random.choice(random_symbols)
    elif random_number in [4,5]: # chooses a random password from the most common passwords list
        return random.choice(finnish_words_file).title() + random.choice(random_symbols)
    elif random_number in [6,7]: # Chooses a password from the common passwords -list
        if random_number == 7: #first letter will be capitalized and a symbol will be added to the end
            return random.choice(common_passwords_list).title() + random.choice(random_symbols) 
            return random.choice(common_passwords_list) # Only a random word from the common passwords list will be chosen
    elif random_number == 8: # A combination of firstname with first letter capitalized + two digits
        firstname = get_firstname()
        return random.choice(common_passwords_list) + firstname.title() + str(random.randrange(10,20))
    elif random_number == 9: # A random finnish word + 2 to 3 digits
        return random.choice(finnish_words_file) + str(random.randrange(10,200))
    elif random_number == 10: # A random english word with first letter capitalized + 2 or 3 digits
        return random.choice(common_english_words_list).title() + str(random.randrange(10,200))
    elif random_number == 11: # A random english word without first letter capitalized + 2 or 3 digits
        return random.choice(common_english_words_list) + str(random.randrange(10,200))
    else: # A random finnish word with first letter capitalized + 2 or 3 digits
        return random.choice(finnish_words_file).title() + str(random.randrange(10,200))

When creating the password, I just quickly threw things from the top of my head so I could get this code working. Same goes for the randomness-weight-implementation.

Running the program
Now it was time to finally run the program. I decided to give make it sleep for 0.1 seconds after each loop. Cause I’m nice.

def phising_bully(userid, email, password):
    print("Sending a form with the following info: userid %s, email %s, password %s" % (userid, email, password))
    url = 'https://haaga-hel1a-helpdesk.weebly.com/ajax/apps/formSubmitAjax.php'
    data = {"_u697771171613901263": userid, "_u770510225617309293": email, "_u347286333246038102": password,
            "ucfid": 407087612984639270}
    r = requests.post(url, data=data)
    html_response = r.text
    soup = BeautifulSoup(html_response)
    response_json = json.loads(soup.find(id="response").text) # They didn't even bother creating a proper response..
    if not response_json:
        print("No response div found")
    if response_json.get('success'):
        print('Succesfully sent fake form data')
        output_file.write("%s   %s  %s  %s\n" % (userid, email, password, datetime.datetime.now())) # I also logged every sent combination to a file
        print('Failed to send form data: ', response_json)

def main():
    password = get_password()
    random_domain = get_domain()
    username = get_username()
    email = ("%s@%s" % (username, random_domain))
    phising_bully(username, email, password)

while True:
Beautiful. Just beautiful.

I let it run for a while and it send around two requests per second. Until bam! Someone pulled the plug. Either it was El barón  himself or it was Weebly, nobody knows. In total, It managed to send around 900 userids.

RIP bot, 2019-2019


Working against time was a really fun experience, as you have to prioritize certain things over others.

I also have to add that at first the program didn’t look exactly like what I posted here — I mistakenly used a combination of firstnames and lastnames in the email, until I remembered that we use our school’s userid for logging in. Duh.

I am also a newbie programmer, so anything I say should not be taken seriously. Feel free to comment if I did something wrong or said something which is absolutely not true.

Update 8.2.2019:

The phishers have spread their emails a few times over the past few weeks, but their sites have been taken down before I’ve had a chance to send things to them. Until now. This time, I managed to send around 20k of random user-id’s. After this, I sadly haven’t gotten any more emails.

You can now also find this little script on github:


Jaakko Paikkala (photo used)

Ilta-Sanomat, 10.9.2009 – 30 yleisintä salasanaa,

Ilta-Sanomat, 10.9.2009 – Yleisimmät salasanat ovat surullista katseltavaa – tee edes tämä itseäsi suojellaksesi

Niko Lipsanen @ domnik.net, 12.11.2011 : 100 yleisintä salasanaa

Saaressa-blogi, 06/2017 – Sanomalehtikielen taajussanasto

First20hours – 10000 most common American English words

1 thought on “Bullying phishers with Python

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this:
search previous next tag category expand menu location phone mail time cart zoom edit close