It has been said that given enough time at a keyboard a monkey would write the complete works of Shakespeare. I don't know about that, but a monkey would probably solve some CTF challenges.
I unintentionally explored this theory while participating in ISSessions CTF 2022 last weekend.
Here's some of my favourite challenges that I solved while playing with w4t3rl0wn (2nd place! 🥳). Update: Came 1st in 2023!
Mandiant: So Predictable
A large group of monkeys on the island think they’re super 1337 haxors. Can you find a way to get into their monkey authentication site? We know the site is out there somewhere, they recently registered ‘hackermonkeys.com’.
There isn't much on hackermonkeys.com besides an email address bubbles@hackermonkeys.com
This isn't super useful yet, but the challenge description also mentions “the site is out there somewhere” and that they “recently registered ‘hackermonkeys.com’ “. So it's probably a subdomain of hackermonkeys.com.
I tried my luck on Certificate Transparency to avoid having to set up a proper subdomain enumeration tool, and it worked out. Plugging hackermonkeys.com into transparencyreport.google.com throws back a few interesting results:
portal.hackermonkeys.com
dnd.hackermonkeys.com
... (some more but they're not important so whatever)
portal.hackermonkeys.com seems to be the ‘monkey authentication site’ mentioned in the challenge description, and just has a simple login field.
Looking at the page source there's a few comments near the bottom of the page. One of them saying implement pw checker to stop lazy monkeys from using easily-guessable passwords like 'SeasonYEAR!' or the current 'MonthYEAR!'
. i.e., the password is going to be in the format SeasonYEAR!
or MonthYEAR!
.
After getting a list of all (Months/Seasons)(2021/2022)!
(thanks to my teammate kilimanjaro for throwing together a base script to generate a list really quickly) and running them against bubbles@hackermonkey.com using Burpsuite Community Edition Intruder (holy shit the rate limiting is horrible on it btw), nothing happened!
This really confused me, and it wasn't until hours later at 1am when I realized I had completely forgotten about the second interesting subdomain I saw, dnd.hackermonkeys.com
.
Sure enough, that subdomain contained a list of ~200 other potential usernames:
It also provided a great introduction to Wade (look at that smile!)
After appending @hackermonkey.com
to each username I decided to do some password spraying with my list of emails and passwords.
In other words, for every email in the email list I would try every password in the password list. Because Burpsuite Community Edition rate limiting sucks, I went with wfuzz this time.
wfuzz -w emails.txt -w passwords.txt -b csrftoken=p4hMKvWu54MVZBTzAY1OqFEOn2gMARIJVxRjzgP84ljDECdt9rDEdh0LCE40D5z2 \
-H "Origin: https://portal.hackermonkeys.com" -H "Content-Type: application/x-www-form-urlencoded" \
-d "csrfmiddlewaretoken=WrPOInOAcVgLTnXwyR5raMX73AfDaHAwsUplx8HebcNtyohq7kHhXoj4ic3RdVrP&email=FUZZ&password=FUZ2Z" \
https://portal.hackermonkeys.com/users/login/
In the command I specify two lists: emails.txt and passwords.txt. I then pass a cookie csrftoken
with -b
, 2 headers Origin
and Content-Type
with -H
, and then finally the body data of the POST request csrfmiddlewaretoken
, email
, password
with -d
. I designate the fields to fuzz (respectively) with FUZZ
and FUZ2Z
.
The csrftoken cookie, Origin header, Content-Type header, and csrfmiddlewaretoken body paramater were all required for a successful request. I also condemn bubbles the monkey web developer for using csrf tokens that don't expire 😔 (it made doing the challenge much easier though so ty challenge author).
And after running the above wfuzz command, I still got no results! This really confused me. Until of course I realized the emails should end in @hackermonkeys.com. Not @hackermonkey.com
Only one of the credential pairs resulted in a 302 response (redirect) instead of a 200: cupcake@hackermonkeys.com:March2022!
Mandiant: Leaked Sensitive Info
The hackermonkeys.com developer is known to make mistakes with his coding skills; can you find anything on the internet he may have accidentally leaked?
Leaked + coding + mistakes = Github
Searching for bubbles@hackermoneys.com
(the developer email mentioned on hackermonkeys.com) on github yields absolutely nothing. Just hackermonkeys.com however returns a single user with 2 repositories and a badass profile pic: https://github.com/hackermonkeyz
Browsing to the readme of the pewpew repository reveals the description:
Selenium spraying script for all those pesky portals.
That probably would have been helpful for last challenge :')
Regardless, there's 220 commits all with the message “bug fixes”. The “scripts” repository only has 1 commit and is generally uninteresting, so the flag/next step is most likely going to be in one of the 220 pewpew commits.
Embracing the monkey theme of the CTF, I decided that 220 commits wasn't enough to justify scripting something or finding a tool to look through all the commits. So like a true monkey I clicked through each one individually before eventually stumbling on one which removed a base64 encoded “password_token”. Before I get clowned on too hard for this, I got first blood on this challenge lol.
Decoding bW9ua2V5Q1RGezh2ZnZ3ZjNrYXc0dnhjejQxZDc4dnp4cmx0djczY3cwfQ==
returns the flag
Mandiant: Light the beacons
One of the monkeys lit a beacon for the others to follow, but we are having a hard time understanding the message. Can you translate this for us? (Anti-Virus solutions may pick this text up as malicious. Please ensure this is in an exclusion folder).
A beacon is a program which creates a connection with an attacker's server to communicate information about the victim machine. However that's not important because CTFs teach us that whenever you see base64 IMMEDIATELY rush to your decoding solution of choice no matter the context. This challenge is no different.
Upon decoding the initial base64 you'll see some more base64 inside of a powershell script. Decode this as well and you'll get a gzip file.
Near the bottom of the decompressed gzip file there's some more base64
[Byte[]]$var_code = [System.Convert]::FromBase64String('TkxNSEZaYHdlWBZWE1JoFnNAYHVqEGERFmVMT3ZTUElSaWJTZWJwG3JhXg==')
for ($x = 0; $x -lt $var_code.Count; $x++) {
$var_code[$x] = $var_code[$x] -bxor 35
}
Side note: After copy and pasting the above code, windows defender freaked out and tried to delete the entire markdown file :D
Moving on, if you try to decode this base64 straight up, you won't get far. It's also run through a for loop where it gets XORd with a key of 35
. Luckily xor does the same thing ‘backwards’ as it does ‘forwards’, so by just xor-ing it with the same key we can decode the text.
Moderator Monkey
My methodology on this challenge was to start doing relatively random operations and manipulations in Python until I got something interesting.
My thought process was essentially: “binary? let's put that into decimal so it's easy to work with. We have numbers and probably want something with readable english words? let's throw a modulus 26 on each number. Now I should have something that vaguely resembles english so I'll use a tool that maps 0=a, 1=b, etc.”
pewh@frame:~/ctfs/issessions/modmonkey$ python
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bins = "01000111010101011010000001010011011100110100111001101110101111011001110001000110101101110011100000011110101010011001111010110110010111010111101110110000001010111011101010011111"
>>> len(bins)
176
>>> 176 / 8
22.0
>>> n = 8
>>> bins = [bins[i:i+n] for i in range(0, len(bins), n)]
>>> bins
['01000111', '01010101', '10100000', '01010011', '01110011', '01001110', '01101110', '10111101', '10011100', '01000110', '10110111', '00111000', '00011110', '10101001', '10011110', '10110110', '01011101', '01111011', '10110000', '00101011', '10111010', '10011111']
>>> nums = []
>>> for b in bins:
... nums.append(int(b,2))
...
>>> nums
[71, 85, 160, 83, 115, 78, 110, 189, 156, 70, 183, 56, 30, 169, 158, 182, 93, 123, 176, 43, 186, 159]
>>> nums = [n%26 for n in nums]
>>> nums
[19, 7, 4, 5, 11, 0, 6, 7, 0, 18, 1, 4, 4, 13, 2, 0, 15, 19, 20, 17, 4, 3]
>>> ' '.join([str(n) for n in nums])
'19 7 4 5 11 0 6 7 0 18 1 4 4 13 2 0 15 19 20 17 4 3'
VI
We managed to retrieve SSH credentials, through a brute force attack. We know that the system was used to develop a login page for a startup. Try finding the password.
ssh monkey@challenges.issessions.ca -p 5706 [Password = monkey]
Since this challenge was named Vi and in the Pwn category, my first reaction was that it's a privilege escalation where the user can run Vi as root.
The second part was true, but it wasn't a large part of the challenge. Exploring the file system a bit shows that we're a developer who is creating a flask app, and that there may be a backdoor.
There isn't much interesting available info in the AuthSec files, but doing ls -alr
reveals that there's a .git
directory.
By going to .git/logs/refs/heads
and cat
-ing main
we can see past commits. One of them mentions “Removed Extra Info”. When trying to use git cat-file -p
on “Created Login Page” to print out the contents of the repository object, we get a permission denied error.
At this point I can take advantage of being able to run vim as root. Doing sudo vim
then :!/bin/bash
will give a root bash console with which I can look through the contents of past commits.
Printing the old contents of app.py, there's a comment with a password (the flag).
Causing Discord
A Discord bot hosted by a newbie programmer from Monkey Island has recently gotten super popular! They are planning on launching a new update that would include login features and even microtransactions. But, a very frightening vulnerability is just waiting to be exploited…
Can you find it before a malicious actor gets to it first?Privately message "ms!help" to the MonkeySee Discord Bot. The bot can be found in the CTF Discord server.
Hint: Try looking at the bot's documentation to see if there's something that could potentially be called unsafely.
This challenge hit home for me. My first big project was a Discord bot, which had a calc
command for whatever reason. As a ‘newbie’ programmer I implemented a calculator by using eval()
. In python, eval()
will execute whatever input is passed to it (I.E., BAD AND SCARY.).
Thankfully someone messaged me about it before someone could exploit (to my knowledge), and I was able to fix it.
Because of this memory, during this challenge I got hyper-fixated on the calc command, and completely ignored everything else for ~15 minutes. The fact that I was able to generate errors didn't help either :')
Finally after moving onto monkeydo
and throwing random inputs at it I found something promising
Anything enclosed in backticks “``” wouldn't show up in the resulting output file. After trying a bunch of commands that resulted in nothing (most likely the machine didn't have what I tried installed), I found that python3 one liners would get executed:
From there it's a simple case of finding the flag file and sending it to output
Harambe
Defenders at Monkey Island security were able to recover the LSASS dump file the attacker created (attached). They are worried that the credentials of the Domain Administrator account (“harambe”) were compromised as he was logged into Win2019 when the dump occured. They want to know if you can find harambe's NT password hash in the dump file. What is harambe's NT hash?
ANSWER FORMAT: monkeyCTF{NTPasswordHashOfHarambe} (All lowercase.)
EXAMPLE ANSWER: monkeyCTF{787a6994b59bbdf146616547b326fa71}
And of course it wouldn't be a monkey CTF writeup if I didn't mention Harambe.
For some background, LSASS is the Windows process that deals with login verification and the likes. If an attacker is able to make an LSASS memory dump (using procdump, taskmanager, mimikatz, etc.) and bring it back to their machine locally they may be able to extract credentials/password hashes like an NT hash. For the challenge, we're given a premade dump and all we have to do is extract harambe's NT hash.
Sounds easy enough right? Unfortunately the hardest part of this challenge was installing one of the many programs made for this exact task.
Volatility? Tried installing in 3 separate VMs before I gave up. Mimikatz? Apparently recent versions have a bug that sometimes pops up when extracting NT hashes, and their github releases removed older versions.
Finally, I installed pypykatz (<3 python). It worked instantly and I grabbed the hash.
NT: 787a6994b59bbdf146616547b326fa71
But wait… I've seen that hash before.
Yep the Example Answer was the real answer. Well played author, well played.
Thanks to ISSessions for putting on a great CTF, I'll definitely play again next year!
I may add some more writeups to this page in the future.