|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
7 p+ ^! g$ n" Q8 k漏洞作者:skysheep
# s, ]& `2 i6 L: j5 o% Q2 k/ O分析作者:Seay
o5 ]9 S) y+ N; b2 e N博客:http://www.cnseay.com/
2 Z7 |; X! \' F+ E: m" Y! `$ E漏洞分析:, ] p4 _5 Z/ [
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* D# f7 H, ^+ S
; k7 ]5 W4 J3 h" i/ ?: r
* @+ i+ d5 y) F0 b5 { 6 J7 W5 X1 A. P t C
public function account_manage_info() { ; {) r6 o! g# K* H0 e. U ?
if(isset($_POST['dosubmit'])) { 1 r3 {2 | i1 t' H% s, I
//更新用户昵称
; ^& a& \6 u1 a; u $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 0 A7 y8 K# d+ N: s8 q4 A# N5 g
if($nickname) {
# ]3 v/ C0 t: T7 {0 L( J& Z N $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
! L1 J# r( e; c+ S- [ p if(!isset($cookietime)) { 0 D. ?. ~' ~5 P' Y' r
$get_cookietime = param::get_cookie('cookietime'); 1 N1 v0 w3 w! D2 k8 o6 H
} . n* J/ ^ W5 N" y8 K2 f
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
, J3 D4 S4 ]' z3 i8 q $cookietime = $_cookietime ? TIME + $_cookietime : 0;
: C; v' x* m' s+ P* b1 B param::set_cookie('_nickname', $nickname, $cookietime); 4 V, Y$ I( C) t7 {, D
} % Z1 f% W+ i% p3 W
require_once CACHE_MODEL_PATH.'member_input.class.php'; ) q3 V( C- T1 r
require_once CACHE_MODEL_PATH.'member_update.class.php';
, y2 d+ o2 f9 p5 D; i $member_input = new member_input($this->memberinfo['modelid']); 0 o; a( n1 O" Y3 R* H. L
$modelinfo = $member_input->get($_POST['info']); 4 J5 t! o W b- \( D {$ c e
$this->db->set_model($this->memberinfo['modelid']);
; j3 l$ q: B4 O5 K; ?! [ $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 0 s. s# h+ R5 \5 S3 x
if(!empty($membermodelinfo)) { - ]6 ]! o8 m `8 D
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 0 m( L; R/ E* ]6 x5 G* d
} else { ; f# P/ O( }! f$ _ E y
$modelinfo['userid'] = $this->memberinfo['userid']; . i9 F& Y+ e' k
$this->db->insert($modelinfo); : D7 H2 ^2 x/ I& U
} ) j% O8 X: e( A% T+ @* V, E
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 V. |/ m3 O9 @在\caches\caches_model\caches_data\ member_input.class.php 文件中:
* V% R" ]- L; j R. w# ~3 @, v( Z
. x; r) P. R' |5 H2 k
. A" z4 C+ ]4 t/ Z1 vfunction get($data) {
9 M5 ~# d+ V1 K) R: S! c/ j; w, h $this->data = $data = trim_script($data); 2 n6 q6 g* ]5 }* t; C
$model_cache = getcache('member_model', 'commons'); 7 R k, R& b7 k
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 7 p* a8 c3 U- W& i
$info = array(); / p" \- w+ A A. S M/ H
$debar_filed = array('catid','title','style','thumb','status','islink','description'); 9 C6 k$ y9 U& g; ^* {4 o* }- _* F: N
if(is_array($data)) {
) `+ y" y ^4 ~% W; y' q foreach($data as $field=>$value) { 8 i& d! B* w9 a1 s# \% ]# m7 a
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
- I) O+ c. t- L0 @ $name = $this->fields[$field]['name'];
3 ^/ j8 q! b7 \2 ?. @. X! l9 T $minlength = $this->fields[$field]['minlength'];
$ i. k+ `1 T; r! s7 K* W $maxlength = $this->fields[$field]['maxlength'];
0 t8 C. ]: ?+ K% x- a1 v& j# h5 u; ?4 B6 c $pattern = $this->fields[$field]['pattern']; # `% z& _) v2 |/ b9 r4 x* N. w
$errortips = $this->fields[$field]['errortips'];
. D9 i9 E; C9 f6 J1 ]; o' F- e/ r if(empty($errortips)) $errortips = "$name 不符合要求!"; / V( k9 L8 ?9 {# L5 W
$length = empty($value) ? 0 : strlen($value); 9 P) B- J8 ]/ X7 a3 z" J
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
% ~7 d3 h& c( ? if($maxlength && $length > $maxlength && !$isimport) { 4 ]. y! R1 v- D
showmessage("$name 不得超过 $maxlength 个字符!");
1 s- k7 g6 @' f; X+ B6 u) f } else { 0 z' d+ S! E, |
str_cut($value, $maxlength); + K2 F3 b; l- f' Y, c p
} ' O- J! u; q; m
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
! r& o5 s1 w. | @4 d/ R if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 8 O% D8 J( T( a6 P/ O. x0 ^2 l# v
$func = $this->fields[$field]['formtype']; 5 X. H, D1 U7 E4 i/ F4 n
if(method_exists($this, $func)) $value = $this->$func($field, $value); 1 e& @" n1 N, d
$info[$field] = $value;
- j3 Z/ W! d& C } 8 j& E0 ]0 N1 J+ f N2 u7 |8 x E
} 9 b3 {5 K5 J' ]( f7 k
return $info; e* ?2 y6 X [/ |% t
}
9 ^( R4 p# i$ ]: Z) x4 Z) ?$ ?/ Ltrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,9 A4 J9 `' ]+ M7 G, n( @. ]
$ x" f! H% T1 v! L: z; ]
再到phpcms\modules\member\index.php 文件account_manage_info函数
$ k. ?) m5 \. j9 i; S9 S* ^过了get()函数之后。
/ }. f( [9 g1 o' L; V0 C: {+ S' d$ \4 j
! M/ u, B0 W0 J
$modelinfo = $member_input->get($_POST['info']); 6 I: h' O5 e2 G* v9 R8 J* q2 d' m
$this->db->set_model($this->memberinfo['modelid']);
5 y9 M4 x5 A. j7 y2 R $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); - C2 e# k1 S8 \9 O
if(!empty($membermodelinfo)) {
- u t# M$ X' \9 t+ r $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ; k& a, N$ n1 U7 `% h$ W
} else {
* L( X1 P( A8 Z# R6 l直接带入数据库,update函数我们跟进看看
" }; J& [: x6 u* ]# n- H2 d% H
# J& \1 |6 G3 z( c+ t- \2 m
! i6 L: L9 `* J. k+ l1 o* T, qpublic function update($data, $table, $where = '') { 6 A7 F+ a4 ?+ k% r. P. T
if($table == '' or $where == '') {
/ Z# \! m6 n/ e; c return false; ! M$ W# ?+ l* e- I/ }& |
} ( H6 z6 V8 ]$ q' }
$where = ' WHERE '.$where;
7 K4 o. A* H4 ^( A9 C $field = ''; 9 z; N( W6 O8 B/ I# X4 o; f: M" y
if(is_string($data) && $data != '') { 7 o4 o4 L5 e* F! }
$field = $data; - u6 y$ _( _; `: K+ X6 e, A9 C
} elseif (is_array($data) && count($data) > 0) {
6 m) W) g5 Q) t: o0 U# L" W $fields = array(); 9 m) N: P* `8 x* ?2 V8 t3 z
foreach($data as $k=>$v) {
+ O9 U; L( U) a switch (substr($v, 0, 2)) {
( Y+ c1 r, b h8 L) \; s w case '+=':
- a" V. d7 w8 E9 Q: ?( t $v = substr($v,2);
6 q, I! E6 }, S if (is_numeric($v)) { G/ q) k" t& P/ Y
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); : U+ m6 b3 _ u2 V& a6 q9 z
} else {
7 t3 S8 ]' e' r' \3 D6 {0 z$ l continue;
: P% U5 s. S0 _3 Y" W }
) ]0 P7 G( `+ ?, { break; " x0 F; x! E& A5 M# b
case '-=':
0 G- N' h# `# c* q/ i& r; m $v = substr($v,2); " B) a# a, L2 M6 S
if (is_numeric($v)) {
" |# i( b3 Q) j7 N+ p, l. L5 `/ W $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); , d$ X2 C" n9 {& Z& R n4 g+ E
} else {
$ A) d6 z# w4 e1 D continue;
$ v$ `, T4 a/ V; w8 c# |$ R }
a$ O7 ?% @8 P; a2 |! o3 F: n3 q break; 6 |& ~: B& N2 B( F9 N7 C8 P, B
default: ( ^+ G% {& _8 ]6 w/ x
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v); 7 {3 A4 M! u+ X. s! J1 a0 S
}
6 Y( w: Z/ _0 Y* \9 f2 `2 }; c } / T8 v$ \2 V3 N" l# B
$field = implode(',', $fields); / ]9 g/ s9 i+ _- X0 U0 o5 g- D% I
} else { + p( V2 F7 M* F& ^9 c) }
return false;
$ }. d1 A2 ]* r: P5 H9 r }
, T# ~% L3 X; x# y: B $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
: F$ G; S3 r' k print_r($sql);
4 q5 F3 G v, b. p7 P return $this->execute($sql);
# d+ P$ [& u! y } ; [4 ~/ F& x- C- E
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 A9 |' j* T, c/ z1 [: t( a& x( X+ [" |/ [7 u0 I d) S& ?
攻击测试:
; R" l6 E! _, l测试地址http://localhost. X, ]0 [! Y/ \# k9 a) s
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
+ @: p, t9 R# F: n* F, M
( ?9 V8 t M, D. e& ?. I 4 b& G/ ~4 E8 d
3 v/ A3 W4 d5 b: r+ D% _: A
% X, z' ^) l2 v" h
2 }9 s- ?& C& L! `3 z1 }1 c. \ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|