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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
) ?9 j# Z8 l) B5 Y$ `漏洞作者:skysheep- J3 p; O% S) {
分析作者:Seay
: R% N/ O& r3 p6 C3 C* H博客:http://www.cnseay.com/
9 f. e: ^0 P  Y/ t3 i% ?漏洞分析:
- }; V+ W1 g7 ?* w; [0 c# `8 I  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
) t0 w; `, m% a8 p& r( u6 u) ]& N: h  K, k, p6 W# E

- K6 k7 O  K. c" Q. J0 [; X
) p6 a+ _, s* Gpublic function account_manage_info() {  
% `, c+ h; x5 J) ]4 Z' l5 }% \       if(isset($_POST['dosubmit'])) {  
+ T' n/ z# r) Z: J# b           //更新用户昵称  6 `) K' @: d% X
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  + P1 {9 K0 l! U* v) x
           if($nickname) {  
$ F& p; M# e" o              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
( x# S9 `1 h" w# Z4 R# R              if(!isset($cookietime)) {  & M2 Z3 s: K4 N* t" {% @+ i
                  $get_cookietime = param::get_cookie('cookietime');  , e2 d6 t9 q2 r# \' _/ _, _7 C# T
              }  
$ h3 r8 [/ b  M* S% s, ^              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
& W2 C( n, n, |& m              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  0 L" u- X, O) {5 U
              param::set_cookie('_nickname', $nickname, $cookietime);  
9 ^$ \+ W$ P* c           }  
, p1 ^/ {. N% r% P0 H; r) n+ b           require_once CACHE_MODEL_PATH.'member_input.class.php';  ! D! [5 J) w5 s% |
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
2 }# Q# {7 l% M           $member_input = new member_input($this->memberinfo['modelid']);  5 E2 A8 y" S6 k" x8 C4 v
           $modelinfo = $member_input->get($_POST['info']);  
$ `9 L9 y' p" i           $this->db->set_model($this->memberinfo['modelid']);  
+ r1 c2 J0 I- W( c1 I. b+ ~           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
' B+ z5 R' H, a- s           if(!empty($membermodelinfo)) {  
/ B8 N8 x) S% k1 o+ {2 Q5 l( J7 N% y              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ) w4 s7 n; q' D" D
           } else {  
, _7 S0 @; |0 o. d5 D  O' O              $modelinfo['userid'] = $this->memberinfo['userid'];  
' w& f4 t4 f. ]. l* ?7 |" x8 v1 G              $this->db->insert($modelinfo);  
% \% o- m4 M7 g           }
2 j# E4 v( N3 l8 P) m代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,. T) f9 M/ j+ l3 [
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
  X+ F* X- k+ O4 J% y6 w5 c3 o8 ]- b9 s8 t" R& Y3 ], N
' R* @4 K- c- G; {+ Q
- w2 x' h7 \$ e+ Q, Y
function get($data) {  # [% t& t( r5 u
       $this->data = $data = trim_script($data);  
0 h; O' S, O7 R4 D; U& O1 E       $model_cache = getcache('member_model', 'commons');  * {4 y! j& k! y/ `! G. L  G* ^
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  1 q1 ^  {4 k: R8 f- W# R; |
       $info = array();  
5 g+ e# _/ |; l       $debar_filed = array('catid','title','style','thumb','status','islink','description');  " r+ A# g  V- Q. q- e
       if(is_array($data)) {  
  j( ~% k; ?6 Y. q, K           foreach($data as $field=>$value) {  % Y3 Y' t5 e# ]" i
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  ! O4 d3 {' }' v8 L
              $name = $this->fields[$field]['name'];  5 I  ]; l* r$ a9 Q+ X% `
              $minlength = $this->fields[$field]['minlength'];  ( S) |: V& [- J- B; c  E
              $maxlength = $this->fields[$field]['maxlength'];  
" ~# Z. S- R! X& G+ s              $pattern = $this->fields[$field]['pattern'];  
: \- I+ C7 r! V              $errortips = $this->fields[$field]['errortips'];  - P4 X7 b: y' D8 Z
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
9 P  H/ I8 G, @              $length = empty($value) ? 0 : strlen($value);  : E3 t3 G% v% u4 [2 ?/ V# D
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
8 K2 [4 ]$ b; F) @              if($maxlength && $length > $maxlength && !$isimport) {  
( v+ T: K' X: E. m: ~+ M7 v! U! [. @                  showmessage("$name 不得超过 $maxlength 个字符!");  5 o: Z+ ]! w9 A
              } else {  
8 G# h+ }+ Q% n1 A                  str_cut($value, $maxlength);  1 K8 o. {; D8 l/ \/ b
              }  
% ]; Q5 c5 ?8 |/ }$ L" f7 Q6 r              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
2 e  F; P2 E! J- B' Z" {                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  $ M. J  k; P1 I; Q7 m
              $func = $this->fields[$field]['formtype'];  
; K$ ~$ L, J6 Z              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
9 r( ?# y* x" a  A              $info[$field] = $value;  * H/ n2 x/ e% B' X
           }  
  x, s4 T) z1 Y# u       }  
+ H3 O" G9 l' \$ q' @. Z       return $info;  ( r1 I# H- C; r* P
    } ; u) |7 d1 `' N
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
: Y0 R/ ]! [, ?& Q
: s9 C4 ?; p3 t; M+ G. \2 N  V再到phpcms\modules\member\index.php 文件account_manage_info函数
6 d* H, \4 }5 r1 v1 q* C& p过了get()函数之后。
' D' {3 w3 x1 ]/ d% A6 m9 c' |
) r/ I$ x8 M8 j2 y! ]' I9 C : l" L! K9 L% N; F! L
$modelinfo = $member_input->get($_POST['info']);  * T, d- {: R% C6 `  ^2 G
           $this->db->set_model($this->memberinfo['modelid']);  
; ?7 U5 g. v2 d8 S           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
, o/ U2 ~9 x7 B           if(!empty($membermodelinfo)) {  
1 l/ }+ O$ q! f: p* E/ [7 \              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  3 G, Y3 r( @0 P0 p! E6 L
           } else { ! w/ h0 A; S  N8 @
直接带入数据库,update函数我们跟进看看
5 @: Z9 V3 b0 \, Y4 w, a& W6 H8 a9 b0 z
7 q: c( W9 r/ |' q
public function update($data, $table, $where = '') {  
, ~- [& Q) l2 D$ |, J5 a& O       if($table == '' or $where == '') {  ) `, G1 d/ B; w; y, b+ Z
           return false;  
" e5 y5 V1 l* |! v1 p) C" h4 X2 h       }  ) b; w/ m7 w: r' b( {
       $where = ' WHERE '.$where;  
) }: m: x3 \- O$ H! l0 v       $field = '';  
$ ~% Z" }1 y$ m* Z0 G1 U       if(is_string($data) && $data != '') {  6 j$ e% b$ j8 X
           $field = $data;  
1 w. _4 {4 M( [9 r& j$ q       } elseif (is_array($data) && count($data) > 0) {  
# b( e  l/ [8 S! C- N$ f1 k5 X4 @           $fields = array();  
' k/ x2 m- d6 T- E: b9 z) m           foreach($data as $k=>$v) {  
! x/ f9 Q, \0 @: u) j0 L0 V              switch (substr($v, 0, 2)) {  
4 Y" H4 j: U6 I( ?+ F1 A                  case '+=':  7 d$ v& D2 z7 Y! m- Z& M) W
                     $v = substr($v,2);  ( H; N9 J- _3 m' }$ Y! E
                     if (is_numeric($v)) {  . |8 j) P3 g5 h- n
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  6 K( T+ V* m9 N- ?+ w2 W" Q% d
                     } else {  
2 }7 t' S, I& B* z                         continue;  
) D; I8 @9 i8 A& E0 N" e8 U6 G                     }  ' g4 p5 E" a. K
                     break;  
6 K9 Z7 {0 x) u5 G/ Z                  case '-=':  
6 x" `4 @1 ~1 ?8 N! C: x9 m/ e0 i                     $v = substr($v,2);  ; Q: z+ J9 U+ A& P
                     if (is_numeric($v)) {  
* h6 ^) J, `' s3 Z                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  ' B& t* Y" e" \- F
                     } else {  
3 L; e" h; S+ c8 z" R2 y" u                         continue;  
9 s  W# {2 f6 \/ b- Y2 q1 N6 E                     }  : {% x& a) B9 a" |) {
                     break;  , d) |( o4 {8 P9 ~
                  default:  
& M4 z+ J4 p' f0 _; B7 `  ~# W0 q                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
* E9 Z3 o. ?: Z. b              }    L6 H9 {2 z' D7 R+ C+ W2 C& [
           }  + t4 w! A2 r' \& `: V+ A
           $field = implode(',', $fields);  
* |# W4 M0 @/ `- @% X) D       } else {  5 B/ Y! X) D/ ^" d' ?! Z& Q. P
           return false;  
; O3 W. l: Z' L' n" w/ J) A% M9 z       }  
+ n% j  f8 p+ l, _/ n. H, A       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
8 ~; E/ \6 L$ \       print_r($sql);  
4 p; Y9 A: L2 u, w9 _6 Y       return $this->execute($sql);  : k9 q0 D, \7 U8 y8 |
    } 9 j  m- l+ `# w( E( R8 |
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。4 P5 B* q0 l! y' \8 l8 p) A0 J1 h! W

; J% ]* ]8 I3 d6 z+ {攻击测试:
+ A& _1 o, n" u! G( y5 x测试地址http://localhost( N* b, y9 a: r% [0 a( Q' i6 J
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! S% G. Y4 a6 b* \6 }

% R2 B, d- u8 Y+ c ) P  Y. }6 b# Y$ k# y

) v- {! T& G! u7 {1 i1 G  K0 R8 H0 m# W9 O* E, V

" i9 ~0 n! _* D4 ^, c0 O/ f+ m' K

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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