中国网络渗透测试联盟

标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告 [打印本页]

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- {) m6 u7 Z& ]" u/ j' M漏洞作者:skysheep/ D5 s5 |$ t' o' W9 q
分析作者:Seay0 ~' I) u9 o: a! i
博客:http://www.cnseay.com/
1 ?, `6 I8 R2 g) w2 z漏洞分析:3 N. I; ~+ I1 ?! m5 ~' b0 y  m8 S
  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
: c* @9 j3 W; k' J* {( A6 ?" y! R
2 @. p: G$ a( J6 [' L7 U# n2 Z2 q' m* B
! }" \) m9 g5 d8 I
public function account_manage_info() {    f" u) T! D7 m- A3 ?! A/ V
       if(isset($_POST['dosubmit'])) {  
" g" A) o) S! o$ O0 Z           //更新用户昵称  6 N* |- m5 |  P: _* _
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
$ |3 R% Q1 G4 @$ C7 r           if($nickname) {  
) D7 S( Y/ g  Y; _+ x4 J8 Q9 l' j3 p              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  ' _4 r  \* o$ S/ R6 c3 D
              if(!isset($cookietime)) {  
3 q; I9 T7 I$ D; S' z2 P' I                  $get_cookietime = param::get_cookie('cookietime');  ( \" r! [5 ^2 T% [0 K$ S* R! [
              }  ( E0 ]) d+ ~0 i
              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  # N% T# g" R$ Q$ R2 X6 K! J
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  
6 f1 s0 z' P) v9 W( x  e              param::set_cookie('_nickname', $nickname, $cookietime);  2 |' i# U* w" W6 @2 O; |
           }  " C* f! w) d- a/ o% |
           require_once CACHE_MODEL_PATH.'member_input.class.php';  # j( z2 L4 W) z! X" _
           require_once CACHE_MODEL_PATH.'member_update.class.php';  ( m5 _2 d' i  v, Q
           $member_input = new member_input($this->memberinfo['modelid']);  , F% s0 C' K) F; Y  N5 ?3 J
           $modelinfo = $member_input->get($_POST['info']);  
+ D! H8 y3 C# q9 f( @; m# U, y           $this->db->set_model($this->memberinfo['modelid']);  0 ]0 S$ M; e% ^& `5 U' _
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
2 V4 p5 r! N3 |           if(!empty($membermodelinfo)) {  ' ~! d% h1 g- r/ p- r. }5 d
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
, ]* w* m# v4 S; v# h& X5 R           } else {  / ^! B# n# S$ G& B4 m- a
              $modelinfo['userid'] = $this->memberinfo['userid'];  
; o2 ~0 i" [. `+ y              $this->db->insert($modelinfo);  
" I* O% w( B: D0 O           } - K9 L/ s/ f* z! s; f6 E& _+ v4 S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
+ G# @" Z: s5 l5 W5 ~, c% O在\caches\caches_model\caches_data\ member_input.class.php 文件中:. l# j2 g8 ~; }# M! E6 y

/ f( ?; R  _8 p$ ]* s
1 p; V6 M( ~) V. C) O% P
7 r/ S- y- V7 l, x6 J6 a) rfunction get($data) {  
( j7 y+ c% n" G4 f: j/ F6 k       $this->data = $data = trim_script($data);  
4 O: h9 ~" H9 C0 s/ E       $model_cache = getcache('member_model', 'commons');  
( n2 w1 X5 J/ t" I2 Y: [" o8 p       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  " n+ f" g: X$ Y% N' A  U! R
       $info = array();  9 R* U2 z" s! }  U
       $debar_filed = array('catid','title','style','thumb','status','islink','description');  + t( Y0 g8 j9 V8 @" O. ^
       if(is_array($data)) {  % v+ a  L5 o3 ?% z- W* E2 J
           foreach($data as $field=>$value) {  
  s  d' U$ I$ ~              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
8 g6 f5 S/ B/ ?. |$ a( }( d              $name = $this->fields[$field]['name'];  
+ L% v3 d( G0 X7 z' s              $minlength = $this->fields[$field]['minlength'];  4 H9 k- i$ T( R* p9 W4 T$ d
              $maxlength = $this->fields[$field]['maxlength'];  
4 i% b/ F) t* q/ \3 j; Y. p              $pattern = $this->fields[$field]['pattern'];  3 i$ X! X9 N! c; r- w
              $errortips = $this->fields[$field]['errortips'];  & z8 `- ^. ~! x0 K* S: e2 j
              if(empty($errortips)) $errortips = "$name 不符合要求!";  
* k* `7 }7 [: o              $length = empty($value) ? 0 : strlen($value);  
+ V) x/ N" Q& F, _( j9 _+ |8 E              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  
& H3 L$ w6 E; d7 N+ \3 b4 g7 i              if($maxlength && $length > $maxlength && !$isimport) {  
$ l) J5 f* ]8 J) c2 S                  showmessage("$name 不得超过 $maxlength 个字符!");  + M# `  _5 f( W5 z
              } else {  + {6 Q5 Q8 {5 |$ M
                  str_cut($value, $maxlength);  
+ \! Y- r5 h" E5 B              }  " y5 B7 b/ U' _
              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  
/ `) N7 m0 m; E/ X) ^  x. I                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
) T4 K8 W$ y$ g, b              $func = $this->fields[$field]['formtype'];  
$ P, k& a" _$ r0 o' @              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
0 G+ |+ I+ N+ \6 i+ g4 w  I              $info[$field] = $value;    o' t" y3 j3 _  Y  Z/ R1 F. n- K
           }  
! Z: K0 P, j. ^3 L) V2 H, z       }  9 F6 t  R! p9 R& Q$ r4 a0 A0 ^- X" g
       return $info;  
! c& A4 w  D9 f, [3 J1 s9 {    } . p( h$ @- n1 C5 t
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,7 [. b  `, K  I: U. ?
; B  A6 C; U$ z" W' n% u
再到phpcms\modules\member\index.php 文件account_manage_info函数
' a7 V9 _9 S# [' ~  m2 t过了get()函数之后。% n; F% p& P" y3 z; F

: c" N- o+ e$ D1 _' z5 q
) v4 {1 [: G/ W$modelinfo = $member_input->get($_POST['info']);  
  a2 X8 d# C& ^. F, u: R           $this->db->set_model($this->memberinfo['modelid']);  
* Q* j( B$ O: h; L2 s2 Q           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  & G7 r! a: }9 r. M+ n5 X: o1 ?
           if(!empty($membermodelinfo)) {  5 N+ i0 x: {1 F9 @
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
( O) c. f2 t& s! W           } else {
6 o0 J$ c; A- R: @0 {- H直接带入数据库,update函数我们跟进看看) Y5 f" J+ {. q  O0 C7 Z* T5 B

  x" ]6 N2 W1 g+ M" d
+ G6 W6 m0 r5 f( rpublic function update($data, $table, $where = '') {  7 C# A% ]# |0 _' W7 e8 I
       if($table == '' or $where == '') {  
7 ]2 f1 w/ e) c8 \           return false;  
0 a8 A, B( K6 X/ k  z7 c) g! _       }  
& A" b2 T" [! W+ c- m       $where = ' WHERE '.$where;  - q9 z3 o9 j- Y% U+ r
       $field = '';  " N( A8 B8 ^& Y/ o
       if(is_string($data) && $data != '') {  
' @9 |, F% B/ x: `7 f: {- D           $field = $data;  
0 c( T( w! u: l$ B+ c  V. a       } elseif (is_array($data) && count($data) > 0) {  
( I# D9 S+ n7 z$ G. W9 ~' k           $fields = array();  ' j% d6 e2 U" d0 I# @
           foreach($data as $k=>$v) {  
: i' ~" r/ Y3 h6 D1 l( X9 p% R              switch (substr($v, 0, 2)) {  
/ Z8 C; k/ F5 t                  case '+=':  
% ~2 r" t: k: @0 N                     $v = substr($v,2);  
4 z2 m1 P' M3 W/ {) Y                     if (is_numeric($v)) {  ' c; u; c4 Y2 ~8 w& h! M" o
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  4 R8 @3 Z1 `' H. r1 ]* }
                     } else {  
6 G. k! C2 w2 H  C                         continue;  7 c) A3 {# w# F" o
                     }  1 I! K+ \1 l7 Q1 V$ d6 t- Q5 J0 \
                     break;  
, R0 z5 c, f* ?  p1 R+ Q                  case '-=':  4 x1 T5 [6 n6 |9 f; g- j
                     $v = substr($v,2);  2 {/ j0 p# p* n  a% E8 D
                     if (is_numeric($v)) {  * j! d4 i) Z! ]; W  M
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  0 k: u( f" \1 {0 M# c
                     } else {  
* C8 i' v7 U" ^$ H+ E/ s6 D, X4 |                         continue;  , P9 t: V0 A2 `( s  p4 ~
                     }  : B# v: F* u9 `, Y9 x' t
                     break;  0 a, ~( y) T- |" Q6 ]6 T2 U& T* J
                  default:  . n$ D5 U& r' f) V2 e; f5 P" p
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  . F9 K: J: [) `2 Y5 Z
              }  
6 b1 z6 t5 u/ f, y2 w3 L/ W- _  C/ r           }  
! I6 t* z: z8 \! A2 r           $field = implode(',', $fields);  
2 c8 d" ^. ^& k; a       } else {  
* Z7 ^& e4 A2 _" {           return false;  % N& \8 X3 E5 i& ^
       }  3 i; {9 x7 t% k! A5 \) y5 n5 E
       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  2 w, s& B2 f/ f$ G& b4 B
       print_r($sql);  
" M. A5 ]) K0 A) a, [       return $this->execute($sql);  
5 W- L4 a9 f$ j; ?    } ! L( Q5 m6 U4 r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。9 s* v- J8 \  u* B2 }2 w7 f/ n( Z

' r% e& i! S1 G6 v攻击测试:  d! T: P! [% H3 K9 A
测试地址http://localhost5 X! r0 `7 {  p8 e! |, A8 O. ]
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' ~/ J8 V3 Q! L# {
% q- Q( |* x5 \! P6 F2 D) K1 f1 F[attach]179[/attach]
- o2 S+ Q$ t/ v" y, W# a3 a) m% [+ e7 V' _; V( {6 C. @  [+ ~
: H8 ^/ L$ R2 c2 ]9 p, G
[attach]180[/attach]
: l( U3 L! J- {0 X# F




欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2