浅析SSRF与文件读取的一些小特性

0x0 前言

  很久之前打比赛的遇到的一个点, 我当时以为这样的读取特性是跟php伪协议有关呢, 之前也看飘零师傅发过朋友圈,但是记忆比较模糊了,刚好最近放假有时间来调试一下才发现原来是curl的锅。

0x1 分析问题

引起我的好奇心是一个师傅分析POSCMS的文章的一个tips:

其实我个人觉得师傅这里解释不是很严谨,这个问题其实主要还是出在了curl请求上面,比如我们请求http://127.0.0.1/1.php?.jpg,那么我们访问的内容就是1.php,而不是名称为`1.php?.jpg`的文件,而`file_get_contents`刚好相反,至于为什么是这样,其实通俗来说原因是按照URL的定义来解析,那么1.php才是资源名,所以才会导致这样的结果, 但是下面我将从php底层来分析下这个原理,说明白下面两段代码的实现差异。

我们先从两段代码开始看起:

<?php
$url = $_GET['url'];
$file = $url . '.jpg';
var_dump($url);
var_dump($file);
echo file_get_contents($url);
echo '</br>';
echo file_get_contents($file);
?>

<?php
var_dump(ini_get('allow_url_fopen'));
$url = $_POST['url'];
$url = $url . '.jpg';
var_dump($url);
// echo file_get_contents($url);

if(function_exists('curl_init') && function_exists('curl_exec')){
    $ch = curl_init($url);
    $data = '';
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $data = curl_exec($ch);
    curl_close($ch);
    var_dump($data);
}
?>

我们经常在代码里面看到下载文件的时候,一般都会有这两种函数去读取文件内容(导致SSRF), 他们一般是先执行file_get_contents判断返回结果为False的则接着去执行curl_exec,否则直接return, 至于为什么这么写,估计是考虑了像我上面列举的情况?

下面我就主要来分析下php底层如何实现file_get_contents的功能。

0x2 从底层分析file_get_contents

0x2.0 debug环境构建

下载:

git clone https://github.com/php/php-src.git
cd php-src
git checkout remotes/origin/PHP-7.2.0

编译:

./buildconf
./configure  --enable-debug  --disable-all --prefix=/Users/xq17/Desktop/PHPCore/
make && make install

0x2.1 开始分析

关于StreamWrapper和protocols的关系: Supported Protocols and Wrappers

关于file://,wrapper.file

PHP流的的概念

我们都知道PHP中的文件操作函数可以打开文件、URL等资源然后返回一个句柄。那么PHP是如何做到使用一致的API对不同数据源进行操作的呢? 其实就得益于PHP在底层对各种操作进行了分装, 再上层将其统一看做成"流"对象,在底层在进行具体解析。

php7.1.8

file_get_contents函数的定义

/ext/standard/file.c 520 line

解析完参数之后,开始解析流,我们跟入php_stream_open_wrapper_ex

/main/streams/streams.c 2010 line

跟进php_stream_locate_url_wrapper 这个函数

解析协议类型,继续向下看

里面看到path[n+3]!='/'这就是为什么我们使用file协议需要:file:///的原因

也就是必须要用绝对路径。

点击收藏 | 1 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖