Scripting – TryHackMe Writeup

In this writeup I’ll give and explain the scripts I used to get the flags for Scripting room on TryHackMe platform. We have three challenges which are aimed to solve them writing some python/bash scripts.

Challenge 1

We are given with a file which has been base64 encoded 50 times. We have to write a script which takes the files as an argument and decodes it.

We should try do this in both Bash and Python, you can see them below:

Bash

#!/bin/bash

if [ $# -ne 1 ]; then    # Make sure b64 encoded file is given in parameters
    echo "Usage: $0 <file to decode>";
    exit 1;
fi

value=`cat $1`    # Read file and save content in variable

while :; do    # Infinite loop
    aux=$(echo $value | base64 -d 2> /dev/null);    # Try to decode the string
    if [ $? -ne 0 ]; then    # If exit status from previous command is not 0
        break;
    fi
    value=$aux;    # If there was not error reassign b64 decoded string
done

echo "Decoded string: "$value;

Python

#!/usr/bin/python3

import base64
import sys

# Make sure b64 encoded file is given in parameters
if len(sys.argv) != 2:
    print("Usage: %s <file to decode>" % sys.argv[0])
    exit(1)

# Read file and save content in variable
f = open(sys.argv[1])
value = f.read().encode()
f.close()

# Decode the string 50 times
for i in range(50):
    value = base64.b64decode(value)

print("Decoded string:", value.decode())

From the previous scripts, I prefer bash version since I don’t have to hard-code how many times to decode the string.

Challenge 2

For this challenge we have the following instructions:

You need to write a script that connects to this webserver on the correct port, do an operation on a number and then move onto the next port. Start your original number at 0.

The format is: operation, number, next port.

For example the website might display, add 900 3212 which would be: add 900 and move onto port 3212.

Then if it was minus 212 3499, you’d minus 212 (from the previous number which was 900) and move onto the next port 3499.

Do this until you the page response is STOP (or you hit port 9765).

https://tryhackme.com/room/scripting

Also we know the following:

  • first port is 1337
  • each port is live for 4 seconds

We are also told the best options is to use sockets python library.

You can see the full script below:

#!/usr/bin/python3

import sys
import socket

# Make sure server IP is given in parameters
if len(sys.argv) != 2:
    print("Usage: %s <ip>" % sys.argv[0])
    exit(1)

# Declaring request snippet
request = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n" % sys.argv[1]
# print(request)

port = 1337  # Starting port
value = 0    # Starting value
waiting = False
print('Port:', port)

while True:
    try:
        # Start socket connection
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect((sys.argv[1], int(port)))  # If port is not open will raise a ConnectionRefusedError exception and will try again
        s.send(request.encode())
        r = s.recv(1024)
        r = r.decode().split('\n')[-1]
        if r != '':
            print('->', r)
    
        if "STOP" in r:
            break
	
        # Declare four operation options
        operation, v, port = r.split(' ')
        if operation == 'add':
            value += float(v)
        if operation == 'minus':
            value -= float(v)
        if operation == 'multiply':
            value *= float(v)
        if operation == 'divide':
            value /= float(v)
        
        print('-' * 32)

        print('Port:', port)
        waiting = False
    
    # If port is still not open
    except ConnectionRefusedError:
        if not waiting:
            print('Waiting for connection...')
            waiting = True
    except ValueError:
        pass

print("\nValue: %.2f" % value)  # Print result rounded 2 decimals places

In below image you can see the output shows when we run the script:

Running script.

After a while, we get the final value:

Getting final flag.

Challenge 3

This challenge is a little bit difficult to understand. We just have the following:

The VM you have to connect to has a UDP server running on port 4000. Once connected to this UDP server, send a UDP message with the payload “hello” to receive more information. You will find some sort of encryption(using the AES-GCM cipher). Using the information from the server, write a script to retrieve the flag.

https://tryhackme.com/room/scripting

The best approach is to use python socket library in this challenge too.

Although this is not the best challenge to solve with a single script, since we have to add a lot of values manually because we have to follow instructions from UDP socket responses with the following script you can get the response as it was taylor-made for it.

I based on this StackOverFlow question about AES-GCM decryption in python.

#!/usr/bin/python3

import sys
import socket
import hashlib
from Cryptodome.Cipher import AES
import binascii

# Make sure server IP is given in parameters
if len(sys.argv) != 2:
    print("Usage: %s <ip>" % sys.argv[0])
    exit(1)

# Save socket address
addr = (sys.argv[1], 4000)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Declare UDP socket
s.sendto(b'hello', addr) # Send payload 'hello' to get more information
r, addr = s.recvfrom(1024)
print(r.decode())

s.sendto(b'ready', addr) # from previous response we know we have to send payload 'ready'
r, addr = s.recvfrom(1024)
print(r)

print()

# Hard-coded splits for easy
key, iv, checksum = r[:28].decode().split(':')[1], r[29:44].decode().split(':')[1], r[104:136]
print('key:', key)
print('iv:', iv)
print('checksum:', checksum)
hex_checksum = binascii.hexlify(checksum).decode()
print('checksum:', hex_checksum)

# AES-GCM decryption
def decoder(key, iv, text, tag):
    cipher = AES.new(key.encode(), AES.MODE_GCM, iv.encode())
    return cipher.decrypt_and_verify(text, tag)

# A per hint some of the tags intentionally corrupted, so we should try multiple times
while True:
    s.sendto(b'final', addr) # buffer size is 1024 bytes
    text, addr = s.recvfrom(1024)
    #print('text:', text)

    s.sendto(b'final', addr) # buffer size is 1024 bytes
    tag, addr = s.recvfrom(1024)
    #print('tag:', tag)
    
    test_flag = decoder(key, iv, text, tag)
    if hashlib.sha256(test_flag).hexdigest() == hex_checksum:  # Compare hexdigest
        print('\nFlag:', test_flag.decode())
        break

In general the challenges were not too hard but the descriptions are not very good (more specific for the last one) and it could be confusing to know what to do and the conditions to solve it.

Anyways, with the scripts in this post you can get the flags in the first attempt.