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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
. t1 Z$ q4 V+ G4 F) A5 h漏洞作者:skysheep
, G& c  |, R5 E" \分析作者:Seay3 z. a; G7 e  r" N
博客:http://www.cnseay.com/, N' A( t1 t5 X6 y8 ~
漏洞分析:
0 e: z+ G% u- n# O5 V  L  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 ^! h) L$ Q" G# ]- b5 ]" X
; h1 r* b& D* f2 o% ]" X+ u

( D$ i9 \2 }/ d" G
" j/ }! Y; p+ c) R& X9 `) Z2 Tpublic function account_manage_info() {  
$ h6 b# M# g; g/ T; m  a/ p8 y! F/ f, r       if(isset($_POST['dosubmit'])) {  
! n' a% O( v7 n3 J' U0 m0 P           //更新用户昵称  
0 P+ k/ B/ I8 x# D: Z           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  3 v$ K) F+ F" O! n" h$ z' J
           if($nickname) {  
. l* v  v5 A/ F! G) t% b              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
8 Q; i/ w2 P* U1 m              if(!isset($cookietime)) {  2 g6 e" T3 }  e0 h. y
                  $get_cookietime = param::get_cookie('cookietime');  0 }3 i0 P6 R: e* d, S6 ?, c
              }  # Y- J( N8 G$ S( e7 z+ \3 Y. h
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
, y4 c! D1 ^7 A, P' o2 [: F              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  ' V4 B. N! q! o5 A4 T
              param::set_cookie('_nickname', $nickname, $cookietime);  & {/ q  ~7 a; a6 ]
           }  , e. r' n- z3 U7 @
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
3 p8 o; o7 D5 {, v           require_once CACHE_MODEL_PATH.'member_update.class.php';  
# L9 b$ E! e% f1 y           $member_input = new member_input($this->memberinfo['modelid']);  * H1 k5 N& r+ C. o( i  [6 d# O+ v
           $modelinfo = $member_input->get($_POST['info']);  
2 c5 |4 P/ z/ W% Y) l           $this->db->set_model($this->memberinfo['modelid']);  6 p; H7 I9 u3 N( T$ w: f; t
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
2 C6 n7 `8 U% ]" n" F8 F9 K1 A% q           if(!empty($membermodelinfo)) {  . l( Z% b1 J9 a" V) ]3 Y. U9 X
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( o' x( U  F, V) ?- c           } else {  
8 O1 b1 i: m- B2 p7 n: k              $modelinfo['userid'] = $this->memberinfo['userid'];  2 ?2 s! d0 Z7 M' \+ \4 V6 {5 Q
              $this->db->insert($modelinfo);  . k; j% o) I/ }. q- B/ J. F1 Z% a
           }
' f# Q/ m5 L4 K) E代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,% Q% D. O+ i1 l/ Y5 X% f
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ {  ~- y, H0 Q/ {5 @0 R) @+ r8 }, R: W+ u8 v

2 j1 q. ?2 m+ \+ L   [/ V6 t- o8 v0 `  u
function get($data) {  
- X+ U) @+ c7 ~, X% Q       $this->data = $data = trim_script($data);  
6 q# J4 [' g# N8 c: J- W# _$ p3 B       $model_cache = getcache('member_model', 'commons');  
" x" K; v1 o# \: R5 H3 X       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ! R/ L4 b% R2 u7 Y
       $info = array();  
  {: ~  D3 {' j9 b+ t3 ^       $debar_filed = array('catid','title','style','thumb','status','islink','description');  0 F. b: A6 ]: m, j- N
       if(is_array($data)) {  
8 J" a' W2 ?+ Q           foreach($data as $field=>$value) {  + N. X4 K0 U" @
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
+ Z/ @3 u: k5 v3 E) [$ L              $name = $this->fields[$field]['name'];  ; C5 O- x$ H* {( f/ U6 H+ L
              $minlength = $this->fields[$field]['minlength'];  
* Z( H3 M& y( t0 S! [. n: |0 v+ |              $maxlength = $this->fields[$field]['maxlength'];  
1 @6 ]% k& Q, d              $pattern = $this->fields[$field]['pattern'];  0 C) a4 c1 u* g  v9 @8 F) }$ N; E
              $errortips = $this->fields[$field]['errortips'];  
5 z( t+ J' [& g& f+ j              if(empty($errortips)) $errortips = "$name 不符合要求!";  
$ z2 h0 `2 u7 q% a7 |: p5 R5 l              $length = empty($value) ? 0 : strlen($value);  * L& Z2 P# r5 ~) u
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
7 k: x- F& {# N+ G2 R              if($maxlength && $length > $maxlength && !$isimport) {  
) o7 j2 e, b9 _# t% V$ U                  showmessage("$name 不得超过 $maxlength 个字符!");  : p+ ?% R* V# h1 Y! P
              } else {  ' q. {5 s9 m% |+ i2 R( c4 R
                  str_cut($value, $maxlength);  7 x. P0 S0 U' [# x6 w
              }  % w; D8 |* \6 I
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
  A* j/ d  Y$ t% R. G                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
9 F0 @4 N: j* p* X* z) U0 j0 g7 D              $func = $this->fields[$field]['formtype'];  
5 E( F1 M6 Q3 W. Z* E, L: I* l              if(method_exists($this, $func)) $value = $this->$func($field, $value);  5 J, }$ S' E9 S1 C. i4 a
              $info[$field] = $value;  / F  s4 P$ F* K; B
           }  4 O7 K& y0 f: q1 a5 w1 k
       }  & o% Z# o4 G! c! N
       return $info;  
4 w2 i' b$ T: ]4 Y; B6 L, x    } 9 R7 c7 ]* g0 u9 e' H
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 w  i  u# E# G( F; u$ f
. e3 f$ A  q( l* E7 c
再到phpcms\modules\member\index.php 文件account_manage_info函数4 r+ s1 ]! n; ]& S5 _: l6 o
过了get()函数之后。
2 o  l  _- R. A  u8 \6 \  {. ?& w2 E# Q2 U# [( q. Y* k

( f/ T# h4 v  O0 T" G2 Y# t2 J$modelinfo = $member_input->get($_POST['info']);  
0 [  Z7 N; F% h0 h" i           $this->db->set_model($this->memberinfo['modelid']);    ~" T4 `) U) R
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  3 V  {+ U, \4 X4 [! ]- r6 b, ^$ C
           if(!empty($membermodelinfo)) {  
: Z, X" V7 k8 ~3 ]6 u; v2 O2 X              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  6 p# j/ Y4 t$ q: `( l( D
           } else { , e  [& ^9 A% u+ \( W) l
直接带入数据库,update函数我们跟进看看
, q$ A' o2 _. d, ]! R. @; Y% g
& I8 y$ @0 N0 e3 N' _# F) j 6 Q  t2 U6 K# J
public function update($data, $table, $where = '') {  6 M6 ^; j/ x" I9 R/ g5 J
       if($table == '' or $where == '') {  + I* G" B+ r6 Z1 F" R; {
           return false;  - W. J' V6 m9 S- A
       }  % ]0 \5 x) S3 L9 D' H4 N& E5 Q, g$ H$ B+ \
       $where = ' WHERE '.$where;  9 h! P" c0 g8 q
       $field = '';  
# n5 q2 Y" x! i8 ~0 k" i       if(is_string($data) && $data != '') {  - N9 `8 c& _: q: \, w/ X; k
           $field = $data;  
# w* e- }2 W; b       } elseif (is_array($data) && count($data) > 0) {  
# N- r1 p: s1 m) h; t           $fields = array();  
  \; d0 U6 U6 C% @           foreach($data as $k=>$v) {  
, w5 ]- b+ G: S2 o! E              switch (substr($v, 0, 2)) {  
+ g, J$ k) {3 a                  case '+=':  
9 J# R" o7 e" N, z4 t; J7 a                     $v = substr($v,2);  
; d5 r: g6 @0 A! b                     if (is_numeric($v)) {  
5 J+ M4 Z, n& T" q                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  % @9 n- M" O/ u# g* c- S
                     } else {  3 L1 Q( o+ B# B; X
                         continue;  * a, O- ~4 Q0 @% u$ e+ i
                     }    k: i# \' I% j  `- F3 E1 j+ z
                     break;  0 u; r# o1 z& |. l) @
                  case '-=':  + S2 B' c4 A" p: Z/ p, u# D
                     $v = substr($v,2);  
/ ?$ }: f" g+ |+ ^7 U7 z' i1 g0 l                     if (is_numeric($v)) {  
( u, {: u  Z- b1 `/ G                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
. P8 W0 H$ y- u2 M$ ^" m                     } else {  
5 d1 [! g8 W1 X# p3 B" ]                         continue;  
# u3 P& |4 @: h2 `8 E                     }  
$ S6 j( F% Y) `6 X5 c5 D                     break;  
0 z8 q4 ]$ B0 n3 r6 _5 y# h4 w                  default:  
+ K4 h1 K* }/ g                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  , \  M: Z$ w. A4 z2 G, E
              }  5 i' v& T9 Z! X% a
           }  : |/ n# Z' B& W4 f; v8 V/ I
           $field = implode(',', $fields);  , Y- @/ O/ Q  x$ m: j5 [
       } else {  0 [6 u6 u+ Z" f6 C" `( F
           return false;  
3 y: t1 Y8 w# t; f5 @       }  
! N( L$ `  D1 N! \       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  4 V3 Y% n  a1 d. ~; U) Y, m
       print_r($sql);  ) W- ]* B/ F7 B  {5 |; A
       return $this->execute($sql);  
+ z1 J" A1 s; G* V. V: p    }
4 d+ S& @1 u- W从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。* Z: s; u* A7 U( c
# c5 C5 Q# Y" G& }5 J$ d
攻击测试:; s$ V- @2 ]( H* H7 Q
测试地址http://localhost
1 e( b( S( o6 ~- f+ V4 I7 B  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" b" R: c' U; a8 k  u& s
- c# r! w! z3 p) f8 c 6 }0 J4 f9 [5 @4 g8 `* Y

/ _( }3 o  J% v
+ g1 M* G' k* D' C. E4 x, x; C! T  h2 H1 E; N# W+ Q

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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