中国网络渗透测试联盟

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

作者: admin    时间: 2013-2-4 16:17
标题: phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告/ W. \2 y" E5 ~+ L' F
漏洞作者:skysheep$ v; D7 e0 z+ v5 r5 }* G1 L3 x. U
分析作者:Seay5 |$ l* d$ `' |2 _" Q) l- Y
博客:http://www.cnseay.com/& k! ?' I: m2 T2 V0 t( L6 V7 C
漏洞分析:
* p# }' q% S' w  漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
; W" N: {/ @. a: L4 l" Z  s% r0 z% l6 j! C/ S0 h+ d/ b

% d0 B8 U9 a1 o" Z1 W/ z 3 M- w6 x5 a2 ]! ]7 g% t
public function account_manage_info() {  5 d% b& {* [9 M! y' X
       if(isset($_POST['dosubmit'])) {  * L% Q4 t! n; K" i
           //更新用户昵称  7 v4 x; g3 }+ k$ ?! y2 ?
           $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';  
0 d' S0 P6 H5 Y$ Y5 _, i7 ~           if($nickname) {  . K: M" B% I! s6 y
              $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));  $ n1 k1 B5 b/ h9 O1 E0 j1 }3 S8 O5 W/ o
              if(!isset($cookietime)) {  
5 D0 \9 a' q$ z& O' B                  $get_cookietime = param::get_cookie('cookietime');  ) Q/ A! _# T8 I
              }  
7 A# `: l& l9 `6 i* v              $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);  8 T# n+ L$ @+ M- N6 \
              $cookietime = $_cookietime ? TIME + $_cookietime : 0;  4 M! Z5 ?/ [7 N0 ]* J9 W( Q+ J" t* D
              param::set_cookie('_nickname', $nickname, $cookietime);  1 Q6 w; Z, \1 n0 Q) h! `$ i3 m
           }  / d. n8 S$ m" h+ @( S
           require_once CACHE_MODEL_PATH.'member_input.class.php';  
; V9 a5 k" ]9 X' }9 m           require_once CACHE_MODEL_PATH.'member_update.class.php';  
; @! _+ A; Z  q4 Q" `6 J( p           $member_input = new member_input($this->memberinfo['modelid']);  
$ c4 P( a! N; V) e* ]- A           $modelinfo = $member_input->get($_POST['info']);  : P5 A2 T! q1 _) P- ~; ~
           $this->db->set_model($this->memberinfo['modelid']);  
. C% N. L. T7 F; E* W           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  
7 a' M3 ^2 x* P4 ^0 I7 u5 h5 ^           if(!empty($membermodelinfo)) {  6 K1 ]+ d0 E7 {$ ]- ?
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
0 Z) I' J  g& T           } else {  
3 O' w' o8 z5 p3 `9 U5 p" Y+ C  [  O8 L* n              $modelinfo['userid'] = $this->memberinfo['userid'];  
* W) t6 U7 R1 f              $this->db->insert($modelinfo);  9 G& i( J9 G1 c, w* y/ B0 H& F
           }
" n9 v7 J) O5 x2 d- S代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
3 Z, c5 s( o6 z$ t' W3 x在\caches\caches_model\caches_data\ member_input.class.php 文件中:: Z  Z/ D: b$ A% [1 H0 m1 Y3 t

# E. A& P1 F( [/ H! C4 A2 I: z" R* Q9 K9 {2 h" S
. ^4 I7 a6 W; \8 t. Q8 q/ B# i; |
function get($data) {  - T/ H+ n( N, W- m& `3 `
       $this->data = $data = trim_script($data);  
7 V7 j# V/ i! X  _* i  l5 @       $model_cache = getcache('member_model', 'commons');  9 G9 ^! ^1 h( }* p7 I& r! j% s
       $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];  - C0 \$ ?, A8 v2 G  b8 p
       $info = array();  
  r. O3 s6 T' e9 @- J( i       $debar_filed = array('catid','title','style','thumb','status','islink','description');  7 V8 `! o# W0 J/ a
       if(is_array($data)) {  
/ ^7 q" ~) u8 g; d7 f7 O: q           foreach($data as $field=>$value) {  
9 N! ~; i9 U2 s3 t              if($data['islink']==1 && !in_array($field,$debar_filed)) continue;  
  k: L8 g9 p' @' D6 t              $name = $this->fields[$field]['name'];    e9 K9 D% T- p
              $minlength = $this->fields[$field]['minlength'];  
/ B1 N( e# A; y4 y              $maxlength = $this->fields[$field]['maxlength'];  
% W# W' `8 E  t! b3 ~              $pattern = $this->fields[$field]['pattern'];  : {4 n; M" }2 A0 H" w
              $errortips = $this->fields[$field]['errortips'];  
- ~" L  a3 Z3 v              if(empty($errortips)) $errortips = "$name 不符合要求!";  8 @1 I; ~7 |: t+ ]) p
              $length = empty($value) ? 0 : strlen($value);  
# L1 m6 Q4 K/ y3 w2 x- T              if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");  * v, a4 M  e& i* ]6 p1 U8 B0 Z  \
              if($maxlength && $length > $maxlength && !$isimport) {  
3 z: G+ x  ^  [9 x                  showmessage("$name 不得超过 $maxlength 个字符!");  ! V4 P( `, o7 s# O3 _0 ~: `
              } else {  1 c! u4 _  o" K! p; C9 n
                  str_cut($value, $maxlength);  
0 K( {( Q3 n4 o9 j- j' h              }  
3 ^2 u: ~( M2 S) f# i              if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);  , |. B: r+ l# X: ]: s' u7 M4 L- `1 @
                if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");  
- o5 T1 r! F' T9 g& d$ d1 n# O              $func = $this->fields[$field]['formtype'];  " _( `7 _  d6 }" C% L
              if(method_exists($this, $func)) $value = $this->$func($field, $value);  
7 t5 p8 T4 l8 y8 p7 i% a              $info[$field] = $value;  % s8 N6 d. }* l
           }  
7 N! j( p, t1 y       }  4 I6 D) v3 H- a7 {9 Z$ L* G6 w
       return $info;  + V9 `) ]. t0 F; J  @
    } ! q: p. q) M" D# p
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,) p/ H# z% n3 B) \0 Z" b6 M$ K: W6 u
: G- Q. M1 E# T) N' P
再到phpcms\modules\member\index.php 文件account_manage_info函数
  V& n+ U: O+ w2 n+ x' `过了get()函数之后。
( k0 E; f% s8 O* h3 `! b  q0 F7 s% j/ V+ V+ r$ |

( M2 l( v5 b  f6 D3 J  R8 S5 O$modelinfo = $member_input->get($_POST['info']);  
2 d$ ]- D2 L, S4 O8 q4 B1 f           $this->db->set_model($this->memberinfo['modelid']);  + D1 R+ s) W2 K% l  _. z
           $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));  " Q* S3 P. `$ e4 [
           if(!empty($membermodelinfo)) {  9 ?  k; F, ?! d; {, o6 h$ X) ]8 }
              $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));  
5 L$ e8 R& z) R( h+ e: P5 [' @           } else {
! v; ~  N" x2 N3 _! u7 R- n+ s直接带入数据库,update函数我们跟进看看
& J& y( T/ t) P3 s! I4 l3 ?3 j) a7 U- L3 o2 i
4 R1 v# l: b9 {$ x
public function update($data, $table, $where = '') {  
6 q( I- X- ?4 ]% e       if($table == '' or $where == '') {  
7 b0 I* w% |( Q) s) u! W0 T           return false;  
& p4 S' ]+ @0 w4 e% d* F       }  
( V% J3 `% p- ~1 J1 g       $where = ' WHERE '.$where;  
* P7 @% K5 c9 Y) K" y       $field = '';  ; p1 W% _: I/ h5 A
       if(is_string($data) && $data != '') {  9 X4 |1 C7 D9 T' @  n& S) q7 ^5 U# Z
           $field = $data;  ) `" K; _( ~$ D! i
       } elseif (is_array($data) && count($data) > 0) {  
  G! b/ d) v$ F/ V$ j- F           $fields = array();  
  e& h9 i# e* x           foreach($data as $k=>$v) {  , Y1 D& h6 c' a
              switch (substr($v, 0, 2)) {  
* |! i9 U" x+ }( k3 ~- ]. L5 ^                  case '+=':  
6 N$ |; y) f' o4 {: R  r                     $v = substr($v,2);  $ c) Q# E, d, H
                     if (is_numeric($v)) {  
: D) O0 x' i( P                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);  ; }* q2 i0 k$ L) Z4 U( u
                     } else {  
. n; E6 d* {# [) {: p3 ?' l% F5 [                         continue;  
) y1 t; U* Z9 X& N1 N4 B                     }  
( J9 C" j' v6 t/ z, L                     break;  
: O5 Z  l: T( a/ _$ ]) I3 H                  case '-=':  
, N0 n$ \7 O) c! x( u6 A                     $v = substr($v,2);  
7 o$ }8 i5 ]& {( \) C                     if (is_numeric($v)) {  ( c3 W' |  ^& ~5 Z
                         $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);  - J7 r/ n7 U* u6 V- Z; x) t# Q
                     } else {  3 a7 q" F0 x  X' y  f! o+ G7 k
                         continue;  
- T0 d: n. J! `) p5 O  l$ w) ]                     }  0 X8 V. i5 S0 Y0 \/ a" r
                     break;  8 U0 g& U/ C! w# A9 b" @' ?
                  default:  * Q: W6 B8 v6 L' d& \
                     $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);  ! ]1 v. `1 P$ T/ k: c
              }  
4 _# j- T- ~% K) i; \& Z+ g           }  
# P( U2 B4 K" L' S1 A           $field = implode(',', $fields);  
9 ?5 |/ s6 Q0 R' T       } else {  ' c* q4 U' `& |5 Z6 y$ G2 o
           return false;  ) n' `9 T2 D. `
       }  
2 g" V2 Z) U) e' M8 b9 o8 b. [       $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;  
/ j2 g' Q% K6 H% F6 B" {$ V1 Q       print_r($sql);  
( U" c( M4 s0 _! u- _, K* Q. B       return $this->execute($sql);  - t3 p4 X% v, h9 U9 ]
    }
+ D- k* }% b! l% E2 k% S从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。; H) ?# `1 W* P$ B+ S  `8 H9 f

; Z9 y4 ]+ x" b$ a! S2 J6 k7 F. d6 N. s攻击测试:% ~4 x. D5 p" O/ R2 O
测试地址http://localhost+ B1 o6 y7 a5 N; K* B  \( n- n
  注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" G# @9 _# p! N- v
' k" L, U' @# t' R  O5 r! F1 U( D: p[attach]179[/attach] 1 H$ p0 j9 {6 y" r0 M9 g7 x

, b& {: Q$ t3 A1 `1 q4 }: i
) E' z' v# I0 {  j1 M! M. r[attach]180[/attach]9 G% H6 I* S& v





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