|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告/ U* L- W, `9 P) F% B* m
漏洞作者:skysheep
: M: W1 N1 Y/ k. O! V2 s/ D分析作者:Seay. z5 a6 H0 u% p
博客:http://www.cnseay.com/# n# J# g. e+ Q7 H6 g
漏洞分析:
: G4 H7 @& ?; m( j 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。: a/ ^% h; X8 d0 g8 I- l( D) L
' O, i7 w8 x* r% s! J: [
/ \5 l' L' f+ f- K8 Z: f5 Q4 h
4 W6 C* D# ` a4 P: j, M' V
public function account_manage_info() { / G+ j4 P1 v6 z+ [6 }4 f
if(isset($_POST['dosubmit'])) { . q9 Y- A$ K; H. Q2 ?1 X
//更新用户昵称 9 ^8 U- ]4 d U0 v& W% A
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; - W9 I2 d: Z) C- f: G' ?
if($nickname) { 9 I5 x* a! q" h6 n: p& M( K+ p
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
) L! g% t4 b8 c3 y2 G( H& C- W if(!isset($cookietime)) { + N! c/ D" n3 D% ^6 L( ]$ I
$get_cookietime = param::get_cookie('cookietime'); ! b5 |2 e7 S+ G$ u# {/ {) N
}
/ \( U1 d7 W: x4 q$ i $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
& c' P D% ]% \1 F% @ $cookietime = $_cookietime ? TIME + $_cookietime : 0;
o( T$ u9 V! b/ j param::set_cookie('_nickname', $nickname, $cookietime);
- ^! c& q1 E" H2 J$ T! s }
5 A+ g9 }8 z- w4 j% W: [ require_once CACHE_MODEL_PATH.'member_input.class.php';
0 q! {% X2 G3 L( k: |* O require_once CACHE_MODEL_PATH.'member_update.class.php';
' W% K" R& |! y. D $member_input = new member_input($this->memberinfo['modelid']);
; T' f5 j6 K& V, T $modelinfo = $member_input->get($_POST['info']); 5 \5 \" J7 b$ O' u# ], K
$this->db->set_model($this->memberinfo['modelid']);
* y- b! j6 p1 A $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); D: R; g& K$ E V% e; [ O7 H7 B
if(!empty($membermodelinfo)) {
& { Z/ F; Z1 m; @# a8 w* D, O/ E $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
: I/ }% s. U) W j# L. N( ]! v! k9 E } else { # y# T- Y1 H9 N8 l0 ]5 |+ x3 H! s
$modelinfo['userid'] = $this->memberinfo['userid'];
- W- [0 l' J$ ^! b $this->db->insert($modelinfo); " R6 t; ^$ a! F+ @6 k
} ! o. I% M U. b- l
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,! Q5 } h# y, l
在\caches\caches_model\caches_data\ member_input.class.php 文件中:3 g M: S/ P9 `2 h9 }% M
, P4 g! y! x" F0 M! O4 L e# k
B- z, M" p" V$ C" ]
0 p- G+ I( q L+ }* K: p
function get($data) {
( ?6 f6 {9 S4 L2 v $this->data = $data = trim_script($data); 7 N0 ~! y- Q. g3 ~! D9 l# X
$model_cache = getcache('member_model', 'commons'); 6 o% a! H& m0 |5 u, U, b7 f. M0 _
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
+ h- l# h) W$ r8 ~6 H $info = array(); 7 d; `' g4 H' w4 f5 f$ b- B
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ( `2 @$ U$ N( `- [& ?- F
if(is_array($data)) {
* A- ]- ^' n+ _0 w foreach($data as $field=>$value) { ; f- e7 Z* [% X; G4 s. D
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
8 \4 e! D3 |8 O& O0 b/ \ $name = $this->fields[$field]['name']; 8 g+ m3 l, p8 n( f* a) d
$minlength = $this->fields[$field]['minlength']; j* e' N& Z. _9 O0 K
$maxlength = $this->fields[$field]['maxlength']; 2 j) y7 q3 c" C% C7 N, w/ s% @
$pattern = $this->fields[$field]['pattern']; 9 b$ |0 P( V$ _. G7 Q$ `
$errortips = $this->fields[$field]['errortips'];
4 }8 Y- e& s7 C if(empty($errortips)) $errortips = "$name 不符合要求!"; 6 E! P9 |7 m2 B9 r; ?5 K* ~6 \
$length = empty($value) ? 0 : strlen($value); - V) J" C) _. p) _/ f
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
& B+ g+ o( @# i- H3 A& f if($maxlength && $length > $maxlength && !$isimport) { 5 Q$ _9 u M2 j* o* i) F; t3 H
showmessage("$name 不得超过 $maxlength 个字符!");
( M7 m+ j1 I6 `( f( Q, ?% ~+ _ } else { 1 {/ O& {& y/ |& K
str_cut($value, $maxlength); $ Y0 m6 r/ l- ^8 ?7 {! J# }) Z
}
O9 {6 k# P) ?+ [1 ]! @; X, u& J4 B if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); - u$ E' O! i: G' {: ]$ u, i
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); & {5 }$ d8 e/ y/ C1 |
$func = $this->fields[$field]['formtype']; ) t9 y! N f( X" I! m5 p4 f
if(method_exists($this, $func)) $value = $this->$func($field, $value);
( M9 x. _* o/ H; l $info[$field] = $value; ; P7 l* V. f+ _/ P, @
} 1 e" w& m ^( H7 u/ G. |
}
/ d( v5 @: v d) [7 S0 x return $info;
( q1 G$ h4 m) y }
8 n* [2 i4 D$ O- b6 Dtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
# X* b+ ]. o% E+ H5 J6 F
) r" n- i1 g- R! m5 y$ x. ^: o/ w再到phpcms\modules\member\index.php 文件account_manage_info函数
7 ~% c, U- W6 f1 g过了get()函数之后。) A, M6 R9 J! f' e: D
: m/ T0 t* m9 ]1 |2 b. Z
3 A/ L0 x' W4 Z- ^4 c$modelinfo = $member_input->get($_POST['info']); - ^2 ^* Z. l# v I) p( M
$this->db->set_model($this->memberinfo['modelid']); 6 _) d' p& L$ Y B
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 4 N' J% q4 A6 M8 _7 ~5 ~0 a5 e- I
if(!empty($membermodelinfo)) {
$ l7 y P- e' F1 ^4 b) y3 Z $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ( Q3 b% Z' {# t& Z3 i! r9 P/ s* w: L
} else { " ~/ `: q m y5 @
直接带入数据库,update函数我们跟进看看
; e9 i8 _" F/ A& G* `1 Y* j( ` N, n# i- h" n5 }' p2 a
$ C* J9 a" j, U4 l) g
public function update($data, $table, $where = '') { 5 ^4 b/ \. c: X, R
if($table == '' or $where == '') { ) \% I8 {( r1 r# P% S8 X' Q
return false;
4 n2 ~ g- f f. ?9 ?2 a } 2 q& @6 y: u* |$ F1 C5 y5 ]
$where = ' WHERE '.$where;
: Q2 |1 `3 z. B, s $field = '';
$ {. K7 q3 b- o- M% v if(is_string($data) && $data != '') { ) j* L4 c# @ P+ n: W& u; j
$field = $data; 7 P8 T) P/ P8 e
} elseif (is_array($data) && count($data) > 0) { 6 v, p d) ~! L( v+ x
$fields = array();
8 B: }3 ?- \* _ X1 g* u* m2 z foreach($data as $k=>$v) { m+ W; Z# o4 ~: p1 g
switch (substr($v, 0, 2)) {
, C1 P C1 D0 y* v" ^) u* U! ?8 y case '+=': 3 F/ g- b3 t3 E& I
$v = substr($v,2); . m/ M6 s0 u4 @3 k$ Q2 a
if (is_numeric($v)) {
8 `) i+ R4 l/ e( f9 I: p; B $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); & R3 Z! ?% l$ W" R" p& I
} else {
2 {8 w- L6 f* @! R, o6 Y& l) _0 j continue;
1 Q0 a) l# N7 `3 w }
* c, W6 D9 ~% L8 H8 d8 O break;
! D( F4 D: q( U, u* |# K case '-=':
2 X/ O" p3 Y5 i8 l* i, y7 S! y $v = substr($v,2);
5 h7 i3 m5 W& `# o if (is_numeric($v)) {
3 e+ y% i _2 T9 G E! p $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); ) D, j$ _2 R) }: h$ ]/ }
} else {
8 c# X; w z" p. H) x continue;
# f, p5 ]$ J8 E% q( U4 m } . |& y# W( M* j7 X1 g5 |
break;
$ u/ Z) G" U7 P9 U9 K default: % J( v& ], M$ k5 g, c
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
; x. ?* k( L8 h5 N+ B }
5 m- Y$ e' F! n) D } : i; _. v& b( X3 O; u8 k/ a- h4 U
$field = implode(',', $fields); 7 T$ j7 F6 t# o8 g( i8 x/ X0 K
} else {
, [" S" D( V1 }) `8 [ return false; 4 f! s2 D4 m6 T
}
' D- q* c1 N# W1 z, X& y: x$ h" R $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
, r, L4 S' p0 A# I- |. C print_r($sql);
/ _0 z8 s* Q: K7 | return $this->execute($sql);
/ i0 [* ]3 K6 m } 3 l. U+ K- ^7 M
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。- Z: T% @* r- l" ~+ c
" J( r3 p. H8 k# f5 |# G; g G. ~攻击测试:( g7 O" ?& m; `, m0 n h( X _
测试地址http://localhost' P& C3 M! [* r8 h- ]
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句9 j' @6 y9 j* W$ G- u
! y5 V7 Z( M6 q o# o, r1 ~
* O) w" R5 F0 E+ ^9 i2 u2 K
$ J# O- B- X1 l: j9 G; c5 o+ p2 V5 |
# V$ m$ E& R6 ~% J
9 Q1 H8 K# _6 }0 b* k$ w |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|