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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! q* Q  i2 v; D2 Y, t. m
漏洞作者:skysheep
' D3 [$ C( d# k. p1 k分析作者:Seay
  _# H% U% }  Y3 d) M. }博客:http://www.cnseay.com/
, O+ v6 t$ L" d( M漏洞分析:! v% T2 X) f3 `- X
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
( w  p7 @3 q5 {- ^! F1 w/ S0 w2 T
* t; ?4 c3 n9 ?9 X$ a, Z
" J! J/ i) g+ T  w: i: g * S1 r+ C5 ]& u( X9 m
public function account_manage_info() {  
( P4 _  b, i4 l5 G       if(isset($_POST['dosubmit'])) {  6 w1 A' Z4 O, E- V
           //更新用户昵称  5 A! _) b6 }& B( B/ `2 C
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
3 l8 |2 R" M+ P+ x' B& w           if($nickname) {  ) |  q3 c- m# [+ d1 U- c  x; k, U
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
% _% e) J. b7 Y8 ^/ d              if(!isset($cookietime)) {  1 \+ o  D' G) Y  m5 _, h1 |
                  $get_cookietime = param::get_cookie('cookietime');  / U) B2 c9 [5 y* b1 Z
              }  
2 U& Y& C; @6 H% m: \: V5 M) p              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
1 E% s4 E& [9 X" _$ h5 B, P              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
4 G5 }7 r. N6 J              param::set_cookie('_nickname', $nickname, $cookietime);  
/ q. {) Q0 x8 l           }  0 T+ _4 y7 ?6 o7 Y8 h; F
           require_once CACHE_MODEL_PATH.'member_input.class.php';  * Z* _* g3 J5 z
           require_once CACHE_MODEL_PATH.'member_update.class.php';  7 p! T! H8 _: z  @: x
           $member_input = new member_input($this->memberinfo['modelid']);  
6 n( H  s! ~/ V1 [. K' T$ |4 n           $modelinfo = $member_input->get($_POST['info']);    H: X. x8 w0 p1 A# r
           $this->db->set_model($this->memberinfo['modelid']);  
7 T7 e3 c5 H7 m( [7 ?) P           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
0 W4 y7 C8 g& A* B6 [           if(!empty($membermodelinfo)) {  6 |  U, I, ^0 E2 Z! |& }& @/ j) R
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  ( r( w+ O5 e- p6 \' M9 d' v
           } else {  4 J# C3 `+ n+ j. Q) n
              $modelinfo['userid'] = $this->memberinfo['userid'];  
8 Z5 n0 W6 G; j/ H3 Y- g              $this->db->insert($modelinfo);  
* t6 g( V* d" q) m1 R           }
. X" V2 \5 {2 e6 C* N代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,7 u0 C, w1 z" z- ?  @$ v! l1 \
在\caches\caches_model\caches_data\ member_input.class.php 文件中:0 n1 j7 D6 X, O7 s7 ~* L; b: E3 v
4 E, ]6 b) n" V, C$ \, q6 t& F

0 X0 C- Y( w9 H6 R1 ~, g, ~   V: S& V& u; ~3 x* f/ b! [2 r
function get($data) {  ' W3 ^/ t- z$ c; [( v
       $this->data = $data = trim_script($data);  
8 H' ]6 F' C7 c9 T0 _       $model_cache = getcache('member_model', 'commons');  5 y& J% Z% L6 _' D" s2 \
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  ' Y5 _/ [* {  V- h( g
       $info = array();  
. @& [3 u  \+ u2 ?$ R( ?: C: u       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
/ s9 b' `8 [- z+ I. s       if(is_array($data)) {  
5 G2 f. P9 f- O- @- I$ S0 h           foreach($data as $field=>$value) {  $ H* j4 \: L% j8 J  Y  G
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
# k* U* a- ~8 \( l' n. [* Z              $name = $this->fields[$field]['name'];  7 a: {- |6 c$ p1 Q! u
              $minlength = $this->fields[$field]['minlength'];  
% |" X( U6 D- r# J3 ]/ p, F# X              $maxlength = $this->fields[$field]['maxlength'];  ( c' D% m  S' O. H# ?3 G
              $pattern = $this->fields[$field]['pattern'];  
  a6 y) J4 L. a  O1 b& Q              $errortips = $this->fields[$field]['errortips'];  
3 I  \* X! j0 f3 a: A              if(empty($errortips)) $errortips = "$name 不符合要求!";  
$ Q/ U9 b1 ?7 i& c              $length = empty($value) ? 0 : strlen($value);  $ P( w( a. D& z+ @
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
, y& k- P: w1 a% @# s1 D  G2 Z8 Y1 O              if($maxlength && $length > $maxlength && !$isimport) {  
5 T- X9 f% d$ h' E% k' x                  showmessage("$name 不得超过 $maxlength 个字符!");  
! g# U: ]* A; k# b) G, M# }, q              } else {  4 r" L  C, I2 b6 Z; F+ Q
                  str_cut($value, $maxlength);  0 O( [# h% Z" @
              }  
3 |4 j7 @' t2 k" W! i& E  ]              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  $ N, R/ [! E) Z% j( C
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
, V4 D, e9 P% J& \+ R. j              $func = $this->fields[$field]['formtype'];  # N; F' ?- x9 e0 C9 G# T  x8 T
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
* V, d& n" |2 d8 a% o' m2 U              $info[$field] = $value;  
' u0 y, u1 ]5 @9 M% i+ P           }  ' s% h% `7 i/ _3 U& t. [/ @$ {
       }  2 h) I* J/ D0 u! h* x
       return $info;  7 u- Q* E( o; B0 {# P' n
    }
/ O! Y, ?2 P0 `4 ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& v4 `& V  f* l+ P& E) z  K
4 U9 y/ M% ~$ [4 O% x: l
再到phpcms\modules\member\index.php 文件account_manage_info函数
& Q6 C" ~& b9 u0 \+ W  a过了get()函数之后。
3 k5 i: d, Q0 ?% ~/ F. ?5 c$ Y/ N  \8 L  y
4 ?& X5 I5 C$ e; G# e( r+ S
$modelinfo = $member_input->get($_POST['info']);  
/ N% L* ^* u: j. T' a" _           $this->db->set_model($this->memberinfo['modelid']);  # E# |- J/ x& W: t
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
! S1 M( D4 Z+ V% G8 ]: v           if(!empty($membermodelinfo)) {  9 q( U4 B+ K7 z2 o- m  [+ s' G
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  " _1 u' Q: {1 u  p+ \' w" ^
           } else { + @. j" M  ]% P/ a
直接带入数据库,update函数我们跟进看看$ C5 R- @3 ?  l* `& q: ?) ]
​4 q/ u- z. l5 x9 m/ t3 o4 G6 @
3 @8 @% s1 D3 e/ [$ X6 M: M
public function update($data, $table, $where = '') {  / `# S  j" E9 P! w$ B$ d5 L
       if($table == '' or $where == '') {    O* q5 `4 s4 {8 S* g# X& X6 i& l$ B! v
           return false;  
6 N9 `( P0 T0 b3 ?+ C       }  
1 {& F+ q. U1 r- a7 ]/ W       $where = ' WHERE '.$where;  
& _' U6 @& Q0 k+ v! I       $field = '';  , G, w, @* ]- |
       if(is_string($data) && $data != '') {  ! b9 {' |4 @) X" Z5 _9 A
           $field = $data;  3 E) b1 S3 d; m- B( m( X
       } elseif (is_array($data) && count($data) > 0) {  0 A( `4 Z( f% H7 x5 J+ t
           $fields = array();  4 V$ c" y* A9 t* a( ]
           foreach($data as $k=>$v) {  
; T* X' y; j" }$ P4 E( N( F5 `              switch (substr($v, 0, 2)) {  
! X( Z: i( C; D* t. E& O, o. T                  case '+=':  5 F0 P! z9 L& x" B6 l5 {/ {
                     $v = substr($v,2);  - ]1 I4 L" R8 I9 \" F* a& t9 Y+ i
                     if (is_numeric($v)) {  
) [# c; \" J# C! e/ v                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
5 ]8 x  k0 ]  o' _9 j                     } else {  
( G; x7 M; i& Q6 [. A7 p! k                         continue;  1 ~# \  w2 S; [
                     }  
1 ?+ o6 |- v) o                     break;  
8 P1 \  g+ X6 a# h6 F5 I) E/ I8 n" y                  case '-=':  ' o& W& @% v; y7 F5 Y4 N3 x
                     $v = substr($v,2);  5 d% f. S, }5 w( M
                     if (is_numeric($v)) {  
+ G9 {4 A6 Y. F, _! E                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
( i/ F, a) S: r' S5 a5 s; d                     } else {  ; N4 O# J0 b0 l$ C
                         continue;  
/ ?+ A- x1 c- d: u                     }  9 R& q- w0 ]' P1 A
                     break;  
* @. P) F0 Q. J! g: _! f                  default:  6 u  P8 w- N( I$ q: W6 R7 @
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
$ n! v( r0 t4 o3 T              }  
* k, h9 [6 Z3 t+ d1 m3 s6 P0 B           }  & W1 @, u( |/ B2 b
           $field = implode(',', $fields);  7 Y6 C- K' a7 j- M
       } else {  
/ m* R1 [! d/ o: y. [           return false;  1 ?  W0 Y1 A8 `: ]: \
       }  
4 y& e& c& J* V' X- z       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  * v# `3 Q' F1 D4 x5 m
       print_r($sql);  
+ D) P2 t1 l8 ]( r       return $this->execute($sql);  6 X8 O: C8 Z4 M/ J- b0 l7 d  E! M! h
    }
+ n& b! e6 r& y# p6 g: }) D从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ @- R+ U0 f3 |6 y: W& H8 y$ }7 O. ~5 [' K& _, _+ s# F! G
攻击测试:/ l: N8 L, \( V/ l7 G" i. m/ n
测试地址http://localhost
- t) `. }1 {0 S- R" _, }" ?' R  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
  L2 B6 y2 g6 J! w) B
7 `6 U- L5 u. F, f6 {* T- b " V8 [' v0 K! A% U- h3 G
+ C# E7 M5 ~4 i( _
4 K0 n" j' Z) ?& ]( ~/ c# D
6 d( ~" i# L. y4 {! `' o: B

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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