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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告: X9 ?2 l( a( x
漏洞作者:skysheep+ T& I4 r) S- D+ M. s! D5 X
分析作者:Seay; e  x* \1 x) g. N4 T9 J0 @# n
博客:http://www.cnseay.com/* b* y1 A1 x4 U
漏洞分析:
0 t+ ?0 b" ]" A# C( l  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) B3 }$ O$ T) A8 t  T+ t5 r% R: F( `- v

4 c( I2 K. n1 s7 X! {9 S$ T2 ]3 { - G- {1 P/ A$ n- T/ P: Y
public function account_manage_info() {  % z/ e$ M* L8 H" m
       if(isset($_POST['dosubmit'])) {  
4 |- ?- ^8 H7 a           //更新用户昵称  % y! i' p$ f) r& U" n' Y# `6 p
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
# b$ l0 D3 `/ D4 y0 C; W: |           if($nickname) {  
& e- `, t: K$ v4 \0 B. R9 A              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  & x7 i. B" i6 E, i* e5 x
              if(!isset($cookietime)) {  % S: o5 P2 D  \. N# ^7 ]
                  $get_cookietime = param::get_cookie('cookietime');  
* v3 Q  {  R6 f* D              }  
% i; P- Q. v: G5 d- K              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  2 d2 V( n% q$ k: p* o: N1 u- M* c8 J) K
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  . A/ Y1 }' P3 D0 f4 D" u
              param::set_cookie('_nickname', $nickname, $cookietime);  
5 X1 ~  x* C" {7 W8 j7 }% V           }  
  P8 h) e6 f- u) I           require_once CACHE_MODEL_PATH.'member_input.class.php';  ! D7 M7 @7 d0 w/ `. O, A
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
7 H/ b, |9 C6 v# i           $member_input = new member_input($this->memberinfo['modelid']);  : }) q! J+ N6 J/ [+ w. y, P
           $modelinfo = $member_input->get($_POST['info']);  
' i6 k! `* K) K$ H8 p           $this->db->set_model($this->memberinfo['modelid']);  7 ~, s; d, p. ?6 l& C
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  2 w# l3 B; j7 e9 i
           if(!empty($membermodelinfo)) {  
0 F  U/ l$ o* i  ~( p' y: h+ f* ^              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
$ B: D& x6 P. H+ V           } else {  
. B! m9 j7 W" x" n' |              $modelinfo['userid'] = $this->memberinfo['userid'];  
4 Q0 h) G! J  A4 v: z              $this->db->insert($modelinfo);  
% ~3 Q' B2 L, `7 Q+ ?7 g           } 1 ?. n/ u( u; y. G; h
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
4 u4 }/ i/ D+ h1 a' o在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ ?  @/ w  e4 o: p  d- q- @3 ?9 d* }' F

" }, U$ P3 U3 s9 ^
/ N) ~4 [% a' W2 F5 ^: |1 Bfunction get($data) {  
( z( j3 E) v+ n1 X, I# E       $this->data = $data = trim_script($data);  4 G' P6 L: j7 V5 \* F% r
       $model_cache = getcache('member_model', 'commons');  1 z  D' c7 u0 h& G
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
5 d0 h& r5 D5 g5 `5 N8 X3 o7 m       $info = array();  
5 |1 r. t4 {) i; Z       $debar_filed = array('catid','title','style','thumb','status','islink','description');    Y; E7 f3 B- U8 L, p1 o% g3 X- Z
       if(is_array($data)) {  
4 C; T: G1 z8 [+ P6 x* w5 `6 M           foreach($data as $field=>$value) {  
4 N3 n/ d  {( M, I  i4 O; [1 A              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  " _+ C( s6 B' U
              $name = $this->fields[$field]['name'];  
$ S& u4 w2 S/ Z4 ]4 |+ j              $minlength = $this->fields[$field]['minlength'];  : p+ K& G% P" D" `
              $maxlength = $this->fields[$field]['maxlength'];  
! i) y  o. W: {0 g1 R5 u$ D$ j              $pattern = $this->fields[$field]['pattern'];  
' V3 i" U; O) `8 W* ~* p2 Z              $errortips = $this->fields[$field]['errortips'];  + Q( Y3 H: h5 A# t/ T4 y! q9 |. @- q
              if(empty($errortips)) $errortips = "$name 不符合要求!";  " w; n0 [8 C' B, X, ^
              $length = empty($value) ? 0 : strlen($value);  " Y/ W% j3 u! L; t
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  + c7 m2 l" M$ ]. L
              if($maxlength && $length > $maxlength && !$isimport) {  
. C' ]6 m" o0 T3 m6 J2 I" n                  showmessage("$name 不得超过 $maxlength 个字符!");  
- m+ q: \/ C, w+ W8 J2 n2 j1 b2 V              } else {  
: ]1 j5 o; H5 B: ]1 X                  str_cut($value, $maxlength);  7 I* ^! p$ B4 t5 |3 O( h' [' R
              }  
" r7 u/ ]% c2 j+ m1 ]6 V              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  ; C2 ^* @9 E1 C. [7 T( X  s. A
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  1 Q3 f! Q, W8 {
              $func = $this->fields[$field]['formtype'];  
2 z* ]- w, ~  r4 u* ?" ~              if(method_exists($this, $func)) $value = $this->$func($field, $value);  8 B) b* ], _7 i9 ]4 `/ I2 G
              $info[$field] = $value;  
1 s) i* ]5 U% o5 U, r           }  
; t! m/ e: G9 s6 y& T       }  8 b/ b# v/ Y4 x4 Y
       return $info;  
+ A: v7 ^7 H3 |$ x  Z6 b. r    }
. \" s, U: a' y& mtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,1 f0 O& U) I) c. E$ E* ^6 I' P

/ q4 b5 O' c! p5 r) |& n再到phpcms\modules\member\index.php 文件account_manage_info函数
, Y1 K( s( G) {; f$ B过了get()函数之后。
% q0 p- a' G- P8 D* i2 I% t; M, A9 Y8 b2 E8 Z+ E0 ]4 Y

$ Q8 K/ ^! C- Q7 W! V$modelinfo = $member_input->get($_POST['info']);  
$ I  ?7 K5 g3 e           $this->db->set_model($this->memberinfo['modelid']);  ; |4 G; S- A5 k1 ?  d6 S
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));    [5 f9 ^8 T" G- i9 |$ r
           if(!empty($membermodelinfo)) {    D& C% I! \5 `- M
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) c* e7 X  {; d  Z6 x/ Q0 Y1 @5 X
           } else {
& x( n8 y5 {' F# |$ E& }/ Z直接带入数据库,update函数我们跟进看看
6 z0 v1 ?* t8 t4 h( C0 ?- q
0 k5 i6 u6 k7 ^: y4 ]5 u/ a8 G  w
3 A2 P# b: ]7 j4 }; J( A5 p+ @public function update($data, $table, $where = '') {  . w! j$ L# C: |6 ~" g
       if($table == '' or $where == '') {  
# }# p8 I5 S  S" j( c4 b  d% O           return false;  
- t' P$ |$ M; h% ~7 q' r       }  
- G/ i3 C9 K; a& o2 w       $where = ' WHERE '.$where;  
( C6 O+ a9 g+ y& N  `5 \+ z       $field = '';  8 t; o% x1 e/ ~3 q) y6 `0 j
       if(is_string($data) && $data != '') {  & L/ f- b& N( I" `- V
           $field = $data;  + W2 M6 h) k. x/ H5 |" f0 ]
       } elseif (is_array($data) && count($data) > 0) {  / ~( \: a8 q" ^1 z3 ^  K3 `
           $fields = array();  
" s: |# b$ d! @# {9 L( C: t           foreach($data as $k=>$v) {  
# J. r' x0 A% b( u! I: b& }8 B+ C9 V              switch (substr($v, 0, 2)) {  
$ w  B* V) w4 X& f3 @' u                  case '+=':  
/ v9 T- ~0 q  v/ D1 w" r" y' H                     $v = substr($v,2);  3 \' `; p) @& l7 _/ F! R" v# E( S: v
                     if (is_numeric($v)) {  ) O  z8 T" B' b  o+ @; E8 x: {
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
0 @; R$ j: b" z                     } else {  7 {; g. K9 q0 U5 I
                         continue;  
; X) ^0 f5 ^# M! ?                     }  2 \9 G3 {- J- |, {* f8 f
                     break;  : y0 S  J1 t+ s4 h, n
                  case '-=':  0 l' H/ _5 B, Z& m. v" b: f
                     $v = substr($v,2);  
' W% q) @2 t  \. |                     if (is_numeric($v)) {  
& s, `. t2 z  g5 l  X                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ! U9 U; s/ R% C+ f+ J! M
                     } else {  ! R2 v7 D+ K4 U1 a* k3 x. p
                         continue;  
( G% F2 ]6 s, j6 F- g5 c                     }  
" j0 b$ z* F1 R% z9 \' z) i                     break;  
9 ]7 T4 Y6 g% T$ _$ _+ l8 i$ F                  default:  
& z7 z& h- R/ L                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  : I6 Y' h1 t$ j$ g/ s
              }  % |" ?2 f7 o# l7 v
           }  , f  V& l( w- e5 W  ~/ X
           $field = implode(',', $fields);  
1 k3 M; t. J0 L: Y1 @       } else {  
2 o! i. O0 h" Y; G! I/ }( I           return false;  
$ Z7 H+ }% \2 N& G       }  " ?1 e2 y4 A( n; f4 V4 h
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  / l. M% }( D$ J! y) t) Y& h
       print_r($sql);  / v; s% O8 Y! _3 ^
       return $this->execute($sql);  
( F. g( p) I2 D  E: ?    }
. W8 G, E7 v6 o: N7 i8 w从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。2 a0 P4 Z9 @! `& c( |, R

- M3 s+ q& V0 `% i4 s$ A/ R攻击测试:
+ }9 q$ k$ W4 o测试地址http://localhost7 g+ X! u0 _7 O% j8 x7 U
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
  f+ W6 v9 m5 H
+ u; ^- U9 H! {0 l
# O4 ~6 \9 ^7 k, l7 R3 b& R7 z9 r* N0 N; Y: Z

' q" Q6 E0 M: w" k" k9 R* S* j0 w, \9 N

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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