|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
9 G; N! V- H: D. B9 ~5 r" u; l& |漏洞作者:skysheep
4 u, ~5 Z. @+ V6 y. t8 h) \分析作者:Seay
$ p* o* [0 |0 P; T) }博客:http://www.cnseay.com/
! U$ B6 \7 Z2 U+ U) F- F9 u( }漏洞分析: ? E0 l- o; p$ M& g
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。) h+ n" P7 f& ?8 d% V, J9 N, R
; B4 U' L3 c: q/ n4 j5 L/ ?6 O2 p. E* a" Y2 m0 j! s. H6 \- k
1 B7 g, g& j6 V- \public function account_manage_info() {
* b/ x) Z0 Y3 T: U0 O9 g2 v if(isset($_POST['dosubmit'])) {
$ K! G$ f# n& D/ } //更新用户昵称
# l9 h$ @4 K& Z+ y! x8 N1 N $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; " u# H. r ^: n/ ~: N
if($nickname) {
: ~" @3 Y2 ~, r- Z" b+ a* @, Y7 ~ $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); / }) w- ?0 ?6 U6 Y$ a* ~& v
if(!isset($cookietime)) {
& P* ^) b: `6 }3 b& l9 I- ` $get_cookietime = param::get_cookie('cookietime');
5 ], `! W& D$ h E } + c: l5 {3 B2 s, k3 Y' h
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
& v0 Y; p' h0 [) F $cookietime = $_cookietime ? TIME + $_cookietime : 0;
3 E/ U' z, r* V g. X param::set_cookie('_nickname', $nickname, $cookietime);
1 ?& V7 T, T) K7 H }
6 p+ f* Z8 ~' b. ^- \" T4 Q require_once CACHE_MODEL_PATH.'member_input.class.php';
9 m3 I, O; R% @1 C0 V. p R0 _/ H& ] require_once CACHE_MODEL_PATH.'member_update.class.php'; 5 r2 D9 ]! x' ?3 T
$member_input = new member_input($this->memberinfo['modelid']); ) A3 u; Y. b4 o) j
$modelinfo = $member_input->get($_POST['info']); % t& A* L/ _. B" e2 S$ v9 g
$this->db->set_model($this->memberinfo['modelid']);
" A: e& y4 w4 z- ^) R2 g $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
7 c1 x" H6 F } if(!empty($membermodelinfo)) {
6 b) r# B$ }2 q8 o/ a $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 9 w, v6 F a3 o
} else {
" g5 L$ \7 o" E0 i# o5 n5 e; S $modelinfo['userid'] = $this->memberinfo['userid']; / l! _1 ?8 \- Q: @, Z+ y- g
$this->db->insert($modelinfo); # l' W9 Y- I& u6 z/ N6 Y9 {0 c
}
! J" k J2 L9 T- Y5 S* b9 O代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
/ A( t# X8 z; A# R在\caches\caches_model\caches_data\ member_input.class.php 文件中:8 d M3 B: S" \2 Q
& ]( w. t: w% d# `3 y5 u! L( w4 O$ I5 M
) j) w7 P; s1 t) w
function get($data) { : A0 @9 u% j# g3 w" Q
$this->data = $data = trim_script($data);
l; Y1 h8 ?0 e9 p8 o; Q) [ $model_cache = getcache('member_model', 'commons'); 9 V# K1 I+ y) k+ i
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; e2 K6 L& u( K+ T& O
$info = array(); 0 V- l+ ~! g9 f! z- r
$debar_filed = array('catid','title','style','thumb','status','islink','description');
/ P3 a, i5 t# g+ }1 M4 @/ I if(is_array($data)) { 4 f8 f0 h" T2 y. e, M, v, s
foreach($data as $field=>$value) { * \( U* o; E" x b6 D; B
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; " I1 J+ I9 H u: i/ g$ C
$name = $this->fields[$field]['name'];
3 e) @* p( g; f. z/ V $minlength = $this->fields[$field]['minlength'];
2 V" C8 a' q+ @2 c, s- M $maxlength = $this->fields[$field]['maxlength'];
! L! _ I, l2 f, e% ~3 y9 Z0 k9 K9 A $pattern = $this->fields[$field]['pattern']; : m7 y/ \4 b: d8 l) p8 }8 [
$errortips = $this->fields[$field]['errortips'];
+ J; e2 A! X0 O; H0 i if(empty($errortips)) $errortips = "$name 不符合要求!";
9 u x7 {) \- F4 l6 ] $length = empty($value) ? 0 : strlen($value);
* y/ p' \7 Z) o if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
1 ?2 i" E% S& k- G! T if($maxlength && $length > $maxlength && !$isimport) { 1 N6 Z) X- w. m: ], [
showmessage("$name 不得超过 $maxlength 个字符!");
& c. o# C- c: K2 u } else {
2 w3 R& H. C' p) p4 v& G str_cut($value, $maxlength); : B! O j( r; `) w" B/ l% S" N
} ! x* g9 G3 Q, `& @* M$ N
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
- N+ R4 m6 |+ D9 O M if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
/ Q4 x. ~& E% C% p6 L' ^2 G $func = $this->fields[$field]['formtype'];
+ p1 L' c. T i% |0 S/ b if(method_exists($this, $func)) $value = $this->$func($field, $value); 6 A% b+ j4 d9 W, w# k
$info[$field] = $value;
8 |# n) V1 k% L3 ~; H }
4 I8 {- x V2 p# |$ G, [! w8 H }
" l) H/ ~) X+ a- a4 C- \, Q7 D return $info;
: }1 H6 v' ^, C1 G }
: t1 M1 V5 p% j; j7 Ztrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,. |' @. H7 v9 [% o
4 m1 b# E( b- ^. H2 F- j. X+ F: P. N
再到phpcms\modules\member\index.php 文件account_manage_info函数3 m7 P. h. Z# p
过了get()函数之后。* x0 \6 d- g; ~: I$ O
# c" r; @% q% t 8 K2 ]- T: F$ `8 G
$modelinfo = $member_input->get($_POST['info']); + [4 L7 r Q6 `4 h
$this->db->set_model($this->memberinfo['modelid']);
3 i0 |/ I3 \9 p/ r9 F/ D+ U1 F $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ) s9 E" r8 g! w3 g. u
if(!empty($membermodelinfo)) { 0 B! V5 {7 d; W$ e
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ' k: ^1 h$ R0 p
} else { ( A: L. C0 G8 q' }
直接带入数据库,update函数我们跟进看看
e( ^, @* d) ^/ M3 S, ]' K
$ F" R) C5 l5 { t7 e ( T1 v$ V9 V) i- z3 _
public function update($data, $table, $where = '') { 9 P, I3 L2 n* B5 V; s0 d i
if($table == '' or $where == '') {
3 H: h$ e U- m1 H* h return false; : k0 O6 ^5 e, h' s' u
} ! x, Y& l3 N; }$ m7 k' a2 `; x3 I$ p6 R" ?
$where = ' WHERE '.$where;
3 I4 X4 S& h0 Z' j% i! i! z2 _' ]1 W $field = '';
. q! h2 F. @; n9 W& [& h5 r2 h' ~ if(is_string($data) && $data != '') {
, [; H R2 i3 P $field = $data; 9 w' E- n2 l8 H4 { \6 ^
} elseif (is_array($data) && count($data) > 0) { * r2 P% n! P! k- [4 P
$fields = array(); 4 s5 ~( ~0 h3 E/ E' ~
foreach($data as $k=>$v) { ; z' u! y' I& Y, ?0 |8 J v
switch (substr($v, 0, 2)) { ! x! C# u( d- f( o
case '+=': 9 ~5 K) x8 f ?7 t5 ?
$v = substr($v,2);
7 w1 u* ]7 E+ `0 s if (is_numeric($v)) { 3 P7 E# p u3 J4 m8 w; _
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
3 Q/ d |7 y3 ^( @2 F } else { ; _5 P4 C" d+ [( v! _. K
continue; s8 i" L8 ~, m2 h
}
9 b. W' @& @( h3 M6 ?5 `4 U break; ; G2 E: I2 S/ i
case '-=': 0 V$ W6 n, o) u/ v" ^
$v = substr($v,2);
. F' Q, {0 Z2 K: E( G1 D if (is_numeric($v)) { 4 ]7 `- f) M0 {" F: K6 e
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
- T6 d' Y8 v1 f& K0 k) | } else {
6 t) v1 y- i% [' Q& c# o7 S continue;
) Z' q) o) B$ z: C }
. p( {: p9 w( I) u$ J1 _ break; 0 a) k, a" y6 [+ D9 ?5 E h6 o0 r
default:
% @+ p! n; E# B8 u9 C $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ! U" a3 N6 L/ q* ^4 n8 N( G- n
} ) ?5 C |1 ~! ]+ G* F5 @' Q/ E% y
} 0 Q9 M2 j- ?5 J5 a/ N# E+ ^' ~
$field = implode(',', $fields); $ }% C1 m3 e) [4 n% [% w
} else { " b. P7 P+ }' a1 e
return false;
9 j& t. e2 c! \2 @. [5 A }
- B3 v3 @! x& n# q/ c6 p9 b $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; $ d( U% Q, H0 `$ L5 ]9 L. q
print_r($sql); 5 o7 e: j- q; q8 W& j) I6 q
return $this->execute($sql); 9 p B- p, S- V, W. a4 D( ^, L
}
$ B% v0 {4 w, w2 g) s# o* [从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
{3 v3 P% [* U" e) \* u, f2 U9 c. _2 q+ T; k
攻击测试:6 i$ T, p' ^8 V. v
测试地址http://localhost1 h# o5 V; ^( P. A1 i
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句7 V+ \2 x$ r4 p5 U9 q7 M
7 N/ y D$ x* t$ {. I6 a
; m: [, J7 Y1 X3 i0 a! Z
7 l; i' ~& @3 Q8 |/ D2 k) c) m7 I. V
& s0 q5 r8 d" o5 S$ E+ H |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|