NahamCon CTF

NahamCon is a two day virtual hacking conference organized by @_JohnHammond, @NahamSec, @STOKFredrik, and @TheCyberMentor.
#NahamCon2020
June 12 - June 13 2020

NahamCon CTF

I focussed mostly on the web app challenges in the time I had, and got my first XXE exploitation on this one! I also discovered carbon.now.sh while writing this us, so the code blocks will be pretty pictures moving forward.

Read the Rules (Warmup)

Please follow the rules for this CTF!
Connect here:
https://ctf.nahamcon.com/rules

I caught this one when I was reading the rules before the CTF. Looking in the page source, there's a flag in a comment:

flag{we_hope_you_enjoy_the_game}

CLIsay (Warmup)

cowsay is hiding something from us!

Opening the provided ELF file in HexFiend, I can see the flag split into two parts:

flag{Y0u_c4n_ and r3Ad_M1nd5}

flag{Y0u_c4n_r3Ad_M1nd5}

Metameme (Warmup)

Hacker memes. So meta.


Opening the provided Mr. Robot Hackerman image I can see EXIF information that contains the flag:

flag{N0t_7h3_4cTuaL_Cr3At0r}

Mr. Robot (Warmup)

Elliot needs your help. You know what to do.
Connect here:
http://jh2i.com:50032

This connects to the FSociety webpage. There's nothing exciting in the page source, and before we go down any stego rabbit holes with the image on the page, let's check the robots.txt file. That gives us the flag:

flag{welcome_to_robots.txt}

UGGC (Warmup)

Become the admin!
Connect here:
http://jh2i.com:50018

This page just has a login form with only a username field. Trying to log in as admin returns the message "Login as admin has been disabled". Trying to log in as user returns the message "You are logged in as user. Sorry, only admin can see the flag"

Looking at the cookies, I can see a user cookie with a consistent four character value "hfre". Setting the cookie to admin, it returns the message "You are logged in as nqzva", which is the ROT13 encoded "admin". Changing the cookie to nqzva, we get logged in as admin and get the flag:

flag{H4cK_aLL_7H3_C0okI3s}

Easy Keesy (Warmup)

Dang it, not again...

Looking at the file provided in HexFiend, there's nothing recognisable in there. Running file on it, we can see it is a Keepass password database. This is a great time to find that my KeepassXC is no longer functional (32-bit?), so I downloaded MacPass instead.

This file doesn't open without a password, not surprisingly, so that's our next step. Running keepass2john, we get the hash, and running that through john using the 10k most common wordlist from the SecLists password collection, we get the password "monkeys". That gets us into the database, which contains only the record "flag":

flag{jtr_found_the_keys_to_kingdom}

Pang (Warmup)

This file does not open!

Looking at the provided pang file in HexFiend, it is clearly a PNG file. Pngcheck shows a bad CRC: CRC error in chunk IHDR (computed f9f1da99, expected f9f1ea99) which we can quickly correct in HexFiend and view the flag in the png:


flag{wham_bam_thank_you_for_the_flag_maam}

Time Keeper (OSINT)

There is some interesting stuff on this website. Or at least, I thought there was...
Connect here:
https://apporima.com/
Note, this flag is not in the usual format.

The page is a blog, with several links, and some interesting but flag-less comments. For example:


Not much else going on in there. From the clue, it's likely we need to look at an older version of the website, so let's check it out on the Wayback Machine. There is a version from April that includes an entry:

Today, I created my first CTF challenge. The flag can be found at forward slash flag dot txt

Navigating there, within the Wayback Machine, we get the flag:

JCTF{the_wayback_machine}

Agent 95 (Web)

They've given you a number, and taken away your name~
Connect here:
http://jh2i.com:50000

Connecting to this site, we get the message:

You don't look like our agent!
We will only give our flag to our Agent 95!
He is still running an old version of Windows...

Googling for a Windows 95 user agent, I came across:

Mozilla/4.0 (compatible; MSIE 5.5; Windows 95; BCD2000)

Changing the browser's user agent to that, we get the flag:

flag{user_agents_undercover}

Localghost (Web)

BooOooOooOOoo! This spooOoOooky client-side cooOoOode sure is scary! What spoOoOoOoky secrets does he have in stooOoOoOore??
Connect here:
http://jh2i.com:50003
Note, this flag is not in the usual format.

This page has a spooky ASCII ghost:


There's nothing interesting in the page source, but looking in the local storage, we get the flag:

JCTF{spoooooky_ghosts_in_storage}

Extraterrestrial (Web)

Have you seen any aliens lately? Let us know!
The flag is at the start of the solar system.
Connect here:
http://jh2i.com:50004

Just a simple form:

Extraterrestrial
We're doing a study on external life.
If you find any alien life forms, please inform us using the form below.

The reference to external life is likely to an XXE

Entering "test" and hitting submit, we get the response:

Not well-formed (invalid token)

Trying:


returns an array with the contents of etc/passwd. Now we need to find our flag on this system. The clue tells us the flag is at the start of the solar system, so perhaps in the root directory?


So, that didn't work, but file:///flag.txt did, so the flag is:

flag{extraterrestrial_extra_entities}

Ksteg (Steganography)

This must be a typo.... it was kust one letter away!

The luke.jpg file looks like a standard jpg file from the header and footer. The clue, however, implies that there is information hidden using jsteg.


Let's start by importing that package:

go get lukechampine.com/jsteg

This is my first foray into Golang, so it took a lot of trial and error to finally get this code:


The key was the Printf, rather than Println, which is what I started with in my first Hello World PoC, and just kept outputting ASCII numbers. This little bit of Go outputs a bunch of junk because we don't know the length of the hidden flag and jsteg will just keep going otherwise. At the beginning, however, we get the flag:

flag{yeast_bit_steganography_oops_another_typo}

Doh (Steganography)

Doh! Stupid steganography...
Note, this flag is not in the usual format.

Again, the doh.jpg file looks like a standard jpg from the header and footer.


Dropping this into the futureboy stego tool, without a password, we get the flag:

JCTF{an_annoyed_grunt}

Beep Boop (Steganography)

That must be a really long phone number... right?

The flag.wav file plays a very, very long phone number tone.

We can probably extract the numbers from the audio using http://dialabc.com/sound/detect/index.html

That gets us:

46327402297754110981468069185383422945309689772058551073955248013949155635325

which doesn't decimal or hex decode to ASCII.

We should probably double-check that by extracting the numbers using the DTMF decoder at https://github.com/ribt/dtmf-decoder. That gets us:

4632740229775411098146806918538342294530968977058551073955248013949155635325

which is off by a number 2, but doesn't give us clean text. Maybe it's an SMS message?

GMDAPG BWQJG WT GMT MW TJDTDGBWGJD WMTWQ JTK PDWKAGT DWGW KMDJDAJ

Well, that looks like encoded words, but they don't decode using a rotation cipher - maybe a cryptogram?

POST-CTF SOLUTION REVIEW: I missed a step in the process. The original number sequence is the decimal equivalent of the hex:

666C61677B646F5F796F755F737065616B5F7468655F626565705F626F6F707D

That hex is what decodes to the ASCII:

flag{do_you_speak_the_beep_boop}

Rotten

Ick, this salad doesn't taste too good!
Connect with:
nc jh2i.com 50034

Connecting we get the text:

send back this line exactly. no flag here, just filler.

Copy-pasting that into the next line, we get:

xjsi gfhp ymnx qnsj jcfhyqd. hmfwfhyjw 18 tk ymj kqfl nx 'd'

This is a ROT of:

send back this line exactly. character 18 of the flag is 'y'

Sending that decoded line back, we get:

pbka yxzh qefp ifkb buxzqiv. kl cixd ebob, grpq cfiibo.

which is ROT of:

send back this line exactly. no flag here, just filler.

Sending that we get:

kwfv tsuc lzak dafw wpsuldq. uzsjsulwj 13 gx lzw xdsy ak 'c'

So, the pattern is:
Send "send back this line exactly. no flag here, just filler.""
Get a line that is rotation encoded.
Rotate it until the first letter is the s in send
Get the flag character and position
Send that cleartext line
Repeat until we have all of the characters in the flag.

And I knocked that up in Python:


And that gets us the flag:

flag{now_you_know_your_caesars}

Rejected Sequel

Look at all these movie sequels that never got released! Will yours make it through??
Connect here:
http://jh2i.com:50008

This page has a simple form for input of a Movie Name. Results returned are rejected movie sequel titles.

Looking in the page source, there is a comment:

if ( isset($_GET["debug"])){ echo($sql_query); }

So, if we get debugging turned on with /?debug in the URL, we get to see the SQL query being submitted. Clearly we have a SQL injection opportunity here.

Testing with ' and ", I get a warning for the double-quotes:

Warning: mysqli_fetch_array() expects parameter 1 to be mysqli_result, boolean given in /app/index.php on line 68

As an aside, I did notice that the results returned normally include the search term if there are no other results, and the application is vulnerable to XSS as well.

I intercepted and copied a whole POST request for the page into a text file (testme.txt) and ran:

sqlmap -r testme.txt -p "name"

The name variable comes back as likely injectable, and the database is detected as MySQL, which agrees with the previous error message. It does not, however, come back with an injection route.

Trying a few manual injections like ' OR 1=1#, we get the sql_query returned as '1=1# without the OR. Replacing OR with ||, we get all of the movies returned, even if another search term is present (e.g. test"||1=1#).

I also noticed that it's stripping spaces and UNION and SELECT. Concat still gets through, though, and SEL/*comment*/ECT gets through.

"sel/*c*?ect\n*\nfrom\ntables;"

But I didn't get any further in the time I had.

Phphonebook (Web)

Ring ring! Need to look up a number? This phonebook has got you covered! But you will only get a flag if it is an emergency!
Connect here:
http://jh2i.com:50002

This page has the message:

Sorry! You are in /index.php/?file= The phonebook is located at phphonebook.php

Changing the URL to:

http://jh2i.com:50002/index.php/?file=phphonebook.php

we get the Phphonebook with a broken image link to book.jpg

This phphonebook was made to look up all sorts of numbers! Have fun...
Enter number:

Reducing the URL to:

http://jh2i.com:50002/?file=phphonebook.php#

we get the book and phone image

Trying 911 or 999, as the clue implies, doesn't do anything. I tried some other path traversal routes without success.

POST-CTF SOLUTION REVIEW: This relied on something I hadn't seen before and that's PHP filters. Looking for file:

?file=php://filter/convert.base64-encode/resource=phphonebook.php

we would get the base64 encoded content of phphonebook.php and that would let us parse the code that controls this page. In there, we would have seen that the source code is simply looking for a set value for a variable "emergency" in the POST request. Using Burp, we could grab the POST, change the number=911 to emergency=911 (for example), and we would get the flag:

flag{phon3_numb3r_3xtr4ct3d}

B'omarr Style

The classic! A grilled-to-perfection patty topped with all the fixin's-- a slice of Gonzo yellow cheese, Revwien lettuce, topato slices, grilled Ojomian onions and...

Note, it is recommended to clear your cookies for this challenge as some may have the same names used in other challenges.

Connect here:
http://one.jh2i.com:50030
http://two.jh2i.com:50030
http://three.jh2i.com:50030
http://four.jh2i.com:50030
http://five.jh2i.com:50030

First of all, the clue is a Star Wars reference to the Coruscant slider, which should include B'omarr-style pickles. Chances are we're looking at serialisation using Python's pickle module.

Checking the cookies, we're clear there. The site is a Star Wars movie blog site. If we click on the Login link at the top, we can login or register a new user. Let's register testa:testb and login. Checking the cookies, we now have:

token eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiIsICJraWQiOiAic2VjcmV0LnR4dCJ9.gANjbWFpbgpVc2VyCnEAKYFx
AX1xAihYCAAAAHVzZXJuYW1lcQNYBQAAAHRlc3RhcQRYBAAAAHJvbGVxBVgEAAAAdXNlcnEGdWIu.twnF4sfP88iIus
9Ju9ga7TqXmlMYOuJUFUoKL6wYauY

The token decodes to:

{"typ": "JWT", "alg": "HS256", "kid": "secret.txt"}cmain User q�)q}q(X���usernameqX���testaqX���roleqX���userqub. ȈI:S:TJ /j

This is a JSON web token. I can see my username in there and a user role. Let's try to change the user role to admin and base64 encoding the lot and putting that in the token cookie. Reloading the page, we get:

Bad Request
not enough values to unpack (expected 3, got 1)

Dropping that same base64 into the debugger at jwt.io, we get a much shorter token.

Putting that in as the token cookie, we get

Bad Request
'kid'

Well, that's progress, since we're probably looking to return the secret.txt that value pairs with kid.

Trying it with username admin and role admin returns the same result.

The header is:

eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiIsICJraWQiOiAic2VjcmV0LnR4dCJ9

The payload is:

gANjbWFpbgpVc2VyCnEAKYFxAX1xAihYCAAAAHVzZXJuYW1lcQNYBQAAAHRlc3RhcQRYBAAAAHJvbGVxBVgEAAAAdXNlcnEGdWIu

which is:

€cmain User q�)q}q(X���usernameqX���testaqX���roleqX���userqub.

The signature is:

twnF4sfP88iIus9Ju9ga7TqXmlMYOuJUFUoKL6wYauY

Changing the payload only to username Admin (like the user credited for the blog posts) and role admin we **URL-safe** base64 encode to:

woADY21haW4KVXNlcgpx77+9KcKBcQF9cQIoWAjvv73vv73vv711c2VybmFtZXEDWAXvv73vv73vv71BZG1pbnEEWAT
vv73vv73vv71yb2xlcQVYBO+/ve+/ve+/vWFkbWlucQZ1Yi4K

Putting the pieces together:

eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiIsICJraWQiOiAic2VjcmV0LnR4dCJ9.woADY21haW4KVXNlcgpx77-9KcKBcQF9cQIoWAjvv73vv73vv711c2VybmFtZXEDWAXvv73vv73vv71BZG1pbnEEWATvv73vv73vv71yb2xlcQVYBO-_ve-_ve-_vWFkbWlucQZ1Yi4K.twnF4sfP88iIus9Ju9ga7TqXmlMYOuJUFUoKL6wYauY

This gives us an invalid JWT signature, though. OK, I clearly need to be using some other tool than just some URL base64 decoders/encoders and JWT.io.

Do we need to reverse engineer the secret key for the signature?

Well, we timed out while working on this one...I've butted heads against JWT and serialisation before, so it's going to be a research item.

POST CTF SOLUTION REVIEW: As it happens, I had a Black Hills Infosec webinar on the topic of JWTs the following week, and I also came across some excellent JWT resources:

Introduction to JWTs - https://jwt.io/introduction/
JWT Attack Playbook - https://github.com/ticarpi/jwt_tool/wiki
Are You Properly Using JWTs? - https://www.youtube.com/watch?v=M3jA0bGDCso
JWT Parkour - https://www.youtube.com/watch?v=zWVRHK3ykfo

What I had failed to note was that we could control the source of the secret key. Currently it is set to "secret.txt", but by manipulating the kid (key ID) variable, we could have pointed it to any other local file, including /dev/null to make it nothing at all. From there, we could craft our own payload and encode it using JWT.io with an empty secret key.

FINAL RESULTS

Final Score: 675
Final Rank: 704/2854