中国网络渗透测试联盟

标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告 [打印本页]

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告( A0 @3 ?0 O2 I) \
漏洞作者:skysheep7 c0 ]3 n9 Y& S& i
分析作者:Seay5 G2 K; D9 {3 Z6 n& P( ?
博客:http://www.cnseay.com/9 v, M9 q+ i: c# t' T
漏洞分析:5 f: D5 S6 q& _2 ^6 G
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。: d  `. g* K# `3 w: f; R0 T8 O

- i2 v0 W. E! _9 m6 s- g6 |
8 z6 ~$ O* |' ?3 N + A: _  L4 c% n: M! B
public function account_manage_info() {  
8 f2 R) }. A* @. p8 [       if(isset($_POST['dosubmit'])) {  , B9 [/ p7 H& X% v7 r: d0 X
           //更新用户昵称  $ M7 _/ b* u; I' g6 `$ ^! C1 f5 L1 k
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
3 H, \4 {- j- g8 h           if($nickname) {  0 M& g! W& _! t( U/ m  _9 z3 g
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ) v' J4 I. C8 [+ Y
              if(!isset($cookietime)) {  
( E" h7 x# U7 Y  ?7 R                  $get_cookietime = param::get_cookie('cookietime');  + c) B+ S# ]* G  H3 M
              }  
' [( b: C, f9 h              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
/ K- _# U# y$ Q0 r& l              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  % k- A" Q, R0 _( c, z
              param::set_cookie('_nickname', $nickname, $cookietime);  4 ?/ }- t( D3 V4 p" |6 H# k
           }  ' w- Q5 s9 |. d- \" d; M+ \
           require_once CACHE_MODEL_PATH.'member_input.class.php';  * H3 a2 p( u/ o+ A  m
           require_once CACHE_MODEL_PATH.'member_update.class.php';  + U( z- A5 e2 ]! n& x7 Z/ W, O' ~
           $member_input = new member_input($this->memberinfo['modelid']);  $ u* x5 g5 E5 U8 U" \2 L0 |/ x$ r
           $modelinfo = $member_input->get($_POST['info']);  ! f+ c) m. L7 f9 c* h' l* j
           $this->db->set_model($this->memberinfo['modelid']);  
( G: }4 @% [0 q1 g0 `           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  , X# Q" S' ?! X2 r% A* \% r
           if(!empty($membermodelinfo)) {  ) P' I; F( ~1 d4 D; X$ i5 m- x
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
  L% I$ M0 L6 D$ y6 `5 O- L           } else {  , S" R/ ], Q9 r3 V! _- k
              $modelinfo['userid'] = $this->memberinfo['userid'];  
$ R2 |9 o9 @+ Z) S( i2 J: c6 }0 Z1 O              $this->db->insert($modelinfo);  2 C8 v. P% X) X. l* W/ |$ y
           }
/ L0 e7 V; n! X( o4 G0 M; @- ?1 E代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  T4 N6 a3 J; e9 s4 A; F( O% c4 G在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 w2 D0 I) D% y* I1 y7 r
* D5 Z- A  f% ^

; K2 ?) l& r7 F
" T1 Q- v  Q# y  d$ V% Q5 tfunction get($data) {    O( E3 n3 `" n, g. Q
       $this->data = $data = trim_script($data);  
0 U1 o, u& D" q) I& q/ c       $model_cache = getcache('member_model', 'commons');  1 ?3 N1 y# E8 N; x1 H
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
; s, f+ Q) V2 @: M( B       $info = array();  
8 {$ C" w2 m' }1 [; Z$ G       $debar_filed = array('catid','title','style','thumb','status','islink','description');  # ~. ~8 B  R) E+ W% }
       if(is_array($data)) {  
0 b5 q& }. U0 ?* J" x* }+ m           foreach($data as $field=>$value) {  % X$ B) r3 O9 G* M0 q
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
  o" r2 w" z+ O* r              $name = $this->fields[$field]['name'];  
: K, q# U0 v& l. W: M1 U              $minlength = $this->fields[$field]['minlength'];  
1 o: _( g: d) m1 @# V              $maxlength = $this->fields[$field]['maxlength'];  
2 }! B% c* O2 C4 e1 `& R! L/ B              $pattern = $this->fields[$field]['pattern'];  
3 v, F& V; \# R# s( I              $errortips = $this->fields[$field]['errortips'];  
# B/ A" V1 ]- \+ ^5 B8 E- l              if(empty($errortips)) $errortips = "$name 不符合要求!";  
+ k" A/ V4 U: ~$ n7 v5 @              $length = empty($value) ? 0 : strlen($value);  $ W+ X0 Q0 ~' j
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
6 x( b  h0 |2 ~1 L0 H! x              if($maxlength && $length > $maxlength && !$isimport) {  
) u, [7 o7 H; U, E                  showmessage("$name 不得超过 $maxlength 个字符!");    x1 ^: W+ d1 X% q/ U6 x# c; X
              } else {  
: V. ^( b: b: K$ T                  str_cut($value, $maxlength);  ! ~) l& q4 ~8 ^5 Z- u" j
              }  . |- `% N8 v% T' O
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
3 _% J, M- G  G7 z                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  + b  c( |' a: b3 k9 _
              $func = $this->fields[$field]['formtype'];  " M; ^9 L8 r( ~* ?% ~
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ; X# R/ G( k. Q' ]* q9 J9 a
              $info[$field] = $value;  
2 I5 n: J1 L8 H. \. E7 d           }  ' O* j% V' Q9 Q& X6 Y9 F$ i- l
       }  & e8 s) _$ g9 _6 D: |; g
       return $info;  - Z. _8 L4 V9 l* k5 f! y! T
    } # w# B7 v% E) ~) r2 F8 J' K
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,0 C0 n8 t+ z2 |& j

+ t; b/ Q0 t" H" d+ ^3 i) H3 N再到phpcms\modules\member\index.php 文件account_manage_info函数
+ `/ B2 j: K/ n" N3 O1 N$ p, }% l过了get()函数之后。
8 a% E9 T# C, B. H9 w8 `/ E) D8 u) q
9 T5 X2 M& u+ Q  c9 L& U
$modelinfo = $member_input->get($_POST['info']);  3 s9 f3 R4 v8 A7 P8 Q9 A5 C/ |
           $this->db->set_model($this->memberinfo['modelid']);  
$ m* p' W- _- W           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * r' w" m; `6 O: T2 ~
           if(!empty($membermodelinfo)) {  
! O/ x) N% e+ g) [              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
. a- k5 K3 v* q; a           } else {
& \5 J3 ~( z. Z直接带入数据库,update函数我们跟进看看
2 b. c4 G7 J+ j' s/ r, `% N( F/ f! }# m5 H

6 d; E" ]/ [# U9 S( Q1 W5 B. Fpublic function update($data, $table, $where = '') {  4 w  [5 a( Q! y1 {( [' b& p, H
       if($table == '' or $where == '') {  ( u" G5 Q# W+ }$ l6 I# E7 w
           return false;  
) p& ~6 u9 U' q5 v+ x& d       }  + F) ]0 B* Q0 G1 j3 ?
       $where = ' WHERE '.$where;  % V6 }8 D' z/ [+ t( W- o+ q
       $field = '';  , q6 P, p9 V  p5 ]7 U* {- ~
       if(is_string($data) && $data != '') {  
. ]1 g7 A+ q4 ?9 ]           $field = $data;    W0 A3 e  m- e* H1 o
       } elseif (is_array($data) && count($data) > 0) {  - E" D. R# s$ ]
           $fields = array();  - }1 I8 e. d9 U% N/ G! j
           foreach($data as $k=>$v) {  7 z) H2 A  x4 l* a+ @; Z
              switch (substr($v, 0, 2)) {  
' L* v8 R6 O: K+ R' K! C" T7 t9 v                  case '+=':  
, Q4 m" N( u7 j, g' ]( E. e$ F) C                     $v = substr($v,2);  
1 y( F8 e, a4 x: ^+ B. `4 s" M                     if (is_numeric($v)) {  
, {2 k9 p" z- o7 }1 L# G# T  B" R                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
2 F( i( r5 t( w                     } else {  
6 f6 p: ]7 q; Q  q$ J2 Z/ l                         continue;  
9 L- X/ D& ?0 V6 ?' A. q" z$ l0 S                     }  ' H& e2 A4 D8 ^7 H
                     break;  
, m5 d. f/ b; R+ P7 H" Y                  case '-=':  
& R% w$ i" ~. d8 O) V                     $v = substr($v,2);  
/ _9 ?6 n! z6 R, u                     if (is_numeric($v)) {  ; X$ B# S3 x$ M
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
# X3 `, O  p; ^% p2 Y" N                     } else {  ( Y) {% n5 X- |% u
                         continue;  
9 L4 d" L% \+ ^$ n                     }  ! j% H+ B$ q8 A0 a! J& c
                     break;  
1 R% |5 W2 w4 r  g# Y: Q! O- n                  default:  
6 {5 i0 Y1 h. T# C* J# Y  p( z                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
6 u0 M& ]5 r% P/ i4 `+ u& Q; L( \              }  
4 G5 P4 e: [/ C1 u           }  
6 S, W1 R' j- t3 [* V. d           $field = implode(',', $fields);  
# q6 h' H3 {3 r) G* L- S5 U       } else {  
# f" m( Q  @" s5 q9 f0 p           return false;  
! U  y3 D$ n% v$ o, M' c3 w       }  9 P, ^& B( I% x& _* c# O! h1 c
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  2 [. G  A5 a3 w. F$ r- O
       print_r($sql);  9 n5 G9 `# c4 F3 s- w* m
       return $this->execute($sql);  ! o( v7 X+ r  S2 U+ c/ k
    } 6 l! |4 Q: J0 h' U
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
; f0 g  n+ n% P- B) X# t! C2 L+ C
攻击测试:
( K4 V( ?! [' {! Y4 I8 K测试地址http://localhost) e( K$ B; N$ T, Y7 x
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
* E2 ~5 M' X; L% w/ g6 `) r5 ~9 g( j% r$ g9 ~
[attach]179[/attach]
0 {$ E0 ?% S# f* y* e" {; g
2 e. a% [) }# C7 D: l0 t* Q
4 T1 P4 p: w7 ]5 I) g[attach]180[/attach]8 L1 a4 r# _- U5 W! k$ }7 a





欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2