Me

Stefan

Cybersecurity Specialist

Issessions 2021

A writeup on Sheridan's ISSessions 2021 CTF

Stefan

14-Minute Read

ISSessions

This post contains some of the challenges that were featured at Sheridan’s ISSessions 2021 CTF.

About

On March 27th and 28th, Sheridan held their ISSessions 2021 Online CTF. The CTF contained over 70 challenges ranging from Web, Cryptography, Programming, Sysadmin, Reversing, Trivia, Forensics, Steganography, Packet Capture Analysis, Data Analysis, and even Threat Hunting!

Over 500 people participated in teams of 4, which included many Colleges and Universities throughout Ontario. Our team Eternal Pizza consisted of myself and three of my friends from Seneca. We went into this competition knowing that Sheridan was gonna put up a good fight (and they definitely did), but most importantly, we came expecting to have fun, and Sheridan definitely delivered.

Prizes

The prizes in this CTF were very generous to say the least. Thanks to all the great sponsors, the top 3 teams would win their choice of a bundle valued at $360! After that, the next top 4 - 20 teams would recieve TryHackMe subscriptions. Even though Alumni were allowed to compete, prizes were only to be redeemed by students.

The “Book Worm” Bundle

  • 4x Blue Team Field Manual (Hard Copy)
  • 4x Red Team Field Manual (Hard Copy)
  • 4x Linux Toolbox Comics (Hard Copy)
  • 4x Black Hat Python, No Starch Press (Hard Copy)

The “Arduinist” Bundle

  • 4x Arduino Ultimate Starter Kit + 260-Pages of Detailed Tutorials
  • 4x Arduino Workshop: A Hands-On Introduction with 65 Projects, No Starch Press (Hard Copy)

The “Generalist” Bundle

  • 4x Sparrows Lockpicking Sets + Practice Cutaway Lock
  • 4x Backdoors & Breaches Card Game by Black Hills InfoSec
  • 4x $25 Amazon Gift Cards

Results

After two long days of constant grinding at these challenges, we ended up finishing in third place.

error loading image

Overall, this CTF was fairly challenging with lots of variety in challenges and difficulty. The hard work that the ISSessions staff put into this CTF definitely paid off, as everyone seemed to have a great time.

At the closing ceremony, the ISSessions staff made a powerpoint presentation outlining some statistics during the CTF, the first of which showed most category solves per team.

error loading image

error loading image

This gave us great insight into what categories we should put more focus on when training for CTFs in the future.

Other statistics were also available for which individuals solved the most challenges, which teams solved most of the challenges, and the score for each individual.

error loading image

error loading image

error loading image

Since we placed third, our prize was going to be whatever was left over after the first two teams made their choices. Fortunately for us, we got very lucky and got exactly what we wanted.

error loading image

Challenges

Although these aren’t all of the challenges that were featured during the CTF, here are some of the solutions I used for the challenges that I found particularly interesting/fun. Most of these are going to be Programming challenges.

Hash Poetry

This was a programming challenge that had us connect to a script that was listening on a TCP port. Once connected using netcat, prompts would come up with a random sentence, with our job being to hash the given sentence in sha1 and send it back to the port. The kicker however, was that we had to do this for 860 sentences consecutively.

Obviously doing this manually would take unreasonably long during a CTF where time is precious, so I wrote the following Python script which completed the challenge in a minute.

import socket
import time
import subprocess
import hashlib

#AF_INET for IPv4, SOCK_STREAM for TCP.
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Tell the socket what IP and port number to connect to.
clientsocket.connect(('challenges.ctf.issessions.ca', 30500))

# Recieve banner data.
data = clientsocket.recv(2048)
time.sleep(1)

# Hash the sentence, send it back, and recieve the next sentence.
for i in range(861):
        print("reading data:", "on line:", i)
        data = clientsocket.recv(2048)
        print(data)
        data = data.split('\n')
        unhashed = data[0][6:]
        hashed = hashlib.sha1(unhashed.strip())
        print(hashed.hexdigest())
        clientsocket.send(hashed.hexdigest().encode()+"\n")
        time.sleep(0.1)

Bell - Threat Intelligence One Tweet At A Time

Bell being one of the sponsors of the CTFs, there were some challenges in there name under the Threat Hunting category. This challenge in particular, was my favourite challenge out of the entire CTF because it was nothing like I’ve ever done before. The challenge instructions went something like this:

Instructions

Background:

We received a new intelligence from our partners at the NSO. They have successfully intercepted a message that contains details on another big scale attacks on Canadian institutions. We believe the parties involved in this conversation are the command center and the operators. Our partners at NSO are expecting us to assist them with finding the main organizations behind this attack, where it can be a low level criminals, financial crime groups or even governmental agencies. We have to find their address of the headquarters and perform necessary action to stop them from launching the attacks. Use Open Source Intelligence (OSINT) methodology to find the information.

Important Notes:

Based on previous investigation, we saw operators were using Facebook, Twitter, LinkedIn, and even reddit to spread messages and instructions. You might need social media account on each platforms to get the full information while performing OSINT.

Task:

Find the address of the main organization headquarters.

In this challenge, all we get is a zip file with a bunch of images and a text file.

error loading image

Our first clue is that the text is in Russian, so we can assume the location is somewhere in Russia. The text file can be translated into the following:

Block 26165 operators:

Our new officer's Twitter handle is attached. Orders for the Canadian offensive from Moscow will be transferred from this account.
use domain.tld / username to access the account, it is not indexed on search engines

PS: use the language of the sea.

Goodbye,
Base plate

In this text file, we know that:

  • The headquarters is located in Moscow, Russia
  • One lead we can use is a Twitter account of an officer
  • We need to use “the language of the sea” to decode something

Doing any forensics on the text file did not give us any further information, so we turned to the 17 images that were also inside the zip file. After doing a bit of research, we learnt that these images contain Semaphore Flags, which is used to convey text using visual flag signals.

error loading image

Decoding this signals gives us the Twitter handle WHITECAMOMILE92. Searching Twitter, we found a user with this exact handle.

error loading image

We then went to this profile and read through the tweets, which gave us a bunch of clues as to where this Officer works. Some of his most notable tweets included:

error loading image

error loading image

error loading image

error loading image

error loading image

error loading image

Reverse image searching all the images in these tweets didn’t get us anywhere, but at least we got a few clues about the office:

  • It has two Helipads
  • Is near Avia Park
  • Is on the Moscow bus route 818 and 175
  • Is right accross Yorkshir Pub
  • Is 5 minutes out from Horosho Mall
  • A Football stadium is located 1KM South from the office

To track these clues, I used Google Maps Satellite View.

With the center of Moscow as my starting location, I began looking for a building with two helipads by looking near some landmarks. Firstly, I looked up the Moscow bus routes 818 and 175, and marked them down. Second, I marked a 400m radius (roughly the distance a person walks in 5 min) around Horosho Mall. Third, I marked a 1km radius around the Football stadium near Horosho Mall (I couldn’t find a Yorkshir pub anywhere near this place)

error loading image

I took a look at the street where the green and red circles and lines cross, and I couldn’t find any buildings with two Helipads, so I kept following the lines south until I finally found the building, just outside the two circles.

error loading image

This location (literally translated to “Main Intelligence Directorate”), was located on Grizodubovoy street, which was the correct answer to the challenge.

Programming Conversion Challenges

In this CTF, there were 4 very similarly written programming challenges. They consisted of having to request a web page, perform the following action written on the page, then send a response within a short amount of time (the following code is going to be extremely messy as a result of rushing).

Programming Base64

For this programming challenge, we would receive a random blob of text that would have to be returned back to the page in base64 encoding within 3 seconds.

import requests
import base64
import hashlib

s = requests.Session()

raw = s.get('http://progbase64.ctf.issessions.ca/')

raw_string = raw.content

print(raw_string)
print(" ")

new_string = raw_string[-210:-50]

print(new_string)
print(" ")

base64_bytes = base64.b64encode(new_string)

base64_text = str(base64_bytes,'utf-8')

print(base64_text)
print(" ")

URL = "http://progbase64.ctf.issessions.ca/index.php?answer=" + str(base64_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

For this challenge, all you would have to do to get the base64 value of the text is to use the Python built-in base64 function.

Programming Binary

For this programming challenge, we would receive a random blob of binary code that would have to be returned back to the page in decimal form within 3 seconds.

import requests
import hashlib
from decimal import Decimal

s = requests.Session()

raw = s.get('http://progbinary.ctf.issessions.ca/')

raw_string = raw.content

print(raw_string)
print(" ")

new_string = raw_string[-64:-50]

better_text = str(new_string,'utf-8')

print(better_text)
print(" ")

decimal_text = 0
acc = 0
for char in better_text:
        acc += 1
        decimal_text += int(char)*(2**(len(better_text)-acc))
        print(decimal_text)
print(decimal_text)
print(" ")

URL = "http://progbinary.ctf.issessions.ca/index.php?answer=" + str(decimal_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

For this challenge, I took every bit in the binary string, converted it into its decimal value, and added it them into a single value.

Programming Sha256

For this programming challenge, we would receive a random blob of text that would have to be hashed using SHA256 and returned back to the page within 3 seconds.

import requests
import hashlib
from decimal import Decimal

s = requests.Session()

raw = s.get('http://progsha256.ctf.issessions.ca/')

raw_string = raw.content

print(raw_string)
print(" ")

new_string = raw_string[-210:-50]

sha256_text = hashlib.sha256(new_string).hexdigest()
print(sha256_text)
print(" ")

URL = "http://progsha256.ctf.issessions.ca/index.php?answer=" + str(sha256_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

For this challenge, I imported the hashlib library for Python, and used the Sha256 function to hash the given text.

Programming Morse Code

For this programming challenge, we would receive a random blob of Morse Code that would have to be decoded and retuirned back to the page within 3 seconds.

import requests
from bs4 import BeautifulSoup

s = requests.Session()

raw = s.get('http://progmorse.ctf.issessions.ca/')

sample_string = raw.content

print(sample_string)
print(" ")

soup = BeautifulSoup(sample_string, "lxml")
item = soup.findAll('p')

better_item = str(item[1])[16:-5]

print(better_item)

def convert(raw):
        newWord = ""
        d = {
        ".-":"a", "-...":"b", "-.-.":"c",
        "-..":"d", ".":"e", "..-.":"f",
        ".----":"1", "--.":"g", "....":"h",
        "..":"i", ".---":"j", "-.-":"k",
        ".-..":"l", "--":"m", "-.":"n",
        "---":"o", ".--.":"p", "--.-":"q",
        ".-.":"r", "...":"s", "-":"t",
        "..-":"u", "...-":"v", ".--":"w",
        "-..-":"x", "-.--":"y", "--..":"z",
        "..---":"2", "...--":"3", "....-":"4",
        ".....":"5", "-....":"6", "--...":"7",
        "---..":"8", "----.":"9", "-----":"0"
        }
        for word in raw.split():
                newWord += d[word]
        return(newWord)

morse_text = convert(better_item)

print(morse_text)
print(" ")

URL = "http://progmorse.ctf.issessions.ca/index.php?answer=" + str(morse_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

In this script, I had to make a dictionary that contains all the letters and numbers, and their morse code definition. Then I would just add the character that the morse code would represent to a sentence, and send the sentence to the page.

Programming Nato

For this programming challenge, we would receive words from the Nato Phoenetic Alphabet which would have to be translated and sent back to the web page within 3 seconds.

import requests
import hashlib
from bs4 import BeautifulSoup

s = requests.Session()

raw = s.get('http://prognato.ctf.issessions.ca/')

sample_string = raw.content

print(sample_string)
print(" ")

soup = BeautifulSoup(sample_string, "lxml")
item = soup.findAll('p')

better_item = str(item[1])[16:-5]

print(better_item)
print(" ")

def convert_nato(raw):
    newWord = ""
    d =  {
        'alpha': 'a',  'bravo': 'b',   'charlie': 'c',
        'delta': 'd',  'echo': 'e',    'foxtrot': 'f',
        'golf': 'g',   'hotel': 'h',   'india': 'i',
        'juliett': 'j','kilo': 'k',    'lima': 'l',
        'mike': 'm',   'november': 'n','oscar': 'o',
        'papa': 'p',   'quebec': 'q',  'romeo': 'r',
        'sierra': 's', 'tango': 't',   'uniform': 'u',
        'victor': 'v', 'whiskey': 'w', 'x-ray': 'x',
        'yankee': 'y', 'zulu': 'z'}
    for word in raw.split():
        newWord += (d[word])
    return(newWord)

nato_text = convert_nato(str(better_item))

URL = "http://prognato.ctf.issessions.ca//index.php?answer=" + str(nato_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

Just like last time, for this script I made dictionary containing all the definitions in the Nato Phoentic alphabet, with their letter counterparts. Then, I just replace each word with the letter, combine them, and send it off to the web page.

Programming Equation

For this programming challenge, we would receive a random math equation to solve, and return the answer back to the page within 3 seconds.

import requests
from bs4 import BeautifulSoup

s = requests.Session()

raw = s.get('http://progequation.ctf.issessions.ca/')

raw_string = raw.content

print(raw_string)
print(" ")

soup = BeautifulSoup(raw_string, "lxml")
item = soup.findAll('p')

better_item = str(item[1])[16:-8]

equation_text = eval(better_item)

print(equation_text)
print(" ")

URL = "http://progequation.ctf.issessions.ca/index.php?answer=" + str(equation_text)
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

In this script, I just used the Python built-in function eval to complete the equation.

Zip-di-doo

This programming challenge gave us a file that was compressed 1000 times, with the final uncompressed file being the flag. To decompress all the files quickly, I wrote this Python script:

import magic
import os
import gzip

for i in range(1001):
        print("Now processing file #"+str(i))
        files = os.popen("ls").read().split('\n')
        filename = files[0]
        print(filename)
        filetype = magic.from_file(filename)
        if ("gzip" in filetype):
                print("decompressing gzip")
                os.system("gzip -d --name "+filename)
        elif ("Zip archive data" in filetype):
                print("decompressing zip")
                os.system("unzip "+filename)
        elif ("POSIX tar archive" in filetype):
                print("decompressing tarball")
                os.system("tar -xvf "+filename)
        elif ("7-zip" in filetype):
                print("decompressing 7zip")
                os.system("7za e "+filename)
        os.system("rm "+filename)

error loading image

This script works by checking the name of each file, reading what type of compression it using, and decompressing it accordingly using the magic library and the os library for running commands in the command-line. This took some trial and error as I didn’t know what all the compression types were, but overall was a pretty quick challenge to solve.

Programming Image1

For this programming challenge, we had to go to a web page that sent a random image with text in it. The goal was to read the text off the image, and send it back to the same webpage within 3 seconds. The image would look something similar to the following:

error loading image

I wrote the following script to solve the challenge:

import requests
from bs4 import BeautifulSoup
import os
import time

s = requests.Session()

raw = s.get('http://progimage.ctf.issessions.ca:15000/')

raw_string = raw.content

print(raw_string)
print(" ")

soup = BeautifulSoup(raw_string, "lxml")
item = soup.findAll('p')

image_url = str(item[1])[70:-19]

print(image_url)

raw_image = s.get('http://progimage.ctf.issessions.ca:15000/'+image_url)
with open('/home/user/Downloads/ISSessions/Programming Image1/image.gif', 'wb') as f:
    f.write(raw_image.content)

os.popen('tesseract image.gif result --dpi 300')
time.sleep(0.5)

image_result = open("/home/user/Downloads/ISSessions/Programming Image1/result.txt", "r")

image_result_string = image_result.read().lower()
image_result.close()

print(image_result_string.strip())
print(" ")

URL = "http://progimage.ctf.issessions.ca:15000/index.php?answer=" + image_result_string.strip()
print(URL)
print(" ")

flag = s.get(URL)
print(flag.content)

For this challenge, I first parsed the page for the image URL. Then, I downloaded the image directly and began reading it. To read the image, I used Tesseract by downloading it on my machine, and running a command from the script to read the image, and put the results in a file. The script would then read the results from the file, and send them for the flag.

Louai The Saboteur

This Forensics challenge tasked us with fixing a broken file, with the flag being the hex bytes we have to add to the file to fix it. After downloading the file, I opened it in an online hex editor to see what was there. After scrolling for a bit, I noticed the headers NETSCAPE2.0, which indicated that the file had the Netscape Looping Application Extension.

error loading image

However, I also noticed that there weren’t any GIF headers in the file. This ended up being the answer, as after I added the GIF headers (that I got from Gary Kessler) to the file and changed the file extension to .gif, the file was able to be played.

error loading image

error loading image

Plaintext Protocols

This packet analysis challenge had us analyze a simple PCAP file showing a GET request being made. The GET request seems requested some kind of file, which can be seen in payloads of the TCP packets, since the request was made through HTTP.

error loading image

Once I exported the file as an HTTP object, I tried to open it to no avail. Checking the headers of the file in a hexdump, I noticed that it had only half of its headers. Can you guess what type of file this is based on the broken header?

error loading image

If you guessed PNG, you are correct. After fixing the headers (with the help of Gary Kessler), I renamed the file with the .png extension and opened the file to see the flag.

error loading image

error loading image

What are you token about Jason

This web challenge had us crack a JWT. We were given a login page, and credentials for an unprivileged account. The goal was to escalate privileges by manipulating the JWT to show our account as an admin account.

The first thing I did was grab the JWT from the Storage section in my browser developer tools, and put it in jwt.io

error loading image

The next reasonable step would be to change is_admin to true. However, every time anything in the token is changed, the token has to be re-signed using the original secret key. Fortunately, there is a tool out there to help us brute force this value. Using this tool, I was able to brute force the secret key.

error loading image

After cracking the secret key, I just changed the is_admin value in the token to true, used my secret key to sign it, and replaced the original JWT value in my browser to give my user higher privileges.

error loading image

Recent Posts

Categories

About

A blog made for posting tutorials about various topics.