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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 T6 \3 }( p3 o0 V, @, y漏洞作者:skysheep
+ ~. s1 A4 D  _. l分析作者:Seay/ [  k$ k" N5 @! f
博客:http://www.cnseay.com/
6 O& y8 y! g* F# J3 S漏洞分析:1 y: [" @1 ]3 [7 N
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
: v' N9 t* N3 Y6 h& g, `" B/ Q
8 g8 x  j8 Y2 B7 i& {3 {
. y9 R, J( d0 o+ p( w 2 Y+ T! Z4 n; d% d/ R. I
public function account_manage_info() {  
. I6 i8 `) f* O8 @: d       if(isset($_POST['dosubmit'])) {  0 y8 a/ \& s3 ]8 V
           //更新用户昵称  . P1 }- ?+ h* z! i
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  + y* E% z, l4 j$ B+ F
           if($nickname) {    X+ S, N" c$ W7 I
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  7 l5 P6 _% X3 ?6 _( p0 k
              if(!isset($cookietime)) {  
+ E4 U7 E# ]# ]  S& t6 w8 ]; I                  $get_cookietime = param::get_cookie('cookietime');  
' k- f1 `" a' ~: E9 k              }  
- v/ A5 p. S. Y  \              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ! F" g! m6 J' k* e/ W  U1 L
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  & q' {8 {: R2 p
              param::set_cookie('_nickname', $nickname, $cookietime);  
7 _# K! W$ b* X0 Y, G           }  
% u/ j( ^3 J, w! @           require_once CACHE_MODEL_PATH.'member_input.class.php';  
# [/ U$ @8 O: ^, q# r           require_once CACHE_MODEL_PATH.'member_update.class.php';  4 `( r- R- x- D! ~. H5 D5 L) U, V
           $member_input = new member_input($this->memberinfo['modelid']);  
; m- D. f! d: p. [1 ^/ o4 S  ]7 B           $modelinfo = $member_input->get($_POST['info']);  & E% C/ S0 ], a
           $this->db->set_model($this->memberinfo['modelid']);  : G' _8 l5 v* |0 D! C( \
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
+ m" N6 e6 W; F1 a8 U3 o2 y           if(!empty($membermodelinfo)) {  . N6 D& f/ g# s" y  s* n, a
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));    h4 x8 c6 W, B" o$ T- Y' |: P
           } else {  8 F. ]. z+ C* W+ b" m: n
              $modelinfo['userid'] = $this->memberinfo['userid'];  
: o" z$ G. z4 H  }' v              $this->db->insert($modelinfo);  
$ A' t" Q/ G7 N8 n3 K9 X# W% F           }
, Q/ J% I& H9 P; ^/ B4 M/ |代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
$ Z4 T, c# c" B" m2 M; [在\caches\caches_model\caches_data\ member_input.class.php 文件中:2 s3 ]# r$ I2 j$ |

$ M9 V) c( T) x' [' K5 m, D+ T5 u4 o
2 y8 a! e1 X: ]2 Q$ R
function get($data) {  - x; N$ P; Y9 N
       $this->data = $data = trim_script($data);  - m' z8 H! s5 ^, n* o8 ]; `
       $model_cache = getcache('member_model', 'commons');  
# k& r/ L( d$ p5 X, h# J! _; v3 {" o       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
6 d  E8 T# T/ u& o, u) g       $info = array();  
. @) G. j, G: L( H# L& B       $debar_filed = array('catid','title','style','thumb','status','islink','description');  1 _8 \2 }( Z5 L+ W: T
       if(is_array($data)) {  . Z+ {$ r$ [& g6 D/ H8 L3 S
           foreach($data as $field=>$value) {  
5 p( F' Q% u6 I5 O0 k' o              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
1 q. U; G4 Q+ B8 ]5 z- p+ K              $name = $this->fields[$field]['name'];  
0 V; i1 e! ~  m' j# z5 R* E              $minlength = $this->fields[$field]['minlength'];  " Q; G2 e: I4 d# O# V, L
              $maxlength = $this->fields[$field]['maxlength'];  3 K3 B% M: T4 p! u8 M7 r0 ]( F  i
              $pattern = $this->fields[$field]['pattern'];  
  i) G1 }* n% C0 O) D0 S2 w$ y( c              $errortips = $this->fields[$field]['errortips'];  ( L8 t! J4 t6 V$ f5 {" t' Q
              if(empty($errortips)) $errortips = "$name 不符合要求!";  + X  [. Y- A  P
              $length = empty($value) ? 0 : strlen($value);  
# o$ H+ o5 l- R              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
  z1 ?2 B8 ]6 X4 f( R6 `1 T2 W              if($maxlength && $length > $maxlength && !$isimport) {  
. n9 B# ?+ l0 k7 }3 _                  showmessage("$name 不得超过 $maxlength 个字符!");  ) S; G5 g1 B! A; f3 \: A
              } else {  5 X: L( i2 o: B, u! ?! w
                  str_cut($value, $maxlength);  ( r9 ~# _$ S/ ]5 u
              }  - d- g" D2 p  e5 t
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  9 H+ \# z: j1 w" o& t
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
7 O0 U* D7 O. l              $func = $this->fields[$field]['formtype'];  / V4 g& A; o9 Z, t. A2 t7 B- f
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
  E7 n3 a% D& e, {              $info[$field] = $value;  
+ b& i2 N9 ?! u) S! N           }  
# \, b# X7 V! }" i4 M$ |. B( P& n       }  
) @6 V' c7 [8 |       return $info;  $ e0 f) P' p# _
    }
: y5 |& N- D% v7 }trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,  l% |4 ?" e) Y' a2 M- }" S
7 ~+ `0 B7 K* S( L& \
再到phpcms\modules\member\index.php 文件account_manage_info函数
: W- H, P5 E) F) i) L过了get()函数之后。
8 n: e- {; _5 ]) V) t4 u7 ]/ Y: @0 |+ X% p" M6 k3 _& |  e
1 l: u# I6 z$ E5 |2 E1 V$ z
$modelinfo = $member_input->get($_POST['info']);  0 g" W6 a! F+ g' m2 H
           $this->db->set_model($this->memberinfo['modelid']);  & {5 I, V- {8 J+ z5 b
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  5 [& G; `; e+ k, o& I
           if(!empty($membermodelinfo)) {  
  S  K$ M: W8 U              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) h9 F. ~  P2 \% z$ E3 W# ~6 J
           } else { 7 P' {' x. b* |! J$ k
直接带入数据库,update函数我们跟进看看
! t) c* V2 a4 {* E' Y1 a
. z5 i6 z2 F3 y' }" g: w
" q) B  l  ~0 X3 Vpublic function update($data, $table, $where = '') {  
, f& C/ `. C, X- S: h  m       if($table == '' or $where == '') {  
2 m1 r, Y0 ~; G7 r; r           return false;  : E. N- W: K; A6 T2 {
       }  % U2 L4 p& k5 G' ~
       $where = ' WHERE '.$where;  ( _2 J+ f% {7 b! U0 q( x" _
       $field = '';  
( a4 C1 `( s: m2 b( F       if(is_string($data) && $data != '') {  . r7 l- b! s# a' _  e3 N
           $field = $data;  5 O4 v. Q" z' t1 M5 S$ V1 x5 d
       } elseif (is_array($data) && count($data) > 0) {  * b4 A+ F4 J8 n  B9 C% b" `
           $fields = array();  : P, s- z$ q8 W! v1 A( V
           foreach($data as $k=>$v) {    {5 ]6 ]1 v* P! O3 X3 [
              switch (substr($v, 0, 2)) {  
6 Y( B. x# I9 ]; \2 t7 J                  case '+=':  
6 s' X+ [/ ?/ [- r. S                     $v = substr($v,2);  9 ?% }1 X6 S( O7 z/ g( {
                     if (is_numeric($v)) {  
$ c5 ]! p. D; k+ ^# f$ Z1 L                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  % w1 U& ~/ O% d" b
                     } else {  
; E- s! l# {/ |: v5 p0 \5 N5 ^8 b                         continue;  
& K% M" n+ W) p( q/ G% t                     }  ! ^; `+ b" d; J8 n
                     break;  ' y5 H. a+ I) J5 \3 }' O# X
                  case '-=':  
3 G! Z) c; f4 I; A8 U                     $v = substr($v,2);  * ?! U; ?; A) R8 T, D/ J/ L) g
                     if (is_numeric($v)) {  
% [8 K; ]6 Q0 _7 [( x* i% u! o                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
- s1 i9 z* i7 p, o% H! K/ U                     } else {  * h) m0 T$ X  g$ f. ~
                         continue;  " Z( N$ H+ A6 J8 f  N1 H
                     }  9 ^! C; K7 F: _5 X- m
                     break;  
# l) A& r% R* L# F# X9 T                  default:  - R8 O$ G0 I% q3 C
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  $ t* n' C8 O+ ^) O+ Q9 q8 N% ?0 x
              }  
3 u( ?2 n! ]# j) P& `; q           }  
) \2 a$ S0 R  s: P           $field = implode(',', $fields);  . d4 J+ m% f% ?0 w0 G
       } else {  
8 Z4 n; d$ e3 `5 L           return false;    G, O, `) A: g- [/ z
       }  7 }- J& K2 M  j+ y
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
. D% H+ S3 E, z" E       print_r($sql);  # p% j. D( L( ^! _! k/ f
       return $this->execute($sql);  
! U( s& i5 U/ S! s. ]    } $ a3 y( w% K( O9 F
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 f% ^6 {+ ?9 h. _4 f* W- z
; \5 R+ p! ^" }5 o6 |- t6 Y9 v攻击测试:, C  W: c. F! X& p9 {( w
测试地址http://localhost" s8 `( B' H# {; s& J: N( ~
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
. o; Y- ]6 ~' D, Z3 R
  k* b( Y- l' H2 s" Z  c, J 5 w- N: q, i& g

8 a/ n9 N$ q9 M+ W: e: c
/ m6 y/ ^. ]: w( B$ J# b% J! ~( A6 w. ]$ S8 n. ?  ^

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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