找回密码
 立即注册
查看: 2702|回复: 0
打印 上一主题 下一主题

phpcms v9 2013-02-01 会员中心注入漏洞分析报告

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
# I1 n0 B& t0 Z: y3 f* i漏洞作者:skysheep+ t4 {1 {3 I3 B' P6 ?  n5 i. N- {1 W3 }
分析作者:Seay
0 A% d* Y$ D0 D2 J" v博客:http://www.cnseay.com/9 H4 o8 b  b: }- r9 n5 Y2 H4 O, P
漏洞分析:
5 r; k- w. P7 F  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。! @2 E1 E. g- B& A8 {
1 W/ q' d' ]( o
7 X: Y3 b) u$ g* k; P' `. [8 t
5 h* J* i  l( C$ Z' M& D2 M5 N
public function account_manage_info() {  ! M+ v. L# ?' E, K5 p% ^0 y: h
       if(isset($_POST['dosubmit'])) {  2 Y5 ^- X* s; d  u' k
           //更新用户昵称  
5 J2 I0 m  i; Y8 u( D           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  8 E3 ]7 Y" F& K' |; o. E% ^6 H3 q# b
           if($nickname) {  9 S# }7 G: ?7 B* h
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  # K( R( ~4 G7 r+ X4 r# Z
              if(!isset($cookietime)) {  $ k/ c) `- E) E' b" s7 j
                  $get_cookietime = param::get_cookie('cookietime');  
% j0 ^# o6 s1 t              }  
9 s7 ]! ~9 w' i% {              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
; b) J) C' m  P- p( j; _              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
7 S& m& o/ |6 z4 H; ?              param::set_cookie('_nickname', $nickname, $cookietime);  ! E& r+ L! i% A
           }  7 h  V  k; T( E
           require_once CACHE_MODEL_PATH.'member_input.class.php';  ) u' w+ I: A3 ~7 N
           require_once CACHE_MODEL_PATH.'member_update.class.php';  2 Q/ n, m5 i( J
           $member_input = new member_input($this->memberinfo['modelid']);  ( ?# ^' ]. f  }4 G3 Q
           $modelinfo = $member_input->get($_POST['info']);  & a5 [/ x1 E- P' O! ?
           $this->db->set_model($this->memberinfo['modelid']);  
: F! T- q2 y7 e- ^% |           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
9 G3 w% s0 F+ q' F           if(!empty($membermodelinfo)) {  ' a. m; K& A- v  @6 j5 y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
* e, [9 q+ L7 i4 Z# B           } else {    x6 p1 u; S! O% p! b
              $modelinfo['userid'] = $this->memberinfo['userid'];  
" ~7 f, x  w. E" x8 X8 j$ ?' N              $this->db->insert($modelinfo);  
3 A3 B  }: u! b           }
$ |- S! H/ c3 O! `! ^# R1 a代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,; O. J& \- l8 x- |, Z! H3 T
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 {& O* I( j: n0 ~4 B
7 F2 b! C+ H) z0 p" {
/ F' q6 I+ I+ G$ d, u& ]" p+ U
; E% l) c" z  V6 M. ~6 \function get($data) {  ' @% c( r0 U' N" A# u
       $this->data = $data = trim_script($data);  
- t+ X# a( G+ d- R: f; I0 j       $model_cache = getcache('member_model', 'commons');  & W- c: \3 r% `. p1 `! g, J
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
. f" E# ]" \; C# ]$ k4 U$ T       $info = array();  : j* A7 n* M" w1 o5 W$ g' p# S
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
/ ~: z* i" k7 p9 @% ^5 L       if(is_array($data)) {  
4 O% x/ m/ x  u+ p8 s8 ?( @           foreach($data as $field=>$value) {  
* V3 U! [7 C- U/ P: h              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
& L7 g  K8 X3 b7 [% v1 K2 C              $name = $this->fields[$field]['name'];  
9 S4 @- P2 C1 l              $minlength = $this->fields[$field]['minlength'];  
4 f/ j$ F: Y' S. ^              $maxlength = $this->fields[$field]['maxlength'];  0 I3 l% U; n7 H
              $pattern = $this->fields[$field]['pattern'];  
% y. {4 t/ N' a$ h6 C0 X              $errortips = $this->fields[$field]['errortips'];  
' Y1 y0 G1 K7 {! |" q7 h              if(empty($errortips)) $errortips = "$name 不符合要求!";  
' d% z7 \+ X* ^4 t, w" T) {              $length = empty($value) ? 0 : strlen($value);  
# U$ z# I3 O0 N0 S) t! T- Q9 A              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
! V, F$ E( y" P/ E  |              if($maxlength && $length > $maxlength && !$isimport) {  
2 t- _- X, S, v9 `7 s5 p( W                  showmessage("$name 不得超过 $maxlength 个字符!");  
/ U1 m/ F' w5 n              } else {  
) V5 ^$ w6 r6 A                  str_cut($value, $maxlength);  / z3 @- n3 t+ V+ b
              }  8 M# s: P& x. D7 N7 W* z& Y; Z9 i( r# b9 L
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  1 s; x4 p5 p+ z4 P
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
  E- g9 h: z" {              $func = $this->fields[$field]['formtype'];  
& Z$ {* D' c  w              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
& E% ^& F3 h2 N2 x0 X5 l              $info[$field] = $value;  3 Y% h% `; I2 m% Z% C# F
           }  . X6 U2 [& h2 C* T9 m( P
       }  
8 V$ ?& c5 h- F5 ^$ X$ ~       return $info;  , H; i3 S7 F# }) V
    } / j$ j0 Q! _* X
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
6 K7 N  Z+ B( K. S8 m8 q) k; y9 H& Y1 ?' s+ Q' i3 w4 [
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ ?: K9 V* i$ N) Q4 |& m+ N" k) N# B过了get()函数之后。
  ~7 ^) k& }1 `2 N3 H/ h: p) d
/ ^5 j' K- y% t: A7 R# r + b2 g6 M" Q) M6 }
$modelinfo = $member_input->get($_POST['info']);  ( I& ~0 c$ t: t% F" d
           $this->db->set_model($this->memberinfo['modelid']);  
; \0 z  N  Z3 d% s0 |. G; s; u5 C+ `           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
3 B% S7 |  f9 Y5 m8 q. R  `! I           if(!empty($membermodelinfo)) {  
3 T: ^# U3 d# |# K& A0 K& N2 `              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
' x+ ?% R) X$ O& y           } else {
( B+ e6 B) G' _  j6 P* x直接带入数据库,update函数我们跟进看看
9 O7 N9 B+ K1 x  o$ }# L  r0 n" b2 A9 f4 k- h2 H+ l) J1 t/ a

4 Q9 I0 Q3 |; p0 N4 vpublic function update($data, $table, $where = '') {  
2 C' b$ I1 `/ ~; \       if($table == '' or $where == '') {  
8 `8 j" N% ]1 m8 y. Z           return false;  0 s6 F4 c# x% b& N# l
       }  # r6 H( `) N" Q! J. B
       $where = ' WHERE '.$where;  
: g1 q5 t5 T5 `0 C% s3 Z       $field = '';  
" K3 y$ D4 n3 C" }' q3 Y       if(is_string($data) && $data != '') {  9 x% x7 g7 C# F; T( e! ^
           $field = $data;  3 }: z7 q( t, y9 f! V
       } elseif (is_array($data) && count($data) > 0) {  
2 ?, y5 g. _: j* y. W; ]1 G           $fields = array();  2 R" f8 S. t( N* Z: M+ S6 A! J! e2 o
           foreach($data as $k=>$v) {  6 R) x' Q5 }) G
              switch (substr($v, 0, 2)) {  
- J; U6 S$ \7 F% q, E3 F! c8 \                  case '+=':  8 Y, c( ~! w3 H" |3 H
                     $v = substr($v,2);  * P7 Q7 _) U/ L. u7 W
                     if (is_numeric($v)) {  
, b" Y+ C; j2 w, _0 E6 p                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
$ g6 _8 k: L' O% M1 t1 E6 K! C                     } else {  + f! l$ `' \4 C
                         continue;  
. I1 j  u+ q1 i5 S                     }  , S( I' D# F' P: Q
                     break;  
5 V; y# h! |) `0 v3 }7 [                  case '-=':  ) s7 n4 H- ~& D  a& P- b7 w$ B. K" V
                     $v = substr($v,2);  
( a+ E6 ~! W' e                     if (is_numeric($v)) {  ' t. T$ }9 x5 m7 M' u* ~- y9 i- q
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  * w4 T; g8 i* C, i- M
                     } else {  
+ R; l$ T& l3 \, d7 }                         continue;  % b* |; a) C( a1 W9 H
                     }  9 x) _& k, V8 w! x8 x" L
                     break;  # F, u& S" ~5 B+ Y/ z- H
                  default:  ; R  o, x. L9 q8 N
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
* R4 O, B8 H! \' l              }  
6 a5 r! n" k& Z4 q! {! F) }           }  4 c/ i5 m$ Y4 Z, ], |
           $field = implode(',', $fields);  0 D# m1 J( W/ u( b7 Q
       } else {  $ j& [8 d" ?" R% b* y+ Q
           return false;  
1 W. D  V4 U; i- j, J) t       }  
: G. w8 |2 r: W       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ' h: c1 ?7 \- y, g4 m
       print_r($sql);  
6 Q1 C( o4 J% S: P. p1 C       return $this->execute($sql);  ( z8 o6 F! J) P) N
    } 7 ~) {# q$ m; t; }
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
) A) I2 g, z, A; A: K& L8 e9 A' `9 N% N* @5 n0 _1 \8 e* A$ x
攻击测试:
& @! O# f( G) m; |: v/ `测试地址http://localhost
1 l( O1 S3 e5 u, G- e1 `* l# B+ a  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
) k3 c- s/ C2 f- e, l  D; ^1 A4 v! ~
  I4 Y( ?! w) f# ?
9 y) T; L7 e6 e" A

" R% c! p0 Z2 e' ?+ F
$ c+ T  P0 Y( p0 @+ z5 V

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表