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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告7 b! k+ p- J7 m0 y" b
漏洞作者:skysheep# ]- v% \3 `4 u5 B0 A& \) d
分析作者:Seay, w, [$ t; D1 w6 d" c5 J( R  r
博客:http://www.cnseay.com/
7 ?! {. Y1 W- ^/ B漏洞分析:
6 V) Y) H2 d8 l# F  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。; z$ [* h7 |" e$ O2 Z. S4 @+ e# F

: M+ o/ ]  o4 m" d2 @" V; K8 |2 E- E# b$ p+ D0 f
( X0 u% a  `  Y8 b# O
public function account_manage_info() {  
" i& U' C+ g; b       if(isset($_POST['dosubmit'])) {  
9 v3 b& l- e# Z; i1 C6 z# F1 B           //更新用户昵称  % D6 {; X' ^+ D" e+ I
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  4 D0 U4 w' ^1 K: J
           if($nickname) {  
( @4 p0 F/ [) {+ `/ [/ n  e" ~, M9 @              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
/ M2 x0 R/ J! c, q% s9 e8 |              if(!isset($cookietime)) {  9 {2 @% e; @+ }6 P
                  $get_cookietime = param::get_cookie('cookietime');  
! l$ X( u1 U/ R3 ~; F              }  5 E5 o- d0 W. @2 X+ J, y6 D
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  $ h6 s3 H3 g9 C9 }
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
& i! V9 }0 o0 ~              param::set_cookie('_nickname', $nickname, $cookietime);  0 [" q2 Q2 t: o2 j$ |5 j2 ?
           }  ) O# N" P$ g$ [: R  i- w# C
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
* D7 k/ K9 ]3 n- v1 c           require_once CACHE_MODEL_PATH.'member_update.class.php';  4 k7 X8 O% X" [4 }2 M
           $member_input = new member_input($this->memberinfo['modelid']);  6 K; |9 U/ W" v8 U$ T  ]5 R) I. K
           $modelinfo = $member_input->get($_POST['info']);  7 k. E. N' c5 V8 E' d  z
           $this->db->set_model($this->memberinfo['modelid']);  
# U2 D7 z; a0 U$ l6 t           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
8 Z3 w3 I  L8 ~, K6 l8 J: n6 a           if(!empty($membermodelinfo)) {  # A5 t8 |- u) x4 C; |  T6 V
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
' o" U( B7 h: t- i0 a4 O# L) r           } else {  
; A, M) F* z& ?0 D9 I- ?. X              $modelinfo['userid'] = $this->memberinfo['userid'];  
9 r+ [& V& v4 Y( u: ?0 l+ _, v6 j  _              $this->db->insert($modelinfo);  
( r# x0 K/ U' p4 ?' x           } 5 S, K" H" |5 |6 n+ N
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
8 S/ F) n1 e! t# _% f在\caches\caches_model\caches_data\ member_input.class.php 文件中:% t3 N( n( |+ a( M
' U9 c/ Z9 i# N, x8 [4 y1 v6 m

  _* ~0 D( t' ~# A & z5 l" k0 Y1 u- H) x. p
function get($data) {  & _7 S; q& Q; |( {0 t" Y" a. \
       $this->data = $data = trim_script($data);  
8 F4 _$ I7 @5 c       $model_cache = getcache('member_model', 'commons');  
5 s' K- \4 b6 A% y. q8 ~* X       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
+ ?/ Y: q5 @. U7 H- w7 u       $info = array();  9 C. b, l% D/ O; y
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  
# D+ k0 f' g; Z5 \9 x; A       if(is_array($data)) {  6 G* T$ H! H7 z/ w% l
           foreach($data as $field=>$value) {  
! n8 J9 w: b6 y( Z( }. d              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
2 m8 [* B: t- x( E5 }& p: Y: M              $name = $this->fields[$field]['name'];  8 U- T' l8 ~$ s' e1 H. ^( [6 m
              $minlength = $this->fields[$field]['minlength'];  , j2 q, s$ d8 o- q: f! ~" P
              $maxlength = $this->fields[$field]['maxlength'];  
2 v. F  e8 e) A# C; V              $pattern = $this->fields[$field]['pattern'];  
, D( h: X# f7 t& S              $errortips = $this->fields[$field]['errortips'];  1 {, y8 d- x) q6 p
              if(empty($errortips)) $errortips = "$name 不符合要求!";  % j& Q: i6 v  h+ v& J
              $length = empty($value) ? 0 : strlen($value);  $ x5 @" O! t" ~' }+ X4 p6 W
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
+ Z& T' Y8 \, e! x! j1 I) F+ p              if($maxlength && $length > $maxlength && !$isimport) {  7 r8 r0 k7 ]6 X7 v+ Q3 t
                  showmessage("$name 不得超过 $maxlength 个字符!");  * Y, _) L) ^, q0 |, E) Y
              } else {  $ A$ ?3 L7 X) y9 n; a+ Q' _0 ~8 W
                  str_cut($value, $maxlength);  $ M9 x8 B! ^) y* W! K
              }  $ G9 d/ E+ Q& ~+ b, e, o- f8 n
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  0 X- q5 Z. f% `( k
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
! m' D" c' Z; C1 a6 E2 a# d4 [              $func = $this->fields[$field]['formtype'];  - j/ S) V: p8 x- L2 P% ~* N
              if(method_exists($this, $func)) $value = $this->$func($field, $value);    y/ I# t6 [2 n) E
              $info[$field] = $value;  
% ~% |; k6 o7 C! B+ T           }  
6 K6 ?2 d% U* u/ @3 P       }  
$ A1 w4 w$ G( F& Z+ S       return $info;  ( P8 Y$ ]$ N* r* a) \
    } ; [+ Y4 e7 \* }3 t2 T6 ?
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,% l0 l) t1 Z+ G2 y
' w: J: X, V6 v: {* [3 U% g2 H
再到phpcms\modules\member\index.php 文件account_manage_info函数
! G1 m! D; W3 z, d4 e8 b过了get()函数之后。
2 [3 p+ p: d5 u/ L8 {+ S" f3 j( `% p' ~/ |: y: P& B

# Q: H* E9 A4 A; U$modelinfo = $member_input->get($_POST['info']);  ( z+ r8 A* O- ~1 S2 L
           $this->db->set_model($this->memberinfo['modelid']);  ' z  n4 V" |6 b* l0 c
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  . [6 r& k( q6 F4 A: T6 B5 G
           if(!empty($membermodelinfo)) {  1 v! w# {5 H( \6 c) b: s7 {
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
6 Z, a4 ~8 \# g) i           } else {
+ a( @/ s9 l/ z直接带入数据库,update函数我们跟进看看- P. S) f7 G  P3 c: N+ g
4 i  O1 J4 F2 r0 {) w6 z
1 ?# ]1 J1 X0 m. A& n! Z
public function update($data, $table, $where = '') {  
# a, p% I6 C& O5 v       if($table == '' or $where == '') {  ' ^7 l0 P+ L+ u) l; S7 N
           return false;  - a) u: w' c) i) ?& w1 z' |9 O( J
       }  ! |: H% ~& p8 S# W. d
       $where = ' WHERE '.$where;  . J8 b8 z9 u8 a& p; P$ v' n, m$ L$ X& V
       $field = '';  
! l2 ~1 l/ h$ q/ b) k; x2 a& Z% f' ]       if(is_string($data) && $data != '') {  
( L( s8 u7 b4 R8 p9 q           $field = $data;  6 H+ }7 }9 K0 E0 @: I2 p5 ]/ v
       } elseif (is_array($data) && count($data) > 0) {  1 \/ W- ^+ K1 d& Y0 I4 J
           $fields = array();  
, z2 [) E5 _: ~" \0 l           foreach($data as $k=>$v) {  
' o* z! g  v0 c! Q              switch (substr($v, 0, 2)) {  
5 Z5 {  @9 \9 q# t8 l' z* c                  case '+=':  4 Q! U( l) H- P0 S
                     $v = substr($v,2);  
* v% T- p# L% n+ \! Y. A) ~                     if (is_numeric($v)) {  , s$ e! x7 {4 W/ \0 I/ [/ E' t; `2 o6 a
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
% V6 G! r9 T& H                     } else {  % {% L2 T$ J2 [* n4 O# p; g* l
                         continue;  
1 z4 t& v+ S0 l# s! ?) e                     }  
0 z2 J& V1 ^3 d! C2 `; K                     break;  
, ?; S5 c9 L& r8 K3 E                  case '-=':  
, p3 D4 C$ E  i* z; J. E0 `2 F                     $v = substr($v,2);  # L: I+ p! M/ X% }
                     if (is_numeric($v)) {  # ]  v# x( d& L8 v
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
6 k- [. t0 ^5 a+ t2 u, L                     } else {  4 R, g: u, V1 _. m
                         continue;  4 c& K$ W7 A5 E( t
                     }  
: P' N& x$ O1 D! i                     break;  ( S7 F7 N$ e- t+ @: P
                  default:  
% P: m. ]3 d( }                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
8 Q3 F" i) r$ S              }  
  V& a: Q. D; i( D: {, [) V. t- V           }  + r; g8 y( t. j7 |* _) H0 E5 k
           $field = implode(',', $fields);  
# a0 g7 n: t+ S8 q0 p       } else {  
" l/ h( u7 N0 F           return false;  
, e; w+ \% V/ k! |) E- w; d       }  
& {* \1 i% d  b$ T) ]: O! G: Z, `       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  3 o0 g+ j9 o# z3 q# W
       print_r($sql);  
) `& k/ e' h) J+ y2 N6 q% H; l       return $this->execute($sql);  
% z1 r# M- P: {3 n    } - i: c8 j2 }$ f4 Q1 h+ M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。" q+ ?! H9 T4 k3 A/ |, O
: q  n5 ]7 d5 t* r# x: ^
攻击测试:
* f2 n1 u& W; Q7 v! l测试地址http://localhost' g$ y9 \, N5 A0 C( ?
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句3 ~% M4 S# \. g. q( N+ F1 f
4 s0 K3 n% t# q- I1 f! _- y7 b
' T9 ~( K; O$ R
. S* u* }+ y$ r+ p3 U
( Q% ~. z5 u! z4 y. D& P6 ]
4 f4 u4 O3 H+ A, P& s

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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