|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
+ L7 L% C+ j z0 X0 [6 X漏洞作者:skysheep }+ J$ ?! ~8 X+ i; n* Q9 }3 X
分析作者:Seay
8 D. E; T+ L I2 n+ c% T& K) ]博客:http://www.cnseay.com/( u5 P6 T0 _" }7 j. B" g- b9 G" n) k
漏洞分析:2 D' D" P }4 m& r. g' d' W
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
2 B `2 }) g2 |+ a6 B' D# g: g+ [9 f. Y2 l
m( }1 [/ K" J. }- `/ `* ~
6 e4 q, ?8 |7 Y. ~1 t
public function account_manage_info() { 7 M! P9 N$ p! s" K) Q
if(isset($_POST['dosubmit'])) { : K6 _# h' z @1 P+ q. {
//更新用户昵称
: J8 W9 P) q1 v8 S% T1 E/ q( ? W $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
) e8 H* T& L3 O2 |: s8 T* e if($nickname) {
- [4 G$ v0 W' \' G! ]! W $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); * s k% B0 \8 ?& |
if(!isset($cookietime)) {
0 b8 Y3 H: R, m- _; r+ f& | $get_cookietime = param::get_cookie('cookietime');
) S6 k5 o* G3 `- Y& _& j, `" C } _$ `8 D# F2 s$ B8 Y& g/ p
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); 9 ^: ^2 Y U ?# C% g
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 1 Y* e. s+ M( }0 g6 j& L% B
param::set_cookie('_nickname', $nickname, $cookietime);
6 Y6 ^; x$ s) x% I }
- r1 v7 J8 `7 }% T+ N8 p require_once CACHE_MODEL_PATH.'member_input.class.php';
# N0 b+ b0 K+ y: | require_once CACHE_MODEL_PATH.'member_update.class.php'; 1 T6 L' u h& } ?0 P8 A
$member_input = new member_input($this->memberinfo['modelid']);
. I6 B/ G% U7 Q& Q! O7 g $modelinfo = $member_input->get($_POST['info']); ' G5 x. p& P8 a6 ~3 C
$this->db->set_model($this->memberinfo['modelid']);
( e/ I( ?' c1 [3 b. h: Z9 w% o $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 x$ |" k* r2 P) K0 E4 E if(!empty($membermodelinfo)) { 3 U* P) Z, H. O# x8 u9 [- X$ f* Y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); : v4 n1 q$ J% ^
} else {
2 `2 ~4 |4 G' u( R. e" |3 | $modelinfo['userid'] = $this->memberinfo['userid'];
0 Z$ f' ~8 V2 ~" c @' v $this->db->insert($modelinfo);
% w% `. R+ X3 s) b8 W1 k, b; J }
; Z& x0 d1 \9 w. m5 I; c5 U代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
5 Q( D! \- K. {& W在\caches\caches_model\caches_data\ member_input.class.php 文件中:5 E; Q, g% f( t3 U: h E, x
6 J: ]2 \& A. ?, d' G1 Q* Y5 x& r: E8 s# D4 Z
" z% D+ I! W' y1 B1 P- |0 O% xfunction get($data) {
% h+ g- @' ?9 w. S $this->data = $data = trim_script($data);
7 p4 n' c1 ]& n: `, J; r4 [ $model_cache = getcache('member_model', 'commons'); + E0 \0 z5 z0 v
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
$ i' b8 C" M9 z7 D/ \9 B $info = array(); * h5 R" l' j4 b# q' M) ~
$debar_filed = array('catid','title','style','thumb','status','islink','description'); ! _* t* v. |3 \/ k3 K$ E! C
if(is_array($data)) { / k6 ^, `6 c( B. f: v+ ], D5 e
foreach($data as $field=>$value) { * T2 l( q+ L4 K" w. V- x
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
9 N+ ~3 e" B; c6 L+ J $name = $this->fields[$field]['name']; 3 p& U P" K& P9 M
$minlength = $this->fields[$field]['minlength'];
' X1 ]: v3 ~0 Q$ B" C$ B $maxlength = $this->fields[$field]['maxlength'];
0 Q$ _" r8 m% g6 X1 P* o+ ~( y $pattern = $this->fields[$field]['pattern'];
. _* m7 F0 c1 G) |5 g' ]: d $errortips = $this->fields[$field]['errortips'];
( _ R; F- A+ x if(empty($errortips)) $errortips = "$name 不符合要求!";
K$ S1 ^2 B, U $length = empty($value) ? 0 : strlen($value); 0 o4 u' K+ ~% J: A5 h& @& O
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
; B4 ^& K U/ G8 z6 G) y if($maxlength && $length > $maxlength && !$isimport) {
& O$ i/ J& h, M2 D- S/ X+ H showmessage("$name 不得超过 $maxlength 个字符!"); ' P9 r8 ]3 ~% q3 A& N6 r
} else { ; I/ n" j2 U: T
str_cut($value, $maxlength);
% r( |% b4 K& n# ^# B1 L }
- }# s* I6 p. U" t# o( } if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 0 }, S8 o' c" w* W9 C3 F
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
$ k% ]2 r4 s) G% S) m4 X2 ?3 h1 x7 o $func = $this->fields[$field]['formtype']; ) E. k! S% v- e8 \3 _
if(method_exists($this, $func)) $value = $this->$func($field, $value); " Y* e3 p5 v( S0 u: v/ Z3 {
$info[$field] = $value; * g9 f# n' F8 r7 M7 J
} # T+ ^6 }& n/ e; p6 E5 e: E7 \4 s
} , r! a0 P2 o$ _6 }$ @" z
return $info; 3 Z9 [, n# m. P$ y
}
+ e6 j; H; j/ j; m4 E& ttrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,$ b0 \! w. [# k
: e+ n5 I' v' { d3 U3 B
再到phpcms\modules\member\index.php 文件account_manage_info函数
( }' e# o; Q' a" y) J" W6 F4 E过了get()函数之后。
$ e! ]7 y) T" Y a4 @( I" S8 C' K; [ @4 n. b5 _9 L: }
) s y1 z% }5 G9 F: x" s$modelinfo = $member_input->get($_POST['info']); 2 ~4 u ]1 @- o6 J% K" V3 G
$this->db->set_model($this->memberinfo['modelid']);
{8 S+ L" ]0 u. g+ N& w $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
5 b) o+ X" u1 X- P# ~) [ if(!empty($membermodelinfo)) {
6 ] b5 ]# F6 t: _) Y4 m5 s7 @ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); % u- t3 X0 w' s9 Z4 X
} else {
: u$ Z2 ?4 x" N# F2 z& w1 C" e8 }8 f直接带入数据库,update函数我们跟进看看
; h7 U' x* x# z3 a3 J3 d
, u' ~- `, Y3 t 3 M% }- Y& ~% _- z
public function update($data, $table, $where = '') { ) u$ f2 l# E9 K1 c1 c+ _3 k, y
if($table == '' or $where == '') { H' h5 S/ T. @$ p( X6 D; W
return false;
5 k* I; f. U* b' {+ i2 r } 1 @# Z* ^( T) J3 M' z4 h- p2 u8 Z
$where = ' WHERE '.$where; + l0 b) g& V/ O' Y. s
$field = '';
) F4 G1 H" b" U3 s7 U5 u" b if(is_string($data) && $data != '') { % R. G; K" A& X) ]' x
$field = $data;
6 U; U8 {% I6 ]9 a1 f2 I } elseif (is_array($data) && count($data) > 0) {
5 ?8 L5 \6 s9 y, Q $fields = array(); $ n' J+ |, |% R/ g' T$ Y
foreach($data as $k=>$v) {
5 G" C+ g q0 K7 t( [ switch (substr($v, 0, 2)) {
: a5 f( o! ^& _4 d* g8 J, d case '+=': ) e; S0 }. e9 y9 v/ O
$v = substr($v,2);
3 s3 ]7 C% W% `8 j# A8 {( ?# U4 ? if (is_numeric($v)) {
5 G- M; T8 Q6 a A0 ? $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
" X( ~1 k8 q/ @- T, `6 O$ x } else { 8 O" S2 Q/ J2 y0 x, R' l4 u1 X! A
continue; + T, j: a9 X- E i8 M( d2 c
} ) j0 Y8 R5 w: [% e2 o% i- N. f3 }( M! k
break;
; H$ N% ^0 ^4 V# w/ x case '-=': ' ? F* v7 z2 \8 p6 N4 w6 O- _
$v = substr($v,2);
( ?2 K" [# `9 k, Y% k r( A% ]9 j. V if (is_numeric($v)) {
; ?/ H2 ]5 f. R- S y+ l $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 2 c6 _, x" u/ O; A
} else { " K( C/ q3 O* \5 _- u# s
continue;
9 k3 d. n- I! u# X3 m: K1 h* h }
" n/ X2 J$ c' V* J$ ] break; E( {) i$ f7 ]- \ T
default: * N9 g& {, R% G: A% `; R
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ' S' l# g3 w$ M) {! S+ o( ]
}
# y/ R" E( O8 _. M3 a }
: @$ Y5 S( h. ^, H6 N. T $field = implode(',', $fields); " g5 v$ \* C, U, v3 v- [
} else { ( k- Z p" S1 e) d4 K- a5 R, M
return false;
1 x6 E- H- v; B- S c3 [& V }
4 d( \6 W8 S# ^- S6 o& }4 F8 L) J7 T $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ; D* M% o p- b3 {
print_r($sql);
D3 |0 K5 m4 `* R& K: o return $this->execute($sql);
1 k- `( ]* c+ z# y } & C6 `) _0 `+ \# n4 H( P
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
6 v8 N8 e2 S7 ^' {# J/ v' l$ p% n. \5 O# k2 e9 Y& G' m
攻击测试:5 B9 q/ f* c& I* O( a* T' y& j
测试地址http://localhost
& f. F: O$ c2 |4 e! w9 h 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
/ |( ?8 i7 f" q
: y3 J5 J5 W% N 0 U2 e& _# X5 Y. f$ {# L) G8 U) P
$ w* `0 l8 t& S3 Y% m( ~
* A( }1 d% y2 I3 r0 C$ i: T7 U# g- v: P, j6 ^5 l
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|