|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
. t1 Z$ q4 V+ G4 F) A5 h漏洞作者:skysheep
, G& c |, R5 E" \分析作者:Seay3 z. a; G7 e r" N
博客:http://www.cnseay.com/, N' A( t1 t5 X6 y8 ~
漏洞分析:
0 e: z+ G% u- n# O5 V L 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。8 ^! h) L$ Q" G# ]- b5 ]" X
; h1 r* b& D* f2 o% ]" X+ u
( D$ i9 \2 }/ d" G
" j/ }! Y; p+ c) R& X9 `) Z2 Tpublic function account_manage_info() {
$ h6 b# M# g; g/ T; m a/ p8 y! F/ f, r if(isset($_POST['dosubmit'])) {
! n' a% O( v7 n3 J' U0 m0 P //更新用户昵称
0 P+ k/ B/ I8 x# D: Z $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 3 v$ K) F+ F" O! n" h$ z' J
if($nickname) {
. l* v v5 A/ F! G) t% b $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
8 Q; i/ w2 P* U1 m if(!isset($cookietime)) { 2 g6 e" T3 } e0 h. y
$get_cookietime = param::get_cookie('cookietime'); 0 }3 i0 P6 R: e* d, S6 ?, c
} # Y- J( N8 G$ S( e7 z+ \3 Y. h
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
, y4 c! D1 ^7 A, P' o2 [: F $cookietime = $_cookietime ? TIME + $_cookietime : 0; ' V4 B. N! q! o5 A4 T
param::set_cookie('_nickname', $nickname, $cookietime); & {/ q ~7 a; a6 ]
} , e. r' n- z3 U7 @
require_once CACHE_MODEL_PATH.'member_input.class.php';
3 p8 o; o7 D5 {, v require_once CACHE_MODEL_PATH.'member_update.class.php';
# L9 b$ E! e% f1 y $member_input = new member_input($this->memberinfo['modelid']); * H1 k5 N& r+ C. o( i [6 d# O+ v
$modelinfo = $member_input->get($_POST['info']);
2 c5 |4 P/ z/ W% Y) l $this->db->set_model($this->memberinfo['modelid']); 6 p; H7 I9 u3 N( T$ w: f; t
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
2 C6 n7 `8 U% ]" n" F8 F9 K1 A% q if(!empty($membermodelinfo)) { . l( Z% b1 J9 a" V) ]3 Y. U9 X
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( o' x( U F, V) ?- c } else {
8 O1 b1 i: m- B2 p7 n: k $modelinfo['userid'] = $this->memberinfo['userid']; 2 ?2 s! d0 Z7 M' \+ \4 V6 {5 Q
$this->db->insert($modelinfo); . k; j% o) I/ }. q- B/ J. F1 Z% a
}
' f# Q/ m5 L4 K) E代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,% Q% D. O+ i1 l/ Y5 X% f
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
/ { ~- y, H0 Q/ {5 @0 R) @+ r8 }, R: W+ u8 v
2 j1 q. ?2 m+ \+ L [/ V6 t- o8 v0 ` u
function get($data) {
- X+ U) @+ c7 ~, X% Q $this->data = $data = trim_script($data);
6 q# J4 [' g# N8 c: J- W# _$ p3 B $model_cache = getcache('member_model', 'commons');
" x" K; v1 o# \: R5 H3 X $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ! R/ L4 b% R2 u7 Y
$info = array();
{: ~ D3 {' j9 b+ t3 ^ $debar_filed = array('catid','title','style','thumb','status','islink','description'); 0 F. b: A6 ]: m, j- N
if(is_array($data)) {
8 J" a' W2 ?+ Q foreach($data as $field=>$value) { + N. X4 K0 U" @
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
+ Z/ @3 u: k5 v3 E) [$ L $name = $this->fields[$field]['name']; ; C5 O- x$ H* {( f/ U6 H+ L
$minlength = $this->fields[$field]['minlength'];
* Z( H3 M& y( t0 S! [. n: |0 v+ | $maxlength = $this->fields[$field]['maxlength'];
1 @6 ]% k& Q, d $pattern = $this->fields[$field]['pattern']; 0 C) a4 c1 u* g v9 @8 F) }$ N; E
$errortips = $this->fields[$field]['errortips'];
5 z( t+ J' [& g& f+ j if(empty($errortips)) $errortips = "$name 不符合要求!";
$ z2 h0 `2 u7 q% a7 |: p5 R5 l $length = empty($value) ? 0 : strlen($value); * L& Z2 P# r5 ~) u
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
7 k: x- F& {# N+ G2 R if($maxlength && $length > $maxlength && !$isimport) {
) o7 j2 e, b9 _# t% V$ U showmessage("$name 不得超过 $maxlength 个字符!"); : p+ ?% R* V# h1 Y! P
} else { ' q. {5 s9 m% |+ i2 R( c4 R
str_cut($value, $maxlength); 7 x. P0 S0 U' [# x6 w
} % w; D8 |* \6 I
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
A* j/ d Y$ t% R. G if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
9 F0 @4 N: j* p* X* z) U0 j0 g7 D $func = $this->fields[$field]['formtype'];
5 E( F1 M6 Q3 W. Z* E, L: I* l if(method_exists($this, $func)) $value = $this->$func($field, $value); 5 J, }$ S' E9 S1 C. i4 a
$info[$field] = $value; / F s4 P$ F* K; B
} 4 O7 K& y0 f: q1 a5 w1 k
} & o% Z# o4 G! c! N
return $info;
4 w2 i' b$ T: ]4 Y; B6 L, x } 9 R7 c7 ]* g0 u9 e' H
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,3 w i u# E# G( F; u$ f
. e3 f$ A q( l* E7 c
再到phpcms\modules\member\index.php 文件account_manage_info函数4 r+ s1 ]! n; ]& S5 _: l6 o
过了get()函数之后。
2 o l _- R. A u8 \6 \ {. ?& w2 E# Q2 U# [( q. Y* k
( f/ T# h4 v O0 T" G2 Y# t2 J$modelinfo = $member_input->get($_POST['info']);
0 [ Z7 N; F% h0 h" i $this->db->set_model($this->memberinfo['modelid']); ~" T4 `) U) R
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 3 V {+ U, \4 X4 [! ]- r6 b, ^$ C
if(!empty($membermodelinfo)) {
: Z, X" V7 k8 ~3 ]6 u; v2 O2 X $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 6 p# j/ Y4 t$ q: `( l( D
} else { , e [& ^9 A% u+ \( W) l
直接带入数据库,update函数我们跟进看看
, q$ A' o2 _. d, ]! R. @; Y% g
& I8 y$ @0 N0 e3 N' _# F) j 6 Q t2 U6 K# J
public function update($data, $table, $where = '') { 6 M6 ^; j/ x" I9 R/ g5 J
if($table == '' or $where == '') { + I* G" B+ r6 Z1 F" R; {
return false; - W. J' V6 m9 S- A
} % ]0 \5 x) S3 L9 D' H4 N& E5 Q, g$ H$ B+ \
$where = ' WHERE '.$where; 9 h! P" c0 g8 q
$field = '';
# n5 q2 Y" x! i8 ~0 k" i if(is_string($data) && $data != '') { - N9 `8 c& _: q: \, w/ X; k
$field = $data;
# w* e- }2 W; b } elseif (is_array($data) && count($data) > 0) {
# N- r1 p: s1 m) h; t $fields = array();
\; d0 U6 U6 C% @ foreach($data as $k=>$v) {
, w5 ]- b+ G: S2 o! E switch (substr($v, 0, 2)) {
+ g, J$ k) {3 a case '+=':
9 J# R" o7 e" N, z4 t; J7 a $v = substr($v,2);
; d5 r: g6 @0 A! b if (is_numeric($v)) {
5 J+ M4 Z, n& T" q $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); % @9 n- M" O/ u# g* c- S
} else { 3 L1 Q( o+ B# B; X
continue; * a, O- ~4 Q0 @% u$ e+ i
} k: i# \' I% j `- F3 E1 j+ z
break; 0 u; r# o1 z& |. l) @
case '-=': + S2 B' c4 A" p: Z/ p, u# D
$v = substr($v,2);
/ ?$ }: f" g+ |+ ^7 U7 z' i1 g0 l if (is_numeric($v)) {
( u, {: u Z- b1 `/ G $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
. P8 W0 H$ y- u2 M$ ^" m } else {
5 d1 [! g8 W1 X# p3 B" ] continue;
# u3 P& |4 @: h2 `8 E }
$ S6 j( F% Y) `6 X5 c5 D break;
0 z8 q4 ]$ B0 n3 r6 _5 y# h4 w default:
+ K4 h1 K* }/ g $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); , \ M: Z$ w. A4 z2 G, E
} 5 i' v& T9 Z! X% a
} : |/ n# Z' B& W4 f; v8 V/ I
$field = implode(',', $fields); , Y- @/ O/ Q x$ m: j5 [
} else { 0 [6 u6 u+ Z" f6 C" `( F
return false;
3 y: t1 Y8 w# t; f5 @ }
! N( L$ ` D1 N! \ $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 4 V3 Y% n a1 d. ~; U) Y, m
print_r($sql); ) W- ]* B/ F7 B {5 |; A
return $this->execute($sql);
+ z1 J" A1 s; G* V. V: p }
4 d+ S& @1 u- W从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。* Z: s; u* A7 U( c
# c5 C5 Q# Y" G& }5 J$ d
攻击测试:; s$ V- @2 ]( H* H7 Q
测试地址http://localhost
1 e( b( S( o6 ~- f+ V4 I7 B 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
" b" R: c' U; a8 k u& s
- c# r! w! z3 p) f8 c 6 }0 J4 f9 [5 @4 g8 `* Y
/ _( }3 o J% v
+ g1 M* G' k* D' C. E4 x, x; C! T h2 H1 E; N# W+ Q
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|