Skip to content

Instantly share code, notes, and snippets.

@tfirdaus
Forked from hakre/other.php
Created April 9, 2020 13:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfirdaus/0b259252d472e090cb7dc4bdc1ac9955 to your computer and use it in GitHub Desktop.
Save tfirdaus/0b259252d472e090cb7dc4bdc1ac9955 to your computer and use it in GitHub Desktop.
inject php declare ticks poc
<?php
/**
* just some file
*/
$a = 1;
if ($a > 0) {
$a += 2;
print($a);
}
<?php
/**
* Streamwrapper Implementation (PoC covering include)
*/
declare(strict_types=1);
class FileStreamWrapper
{
/**
* @var resource
*/
public $context;
/**
* @var resource
*/
private $handle;
/**
* @var string
*/
const protocol = 'file';
public static function init()
{
$result = stream_wrapper_unregister(self::protocol);
if (false === $result) {
throw new UnexpectedValueException('Failed to unregister');
}
stream_wrapper_register(self::protocol, FileStreamWrapper::class, 0);
}
private static function restore()
{
$result = stream_wrapper_restore(self::protocol);
if (false === $result) {
throw new UnexpectedValueException('Failed to restore');
}
}
public function stream_open($path, $mode, $options, &$opened_path): bool
{
if (isset($this->handle)) {
throw new UnexpectedValueException('Handle congruency');
}
$use_include_path = null;
// TODO(hakre): implenent based on options
$use_include_path = true;
$context = $this->context;
if (null === $context) {
$context = stream_context_get_default();
}
self::restore();
$handle = fopen($path, $mode, $use_include_path, $context);
self::init();
if (false === $handle) {
return false;
}
$meta = stream_get_meta_data($handle);
if (!isset($meta['uri'])) {
throw new UnexpectedValueException('Uri not in meta data');
}
$opened_path = $meta['uri'];
$this->handle = $handle;
return true;
}
/**
* @return array
*/
public function stream_stat(): array
{
self::restore();
$array = fstat($this->handle);
self::init();
return $array;
}
private $declaredTicks = false;
/**
* @param $count
*
* @return string
*/
public function stream_read(int $count): string
{
self::restore();
$result = fread($this->handle, $count);
self::init();
if (!$this->declaredTicks) {
$result = preg_replace(
'~^(<\?php\s*)$~m',
"\\0\ndeclare(ticks=1);\n",
$result,
1
);
$this->declaredTicks = true;
}
return $result;
}
public function stream_eof(): bool
{
self::restore();
$result = feof($this->handle);
self::init();
return $result;
}
}
<?php
/**
* Inject declare ticks on include
*/
declare(ticks=1);
require __DIR__ . '/streamwrapper.php';
FileStreamWrapper::init();
// using a function as the callback
register_tick_function('tick_handler', true);
// Function which is called on each tick-event
function tick_handler()
{
echo "tick_handler() called\n";
debug_print_backtrace();
}
register_tick_function('tick_handler');
include "other.php";
include "another.php"; # this file does not exists
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment