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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告* g' s: t9 l) v1 G' [' J
漏洞作者:skysheep
' J# }! O# }) L8 I分析作者:Seay
& ~- \9 O0 L2 H/ o5 ~1 u- a博客:http://www.cnseay.com/
! F6 W5 k7 }# G1 H1 X! L8 G漏洞分析:
& z/ y$ E- R0 t: g! e6 X% O( R+ s3 c1 K  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。! }% C% x$ c: Z. I' q
9 b2 J9 b( Z" D  w' t( Z" Z
2 c2 c2 z0 ?. |

, c  F- V3 a, p7 Bpublic function account_manage_info() {  2 R# J4 H& Z0 ]9 |
       if(isset($_POST['dosubmit'])) {  ( W. w, y1 ?% x
           //更新用户昵称  
7 y% T/ R+ b# E, o           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
$ S1 B9 ?- Y0 q! W/ s           if($nickname) {  
( h8 k+ n( i8 K7 k              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  % o3 h- d, i2 A! @4 U9 Q% A
              if(!isset($cookietime)) {  
. `) I" v7 d3 I- A' A4 c                  $get_cookietime = param::get_cookie('cookietime');  
0 j% R6 K1 T! M5 z8 p  L2 o% ~, ?              }  ( Q0 t. m3 Y9 u+ y, e2 P2 o3 f
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  ' u! R7 ]* U- W8 J1 w& W
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  9 N+ p9 _+ z5 w$ s8 j& C
              param::set_cookie('_nickname', $nickname, $cookietime);  
" c* O1 l5 V' \1 W           }  
0 O" k/ Z. \: y4 w           require_once CACHE_MODEL_PATH.'member_input.class.php';  2 ]% Q( h( x3 d" u
           require_once CACHE_MODEL_PATH.'member_update.class.php';  
. k9 ]! o- N/ ^3 a" x. n           $member_input = new member_input($this->memberinfo['modelid']);  
" \* Y! ^: e5 G           $modelinfo = $member_input->get($_POST['info']);  
9 T1 b/ T: ?4 W3 A           $this->db->set_model($this->memberinfo['modelid']);  5 L1 Q+ b* Q$ t& x& u  G
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  * Q, h. ]5 C# u1 Y
           if(!empty($membermodelinfo)) {  4 H: c7 _+ a6 ]: G, [7 b
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  6 t8 y" Q) O9 N, f3 [* j
           } else {  # G( q$ @+ n" h
              $modelinfo['userid'] = $this->memberinfo['userid'];  ( X, V0 [) K+ W0 y; T! L- R
              $this->db->insert($modelinfo);  
1 {2 I* b9 H4 w0 y1 Q4 ]# i6 s           } - k$ R$ h8 A0 _1 ?) P5 e, @
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,2 h! T# k& g9 V
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
3 V; H2 q+ U5 [/ ]% `# Z; M2 |& A
& e1 h- E$ v+ U; S7 @/ b: v3 |
. T$ t  r  K' o: R
function get($data) {  ! s4 R  @# }- I" J" k2 z+ D
       $this->data = $data = trim_script($data);  
0 W; _( \; O2 U8 d% k( w       $model_cache = getcache('member_model', 'commons');  
8 O1 @+ H2 b3 I' p2 O0 X' l       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
- u; D% a& \& E  W9 Q  [  q2 p       $info = array();  
6 ]8 h: \* Q4 E' e- R: b       $debar_filed = array('catid','title','style','thumb','status','islink','description');  3 g6 F6 t5 ]. u7 I& \
       if(is_array($data)) {  
* u8 o. D# M. K1 l- B9 {$ M) O           foreach($data as $field=>$value) {  
7 I) k& s* o! t" O% M              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  5 ~( f2 `9 ^( W8 h
              $name = $this->fields[$field]['name'];  
7 V. o2 f. @  }: S: c              $minlength = $this->fields[$field]['minlength'];    M# ^* C' i# l2 E; E; \
              $maxlength = $this->fields[$field]['maxlength'];  
# @6 R) k2 T; q' m" i: [              $pattern = $this->fields[$field]['pattern'];  
3 l5 n8 N& d4 ?6 o5 ?6 P              $errortips = $this->fields[$field]['errortips'];  0 Q& i" [0 l) y: e2 @
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
) C; M  h/ L9 k              $length = empty($value) ? 0 : strlen($value);  
9 R8 g5 q7 F( c% l3 T9 A              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
0 |8 `4 R: s" R+ a) \4 r              if($maxlength && $length > $maxlength && !$isimport) {  & G# ~4 e4 {( w3 T* C
                  showmessage("$name 不得超过 $maxlength 个字符!");  
  k; m4 V1 D4 H( q4 A+ R3 U' S5 x              } else {  
/ R! t8 ^7 B) F6 h6 Z6 P                  str_cut($value, $maxlength);  9 n$ D6 L0 s' d. @" S$ L
              }  " f8 R: }5 y5 Y# q
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  & W# P8 I+ K! T
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  & ^6 J% [) q  D. G$ P: y7 N( Y
              $func = $this->fields[$field]['formtype'];  
4 d/ ]$ |# ^/ \              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
( \% J+ }' L, b* x              $info[$field] = $value;  
3 ]1 F2 Q, f$ Z; e4 C8 j           }  
) r+ f( @, H: L- m) q, \/ R       }  8 H6 F+ H" w7 y
       return $info;  
5 C) p& R2 Q8 Y& {7 t  a    } # u' i" x" V& p( |7 E% V$ r
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,1 S, H: _3 k8 G3 i5 @$ ]

. C1 x! Y6 p6 a0 d# l再到phpcms\modules\member\index.php 文件account_manage_info函数' P0 t: M0 V# K5 x
过了get()函数之后。# t2 }4 _( x1 T) y5 M% Y
& j- Y' D+ D8 ~5 x" ?5 v& H
# P" i+ A" Y7 }: K5 |
$modelinfo = $member_input->get($_POST['info']);  2 Z1 U) _% ]* c# W! K
           $this->db->set_model($this->memberinfo['modelid']);  
# B8 W  }, Z4 `           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
( X+ \1 U# T% w8 D# P6 g" }           if(!empty($membermodelinfo)) {  
  K0 Q/ G6 x8 L  v( K% \              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
; S& R  w# W( e/ ~* E1 b           } else { 3 z8 n$ z. c2 p1 u7 J. c( y% @( {3 @
直接带入数据库,update函数我们跟进看看
7 ?/ q7 ~& z: p0 U8 \​
0 n, h* ~7 N& X0 y& h) q; \
" U: u% j+ n) s) s  k9 q2 W$ v1 o+ Gpublic function update($data, $table, $where = '') {  " V* z$ K* Z& l, W5 n: ~
       if($table == '' or $where == '') {  % ]! [& n* `5 |6 B
           return false;  
- w" J# o( d% w: g% c% H* }! j! n       }  4 ~9 @4 T* H% V0 [& w
       $where = ' WHERE '.$where;  
) O8 c) r8 t) ^! x  W3 S! j       $field = '';  
' l! v6 p9 h& w# |9 ]$ ^1 f       if(is_string($data) && $data != '') {  / V0 Y- X' Q7 z+ Y( e- L5 X+ G( E7 [
           $field = $data;  
  z9 ]7 o4 y5 K6 d6 W  b+ o       } elseif (is_array($data) && count($data) > 0) {  
( s% n; W+ c. m! C2 p6 V           $fields = array();  
7 t8 T$ ^6 x: x/ E  M$ @- Z' L           foreach($data as $k=>$v) {  1 f% O' B) d  e* B
              switch (substr($v, 0, 2)) {  
2 Z7 N8 E/ L* Y                  case '+=':  % ^2 @* ?5 _+ k# {
                     $v = substr($v,2);  
- f: k3 T4 w$ M" m9 e6 z/ L                     if (is_numeric($v)) {  
1 y  F# V- q% z$ v                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);    O  F; B5 x5 p5 _) b+ K5 M
                     } else {  
# h0 Z7 K* D6 k: C                         continue;  
8 t* z4 y& |4 h, `                     }  
' I6 Z" ^! T: S' c7 W$ X                     break;  / y) @3 }3 a% G6 Q% J8 P( V
                  case '-=':  
, P$ A. I" _2 S. R" L9 E+ {  |                     $v = substr($v,2);  
7 M+ [  J- e& e% Q                     if (is_numeric($v)) {  0 U7 J( P- `5 i, Q* w  a' k* W5 L. T
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
% k5 g% u, m, R$ i9 Z: d                     } else {  ; N# T& i' S3 _) s1 u
                         continue;  
6 L& L0 J' }% ]6 ]% O& b6 t                     }  ; d# h$ |1 Z$ \6 W5 V
                     break;  0 P0 z6 f& `% V3 p
                  default:  
; h4 |$ L5 w5 ~8 W- k) g. o, e                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
6 C7 B( i: @) d0 [# K              }  ) ?7 ^. ^) s! t. M; Z0 \
           }  
4 g% |, v. u) T6 e9 w           $field = implode(',', $fields);  : b$ i3 k2 S: {7 q7 k0 E* H9 k5 M
       } else {  2 J+ s: e3 X" ~' j
           return false;  % v4 s4 k9 c) V2 W5 C1 _
       }  
" ^  Y, Q3 \: B       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
" A6 F- T, i2 j, L7 I       print_r($sql);  
2 ~- \' G- c- {' U9 K       return $this->execute($sql);  
5 ^/ U- ~; V. y4 n    } * p' M1 D! P4 J6 b' q
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
9 n  Q* @) M( B, M7 p; ~8 A
1 V3 q% r  B  O# ^' }0 G. [攻击测试:
$ u& [+ F  S( A测试地址http://localhost1 Q: s; E, N+ ~5 R# N
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# y1 ~) Z+ Y! M5 b; n2 q# [# O; O4 s+ S# s) o& d

3 C2 Z3 O7 W4 G, }' P, c4 E: |) M' h: B7 S  V
1 G6 D+ o5 E) r0 A
* c- ^+ z9 k" E* c

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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