找回密码
 立即注册
查看: 2569|回复: 0
打印 上一主题 下一主题

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告9 C3 ?, J8 v/ ~* c" h% F2 x% D
漏洞作者:skysheep
3 g' N8 K4 p! h8 t$ B5 C6 E分析作者:Seay( E# b+ P8 O& e) y. _- }# P
博客:http://www.cnseay.com/* u' C4 G6 q8 {
漏洞分析:
7 _) ^8 I! M& M  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。) F) R- k! _: x* {! Z2 ]0 b
; L; ?( n& T3 |/ U- i2 t/ e$ b& Y

0 ^" c5 a  F7 O" L 3 z, V! C( E- N2 p
public function account_manage_info() {  
4 p* Y& X* U( F8 W       if(isset($_POST['dosubmit'])) {  
5 X, F( R. v1 {* T- a' ?           //更新用户昵称  ) f% W) K% d2 X; Y4 t8 C
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  & I9 Q2 {! f; P- f: h6 \7 V
           if($nickname) {  2 H" ?; B" |! j: e+ _
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  " c6 Y3 p7 x+ g4 X1 G
              if(!isset($cookietime)) {  - e) J) v% J3 k$ S2 y3 D- J6 {
                  $get_cookietime = param::get_cookie('cookietime');  
, X) v6 x& C5 W: `. ?) \9 |              }  1 g& ?2 A: A# \/ i+ ?9 ]
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
: ?3 v0 y: Z, ]8 T4 D) g$ j              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  2 j' k% O& Q/ m* I0 J  i
              param::set_cookie('_nickname', $nickname, $cookietime);  
4 P' h* v: O( B5 V' e4 y           }  - @" w; z6 F- z, M
           require_once CACHE_MODEL_PATH.'member_input.class.php';  5 P! `2 ]5 U0 j
           require_once CACHE_MODEL_PATH.'member_update.class.php';  6 }" j' M. x6 j; @
           $member_input = new member_input($this->memberinfo['modelid']);  % d+ H% c* a0 _! L- I
           $modelinfo = $member_input->get($_POST['info']);  7 X5 f0 V" A- O% x& _
           $this->db->set_model($this->memberinfo['modelid']);  
' s$ G( h$ d- S1 W# U( K           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  9 u" d% G/ J+ q& z- [
           if(!empty($membermodelinfo)) {  
7 u2 b6 A# _# }5 L) t1 a              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
- ~+ ]- {. J7 f/ m6 A           } else {  ) v/ q( K: W! f" a# J- F; X
              $modelinfo['userid'] = $this->memberinfo['userid'];  8 h, @) ], Y) I) ?4 {: W( I3 o4 z
              $this->db->insert($modelinfo);  
0 a' U9 i9 H3 x6 r8 k, r% J           }
7 m9 e+ M7 A$ z8 W6 K  O代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
  c8 _* j& S5 w7 |4 U在\caches\caches_model\caches_data\ member_input.class.php 文件中:
  B  L; C! @8 P2 X8 |
' ^7 T/ o/ ~8 h2 D
/ R' M$ l9 x- ^& _' D/ w1 M; h2 n ( _" z7 Z" I, ^
function get($data) {  
5 d6 `, D9 l: b  I! S       $this->data = $data = trim_script($data);  * ]1 o- K$ O+ S6 Z1 d( ~0 a
       $model_cache = getcache('member_model', 'commons');  # V+ n$ W9 R1 _& L7 M
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
3 |  Y5 M5 E4 ~3 T' W, J       $info = array();  . r7 r$ q  Y* S# l; @& |: a0 j/ w9 u
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
/ B1 R) t! }8 h0 [" I. y6 c/ W* M       if(is_array($data)) {  7 D: i( T7 \1 K- J- a$ D# u
           foreach($data as $field=>$value) {  
! u: e" h0 W; K( E              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
+ D' I! I. y0 w& a; ?5 \              $name = $this->fields[$field]['name'];  
* a" B9 {- M' W' I              $minlength = $this->fields[$field]['minlength'];  
1 p6 u( w- T# F              $maxlength = $this->fields[$field]['maxlength'];  5 |  Z( V+ l; R6 V& d$ l
              $pattern = $this->fields[$field]['pattern'];  
1 {( N6 M  U7 K+ I# ~8 a: \) v# L              $errortips = $this->fields[$field]['errortips'];  
* q( z5 y+ J! D$ v# {4 j' F9 \& H              if(empty($errortips)) $errortips = "$name 不符合要求!";  % W- i, k2 r- U2 i* |/ l* F
              $length = empty($value) ? 0 : strlen($value);  
6 p/ O. i2 b! I: \: T              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
* I9 i7 e# ^+ |/ o, x              if($maxlength && $length > $maxlength && !$isimport) {  0 M) x3 F0 N2 W3 p- P
                  showmessage("$name 不得超过 $maxlength 个字符!");  ' a& X8 y. x  I+ X, ^- }
              } else {  8 n1 O6 f3 \5 j
                  str_cut($value, $maxlength);  
/ |* W! K0 [" C              }  
- u+ Z0 ]7 i8 t* S& x              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
, e! F3 c( T; N                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
4 Y, Z$ T- {4 D# F              $func = $this->fields[$field]['formtype'];  5 e0 c8 z% l1 ]4 E* _+ K: l! ~7 L
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  2 b) [) d: I; W, R" N8 C
              $info[$field] = $value;  ! ~( I8 o0 s7 C+ i! E/ l; g0 c
           }  , `- J# ?8 t" i4 D8 ]4 \. Q
       }  8 m# I& O; |* Z
       return $info;  % d: g: P# C+ S- J* F  r
    }
6 _+ R- z- ?/ Z& Atrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 }8 M/ C4 V$ ^
* V) n+ o3 j3 Z8 t: C6 l( p
再到phpcms\modules\member\index.php 文件account_manage_info函数; f0 I. }, h' G+ ^5 Z
过了get()函数之后。
! i* z5 V4 I( }4 L* _. w
' Q. H. k! C+ d  M
  }$ N* m1 X6 _7 q, w) F( ]$modelinfo = $member_input->get($_POST['info']);  : N; [" d, o# K% E  f- t* O1 u0 _6 v
           $this->db->set_model($this->memberinfo['modelid']);  / x, h# E; l0 y( H! c8 }" L
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  # D% M0 ^6 [- _' ]9 x2 F- ^
           if(!empty($membermodelinfo)) {  & n5 R; F3 x, q0 f1 j) e
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  3 A, O; B8 n) y/ P4 ?0 U1 Q: i: L
           } else { 0 @$ p, u9 ~4 t$ e. j
直接带入数据库,update函数我们跟进看看
: E2 K" m) V. H4 J4 O. _3 E
( \- t& Q( |& B, B; t% I9 p" V ' R  b3 l" I9 H, r' i: v7 v
public function update($data, $table, $where = '') {  $ ?" Z5 b6 d, f/ l- c& }7 ?8 s6 O& C
       if($table == '' or $where == '') {  
( z9 ^4 s& l) K  }8 t           return false;  
" F- t: T' W# T+ n4 U# g' p       }  3 \+ \+ ^% c! d4 G4 [( C
       $where = ' WHERE '.$where;  
  G9 A" H' X* t  _/ k( k       $field = '';  
, \/ N. O) Z% y( z& v+ `7 k  [       if(is_string($data) && $data != '') {  ! K0 i4 R: W- ~2 }) u( g
           $field = $data;  
7 C+ o% s+ q7 s& ?) S8 N5 q       } elseif (is_array($data) && count($data) > 0) {  
4 x# k- j7 `4 U/ p5 {           $fields = array();  0 O% R) ~: N: T" g0 o' E) F  @
           foreach($data as $k=>$v) {  
) t; U. U+ T7 E6 A& V4 F/ h6 ]/ U1 ]8 [              switch (substr($v, 0, 2)) {  
2 ^" s, P1 r2 a, b( k                  case '+=':  / I7 X8 I$ x1 c( M. j% D& a6 R9 J
                     $v = substr($v,2);  
" Z4 _6 w$ ~& Z- x  n                     if (is_numeric($v)) {  
4 m3 Q/ Z. d0 e5 M( X+ u* U                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
; m6 O/ U* Y2 V& ^5 a" \                     } else {  
% l( v4 }" K* Y9 Q' Y/ g( J                         continue;  
9 Z2 g# N3 B5 q) `: E8 R; A' c) E                     }  
' o* C. e- I2 G# L4 f                     break;  ) E+ {/ E0 d0 E6 l
                  case '-=':  - N' y2 ?5 e* t/ G+ J! s$ P
                     $v = substr($v,2);  
6 r+ s; p% T4 ]+ H% l2 C& j" b                     if (is_numeric($v)) {  8 _7 X/ K- e4 x8 E$ V
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
6 p3 Z+ _( {% C: ^                     } else {  
  W9 Q$ N, Y; r' I                         continue;  
6 {9 }+ m; ]$ s/ Y                     }  4 F+ R8 ~, Y( m9 f+ n
                     break;  ' E; W2 w$ b( T3 V
                  default:  
8 N  [6 [! l  Y4 R/ S6 t                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
6 u  j% h( [" i* g' L8 L. |              }  
4 O8 B' l, |. r' [           }    B# }5 U% t% S8 S$ E3 B6 P
           $field = implode(',', $fields);  
+ \2 e$ N( U  ^       } else {  " z* r/ v0 |& O
           return false;  
4 l4 N: {% X% C: Q1 A& t1 T6 }       }  
& [( f& S0 I$ d. w0 t7 ~& }0 r2 K       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  # i0 w" t' ~4 u# N
       print_r($sql);  : R3 U& ~0 C3 j
       return $this->execute($sql);  * X% q. |0 b" O* h9 C; V! I
    } 8 A  I( C8 _: y, f( A; Z3 I
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。& j1 K4 V2 A8 `0 P
. I# w6 b7 l( h  M: G% V
攻击测试:
& A; I5 y; t/ y$ y测试地址http://localhost
/ q( E! _( P8 q% T0 s  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句! S' L+ L( S  ?" n- ?

) I' y. X( M, e  R- Z$ P
" w: Y5 H9 X3 d- Z3 ?" I- s7 q3 G. F( l

  P7 ^/ G0 d3 g* x1 j3 ]" y. J
  e" _% S) J6 {: j: b( ~/ v

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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