从一道CTF练习题浅谈php原生文件操作类 – 安全客,安全资讯平台 | xxx从一道CTF练习题浅谈php原生文件操作类 – 安全客,安全资讯平台 – xxx
菜单

从一道CTF练习题浅谈php原生文件操作类 – 安全客,安全资讯平台

十二月 4, 2018 - 安全客

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

 

前言

记一次CTF练习有感,觉得需要记录一波…还有就是最近CTF比赛中利用php原生类来进行反序列化的题目比较多,所以就紧跟时代潮流…

 

一.SPL

顾名思义,SPL就是Standard PHP Library的缩写。据手册显示,SPL是用于解决典型问题(standard problems)的一组接口与类的集合。打开手册,正如上面的定义一样,有许多封装好的类。因为是要解决典型问题,免不了有一些处理文件的类。

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

一.可遍历目录类

DirectoryIterator

FilesystemIterator

GlobIterator 与上面略不同,该类可以通过模式匹配来寻找文件路径。

二.可读取文件类

SplFileObject 在此函数中,URL 可作为文件名,不过也要受到allow_url_fopen影响。

 

二.文件系统相关扩展

finfo 该类的构造函数finfo::__construct — 别名 finfo_open(),也可以读取文件。

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

 

三.例题

题目是websec上面的第12关

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

题目可以见源码就一句

echo new [class]([first parameter],[second parameter]); 

类的名字可控,类的参数可控,个数为二,题目说过滤了(实际可以绕过)上述提到的`splfileobject, globiterator, filesystemiterator,and directoryiterator等诸多函数,考虑使用finfo类,正好开了此拓展。

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

可以读到文件,但是$key未知

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

关键代码大致如下:

<?php     ini_set('display_errors', 'on');     ini_set('error_reporting', E_ALL);     ....     $key = ini_get ('user_agent');     if ($_SERVER['REMOTE_ADDR'] === '127.0.0.1') {          if ($_SERVER['HTTP_USER_AGENT'] !== $key) {              die ("Cheating is bad, m'kay?");          }     }      $i = 0;     $flag = '';     foreach (str_split (base64_decode ($text)) as $letter) {         $flag .= chr (ord ($key[$i++]) ^ ord ($letter));     } 

要想得到flag,则需要知道php.ini文件中的user_agent,尝试读取php.ini文件,路径未知,无果。

尝试使用SplFileObject访问VPS,得到服务器自身的user_agent。利用绕过得到user_agent

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

 

四.问题成因分析

调试一下为什么finfo会将文件信息(当然这一些都是建立在程序警告、报错消息开启的情况下

ini_set('display_errors', 'on');ini_set('error_reporting', E_ALL);)爆出来的原因。

编译过程就略过了,用的是php-7.0的源码。在ext/fileinfo/fileinfo.c文件的285行出下断点。也即finfo_open函数处

/* {{{ proto resource finfo_open([int options [, string arg]])    Create a new fileinfo resource. */ PHP_FUNCTION(finfo_open) {     zend_long options = MAGIC_NONE;     char *file = NULL; 

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

1.php内容为:

<?php ini_set('display_errors', 'on'); ini_set('error_reporting', E_ALL); echo new finfo(1,'1.php') 

忽略一些过渡的函数调用如magic_load->file_apprentice->apprentice_1->apprentice_load->load_1来到 ext/fileinfo/apprentice.c文件1025行处,也即load_1函数处

/*  * Load and parse one file.  */ private void load_1(struct magic_set *ms, int action, const char *fn, int *errs,#flag    struct magic_entry_set *mset) {     char buffer[BUFSIZ + 1];     char *line = NULL;     size_t len;     size_t lineno = 0;     struct magic_entry me;      php_stream *stream;       ms->file = fn;     stream = php_stream_open_wrapper((char *)fn, "rb", REPORT_ERRORS, NULL);      if (stream == NULL) {         if (errno != ENOENT)             file_error(ms, errno, "cannot read magic file `%s'",                    fn);         (*errs)++;         return;     }      memset(&me, 0, sizeof(me));     /* read and parse this file */     for (ms->line = 1; (line = php_stream_get_line(stream, buffer , BUFSIZ, &len)) != NULL; ms->line++) {         if (len == 0) /* null line, garbage, etc */             continue;         if (line[len - 1] == 'n') {             lineno++;             line[len - 1] = ''; /* delete newline */         } 

php_stream_get_line函数将finfo要读取的文件一行行读出

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

随后将一行行内容进入parse函数进行解析,

        switch (parse(ms, &me, line, lineno, action)) {             case 0:                 continue;             case 1:                 (void)addentry(ms, &me, mset); 

parse函数解析内容是否有效

/*  * parse one line from magic file, put into magic[index++] if valid  */ private int parse(struct magic_set *ms, struct magic_entry *me, const char *line,     size_t lineno, int action) { 

对于不符合magic文件内容格式的则会发出相应警告,从而一句句报出文件信息。

    file_magwarn(ms, "offset `%s' invalid", l);#1802行     file_magwarn(ms, "type `%s' invalid", l);  #1950行 

从一道CTF练习题浅谈php原生文件操作类 - 安全客,安全资讯平台

 

五.后记:

上诉里面有一些东西只是简单提一下,希望能抛砖引玉。在一些情况下,如反序列化和其他某些特定场所,原生文件操作类也许能发挥不小的作用。


Notice: Undefined variable: canUpdate in /var/www/html/wordpress/wp-content/plugins/wp-autopost-pro/wp-autopost-function.php on line 51