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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告; L2 \, u. R6 ~0 l7 l
漏洞作者:skysheep
# \  @5 d0 W5 R8 i9 k分析作者:Seay# p+ o$ }& t1 N8 ?' C) {- o
博客:http://www.cnseay.com/; `8 b) u: Y. o9 s9 M8 p
漏洞分析:
! Z) Y" e; o3 L- z  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
2 }' Q3 [3 ?/ [) j/ Y! W, M* m& M, u2 w5 H" e7 [0 @5 p' k
- L! s* O) K7 Q; _9 H

) @8 _& E8 E5 ~% a/ npublic function account_manage_info() {  
! x  w; c3 m3 ~7 D% T- x& Y! Z       if(isset($_POST['dosubmit'])) {  : q4 A( E4 y- a' B
           //更新用户昵称  0 X% L# a3 M6 J. Y6 J5 `% ~2 M5 q
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
! z) k' f& ?" i+ y0 D, g           if($nickname) {  
: \: w# ]9 N# q              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
" I& G$ D( w- a2 |              if(!isset($cookietime)) {  
: b! u0 r5 f$ r( Q9 V& O                  $get_cookietime = param::get_cookie('cookietime');  ) }: e# N3 V9 O
              }  % D. o% ^6 c' t2 b# y. f
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  & n, v7 S$ B! p3 M& m; L
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  / r  |: o0 F) y7 X) e
              param::set_cookie('_nickname', $nickname, $cookietime);  1 z) u. [) B2 E: K4 z# U$ F' l
           }  
: f. q: l) J% \           require_once CACHE_MODEL_PATH.'member_input.class.php';  
# }2 y& S$ \: p, m           require_once CACHE_MODEL_PATH.'member_update.class.php';  
+ u1 o8 ~# I0 r! p           $member_input = new member_input($this->memberinfo['modelid']);  
0 l% [* F! L. [9 \5 ^           $modelinfo = $member_input->get($_POST['info']);  
6 l& x; w5 Q+ l2 ]: B* |2 g           $this->db->set_model($this->memberinfo['modelid']);  $ a' W. i2 L' h2 ?8 S+ y
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
; ?* k  |& E5 H+ C$ _' |           if(!empty($membermodelinfo)) {  2 p, [# H/ J9 ]" V' t- |, y
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  $ a3 K! M. f  P9 Y) X% i
           } else {  
& s/ L  f( @, a              $modelinfo['userid'] = $this->memberinfo['userid'];  
4 r5 W( `  x* U7 a4 [& z* K              $this->db->insert($modelinfo);  ' l# W( u/ g; S/ g: p/ |9 {3 }
           }
4 f% `5 F! ]- U6 E8 \: Q代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  y- c+ X: W3 ~9 t- ~在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& F8 w  p9 ~  k/ l# G7 h) ?' ^- \+ K, Y4 f9 V3 y% W, M
3 A0 c9 j8 v7 \( E- s) F+ {, E
1 _5 C- U- N. x
function get($data) {  
! E6 D, x: T/ W/ C2 n       $this->data = $data = trim_script($data);  * o+ r# ?2 O2 l
       $model_cache = getcache('member_model', 'commons');  
/ R4 T& @) a1 ]/ u! A6 h7 w4 p/ \       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  8 [5 V5 J( C/ c  y1 f6 T7 Z
       $info = array();  " y9 ?2 L$ U. q0 C6 ^
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
6 ]+ \5 h! C& |3 u       if(is_array($data)) {  * x$ H* k4 `. m. d$ q. e3 ~8 P+ A
           foreach($data as $field=>$value) {  
! m! c& {5 X$ [. z3 y              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  + R' R. q! f3 J; G4 N/ t9 S6 e
              $name = $this->fields[$field]['name'];    j# F4 t$ w9 o: J& Q$ k% a. F
              $minlength = $this->fields[$field]['minlength'];  5 W8 t" w/ }6 Y3 P6 O. ~% T. Y: d
              $maxlength = $this->fields[$field]['maxlength'];  
1 }7 b$ ^! o7 u; o0 ^9 `              $pattern = $this->fields[$field]['pattern'];  
2 A+ [0 |: C1 e. W7 @, t4 f              $errortips = $this->fields[$field]['errortips'];  % N/ A" v4 J8 J8 w9 k
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
. ]$ A/ S- c) ^2 p9 I& t2 Y! ~              $length = empty($value) ? 0 : strlen($value);  2 V+ w5 ^# i0 a3 S# @8 F) S1 G
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  & ?7 @+ ]8 n* ^- j
              if($maxlength && $length > $maxlength && !$isimport) {  # }- Q6 x/ M/ A
                  showmessage("$name 不得超过 $maxlength 个字符!");  3 ~0 M+ U; h' r1 z8 ]/ z
              } else {  
; |( {" v* V+ B: O5 E- q                  str_cut($value, $maxlength);  ! H  }- k. m. f
              }  4 H; R/ v4 G2 F+ ?$ Q& Y  E
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
5 h. i$ t8 s2 K+ b3 Q" ^* i  V6 D                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
& o: t) a* I/ O" ~9 P0 d4 c  o              $func = $this->fields[$field]['formtype'];  ; h$ a( B. b4 M7 A* V7 }; ^
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
( {! [8 O. v6 P" c' X# S              $info[$field] = $value;  
( C7 b4 ]+ L/ \6 ]7 F' w- j           }  
+ N/ d* \6 v( a9 D1 f8 r7 v       }  9 E8 X( X8 z5 B& j4 O; Q
       return $info;  
+ J3 m' Z$ S4 V    } ) Q) R" {! j/ X& I/ @7 r( }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,/ G' g, ~. y$ p7 Y

9 m/ I5 M$ |) [+ n; q再到phpcms\modules\member\index.php 文件account_manage_info函数
# O# g& n; e5 r; I9 t过了get()函数之后。+ p3 S: ]1 q* L1 u8 s. O2 U

8 k0 m+ g0 W" X1 o, ]/ `) z
' P0 j0 v$ f0 n; x" g$modelinfo = $member_input->get($_POST['info']);  
2 F! W( l( |; g# j* q% P, o           $this->db->set_model($this->memberinfo['modelid']);  ! W* |8 {& M! r8 g+ |( ]0 s
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
3 U. P2 p7 C/ R/ C' \+ y           if(!empty($membermodelinfo)) {  ) P& q- [& s0 y: I, z1 |
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
* J) i/ X) }, b% ?. w" S2 @           } else { 8 T1 q. {' B+ r
直接带入数据库,update函数我们跟进看看
8 V: s% o( z( r2 C0 x+ {$ J- D' ]3 ^, j+ s: H

# p4 D, ~! _% I) c! m7 h  L* Wpublic function update($data, $table, $where = '') {  - s$ \. ?% Q+ y  y$ ^- L
       if($table == '' or $where == '') {  
; x. Y% ~* ?2 U           return false;  
& s8 P2 L6 p& v) F       }  , D# {( n  R) C9 {
       $where = ' WHERE '.$where;  
, o& l5 e. k. Q# ^$ Z5 l9 Z/ O: G       $field = '';  
9 j8 T2 y( H% c* l, ^       if(is_string($data) && $data != '') {  # j* V. ]+ U5 K( i3 T
           $field = $data;  7 U3 `) U$ v9 n6 q1 ^- D( [
       } elseif (is_array($data) && count($data) > 0) {  
/ M% H% o5 @5 C$ \* Q           $fields = array();  6 V( N: x' p8 I7 s
           foreach($data as $k=>$v) {  
) H+ C# e2 P/ t+ D              switch (substr($v, 0, 2)) {  
# U+ ^: C/ j6 |+ B# B& e* ?                  case '+=':  3 z3 p1 c  B" f6 t8 B6 h
                     $v = substr($v,2);  & k* S: M# b5 |
                     if (is_numeric($v)) {  
$ C# o% n/ A, k                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
& C0 S9 H3 o* N) u& F! I9 G; b                     } else {  9 }* e: I) u& N2 J* ~
                         continue;  7 p( U" l- G/ N
                     }  
8 }% Y! U' i- v2 J( R. }3 _                     break;  
$ D2 I- l- f" O+ s4 U% J                  case '-=':  
! |9 |- B, ~2 M& t0 y                     $v = substr($v,2);  & j/ @( l0 e0 G; t! k4 B
                     if (is_numeric($v)) {  : P0 V; V( O. g- c( j
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
( y& j, F1 O) h5 v                     } else {  ) J$ S# F5 C, i9 j- Z
                         continue;  
9 Z8 C* A% f3 J9 `% t$ w                     }  2 F' N9 @5 i3 X3 J' ~. t# i
                     break;  ( k# W' z7 A4 O# U* `4 F
                  default:  
  [3 a; l7 k8 T! i; R7 O: t8 V                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
' C6 Q  D$ t- ^- X+ a3 X              }  5 R4 P7 g- a: }" V+ ?! i) Y, D6 {0 c
           }  . N% t& T8 v1 V, c: G4 E& {
           $field = implode(',', $fields);  
2 W1 }9 _" M/ H; e% T; s. `1 R6 B       } else {  4 z: p* M* X" F# M
           return false;  
1 k4 \( G+ p5 s) Q       }  
  @9 z7 z$ i. ~/ n8 f% X; v1 N/ h       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ) d( G) x! N2 Y2 r& h& O
       print_r($sql);  # ?5 }" L/ H* r" i' z
       return $this->execute($sql);  
7 X9 P0 H, i$ v9 m6 u! u- Y9 t    } : j5 Q* i! Z8 {/ ^: ^. D
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ A! @# w/ ~& a$ W6 r4 ?+ N4 p5 b
9 I: u& v8 o. B+ m, a攻击测试:8 C0 p  c4 i* ?! b, w
测试地址http://localhost
  x0 u$ a0 Q9 A' g  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
  H/ B1 u4 v( D0 v/ g% M$ j& `+ i0 \+ G' n6 c. w

. e0 z& Y, c% g- S1 o1 `1 M& d) C) i) z/ n# Y3 {8 {* j0 W
7 p1 O. ~& H# v2 i! q0 Q

! y+ L0 O( a  n

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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