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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
: {" x0 T% e% A/ z+ g" Q漏洞作者:skysheep) I0 X  W- B: W+ A1 S+ a
分析作者:Seay
1 s7 l2 |0 @5 ^0 {博客:http://www.cnseay.com/
& S7 n1 j% `/ ~漏洞分析:! v# o( R1 O: y# w  {4 x  }! R
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
1 c/ ?! d. `7 P
3 q% E8 {% {4 N$ ~0 _9 M
# W, }9 @; z+ o$ y9 { 1 _9 v' e4 @4 s+ Y. ]& K
public function account_manage_info() {  
1 M6 U$ d5 C/ L# w/ H# w: ?: A1 i       if(isset($_POST['dosubmit'])) {  
( O" W. w4 {! A9 q           //更新用户昵称  
- h; o1 ~# R3 v9 s, W           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
( n4 r: C% s# C- U           if($nickname) {  ; C0 V$ z7 A4 }
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
" [5 W: w5 m# N7 i, M              if(!isset($cookietime)) {    Y0 J! J$ `, ?9 z* l4 K% Z
                  $get_cookietime = param::get_cookie('cookietime');  $ j8 S1 d+ e4 ^, n
              }  
. m, w2 I. E! r( ^              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
1 B: B1 H2 T' a% c6 ~, B              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
1 i% C' B! a  @, V  L2 e) [              param::set_cookie('_nickname', $nickname, $cookietime);  
$ e, S; {( K$ ~+ T& w           }  9 H! g) C0 e" S) C. I
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
1 f5 p4 t' d" g) S/ P- G           require_once CACHE_MODEL_PATH.'member_update.class.php';  
9 k1 c) @! n+ _) X           $member_input = new member_input($this->memberinfo['modelid']);  
% F0 V) ~1 Q# u9 [           $modelinfo = $member_input->get($_POST['info']);  . {7 _0 i! W* }5 J/ C& o8 y; ~
           $this->db->set_model($this->memberinfo['modelid']);  
" W9 M4 v* b" f0 h6 c. r           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  / @3 F0 D+ \# F& H+ T: M
           if(!empty($membermodelinfo)) {  
. ^0 P% F1 a& L* N1 M: S              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
- Q8 d) R$ D% V( O% i           } else {  0 M, x5 i; S' v+ J+ u9 L
              $modelinfo['userid'] = $this->memberinfo['userid'];  
: m1 N* f6 @# P              $this->db->insert($modelinfo);  
" ?! g  m6 G" r) B           } - \  N/ {7 P4 D) }0 K  ~( f
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,& t1 `3 b- t2 n- J" O5 d; C$ A) x- _
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 F$ w1 p6 j8 h" h+ e& N2 S

: |9 Y- W0 `, J" H
- E/ C( p$ S* a, h8 X. _; w3 T( s ' I, `* _7 Q8 p  K. O
function get($data) {  4 _$ B. O, w6 |7 a5 _' j+ ^4 q
       $this->data = $data = trim_script($data);  8 n* C1 \7 u0 M3 b0 M. z5 g( V
       $model_cache = getcache('member_model', 'commons');  
2 M; V9 A. [% e7 Q; p* r( e       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  
6 o; O. s/ k5 J0 f2 L       $info = array();  ( `9 S+ Y( S2 L* S  ^- ~
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  ! r! c) k) d9 E4 a2 J% Q  _
       if(is_array($data)) {  
) g& `+ C" n7 X6 \2 q  D           foreach($data as $field=>$value) {  + @% X( O% N2 B6 z6 M
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  8 a+ k4 q5 V+ ^7 \$ `
              $name = $this->fields[$field]['name'];  
3 N% d/ d( [% K- g% l' X6 ~% V; `4 F              $minlength = $this->fields[$field]['minlength'];  3 m) B$ c/ u% x, M1 c9 k1 R  }) u( H
              $maxlength = $this->fields[$field]['maxlength'];  
- n$ o0 o% ~7 z& o5 \  `              $pattern = $this->fields[$field]['pattern'];  $ `9 M0 d# r& p8 Y" N( F0 m
              $errortips = $this->fields[$field]['errortips'];  
) a  B. F- r, ~4 p4 f: `              if(empty($errortips)) $errortips = "$name 不符合要求!";  " x1 [& L' f" g, X
              $length = empty($value) ? 0 : strlen($value);  
# p2 u+ ^6 v# {& z              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  ' y9 d. d1 g! x# m4 }  K, f1 o6 M
              if($maxlength && $length > $maxlength && !$isimport) {  3 `5 s, ^! V* Z, U- Z! u
                  showmessage("$name 不得超过 $maxlength 个字符!");  ; E/ I" l, U. {
              } else {  
3 i- S4 O& @+ o$ W& \, V                  str_cut($value, $maxlength);  
3 M0 _1 Q: m7 a              }  : ^/ p* z, ]* M% ~
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  / z& T) s& ^7 M1 f) n2 f7 y# A
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
' s" u- L5 i# E8 w& F              $func = $this->fields[$field]['formtype'];  
/ R0 p6 i8 B# J$ O' e              if(method_exists($this, $func)) $value = $this->$func($field, $value);  ! L3 r% t5 K& v. Y/ ~
              $info[$field] = $value;  5 i4 f: ?% v9 A% o5 O
           }  
$ {$ i4 }3 |6 v7 B' J4 g       }  1 S0 h3 g: ?! v; t: S
       return $info;  
8 v# L) G7 {2 [" B$ ^7 o    } : \! ]+ D. {4 N. s' }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
! B: i4 S" |; Z9 ]5 A- D
+ m4 m2 |( j9 ~% U再到phpcms\modules\member\index.php 文件account_manage_info函数, q( `$ Y8 K2 M" o7 t& O  S0 }+ h3 }
过了get()函数之后。
5 U  M3 _; c* r' Z4 R, Q+ m5 ^" z7 A( P7 _; i7 t) _( U
* o  O3 C- d5 k  i5 }
$modelinfo = $member_input->get($_POST['info']);  : B' h% d+ T& c, e2 G
           $this->db->set_model($this->memberinfo['modelid']);  
0 A! ?( Z4 y! y6 Q# {, o& z           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
6 [7 m, v8 u' _; I7 i/ i8 N0 H           if(!empty($membermodelinfo)) {  0 A0 d9 v7 A& q
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
; z+ O6 c# ^' l8 `5 n9 F2 k           } else {
8 B% E/ b, o3 ^& Q直接带入数据库,update函数我们跟进看看
9 U0 b% v' ~# X  A$ o
# r) h2 B9 X. Y9 J( f! w0 u- F' Z
& A8 `! F) y- Y% C5 N& Kpublic function update($data, $table, $where = '') {  % z+ t; L6 {, ^
       if($table == '' or $where == '') {  
* Y* S9 I3 w. z4 `  `2 b; b3 m           return false;  
; w( K4 q! L; t6 K: X. q       }  3 G7 `3 z7 G; Y$ C
       $where = ' WHERE '.$where;  $ P' s( i$ w- N. n2 Y0 g/ r5 W
       $field = '';  
& V  F3 \7 P$ s# Z4 H       if(is_string($data) && $data != '') {  ) F) S* V: X  }) J; \0 w! n; Q: }8 |
           $field = $data;  ! @+ j8 Z1 z; o6 S: |8 A2 a" e! g, s7 e* {
       } elseif (is_array($data) && count($data) > 0) {  3 b8 h. u- k  }5 ?- g
           $fields = array();  
0 _- B$ e/ I; k9 ~' v1 k           foreach($data as $k=>$v) {  
% U1 J! ?& y' K  c' R. {! w              switch (substr($v, 0, 2)) {  
4 f" Q5 q' E- m- p& V% j" J                  case '+=':  
' `& q% s( v% W+ k! d                     $v = substr($v,2);  / x, y. F. _$ [  R
                     if (is_numeric($v)) {  
2 B; ?: A+ I9 P8 b                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
6 \; g/ n' `, r% G; K- V8 X                     } else {  
" y$ {0 z2 `' p* c5 I) u                         continue;  
- d. K, }+ q) m/ \' x( E/ `6 U2 U                     }  5 z* h. ]* }  U& e& e4 J9 b& M
                     break;  * G8 k# y# x- r5 u9 `3 D; A# d- h
                  case '-=':  
% o9 P% R# N' m$ N) r% f                     $v = substr($v,2);  ! q  U0 v8 |' [0 j  w
                     if (is_numeric($v)) {  
+ h8 q/ R4 D" {                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  
  b. l+ d* x) r$ L* X& T/ P                     } else {  + E' u- @. H0 n# W. J+ r
                         continue;  
1 r! m/ k, |3 x1 P( X! W( V% S- j                     }  " A  S' ~  J. ~) Y
                     break;  
( g! |- x" o8 T                  default:  ( s3 o% z& a( Y( R
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  - t6 e9 `( G  U- H* ~- z! k* v
              }  
- f+ c: w$ Y- A" D7 L           }  4 r7 e( Y% F1 g% r6 [
           $field = implode(',', $fields);  
% l  n, T1 i' v' ]0 W' D       } else {  # F( ?* t2 ^* ^6 R1 J4 T7 `* {( t
           return false;  8 _0 y) X0 A: c2 A$ G# f4 @/ ]
       }  5 b6 l% y: u0 S0 A' i9 s
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  ) K- m/ J* c! n7 C0 `+ U% m& f+ V
       print_r($sql);  
( c0 t' |) N' I: q: Q6 l       return $this->execute($sql);  % s" g5 W. W5 n) C! {! y
    } 9 m! f) @+ |+ Y' f
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。7 f  @# k% S$ b( H: p/ @# w5 `

6 B2 V# I. A) z5 z攻击测试:
7 W& E: B# |( \$ s# V测试地址http://localhost
0 P6 v6 \# }4 b1 y% J$ k  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
6 A3 o- }: p/ R1 s: [$ L8 U
; _- P4 K- |! V+ Y/ H, n
' U, P9 P. h1 V
( b. }4 s% Z. h9 g' P1 S: q/ B4 X3 Y% F
! p) P- W5 [7 K% Q1 Z

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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