Rating:

NeverLAN CTF 2020

Start: Sat, Feb 8 2020, 10:00 AM EST

End: Tue, Feb 11 2020, 7:00 PM EST

Placing

  • Students: 1st (max score)
  • General: 31st

Review

This weekend I played in NeverlanCTF as the solo team bosh. The challenges were alright, although many of them were just guessing.

I didn't like how they did not announce challenge drops before they actually released them, and how the platform went down after the last wave of challenge releases.

Writeups

Table of contents

Challenge NameCategoryPoints
Adobe PayrollReverse Engineering100
Script KiddieReverse Engineering100
Reverse EngineerReverse Engineering200
PigsflyCryptography30
BaseNot64Cryptography50
Don't Take All KnightCryptography75
The InvisiblesCryptography75
Stupid CupidCryptography100
My Own EncodingCryptography200
BabyRSACryptography250
CryptoHoleCryptography250
It is like an onion of secretsCryptography300
Unsecured LoginPCAP50
Unsecured Login2PCAP75
FTPPCAP100
Teletype NetworkPCAP125
hidden-ctf-on-my-networkPCAP250
Listen to thisForensics125
Open BackpackForensics150
Look into the pastForensics250
DasPrimeProgramming100
password_crackProgramming100
Robot TalkProgramming200
BitsnBytesProgramming200
EvilProgramming200
Front page of the InternetRecon50
The Big StageRecon75
The LinkRecon75
Thats just PhreakyRecon200
Cookie MonsterWeb10
Stop the BotWeb50
SQL BreakerWeb50
SQL Breaker 2Web75
Follow Me!Web100
Browser BiasWeb150
Chicken Little 1Chicken Little35
Chicken Little 2Chicken Little36
Chicken Little 3Chicken Little37
Chicken Little 4Chicken Little38
Chicken Little 5Chicken Little39
Chicken Little 6Chicken Little40
Chicken Little 7Chicken Little100
Milk PleaseTrivia10
Professional GuessingTrivia10
Base 2^6Trivia10
AAAAAAAAAAAAAA! I hate CVEsTrivia20
Rick Rolled by the NSA???Trivia50

Reverse Engineering

Adobe Payroll

Once we unzip with 7z we end up with two files: description.MD Adobe_Employee_Payroll.exe

description.MD hints towards a software called DotPeek.

DotPeek decompiles (read: translates to semi human readable code) .NET files, such as our .exe file. After we open Adobe_Employee_Payroll.exe up, we can explore the files:

Double clicking on something seems like a good idea. Now we can see the decompiled code for something that looks important: As you can see, r1, r2, r3 and so (till r38) on seem to be holding integers. They seem like they're ASCII values... and they are!

Without even looking at the rest of the program, we can find the flag by converting all the values from r1 through r38 to letters.

Script Kiddie

The "encrypted" encrypted_db is really just a bunch of base64 strings, which are then encoded using hex.

We can decode by using this script:

from base64 import b64decode

f = open("encrypted_db").read().strip().replace("\n", "").decode("hex")

f = b64decode(f.strip())

print(re.findall(r'flag{.+?}', f))

Running the script gives the flag.

Reverse Engineer

Let's open it up in Ghidra. We scroll until we find a cool looking function called print. It seems to be building a string from hex values.

Converting all those from hex to ASCII gives the flag.

Cryptography

Pigsfly

Basic substitution cipher, using the pigpen cipher alphabet.

Decrypting gives the flag.

BaseNot64

As from the title, the data is not in base 64. We play around with the different encodings in cyberchef.

Noticing that there are only capital letters / symbols in the plaintext, we can probably guess that the base will be relatively small. We try to decode it as a base 32 string, and it works.

Decoding gives the flag.

Don't Take All Knight

Another basic substitution cipher, using the Knights Templar Code cipher. Decode.f<span>r</span> has a nice solver.

Decrypting gives the flag.

The Invisibles

Yet another basic substitution cipher, using the Arthurs and the Invisibles alphabet. Decode.f<span>r</span> has a nice solver.

Decrypting gives the flag.

Stupid Cupid

Googling Cupid cipher gives us some results about how James Madison hid his plaintexts in some text using numbers.

When we count the amount of numbers in the ciphertext at the top of the file, we find that it is equal to the amount of rows there are, and the highest number in the ciphertext does not exceed the number of columns :eyes:

We then guess that each row corresponds to a character of the plaintext, and each number in the ciphertext is the corresponding column to pick from.

For example: The first number in the ciphertext is 6. The character at the first row, 6th column is V. Then, the second number in the ciphertext is 12. The character at the second row, 12th column is E.

Continue to get the flag.

My Own Encoding

We are presented with 16 5x5 grids, each with a single box blacked out. Shot in the dark. We reason that since it is 5x5, there are 25 choices which is close enough to 26 (letters of the alphabet).

We assume the top left represents the letter A, and the next one to the right represents B, and so on.

Doing so, we get MHBDI..., until we reach the 12th box. There is no marking here! Another shot in the dark. We assume that no marking represents the letter A.

Thus, our previously transcribed "plaintext" has to be Caesar shifted by 1 to get the flag, easy enough.

BabyRSA

Questionable RSA. I didn't know what to do with the numbers since they were spaced out. I finally guessed they were individual characters of the flag and successfully decrypted them as such.

Using factordb, we can factor n into 17 * 149, obviously the p and q for this challenge.

Now, using modified code from a StackExchange article, we can decrypt the flag character by character.

# Function from Geeks for Geeks
def modInverse(a, m): 
    m0 = m, y = 0, x = 1
    if (m == 1): 
        return 0
    while (a > 1) : 
        q = a // m 
        t = m 
        m = a % m 
        a = t 
        t = y 
        y = x - q * y 
        x = t
        
    if (x < 0):
        x += m0
        
    return x 


def decrypt(ct):
    p = 17
    q = 149
    e = 569

    # compute n
    n = p * q

    # Compute phi(n)
    phi = (p - 1) * (q - 1)

    # Compute mult mod inv of e
    d = modInverse(e, phi)

    # Decrypt ciphertext
    pt = pow(ct, d, n)
    print(chr(pt), end="")


chall = "2193 1745 2164 970 1466 2495 1438 1412 1745 1745 2302 1163 2181 1613 1438 884 2495 2302 2164 2181 884 2302 1703 1924 2302 1801 1412 2495 53 1337 2217".split()

# this is probably bad practice but it works
list(map(lambda x: decrypt(int(x)), chall))

CryptoHole

Basically just a bunch of ciphers. Each level has a chal.txt which contains an encrypted password, and a password-protected zip file for the next layer.

Here is the order:

Layer 1 -A ffine Cipher here 3:

  • Affine Cipher
  • Brute force
  • Password is AfvqPZW0bDMB&HTfzo

Layer 2 - Two is better than one

  • Double Transposition Cipher
  • Both keys are NEVERLANCTF
  • Ciphertext decrypts to PASSWORDV78DTNRI6KBD3SDFQXXXXXXXX
  • Password is V78DTNRI6KBD3SDFQ

Layer 3 - I'm on the fence with this one

  • Rail Fence Cipher:
  • Brute force
  • password:·VSEAS5aevg8Bwlovr

Layer 4 - Salad Time

  • Keyed Caesar Cipher
  • Key is neverLANCTF
  • Shift is 0
  • Password is gTLvCGk$HyRVSssXVaSX

Layer 5 - ROTten

  • Standard Caesar Cipher
  • Shift is 13
  • Password is e1Ydr*zxOOybF6RR%h5f

Layer 6 - Vigenere Equivent E

  • Standard Caesar Cipher
  • Shift is 22
  • Password is fI7BPZL#ZN5PI!&pbTXc

Layer 7 - Easy one

  • Base64 encoded string
  • Password is vxw@Ztet#ZfBnYVxJ1IM

Layer 8 - Message indigestion

  • MD5 digest
  • Brute force
  • Password is password23

Layer 9 - For SHA dude

  • SHA1 digest
  • Brute force
  • Password is applez14

Layer 10 - ONE more TIME

  • One Time Pad
  • First part of key is This is our world now... from chal.txt
    • Hints to a section from the Hacker Manifesto
  • Full key is: This is our world now... the world of the electron and the switch, the beauty of the baud. We make use of a service already existing without paying for what could be
  • Decrypt the OTP to get the flag

It is like an onion of secrets

We get download the png image. In it is a funky lookin dog with no flag :(

We guess it is LSB encrypted since Binwalk doesn't return anything useful.

We plug it into stylesuxx's LSB tool and we decode.

We get some base64, which we now play with a bunch in CyberChef.

After we base64 decode it twice, we get a bunch of: lspv wwat kl rljvzfciggvnclzv

Now we guess again and decrypt it using the Variant Beaufort Vigenere cipher (found on Cryptii's Vigenere tool) and key NeverLANCTF. The plaintext gives the flag.

PCAP

Unsecured Login

You don't even need Wireshark, just use strings mysite.pcap | grep flag

Unsecured Login2

You don't even need Wireshark, just use strings mysite2.pcap | grep flag

FTP

You don't even need Wireshark, just use strings ftp.pcap | grep flag If you want to, you can right click on any ftp packet and follow the tcp stream. If there's no flag, then move on to the next stream.

Teletype Network

You don't even need Wireshark, just use strings telnet.pcap | grep flag

hidden-ctf-on-my-network

You don't even need Wireshark, just use strings telnet.pcap | grep flag

Forensics

Listen to this

When we listen to the mp3 initially, we can hear a faint beeping in the background. This sounds like Morse code, so let's find an easy way to transcribe it to text dits and dahs (radio speak for dots and dashes).

We open it up in Audacity, and notice there are two tracks. Let's switch to spectrogram view. We can do this by clicking on the arrow pointing downwards next to the track name, then clicking Spectrogram:

If we zoom in on the beginning of the track, we can see that the Morse code is in the second track:

One huge issue right now is that the Morse code is "covered" by the actual vocals of the track. I was stuck on this portion for a while.

Let's split the tracks to mono using the track settings.

Then, we subtract the original track from the track with morse code. After that, we'll be able to clearly see the morse code in spectrogram view.

First, we select one track, and invert it (Effect > Invert). Then, we select both tracks and mix them together (Tracks > Mix > Mix and Render).

It's still a bit hard to see, so let's change the coloring.

We open up track settings and then select Spectrogram Settings. We change the color range to 20 dB instead of 80 dB (decibels).

The result is morse code. Now all we have to do is open up a text editor, copy the morse code down, and convert it to text using an online tool (:

The short ones are dits and the long ones are dahs. I usually represent the short ones with . and the long ones with -, which is a format most online morse-to-text tools use as well.

That would be the first letter in the text.

Open Backpack

The image says something is unzipped... Let's try binwalk.

Command: binwalk -e openbackpack.jpg

Binwalk extracts two files, a zip and a file called flag.png, which has the flag of course.

Look into the past

We download and unzip it. It seems they just compressed a system's directories from / and gave it to us...

We cd into the home directory for the user (/home/User/). Nothing seems interesting except for a .bash_history file, which reveals that they encrypted a flag using a concatenation of 3 passwords. We have to find the 3 passwords to decrypt the encrypted flag.

Password 1: It's in an image in ~/Pictures. We use strings and find the signature for steghide. We can use steghide extract -sf doggo.jpeg. It takes a bit more guesswork to guess that there is no password. The password is in the extracted text file.

Password 2: The password is the password to the user named user. We read /etc/shadow/, and find the password.

Password 3: We first uncompress the table.db.tar.gz inside /opt.

Let's get everything from table.db: sqlite3 table.db "SELECT * FROM passwords". This gives us the third password.

Now that we have all 3 passwords, we can decrypt the encrypted text file using openssl enc -aes-256-cbc -d -in flag.txt.enc -out file.txt. We supply the concatenation of the 3 passwords, and we read file.txt for the decrypted flag.

Programming

DasPrime

We want to find the 10947th prime number. We are given the following algorithm:

import math
def main():
    primes = []
    count = 2
    index = 0
    while True:
        isprime = False
        for x in range(2, int(math.sqrt(count) + 1)):
            if count % x == 0: 
                isprime = True
                continue
        if isprime:
            primes.append(count)
            print(index, primes[index])
            index += 1
        count += 1
if __name__ == "__main__":
    main()

This algorithm seems kind of slow...

We can write a faster script such as this one:

from math import sqrt

def is_prime(n):
    if (n <= 1):
        return False
    if (n == 2):
        return True
    if (n % 2 == 0):
        return False

    i = 3
    while i <= sqrt(n):
        if n % i == 0:
            return False
        i = i + 2

    return True


def prime_generator():
    n = 1
    while True:
        n += 1
        if is_prime(n):
            yield n

generator = prime_generator()

x = []


for i in range(10948):
    x.append(next(generator))

We access the 10497th element of x to get the flag (x[10496]).

password_crack

Simple MD5 brute force. I got the author names from the Discord server.

Robot Talk

We just have to convert 5 base64 values to ASCII.

I used pwntools, a pretty neat library for tasks like these.

from pwn import *
import base64

conn = remote("challenges.neverlanctf.com", 1120)


for i in range(5):
    print(conn.recvuntil("decrypt: "))
    x = conn.recv().strip()
    print(x)
    y = base64.b64decode(x)
    print(y)
    conn.send(y)
    print(conn.recvline())

print(conn.recv())

BitsnBytes

https://challenges.neverlanctf.com:1150

This site gives an svg which we can download. Quite obviously, the colors represent 0 and 1 in binary (gray is 1, green is 0).

We find out that we can download the svg information directly from /svg.php, which makes it much easier for a script.

We parse the image, using regex to remove all different attributes such as x, y, width, and height for each <rect> in the svg. Then, we simply just use str.replace() in Python to get the binary string.

Then we convert that binary string into text.

However, most of the time the server will return an svg that doesn't have the flag. Instead, it will return some time hash information which is useless to us.

We can repeatedly query the server for an svg until it gives us a flag svg.

from __future__ import print_function


import requests

while True:
        f = requests.get("https://challenges.neverlanctf.com:1150/svg.php").text.decode()


        f = f[336:-9]

        f= f.strip()

        import re

        s = re.compile(r"\sid='\d*'")
        a = re.compile(r"\sx='\d*'")
        b = re.compile(r"\sy='\d*'")

        c = re.compile(r"width='\d*' ")
        d = re.compile(r"height='\d*' ")

        f = s.sub("", f)
        f = a.sub("", f)
        f = b.sub("", f)
        f = c.sub("", f)
        f = d.sub("", f)

        #print(f)

        f = f.replace("<rect style='fill:#333136'/>", "1")
        f = f.replace("<rect style='fill:#00ff00'/>", "0")

        f = [f[i:i+8] for i in range(0, len(f), 8)]

        #print(f)

        x = "".join([chr(int(i, 2)) for i in f])

        if not "time hash:" in x:
                print(x)

Evil

ssh neverlan@medusa.neverlanctf.com -p 3333 password: eyesofstone

We initially find an intel.txt, giving us information. We have to ssh onto evil@victim.

Using Medusa (an ssh password cracker), we can bruteforce the password easily. It is 0024.

Once ssh'd, we find a zip file with some base64 as its name. It's password protected, so we try decoding the name.

Decoded, the name of the zip file is stonecold, which is used to unzip the zip file.

The content of the zip file is the flag.

Recon

Front page of the Internet

The "front page of the Internet" is Reddit. The author is ZestyFE, so we guess that he has a Reddit account under the same name.

We navigate to /u/ZestyFE on Reddit, and find the flag in one of ZestyFE's comments.

The Big Stage

We search up SaintCon keynotes and find that NeverLAN keynoted in 2018

Unfortunately the SaintCon site is not 100% functional so we're stuck...

Then we guses that keynoting a conference is pretty cool and they must have posted something to commemorate the event on their Twitter.

They actually link us their slides

The flag is right next to a picture of Rick Astley :p

The Link

During the competition there were streams for music and the like. If we go under their music tab and select track #2 we see a YouTube video.

Exploring the comments reveals a flag that someone commented.

Thats just Phreaky

We Google for 01 September 2017 | 14:01 phreak.

We find the first Darknet Diaries episode.

By some stroke of pure geniosity we right click to view the source code of the site and the flag is at the bottom.

Web

Cookie Monster

https://challenges.neverlanctf.com:1110

The title hints that it has something to do with cookies.

When we visit the site, it says He's my favorite Red guy. We guess this to be Elmo from Sesame Street.

We look at the cookies, and find a cookie Red_Guy's_Name: NameGoesHere. We replace NameGoesHere with elmo.

We get the flag by refreshing the tab.

Stop the Bot

https://challenges.neverlanctf.com:1140

The site looks pretty boring, so let's take a look at /robots.txt, which is hinted at by the title:

User-agent: *
Disallow: /
Disallow: flag.txt

We navigate to /flag.txt for the flag.

SQL Breaker

https://challenges.neverlanctf.com:1160/

Simple SQL injection in the login page. Note that the password does not matter, only the username is vulnerable.

The goal is to log in as an admin.

Payload:

Username: ' OR 1=1;-- 
Password: asdf

Return to the home page for the flag.

SQL Breaker 2

https://challenges.neverlanctf.com:1165/

Simple SQL injection in the login page. Note that the password does not matter, only the username is vulnerable.

The goal is to log in as an admin.

This time, there seems to be multiple accounts, and the previous payload logs us in as John, who is not an admin. We have to skip over John's account using SQL's LIMIT to log in as admin.

Payload:

Username: ' OR 1=1, LIMIT 1;-- 
Password: asdf

Return to the home page for the flag.

Follow Me

https://7aimehagbl.neverlanctf.com

This website redirects so many times your browser just gives up. We can use Python's requests module and the follow_redirects=False option:

import requests
p = requests.get("https://7aimehagbl.neverlanctf.com", allow_redirects=False)

On the first visit (using Python), the page states where it's redirecting. How convenient. How about we just follow the trail?

import requests

url = "https://7aimehagbl.neverlanctf.com"

while True:
    p = requests.get(url, allow_redirects=False)
    print(p.text)
    url = "https://" + p.text.split()[-1]

The flag is in one of the sites that we get redirected to.

Browser Bias

https://challenges.neverlanctf.com:1130

When we try to visit the site normally, it says that the site is only for commodo 64 browsers.

We guess that the server tells what browser we are using based on the User-Agent header of the request.

When we Google for Commodo 64 browsers, we end up with a browser named Contiki. We search for the Contiki User-Agent.

We find a really long list of User-Agents that ever downloaded something from PyPI on GitHub Gists, and we Ctrl-F for Contiki.

We find the User-Agent for Contiki as Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/).

We set that as our User-Agent using Python's requests, and request the home page, which gives us the flag.

Chicken Little

We basically have to ssh onto a server and retrieve the flag from it. We start from Chicken Little 1. We use the flag from Chicken Little 1 as the password for the ssh onto Chicken Little 2, etc.

Chicken Little 1

Use an ls and find Welcome.txt. Read the file for the flag.

Chicken Little 2

Use an ls -a (-a flag to show hidden files) and find .chicken.txt. Read the file for the flag.

Chicken Little 3

We find BAWK.txt with a bunch of "BAWK"s in it. Knowing the password format we guess that this password also has a - in it, so we grep for - in the file for the flag.

Chicken Little 4

We find a binary file with the flag presumably hidden in it. We can use strings to first extract the printable strings, then grep for - in it, which gives the flag.

Chicken Little 5

We can use binwalk -e to extract files from the file, giving us a file with the flag.

Chicken Little 6

We can use scp to transfer the file to our local machine since curl was removed.

scp -P 3333 level5@44.233.149.141:~/chicken-little.png .

The flag is visible in the image.

Chicken Little 7

We are told we need to find the password to the user level7 on the system. We read /etc/shadow for the level7 password hash.

We generate a wordlist of all possible 4-character combinations using crunch 1 4 > pass.txt.

We can use hashcat -m 1800 -a 0 h.hash pass.txt to eventually break the hash (it was supposed to take ~15 minutes).

I used a brute-force script on a remote server because my 2011 mac really did not like running SHA512 a lot of times.

Trivia

Milk Please

Trivia Question: a reliable mechanism for websites to remember stateful information. Yummy!

It's talking about cookies, which is the flag.

Professional Guessing

The process of attempting to gain Unauthorized access to restricted systems using common passwords or algorithms that guess passwords

Google the description.

An article defines the description for the term "password cracking", which is the flag.

Base 2^6

A group of binary-to-text encoding schemes that represent binary data in an ASCII string format by translating it into a radix-64 representation

Radix-64 basically tells us that the flag is base64.

AAAAAAAAAAAAAA! I hate CVEs

This CVE reminds me of some old school exploits. If flag is enabled in sudoers

We Google If flag is enabled in sudoers cve and find this site, which has the flag in it.

Rick Rolled by the NSA

This CVE Proof of concept Shows NSA.go<span>v</span> playing "Never Gonna Give You Up," by 1980s heart-throb Rick Astley. Use the CVE ID for the flag. flag{CVE-?????????}

I remember seeing this as a meme on Reddit :laughing: Googling the description gives news articles detailing the correct CVE ID for the flag.

Original writeup (https://github.com/joshdabosh/writeups/blob/master/NeverLanCTF2020/index.md#my-own-encoding).