找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 2123|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告2 _! j. b  ?( L
漏洞作者:skysheep3 o3 }% u6 M* ~! H* k# z
分析作者:Seay2 m0 ~' r2 Z' ^3 J
博客:http://www.cnseay.com/1 y! e! K0 h0 p1 a9 c! t
漏洞分析:' a! B5 R7 z# G1 s% P6 \
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。7 H1 g& U3 o8 [
/ x# V1 J: G) B# ^, f

( Y8 E/ U1 v. U7 u& |4 A * P( z5 }5 b  ?5 I# l0 n- o
public function account_manage_info() {    W/ i6 K) f4 b1 p  C, f
       if(isset($_POST['dosubmit'])) {  
! w5 b  A8 X# o. p- C           //更新用户昵称  
, _8 w! h( B5 }  s           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
7 s, v  T2 R5 G) ~           if($nickname) {  0 ?  H2 y3 K/ E4 W
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  / e1 ?6 M8 r. z% e- S6 a, }5 F
              if(!isset($cookietime)) {  : j3 I* A3 H5 [$ D
                  $get_cookietime = param::get_cookie('cookietime');  
, ~1 ^8 f2 y* e! `5 H+ d              }  7 X# v# j6 u3 f# G, L
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  + h: m% a* K3 R: J  ]5 I9 V
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
7 n( f+ Y- z" ?8 E* l' E9 V              param::set_cookie('_nickname', $nickname, $cookietime);  2 b* ^) F; v- A3 C9 _
           }  ' S* h+ r5 d% i3 O/ L
           require_once CACHE_MODEL_PATH.'member_input.class.php';  6 O# \# Q, G9 K" t9 u
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
- h0 C' ~: d9 b5 C9 j4 U           $member_input = new member_input($this->memberinfo['modelid']);  
5 D: L# G6 @2 z% J# }9 w           $modelinfo = $member_input->get($_POST['info']);  
  m+ K  _, w# o: j* U  T           $this->db->set_model($this->memberinfo['modelid']);  0 U0 [" U" p9 N" L
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  ! `8 f5 `" F! k  Q- q. V+ G7 `
           if(!empty($membermodelinfo)) {  ( i, w7 g. R2 _2 M* d5 e! H
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
3 @5 r; S! x2 \7 r           } else {  5 A' _5 l1 E1 S% M
              $modelinfo['userid'] = $this->memberinfo['userid'];  : T2 R+ k  `) e) l/ H6 M1 C
              $this->db->insert($modelinfo);  , X' k& [4 \- I( Y- ?5 d8 p6 ?
           }
' v" ~9 ]3 t- K5 a; l& Y代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,4 S" v5 J6 X1 r) ^
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& B5 o( R9 v5 l. z
5 O( i3 w4 Z: O! h
! U' N8 C* |, z# I2 _9 b : D: P& O6 Q8 u, M! @- g$ x6 P
function get($data) {  
& m. H; a" E- v4 Y6 e$ _       $this->data = $data = trim_script($data);  . M6 C1 f  b- s9 s
       $model_cache = getcache('member_model', 'commons');  
- `3 o( U' J" X; O) x* s       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
7 X5 P8 g6 m3 L' X1 w( S       $info = array();  " M4 G/ M& N7 z7 v
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  - E0 n) V$ r) _5 i# U/ [! h, h9 E- b8 b
       if(is_array($data)) {  
( W2 C6 r! u. J           foreach($data as $field=>$value) {  - v, J9 d/ [& W7 e; O. d
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
: K( W0 Z$ P$ K3 z5 S& }              $name = $this->fields[$field]['name'];  ) D; C% C1 O! R0 i9 i% ?1 d
              $minlength = $this->fields[$field]['minlength'];  
- N" r% e' X6 A( y$ }              $maxlength = $this->fields[$field]['maxlength'];  2 a% d! b8 r# f, d9 P
              $pattern = $this->fields[$field]['pattern'];  ) k9 p% C* l% }3 a: x6 s" f3 r- J% M
              $errortips = $this->fields[$field]['errortips'];  6 `1 o: t2 Z' G6 p, u. W
              if(empty($errortips)) $errortips = "$name 不符合要求!";  ' r2 {, r" m# O8 L' }3 _
              $length = empty($value) ? 0 : strlen($value);  5 q7 ]+ }: }' t/ F) Y' A
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
- v- B& R0 p& d. n9 y              if($maxlength && $length > $maxlength && !$isimport) {  . |9 O  B  x- A# b9 a
                  showmessage("$name 不得超过 $maxlength 个字符!");  6 M. d5 j* j* j: g- ^
              } else {  
; v1 q% ^. J7 v1 r9 Z: t" d" D                  str_cut($value, $maxlength);  
# S. k9 \/ G+ y, z              }  
" m  A9 ~7 }9 u) g2 H              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  5 N& M0 y4 m* V
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
! t/ G( c' R9 E, s. O& \) u. H              $func = $this->fields[$field]['formtype'];  
4 E9 N7 ]- |" \- H; n$ `9 Y$ r% m              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
% P6 Z1 \! N+ p) ^& }              $info[$field] = $value;  
1 w7 ~  T7 t) J           }  / Y1 T2 J& z8 F) K  I4 Q
       }  
: l% `8 t9 s. \$ _0 L2 }  \$ r       return $info;  
/ @& F! ?1 N) M+ \$ [    } 4 N8 p- X+ _) q- N7 w" |) a# {
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
$ D/ Q* n: _7 B4 \+ l  T& U* L0 U1 c! g; d, s+ {( T4 S6 L: ^
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ O2 a: T  u9 w$ V/ Z过了get()函数之后。2 T% ]; b9 O9 N

! i5 H9 T  H4 W" V- v
. y) a+ R9 g" W( z1 r8 [. l! X$modelinfo = $member_input->get($_POST['info']);  # _$ r( X/ x& ?1 |+ w
           $this->db->set_model($this->memberinfo['modelid']);  2 \/ U, Q& B( K# W# }5 s
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));    U# u: r( J3 C# C
           if(!empty($membermodelinfo)) {  
& y1 r( `; `& s, f" o/ c              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ! h7 a" `6 s& I/ d
           } else { + C4 b  N9 U: l7 ^
直接带入数据库,update函数我们跟进看看- i8 d8 W0 ]# I' D7 n1 u1 e% S
- @. S/ b5 I, N0 `' V

0 y" b( ]/ B2 m; C/ D3 [& F7 ?7 spublic function update($data, $table, $where = '') {  5 x' s/ `& s' A- J& ?
       if($table == '' or $where == '') {  
5 e- [% u& T: ]' I: ]           return false;  ' v/ ?% i1 q# T0 q3 O! |6 C! F
       }  7 a+ z) X: ^9 w
       $where = ' WHERE '.$where;  
. G6 |2 R. g8 ~; j$ s3 K! h8 }7 h       $field = '';  ( N2 q/ C* [4 S: K' l4 {) I) Z1 ]
       if(is_string($data) && $data != '') {  
+ R; Z! f) Z* M  Z  H4 X' g           $field = $data;  
& b) {, ?2 Q) P) Q+ m4 f/ I       } elseif (is_array($data) && count($data) > 0) {  8 ~$ O; E8 {3 {* O9 h' e
           $fields = array();    J" s7 R# s" _* E% a
           foreach($data as $k=>$v) {  
' g& ^! G8 G& d  O  ]              switch (substr($v, 0, 2)) {  
1 D% r6 H, ~) t                  case '+=':  ! V- y0 g3 j( p* T* W; E
                     $v = substr($v,2);  7 H( V/ k% c4 ]* f3 H: g3 d
                     if (is_numeric($v)) {    U1 u$ d/ v0 f/ \. R3 K
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
; @7 X3 M9 c  X                     } else {  ; g" i1 W; j) M! d" X5 b: r
                         continue;  " n4 I/ ]) v9 F' _# z0 q2 Z
                     }  
! i% S: ]) \5 w2 }6 G: ?5 x                     break;  
; ^  D" p7 Y8 e; t1 ]# I5 T9 L                  case '-=':  
4 w# s8 u$ c7 w# p, r                     $v = substr($v,2);  ! [# e7 d! q  K" n1 y
                     if (is_numeric($v)) {  9 r' Z% C/ u7 J0 t* W9 A, f
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  9 x" D: R3 m0 z
                     } else {  
* g2 S$ _1 |0 F' T& t  ?                         continue;  2 h0 E+ ~  N! @
                     }  9 e6 o7 {9 U2 y9 w/ z! t' d+ ?, c
                     break;  $ Z# j( O. q* v
                  default:  : a2 {- g6 _3 M* u7 z/ Z9 C3 O. ?
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
' c3 a/ Z/ U& H- c* E4 g              }  
) H  f& s: f: L2 h; \# I           }  
1 _, g5 P' A1 i5 y7 C1 f           $field = implode(',', $fields);  9 m: k4 B& n; h$ q
       } else {  1 t" J6 T1 {# n
           return false;  " I4 @6 d5 F6 |7 J
       }  9 r# o7 H( O# S+ c3 d3 q
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  * y+ p4 U) o  c7 X& \8 }
       print_r($sql);  9 O/ ^* G* n4 j6 R& {5 W$ x$ E1 A  ?! C
       return $this->execute($sql);  
5 P) `# q5 i: S* Z4 ^) ]    }
5 T! m$ o9 T% \1 y% G$ ?从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。+ D0 u6 g7 o. T. T# g6 ~
% v3 h0 v& V$ X
攻击测试:7 `: ]; `& J1 m& s* ~9 k* u
测试地址http://localhost
/ p0 L4 P( n$ M, c. y- D  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
0 O8 m5 }; D+ j2 m- D- q: q3 N
; ]3 D0 J5 y9 o  s( t * m, W' |$ L: `9 m8 ?

( Y+ Z0 s1 y" m6 @* |0 z& X3 G
+ W  `/ w* w3 h7 @
" Y/ G) d9 e$ h

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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