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

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

[复制链接]
跳转到指定楼层
楼主
发表于 2013-2-4 16:17:41 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
% F% R0 S0 |) }3 Y2 X1 {4 F, c漏洞作者:skysheep
( t0 d! u3 S1 z8 {8 B2 C分析作者:Seay
: D" A: K( f& H& d" h8 s博客:http://www.cnseay.com/
" b/ }# E9 \5 J. g( q9 d漏洞分析:! f2 b% c$ _, ?. d' d8 w% h0 w3 }8 U
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 M6 w$ r# z1 Q/ C9 v9 ~) m+ i9 f& a( E5 o" V

# C7 M7 U2 @  m6 s3 c' W+ K6 \' N 1 o- |. Y* U6 Y
public function account_manage_info() {  
" [$ R, G8 g/ ]( o       if(isset($_POST['dosubmit'])) {  ; x" x3 t9 d9 \5 w" Q! P  |% Y5 w0 Y
           //更新用户昵称  - ]% E1 Y5 m  p$ W9 j6 ~# F
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  # j" I- S3 [6 R) p6 e
           if($nickname) {  5 R' I0 T: g1 ^' K/ l, E; A
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  
- |. c$ ~; D  y7 T. L5 N/ H              if(!isset($cookietime)) {  
0 T& G  G* D9 j# ~5 |1 ^- \0 W; t                  $get_cookietime = param::get_cookie('cookietime');  
, z) T9 m7 _6 G              }  
% k4 @5 z& o3 {8 v              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  
# i" y; t8 E: a3 I              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
& Z/ i& s% f2 p/ @( ~              param::set_cookie('_nickname', $nickname, $cookietime);  
% \2 l( H$ B& a1 R2 a           }  
% X% B, A1 K' T+ C1 U) m) w: s- t           require_once CACHE_MODEL_PATH.'member_input.class.php';  
! @7 f' B( m6 ~! m           require_once CACHE_MODEL_PATH.'member_update.class.php';  
, B0 M8 M7 _" n2 K  `           $member_input = new member_input($this->memberinfo['modelid']);  
$ v2 s% H! b2 X& F           $modelinfo = $member_input->get($_POST['info']);  $ q+ d) p* ?1 C# u# b: c  o
           $this->db->set_model($this->memberinfo['modelid']);  * v/ K8 I& O$ g+ K: n7 x8 {
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
! J  y. r, E0 R& c           if(!empty($membermodelinfo)) {  5 e8 o7 |, [6 u5 i
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
4 s: l" ~% q! l+ J) G           } else {  
1 \+ \& A% Y# o; L9 M              $modelinfo['userid'] = $this->memberinfo['userid'];  0 b, S8 h6 J8 T% ], ]0 Y7 V! E3 T# B
              $this->db->insert($modelinfo);  
' [# T4 r' \) Q           } + R/ y' u* U; J
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,  w( h( n5 a) a3 }- |' i
在\caches\caches_model\caches_data\ member_input.class.php 文件中:. p- f) w" }' ]. r3 u
5 ~1 L2 b  I) Q. u

9 [: C% x& P; A* y3 P 2 f7 z: F% Y& B( E! J& a
function get($data) {  
/ U4 {2 D6 ~. N5 S1 n6 r2 l       $this->data = $data = trim_script($data);  9 p+ K7 {, r. E* r
       $model_cache = getcache('member_model', 'commons');  + S$ W8 y) ~4 b2 `( v% X
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  0 G* m$ _! _( ^1 V% a
       $info = array();    K; Y; Y4 h' E! \8 N- a
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  5 t2 I! N8 I( J2 v0 n
       if(is_array($data)) {  - I, W1 o# C, _6 b' n
           foreach($data as $field=>$value) {  , a* {) x+ p% ^/ {7 D: ~
              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
3 ~' T. }3 a' c              $name = $this->fields[$field]['name'];  
9 Q% y$ ^) |6 ]! }              $minlength = $this->fields[$field]['minlength'];  ) l4 e$ r* O9 i. C" v
              $maxlength = $this->fields[$field]['maxlength'];  
7 f/ i% o7 R" X7 N0 K& p6 @              $pattern = $this->fields[$field]['pattern'];  1 V7 Y$ f' Q; _. u! t
              $errortips = $this->fields[$field]['errortips'];  
0 T/ d( v# {5 d* d6 _! o! D              if(empty($errortips)) $errortips = "$name 不符合要求!";  - Q, ~! D3 I9 T# T! G4 M( B- T4 T
              $length = empty($value) ? 0 : strlen($value);    E: m% a7 l; C1 V0 q: G
              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
2 t3 E. ^9 ^; X- |              if($maxlength && $length > $maxlength && !$isimport) {    ]1 e! r  f" m$ d# N; b
                  showmessage("$name 不得超过 $maxlength 个字符!");  . I$ W! s) Y0 I1 g! n3 K
              } else {  
+ P; V# x1 [- e# y                  str_cut($value, $maxlength);  : U5 C% R- c6 J5 o3 y# c, Z
              }  
+ q/ @8 K3 I3 K8 o1 ~* r5 ~' m              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
7 }  m; O% Q3 C                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  7 ^+ w; m$ D# \  L# O
              $func = $this->fields[$field]['formtype'];  
& c3 K5 X/ r( O% {              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
! r8 m  R# W6 l# m) G8 |, `              $info[$field] = $value;  5 j) B* Z- V# ~' n+ u
           }  7 d+ B4 l5 v& `0 j7 M* w
       }  . N, n7 U' h$ g, e  k
       return $info;  
, W% ?2 {; \) r6 P: S, E    } 7 M2 @& r' a; n2 d) K$ }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
' ?4 |. {7 j0 d$ ^1 }$ h. f- [( n9 n6 r! L6 u
再到phpcms\modules\member\index.php 文件account_manage_info函数
* A% X7 U" [3 X7 j) i9 a* f5 f$ H过了get()函数之后。
; I3 @- A" _# O" B. K7 N/ m! H- A5 T( r' I3 ]% H/ S

# C' A" W# \4 h: A4 _4 K$ L$modelinfo = $member_input->get($_POST['info']);  ; z1 m  F% y9 z; J
           $this->db->set_model($this->memberinfo['modelid']);  - i/ }! ^5 h4 X& I& P1 l
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
7 w+ f1 T/ y& e+ J           if(!empty($membermodelinfo)) {  2 d5 k4 H: p8 S3 y6 Y- G( E' o
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  * L$ l9 e! B8 D5 l) Q6 ]3 V- p
           } else { & m' @8 \( l- j) G
直接带入数据库,update函数我们跟进看看
% M6 q! H6 D" \( c% b. T
# X% c9 L2 c/ `( r- S4 ? ) R9 V  h# H( X2 m$ Z% l3 P
public function update($data, $table, $where = '') {  
8 s, x3 W% y5 D6 Y# F; S       if($table == '' or $where == '') {  
8 l! J8 j* y: \. P! Q           return false;  ; S7 j; K/ @" y$ t  A
       }  
; O" e" g! m! E       $where = ' WHERE '.$where;  5 i: P  s) y3 Y1 M8 M8 o* e5 C
       $field = '';  9 v! s4 @2 q( ^: j5 {% ?: a6 L
       if(is_string($data) && $data != '') {  
) I, H/ V: I% U           $field = $data;  
6 C. M3 X( k+ }$ m. a( p       } elseif (is_array($data) && count($data) > 0) {  ! A, D' O+ ?5 ~" z" X8 B( Z. v; n6 R4 a2 L
           $fields = array();  
5 X, X& ]4 z3 ^3 L, z$ n8 x4 {; Y           foreach($data as $k=>$v) {  
$ ~& e& R) l* y3 l              switch (substr($v, 0, 2)) {  
+ J; L, v4 {6 M) H# s* X                  case '+=':  
% U+ d3 k2 p3 p  A( I& }. L                     $v = substr($v,2);  4 _1 o' I' z1 x( j% K3 _1 C' D# s/ g
                     if (is_numeric($v)) {  
3 ]& v; }. X3 G1 w, x1 Y                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  
# _% w* T1 c, [' O                     } else {  ) f( J2 F# Y+ j: P% e& W
                         continue;  % e8 N2 X( W- D+ h
                     }  # N0 s# T* L+ Q6 o! Q- |) A: K' E
                     break;  
4 X/ ?# \* Z# l                  case '-=':  : |, u  D, ]$ N0 g; B, [- z
                     $v = substr($v,2);  
+ L- @: Q$ x- V7 M0 D( Z( @                     if (is_numeric($v)) {  
! U! e7 Y* W  J" M$ G& |' T' w% x+ e                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  8 ^- g$ ^+ l9 Z
                     } else {  ; Z& h& h( M$ b* y$ F  N' P* @0 S, G
                         continue;  ) f/ _( N( x9 |( ?, g! Y9 N  ~
                     }  
6 v' z0 Q+ r- ^+ I; N                     break;  
: u3 N/ y0 S. j: @$ p8 C# ?9 d; q                  default:  
4 K  S" P: O, X  t% r6 y# H; Y                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  
. h1 y6 b/ G2 g0 S4 A# B              }  ; W* ]: L2 D- }* o
           }  
5 C) I3 ~- A, ~0 G           $field = implode(',', $fields);  
% Y" Y9 z* Q9 z+ z/ r; C       } else {  , F+ ~2 |& {- Y; i
           return false;  
' e* R: N2 c' P6 t. c; r$ W       }  
2 d6 G& l# D5 z' o0 e* E# n4 k( R       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  9 e3 l5 ^1 }) f6 X
       print_r($sql);    x7 F/ e9 b# S' B0 Y# T# O0 ?
       return $this->execute($sql);  
% |7 O% u/ k3 F0 W4 }3 R# ]! q    } . p6 t  @+ M7 ]3 ~& ~$ ]' f8 K  a/ r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。' N' x7 Z6 N- E. O
9 {2 s- q9 r) C* u
攻击测试:( Z  f# D/ {1 Q/ u2 l
测试地址http://localhost
. h$ F& ^! O. n  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
# G8 V+ x, P& ]! }4 ]+ B% ^* |3 m& x9 s9 I& U5 R
$ H  O" }8 u, n3 R' g; d6 h5 T
# X' ?& i! _' O* `! s. I& \3 m9 ]8 w
- E! X. a& v1 |% w

& U& W6 c" `' ~3 E! E

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

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