中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告+ g% L- d' F5 {3 u1 `' {: J
漏洞作者:skysheep
1 z* S* K8 Z* ^! s) h分析作者:Seay0 ]& x" F. S( X
博客:http://www.cnseay.com/; ]1 z/ w% k3 q
漏洞分析:
3 h, P3 X; d& E3 F# y/ q  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。3 ?% g: D2 m7 g3 r4 [9 E

7 h! }& X4 |) x8 V4 }( m! N0 H: `! H0 @
# I% L1 _/ L$ b0 l" J
public function account_manage_info() {  ) {! ]$ k  L8 N8 N9 I
       if(isset($_POST['dosubmit'])) {  
; X6 N  C8 m$ F0 t2 i8 F           //更新用户昵称  0 O) O- R7 P9 T/ R
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
4 G9 V3 _- j0 E. Y& T/ o           if($nickname) {  ; X+ }0 u, ^- l8 Q2 r4 h  N3 }. W
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  4 Q4 [2 t, f9 o3 ^+ P! I
              if(!isset($cookietime)) {  , L; p& k$ n. N- E) }+ j# |
                  $get_cookietime = param::get_cookie('cookietime');  . K8 i/ s( j4 c  x" C
              }  * Z" M) i* @1 j
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
% I  ~( K! p3 [$ C# [3 N: o8 C              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
7 Y1 v! U: u/ P/ M              param::set_cookie('_nickname', $nickname, $cookietime);  2 V( N4 T' k# Q' p6 J
           }  ) N% Y( [. I5 f9 a
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
+ `6 V, z+ N- v& @           require_once CACHE_MODEL_PATH.'member_update.class.php';  - R5 b8 Q& H6 R. N6 v. N
           $member_input = new member_input($this->memberinfo['modelid']);  ) b" f3 ]2 p7 K
           $modelinfo = $member_input->get($_POST['info']);  9 g1 w% y+ D3 V
           $this->db->set_model($this->memberinfo['modelid']);  
" ?% V$ R+ V! P6 [1 a: W           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  - t. |, T9 W: N5 t: F2 y
           if(!empty($membermodelinfo)) {  
2 c7 m& w6 c0 U1 K$ U  }/ }# a" h! Y7 ^              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  * _4 x" K! O% g1 m+ p  w2 B# F1 Z6 q
           } else {  6 n" h7 r9 j; [: w0 M
              $modelinfo['userid'] = $this->memberinfo['userid'];  4 F, N" h0 E' {
              $this->db->insert($modelinfo);  
* o6 p9 b% U8 G& w           } , D8 |' q4 ^5 v3 I- J4 O
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 n1 l3 Q2 I7 D( Q* e8 z* n在\caches\caches_model\caches_data\ member_input.class.php 文件中:0 t& O3 A7 T3 M4 U
3 |( L& o( [% Y* V4 H
' y& i3 ^' F* ~8 |8 u0 t8 t
! y; E4 f0 U" ]5 q. ]$ d0 }
function get($data) {  
$ d2 e; ^2 l5 y/ }       $this->data = $data = trim_script($data);  9 b6 P, N: N" g& e2 @" F
       $model_cache = getcache('member_model', 'commons');  
" F- O0 ]2 K* ~6 K& Q* ~       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  % S+ w5 V; E! q
       $info = array();  
& S- B9 N; e8 s! ]: [       $debar_filed = array('catid','title','style','thumb','status','islink','description');  " a& [4 w2 t+ F6 k6 q( s
       if(is_array($data)) {  
0 [/ m. t1 W# _' ?           foreach($data as $field=>$value) {  
* l# Z: v0 A& I' t- J7 K8 k2 n% o              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  8 y$ \) g: x! f
              $name = $this->fields[$field]['name'];  # V* [- ~" e. V2 s8 t6 o/ T# K
              $minlength = $this->fields[$field]['minlength'];  
3 X5 M& H: J$ n              $maxlength = $this->fields[$field]['maxlength'];  
+ [+ K4 {4 z, Z/ t              $pattern = $this->fields[$field]['pattern'];  " `/ d5 W6 w. O0 `3 f4 B" x2 ^
              $errortips = $this->fields[$field]['errortips'];  
8 g- f4 B- O/ A2 P$ K/ c* ?              if(empty($errortips)) $errortips = "$name 不符合要求!";  * h5 f6 ]* h; m
              $length = empty($value) ? 0 : strlen($value);  3 K# c: g3 B. o5 Q
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
, M# e1 P) @: x5 \              if($maxlength && $length > $maxlength && !$isimport) {  
, n( u9 u$ F1 P0 F" }. J& l                  showmessage("$name 不得超过 $maxlength 个字符!");  
" A# f& V- P' {: Q0 p2 h              } else {  3 H; C/ a6 R5 b% {0 o  ?
                  str_cut($value, $maxlength);  
( B0 e" Z& h  R% S              }  
2 @2 z. H0 B' _* Z/ W- G1 e              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  # V1 _$ I5 F1 C' Y8 u" j9 U/ K" d
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
2 P5 y% y- S8 i) w1 ?              $func = $this->fields[$field]['formtype'];  
! |. {, R6 z, R6 l+ K/ d              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ) m; u9 P3 \! X/ z$ C, [6 l
              $info[$field] = $value;  
* t- v; j& c; s8 Y% g           }  
/ j5 d9 b) `% g/ B! o       }    c: C  \( O7 J1 I: M0 C
       return $info;  0 ]4 u7 ~+ X! O$ `  u' r, z' l/ H
    } # j) Q8 X0 }2 p/ Q
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 E: |6 J* `$ `- A) o0 h# f) J) X" y
; [& U! a0 o% q$ l/ e. ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
" g9 A9 F- C- `% R* W过了get()函数之后。6 c9 [+ [5 S" e
( o/ Y/ {  N1 L" Z' G
% h. e. Z, v5 x' {0 M
$modelinfo = $member_input->get($_POST['info']);  7 n5 f6 Z5 P1 E7 p' S" o$ w
           $this->db->set_model($this->memberinfo['modelid']);  4 M! r) t1 H1 b# {8 N2 i, l. _& B
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
! Y' J* \) j6 ~1 L8 a           if(!empty($membermodelinfo)) {  2 `3 f- q& q% u- r
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
) U) |# ]+ {  z           } else {
( r2 h7 w( Z: P: \5 k0 r- A直接带入数据库,update函数我们跟进看看% r% E7 B8 P) Q8 e; [8 r: g
/ f( L6 s, R# U5 {8 T; E

: l9 A- E8 H( i3 I" [% zpublic function update($data, $table, $where = '') {  5 [  J3 M- c. s9 [( L8 ]& K1 ^: q
       if($table == '' or $where == '') {  , E# v5 ?& e/ t$ h8 k
           return false;  
. G+ q- Z8 U$ r7 q" }8 C       }  7 b  y( P, r7 }; Q" [* l
       $where = ' WHERE '.$where;  
# Q& }( b$ v$ T3 e$ j# _       $field = '';  # W! t( j3 O" M& q
       if(is_string($data) && $data != '') {  
: ~! h' |6 C; w3 I$ @5 ?' r& s+ A           $field = $data;  
1 y6 [! w, M; y9 B       } elseif (is_array($data) && count($data) > 0) {  4 N% {0 c. H0 j. k* q
           $fields = array();  
8 M/ }1 o1 E& j& @% j9 s3 t           foreach($data as $k=>$v) {  
& s) I& W# b7 @8 O6 |              switch (substr($v, 0, 2)) {  
4 ~+ {) |* M4 s% j                  case '+=':  / ]* [' N- o' Y7 |7 @8 M
                     $v = substr($v,2);  - ?5 |: q) \5 }% q1 [
                     if (is_numeric($v)) {  " Q: L$ v  Y, ]. I
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  6 f- o- C2 z% e9 H
                     } else {  
. i# D8 E7 ^: U; K, I                         continue;  & p2 }6 t5 C3 D( y% E3 [; G
                     }  " Q  X& {5 I& S' G
                     break;  3 H8 w! H3 v  |; }5 G% e
                  case '-=':  
7 [+ B2 X6 i3 E: ]: [8 K6 H: O                     $v = substr($v,2);  5 u5 T; |+ I. d, o% x
                     if (is_numeric($v)) {  
0 h) n( G! z8 V                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
$ b5 e; q  {. F: n# ~                     } else {  ( w2 _# a! O' m" A& g
                         continue;  - D$ _. @) b9 m& h% T6 [9 @
                     }  ! D2 G1 O' W2 o5 t* q, b- g
                     break;  
* r7 K' r* `5 [) c8 Y$ q% t, E3 C                  default:  2 L/ {9 O6 V# }7 u- r! s
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
' {! i8 t$ ^3 l. H( A, x5 Q              }  
& ^1 p7 B+ \1 c           }  4 c( ^3 _  A; v6 v9 C& P! J
           $field = implode(',', $fields);  
) \6 T" {# @: L2 Q: Q       } else {  - A0 c: X, Z4 d( a6 T" m. h
           return false;  
. b, A) ]  E, [0 d% h       }  
0 R& v' G, s- @% x6 ]0 [, F       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ' g5 P6 e& W8 D: ^8 e7 j
       print_r($sql);  
- T0 Q/ Y( V; i. \0 D       return $this->execute($sql);  " `! }, k7 f, G2 \
    }
. S* j7 D* C5 s0 j0 s+ @+ v, U从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 T6 G8 w* m$ ]! X* ^) l" T2 E: V7 y# X1 P) {3 f+ ^6 R; ]) V
攻击测试:
4 W: ?- Q7 i( \测试地址http://localhost6 s4 e" z1 [$ X; x' j( T% ~
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句/ f: v* H; V0 P* c2 C

; w# C) \: H$ T# k& ?% z[attach]179[/attach]
- n. E; |1 J9 X( ~  @6 S! y( |! y
) Q! a% y3 q. M0 b& \# u
[attach]180[/attach]
7 h% l9 m: C% z




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