CodeMash CTF 2019

This past year CodeMash held their annual Capture the Flag competition. With the event wrapping up, I thought it would be a great learning opportunity for everyone to describe each of the challenges and explain the steps I took in order to solve each one.

There are fifteen in total so without further ado, lets begin!

  1. ASCII Art
  2. Busted File
  3. Esoteric Stuff
  4. Espresso
  5. Krafty Kat
  6. Dot Nutcracker
  7. Capture the Falg
  8. Ghost Text
  9. The Parrot
  10. Stacked Up
  11. Unicorn
  12. Danger Zone
  13. Mrs Robot
  14. Cars
  15. On Site Challenge

ASCII Art

The first challenge that was unlocked was some ASCII art that spelled out "CODE MASH". It also included this sentence: I love ASCII art. What about you?

Though if you notice, the word CODE has some numbers scattered throughout.

The problem seems to be hinting pretty hard that this problem is relating to ASCII and ASCII codes. So I tried taking each number in the word CODE, assumed it was an ASCII number, and tried to translate it to a character.

cm19-asci-CODE-a1nt-H4RD

Well, that looks like a flag to me!

Busted File

We are given a zip file (busted.zip) that contains a single image (image.png). Though when I tried to extract the image, I got an error saying that the archive is corrupt.

Upon trying to repair the archive using WinRAR, I was given an error about a Corrupt header.

I had no idea what a zip header should look like, so I created my own zip file and opened it up into a hex editor.

You can see here that the newly created (and valid) header contains:

50 4B 03 04 14 00 02 00 08

Opening the corrupt file, you'll notice that the header contains:

DE AD BE EF 14 00 00 00 08

Using the hex editor to replace the corrupted header with the expected header allowed me to successfully unzip the file.

The contents of the zip file contained the following image:

This approach of hiding text within an image is called steganography. There are a couple ways that this could have been done.

  1. The flag could be rendered into the image itself. Typical methods of extraction could be to invert the colors, adjust the hue and saturation, etc. Play with the layers and colors to see if the original author put the text into the image itself.
  2. Another approach, and the actual solution to this challenge, is to embed the text into the actual file. If you open up this image with Notepad or a hex editor, towards the beginning of the file you'll see the flag in plain text.

Esoteric Stuff

The third challenge contained a large block of periods, exclamation points, and question marks.

....................!?.?...?....
...?...............?............
........?.?.?.?.!!?!.?.?.?.?!!!.
....................!.?.?.......
................................
!.................!.!!!!!!!!!!!!
!!!!!!!!!!!!!..?.?!!!!!!!!!!!!!!
!!!.............................
!.?.?.......!..?.?!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!.?.?.!!!!!!!.
.?.?........................!.!!
!!!!!!!!!!!!!!!!!!!!!...!.?.....
..!.?.!..?.?....................
........!.!!!!!!!!!!!!!!!!!!!!!!
!!!!!...........................
....!.?...........!.?.!..?.?!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!.?.?.......!..?.?..!...!.?.?.!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.

It also included this sentence: It's getting a bit esoteric now. Ookay?!

Note the bolded text.

If you're unfamiliar with the term esoteric it means that something is only understood by a small set of individuals. In the world of programming, it refers to esoteric programming languages. These languages are typically just meant for fun and really aren't intended for production use. My goto example of an esoteric language is LOLCODE.

I also noticed that the Ook in Ookay was bolded. I made the conclusion that Ook must be another esoteric programming language and the given text is source code written in Ook.

I attempted to use an Ook compiler to retrieve the flag, and.. success!

Espresso

This challenge only provided a single file, Espresso.class

Luckily, class files are something that you can decompile pretty easily, so the first step I took was to take the class file and put it through a Java decompiler which resulted in the following source:

Looking at the source of the class file, I saw that there is a conditional that checks to see if the user's input was equal to localStringBuffer. If it matches, the program will print out: Flag correct, well done!

That seems like a pretty good starting point, right?

locationStringBuffer is a private variable that is defined and then built up in a for loop. After the for loop completes, the variable doesn't change and should theoretically contain the flag.

To get what localStringBuffer would be after running the for loop, I just went ahead and removed all of the extra code and just focused on getting localStringBuffer to be built.

Running this slimmed down version of Espresso.class yields the flag.

Krafty Kat

RSA encryption.. my dear old friend. This was the first challenge with a difficulty of "hard", and honestly took a decent bit longer than the other ones to crack.

This challenge has a flag.enc file and a key.pem file. The flag is obviously in the flag.enc file, and is encrypted. I knew I needed to use the provided public key in order to gain access to the encrypted file. The public key looked like this:

-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEIAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEcwIDAQAB
-----END PUBLIC KEY-----

Now, how RSA encryption works could be a post all in itself, so I'll just go over each step and describe whats happening.

The above public key is a base64 encoded string that contains what is called the Modulus in RSA. We can use openssl to extract this result like so:

Here we can see the Modulus and the Exponent (65537). Now, the Modulus is currently in hex, but I need to get this number as an integer.

The first step to do this is to strip away all of the colons

800000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000473

The next step is to convert this into an integer. I used python to cast it, but the method doesn't really matter.

258536048570605626988915097172641057803353415992887757589736645808726151727194085610744190012322229640908553929414833022731988789391928353742297595957524280335918484224592037842886381777837922164216258913020883767452162759055614423529478656392491416399318604362756647059576165428708885769576577464120568116338612287783043168328279883802253392551292712278925133675758884573430117126518137049529478694956180472120065649305066144792948879520988172974401270565515875720597321219813209954571652637983550511681618170361995093036165713586725018263017699754932627926751512891645482818654590419836934782173099306176053375927411

When working with RSA, a really important formula is n = p * q, and the number above (the really big one), is my n value. n is derived from the product of p and q (both of which are prime numbers) resulting in the equation n = p * q. If you know p and q, you can derive the private key.

However, these prime numbers are kept secret and should not be known. It is possible to figure out what they are though if the RSA was implemented poorly (e.g. bad primes were chosen).

So if n is the product of p and q, and both p and q must be prime. How do we figure out what they could possibly be? Brute force is one option, but would take.. a long time. Too much time.

The alternative is to see if any factors of n already exist. I did this through a website called factordb. I plugged in the n value I calculated above and noticed that there are no factors.l

Well.. if n = p * q and p and q must be prime.. but n has no factors.. p must be equal to n and then q must be equal to 1.

At this point we know p, q, e, and n. The last value we need is d or the decryption key. Honestly at this point, d can just be calculated from all of our other values so I just wrote a script in Python to do just that.  

from Crypto.Util.number import inverse
from Crypto.PublicKey import RSA
import gmpy2

#open the provided key file
pub = open("key.pem", "r").read()
pub = RSA.importKey(pub)

#set the values of n and e from the key file
n = int(pub.n)
e = int(pub.e)

#set p and q from factordb
p = 258536048570605626988915097172641057803353415992887757589736645808726151727194085610744190012322229640908553929414833022731988789391928353742297595957524280335918484224592037842886381777837922164216258913020883767452162759055614423529478656392491416399318604362756647059576165428708885769576577464120568116338612287783043168328279883802253392551292712278925133675758884573430117126518137049529478694956180472120065649305066144792948879520988172974401270565515875720597321219813209954571652637983550511681618170361995093036165713586725018263017699754932627926751512891645482818654590419836934782173099306176053375927411
q = 1

#calculate d
d = int(gmpy2.invert(e,n-1))

#use d to create a private key and read the message
key = RSA.construct((n,e,d))
message = key.decrypt(open('flag.enc').read(), '')

print(message)

This will print the contents of the flag.enc, which contains the flag itself!

Dot Nutcracker

In this challenge, there was an executable file that gave you three attempts in order to type in a password. If you got the password correct, the program would output what the flag was.

Now obviously, the three attempts were just fluff, as you could just start the program again. Regardless, the password (and the flag) have to be inside of the executable somewhere.

To get at the source code of the application, I used decompiler called dotPeek which allowed me to view the source of the executable. I even took it a step further and exported the source to a new project. This allowed me to make modifications to the program itself, as well as do any sort of debugging.

Reading through the source code, I came across the primary if check that compared the users input with the stored password.

      while (num > 0)
      {
        Console.WriteLine(string.Format("You have {0}/3 attempt(s) left.", (object) num));
        Console.Write("Enter passphrase: ");
        string str1 = Console.ReadLine();
        string str2 = CryptoHelper.DecryptStringAES("EAAAAH5ZA4kASLVjLUsYmLK3h74KWmkS4BvBS61BuaD4lnyqdz3AO8/xfGO1atVdci0x1g==");
        if (str1 != null && cryptoHelper.Equals(str2, str1))
        {
          Console.WriteLine(string.Format("Decrypted Flag is: {0}", (object) CryptoHelper.DecryptStringAES("EAAAAOlDKPcRaUj/ITV1q9IHN1bAQyUWxZqVob+G1gpmyoIJIPej1O3T4TWnRUndqp4NnA==", CryptoHelper.GetHashString(str2), str1, str1)));
          Console.WriteLine("Press enter to quit");
          Console.ReadLine();
          Environment.Exit(1337);
        }
        --num;
        Console.WriteLine("That's wrong.");
      }

Reading the code above, it looks like str2 contains the password. I did a Console.Writeline of str2 and saw that acrackeradaykeepsthedoctoraway is printed out. Success! Found the password.

However, when attempting to use the password, the program crashes with the following message:

I went through the source code some more and noticed that the original if statement that compares the passwords is actually using CryptoHelper's Equals method and it has the following implementation:

public bool Equals(string a, string b)
{
  if (!b.Equals(a))
    return a.Equals(b + "y");
  return true;
}

This means that both acrackeradaykeepsthedoctoraway and acrackeradaykeepsthedoctorawa should both be correct passwords. Well, if the former causes the application to crash, maybe the second one wont.

It did not!

Capture the Falg

This challenge had the following text:

This falg is easy to catch, isn't it?
criSdmetio1AUdW9dP3n----

and gave this picture:

If you notice, the name of the challenge is Capture the Falg and not Flag. The a and l are jumbled up. The image is also jumbled up. Another interesting notice is that the word falg has 4 letters, and theres also 4 boxes for each piece of the flag.

To render the image correctly, you'd want the 1st image to stay where it's at. Followed by the image directly below it, followed by the top right image, and lastly keep the last image where it's at.

Applying this same approach the word falg, you would get the following:

fa
lg

To read the word flag you need to start from the top left, move down, and then go back to the top in the next column.

Now, if we were to apply this same pattern to the string that we were given (presumably the flag itself), we get the following:

criSd
metio
lAUdW
9dP3n
----

Reading the text just like before will render the flag.

Ghost Text

For this challenge the description was:

Ghost text is ... invisible!

The challenge also provided a text file with this text:

​In​ ​folk​l​or​e​, ​a​ g​hos​t​ is ​the​ ​s​oul​ or​ s​p​ir​it​ ​o​f a​ dea​d​ pe​r​son​ ​or​ ani​m​al ​that ​ca​n​ a​pp​e​ar t​o t​h​e​ l​ivin​g​.​ I​n​ ​gh​o​st​l​o​re, ​gh​o​st​ d​e​s​cr​ipti​o​ns​ vary​ ​wid​el​y,​ from​ ​inv​is​i​bl​e ​pre​sence​s to l​if​el​ike​ vi​si​ons​.

Now, given the challenge description, I assumed there was something hidden inside of the text file that was given. So I opened it with my trusty HexEditor.

You'll notice that there's a lot of jumbled characters intertwined between the text itself, characters we could not see before. These characters are known as zero width characters and are a common approach to hiding messages in text.

To more easily digest how this message really looks, we can use a unicode analyzer. I've provided the link below.

https://www.fontspace.com/unicode/analyzer/?q=%E2%80%8BIn%E2%80%8B+%E2%80%8Bfolk%E2%80%8Bl%E2%80%8Bor%E2%80%8Be%E2%80%8B%2C+%E2%80%8Ba%E2%80%8B+g%E2%80%8Bhos%E2%80%8Bt%E2%80%8B+is+%E2%80%8Bthe%E2%80%8B+%E2%80%8Bs%E2%80%8Boul%E2%80%8B+or%E2%80%8B+s%E2%80%8Bp%E2%80%8Bir%E2%80%8Bit%E2%80%8B+%E2%80%8Bo%E2%80%8Bf+a%E2%80%8B+dea%E2%80%8Bd%E2%80%8B+pe%E2%80%8Br%E2%80%8Bson%E2%80%8B+%E2%80%8Bor%E2%80%8B+ani%E2%80%8Bm%E2%80%8Bal+%E2%80%8Bthat+%E2%80%8Bca%E2%80%8Bn%E2%80%8B+a%E2%80%8Bpp%E2%80%8Be%E2%80%8Bar+t%E2%80%8Bo+t%E2%80%8Bh%E2%80%8Be%E2%80%8B+l%E2%80%8Bivin%E2%80%8Bg%E2%80%8B.%E2%80%8B+I%E2%80%8Bn%E2%80%8B+%E2%80%8Bgh%E2%80%8Bo%E2%80%8Bst%E2%80%8Bl%E2%80%8Bo%E2%80%8Bre%2C+%E2%80%8Bgh%E2%80%8Bo%E2%80%8Bst%E2%80%8B+d%E2%80%8Be%E2%80%8Bs%E2%80%8Bcr%E2%80%8Bipti%E2%80%8Bo%E2%80%8Bns%E2%80%8B+vary%E2%80%8B+%E2%80%8Bwid%E2%80%8Bel%E2%80%8By%2C%E2%80%8B+from%E2%80%8B+%E2%80%8Binv%E2%80%8Bis%E2%80%8Bi%E2%80%8Bbl%E2%80%8Be+%E2%80%8Bpre%E2%80%8Bsence%E2%80%8Bs+to+l%E2%80%8Bif%E2%80%8Bel%E2%80%8Bike%E2%80%8B+vi%E2%80%8Bsi%E2%80%8Bons%E2%80%8B.%E2%80%8B

Using this website, we can see every single character that is contained within the string. You'll notice that there are "ZERO WIDTH SPACE" characters throughout the text. This is important!

This approach to hiding messages in text generally involves the use of binary. Every approach to encryption is going to be different, but most messages end up as some sort of binary representation. Maybe each zero width character is a 1 and each real space is a 0. These 1's and 0's will form a binary string, which can then be translated into readable text.

It's also very common for CTF events to use the same flag structure. In this one, all of the flags (so far) have started with cm19.

With this in mind, I wanted to see if I could extract cm19 in binary from the zero width spaces.

cm19 in binary is: 01100011 01101101 00110001 00111001

If you reference the unicode analyzer page linked previously, you may see a pattern here.

The word "in" is followed by a zero width space.

The single space afterwards is followed by a zero width space.

The word "folklore" has a zero width space after "folk"

If we assume that the zero width spaces are some sort of terminator, it actually starts to fit our assumption of the cm19 binary string.

In would be 01

A single space would be 1

Folk would be 0001

.. and so on. If we combine the above we get 0110001 which is the start of our cm19 binary string!

Now I had no desire to go through this entire sentence by hand and finish out the sequence, so I wrote a quick GOLANG program to do it for me.

package main

import "strings"

func main() {
	input := "​In​ ​folk​l​or​e​, ​a​ g​hos​t​ is ​the​ ​s​oul​ or​ s​p​ir​it​ ​o​f a​ dea​d​ pe​r​son​ ​or​ ani​m​al ​that ​ca​n​ a​pp​e​ar t​o t​h​e​ l​ivin​g​.​ I​n​ ​gh​o​st​l​o​re, ​gh​o​st​ d​e​s​cr​ipti​o​ns​ vary​ ​wid​el​y,​ from​ ​inv​is​i​bl​e ​pre​sence​s to l​if​el​ike​ vi​si​ons​.​"

	result := ""

	for _, v := range input {

		if v == 8203 {
			result = strings.TrimSuffix(result, "0")
			result += "1"

		} else {
			result += "0"
		}

	}

	print(result)
}

Running the application gives the following binary string:

011000110110110100110001001110010010110101110010001100110011010001100100001011010110001001110100011101110110111000101101011101000110100001100101010000110010110101001000010000010101001001010011

Which can then be converted to the flag itself using an ASCII to hex converter.

The Parrot

This challenge had a single pdf file called parrot.pdf. Though when attempting to open the pdf to view it's contents, I was presented with a password dialog.

This is a password protected PDF and I needed a way to get the password so that I can open the PDF in order to see what's inside.

For this, I used a tool called PDFcrack and a collection of commonly used passwords called rockyou.txt. Putting the two together, I got the following result:

You'll notice on the very last line, the program managed to find the password "cracker".

Note: I later found out that if you open the pdf in a text editor and look at the Authors, it actually spells out the password if you look closely:

<< /Authors: charlie romeo alfa charlie kilo echo romeo >>

Moving on!

When the PDF was opened, it contained the following image:

In order to get the image out of the PDF, I used another tool called pdfimages. At this point, I already knew the password, so I ran pdfimages with the password 'cracker'.

pdfimages parrot.pdf -upw cracker .

This command extracted two images in .ppm format. Opening the first image and.. tada!

Stacked Up

The challenge description stated that there was a vulnerable service running in the cloud and that it could be exploited in order to get the flag. A copy of just the binary was given freely for download.

Running the program locally, it just looked like it echo'd back whatever I typed in.

Doesn't seem like a very useful program, does it? I checked out what the hint had to say about this challenge. The hint plainly said:

HINT: What happens if you input a veeeeeeeeeery long string?

Fair enough, I tried entering in a long string.

I didn't think that was incredibly surprising. With input that large I probably caused a buffer overflow.

However, at this point, I went down a pretty large rabbit hole. I spent a lot of time focus'd on the core dump, convinced that there was going to be something valuable inside of the stack trace when the program errored. Ultimately, I did not really get anywhere going down that path.

The next thing I wanted to try, was just to decompile this binary and see if I could make any sense of what was going on inside.

After decompiling the binary using IDA, I noticed a couple really interesting things.

First, the main method had no jumps. There weren't any if statements and all the main() method did was get user input and print it out. I did not expect that at all.

Second, I noticed that there was a method called flag()

This method looked almost identical to the main() method with the exception that this method tried to open a file called flag.txt, and then print its contents.

I knew at this point the goal was going to be to call the flag() method. There must exist a flag.txt file on the remote server where the binary is running, and calling the flag method would read that file and print it out.

In order to do this, the return address of the main() method would need to be overwritten.

When a function is called (in this case main), the return address is stored on the stack and is just above the base pointer. If I overwrite the return address of the main method to point to the flag() method instead of the default return address, the program should call the flag method.

To accomplish this I needed a couple of things.

First, since the return address is right after the base pointer, I needed to know exactly how many characters were required to overflow the program before I started to spill over into the return address.

You can see here in this photo that the return address looks like

0xff021b92 0x00007fff (the last two hex values on the top row)

When I used a lot of A's in order to get the program to overflow, I noticed that the return address starts to get overwritten at the 1033rd character.

See the 41's that are bleeding over into the 3rd column?

Ok, good progress! The 1033rd character is the start of the return address, so we need to fill the buffer with 1032 characters of junk, and then tack on the return address of the flag() method.

Using IDA again, I got the address of the flag() method just by clicking on the starting line of the method (0x400676):

At this point, I have everything needed in order to call the flag() method. To create the input for the application, I just used python to generate all of the junk characters as well as append the address of the flag method to the end of the string

python -c 'print "A"*1032 + p32(0x400676)'

Connecting to the remote service, and using the generated input overwrites the return address, calls the flag() method, and prints the flag!

Unicorn

This challenge was relatively straight forward. A Unicorn.svg file was given, and that's about it.

Typically in CTFs, if you're given a svg, they expect you to manipulate the svg in some way in order to see the flag.

In the case of our cute little Unicorn, I just opened the file into an SVG editor and deleted the Unicorn. The flag was hidden behind it.

Danger Zone

This challenge had the following description:

Can you enter the **danger.zone**, and find the flag?

Service listening on:

whale.hacking-lab.com:**5553**

Note: this is **NOT** about the web site danger.zone!

Like most CTFs, a lot of clues on how to solve the problem are given in the name and the description. For this challenge, it talks about danger.zone and a service that is running on port 5553. The hint itself also really helpful:

HINT: Find out what service is listening on the port. nmap could be helpful for this kind of fingerprinting.

Sounds like a good idea to start off this challenge, so I did just that. Running nmap for port 5553 tells us that the service that is running is a domain service with a version of ISC BIND 9.9.5

With this information, we now know that port 5553 is running a domain service, which means its most likely running a custom DNS service.

For DNS problems, dig can be incredibly helpful. Since I now knew that the service is a DNS service (and is probably vulnerable), I used dig to look up some more information about it while trying to access danger.zone.

I was able to successfully query danger.zone using the DNS service, and on top of that, danger.zone exists as a record in the zone file for the DNS! Which ultimately tells me that I should be able to perform a zone transfer.

After performing the zone transfer, the zone file is printed to the screen, and I can see the flag that was put inside.

Mrs Robot

For this challenge, a webpage that prompted us for a secret word was given.

Viewing the source didn't really bring about anything of interest. Maybe the fact that each word that was typed in just redirected to a webpage that was an md5 hash of the input:

This led me to believe that the flag was an HTML file on the webserver, and the name of the HTML file was the secret word in an md5 hash.

I could use something like DirBuster to find the file for me, hashing common words and checking if the file exists, but I might be there for awhile.

Luckily, the site did have a robots.txt file that contained the following:

User-agent: *
Disallow: /481065fd79b253104aeab5ca5c717cd5.html

Well that looks promising!

Upon landing on the page in the robots.txt file, I was presented with the text:

If you're not already familiar with the Robot Interaction Language, a simple google search for both of these terms will bring up a table with all of the translations necessary to get this text into English.

Leveraging the table I got the secret word, winter, and thus the flag itself.

Cars

For this challenge, a website with a search box and a login page was given. The goal was to login to the page using the admin account.

I was pretty sure this was going to be a SQL injection challenge, and the hint confirmed that:

HINT: Can you find a way to make the search return more data than expected? What you might get, is not the password yet.

I started off by throwing some quotes at the search box as that's a pretty common approach to get some more information about the version of SQL and the query running behind the scenes. Using the following query:

a'

The result that came back was:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%'' at line 1

I didn't even use a %, and the behavior of the page did seem to lend itself to being a LIKE query. Inspecting the error more closely, I can see that the trailing SQL query is %'. So, my query of a' must have terminated the query, and %' is what was left over.

With this in mind I felt pretty confident the query was structured as so:

SELECT .. FROM .. WHERE .. LIKE '%' + [input] + '%'

The next step was to start filling in the blanks in order to get some more information about the underlying database.

Using the following query, we can get a dump of all of the underlying tables and their columns

a' union select table_name, column_name FROM information_schema.columns#

The a' is to finish the SELECT statement so that it's valid, and then I can UNION another SELECT statement to pull back information about the schema. Since this is a MySQL database, the pound symbol (#) is used to comment out the rest of the query. It would look something like the following:

SELECT .. FROM .. WHERE .. LIKE '%a' UNION SELECT table_name, column_name FROM information_schema.columns#%'

The hash comments out the trailing %' from the real LIKE query

The results of this query looked like this:

At this point, we have all of the information we need to pull back the data that we're looking for. The users and the password.

Doing another injection using the new tables and columns gives us this query:

a' UNION select username,password_hash from Users#

Resulting in the following records:

There it is! The hash of the admin password.

I was pretty confident the hash was going to be an MD5 hash, so I knew I needed a rainbow table and hope that the hash had been cracked in the past.

I ended up using an online Reverse Hash Calculator which did have the hash already stored.

Using the username of admin and the password of dodge123, revealed the flag.

On Site Challenge

The description for the on site challenge was in the form of a Haiku:

Games are played all night.
This one online, but there, board.
Code is history.

Reading the poem, I saw "but there, board". To me, there is hinting at the location of the flag. It is an on site challenge after all. On site, there is a place where board games are played, so that seemed like a logical conclusion to me.

Upon entering the room, I saw multiple identical QR codes on the wall:

Using a QR code reader, I saw that the QR code is actually a link to:

https://www.owasp.org/index.php/User:Bill_Sempf

Looking at the remainder of the Haiku "Code is history" I eventually figure out that its referring to the "View history" of the wiki which contains a strange entry that was added and then immediately removed about a minute later.

Viewing the revision in which lines were added, I saw what appears to be a Base64 encoded string. Putting that string into a Base64 decoder reveals the flag.