|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 O4 O" F$ y. l1 u) L" K3 E$ g2 y漏洞作者:skysheep1 l' z& c) c+ c$ B- D- [3 ^# [ ?4 l
分析作者:Seay: y/ a; b* P/ w# P
博客:http://www.cnseay.com/
' W+ X& S* C" e* k6 [% H# ^! o* M2 i漏洞分析:) N7 C5 |( n p4 ?
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
" y+ D& [" G/ K' R1 B
" X8 o. \4 t8 v" i, j3 f; _8 g) S; d
" ?5 t/ G. q& ~
public function account_manage_info() { * S3 M$ G8 i; ]' q$ o5 Q
if(isset($_POST['dosubmit'])) { - u& d! E" @4 j$ Q4 w' E
//更新用户昵称 ) d) J( ?; @) X" m2 g
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; & G: g4 I }: G
if($nickname) { ' }" _& V; E" F/ X
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); ( ]# S) z7 l2 `
if(!isset($cookietime)) { ! u6 X% G. n3 \' W3 @% f
$get_cookietime = param::get_cookie('cookietime'); / Q0 v3 L- V+ X8 \9 O8 O
}
: A8 r- |* l5 L+ P& `1 q $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); ( H7 Q5 S4 ?: X1 v" o- F) S9 G
$cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 e6 L3 i j. ]2 i
param::set_cookie('_nickname', $nickname, $cookietime);
) H( |* }* M5 J# f }
* W' A) z: \/ K. b require_once CACHE_MODEL_PATH.'member_input.class.php'; ( Y& J3 K. L9 j$ h9 E" G
require_once CACHE_MODEL_PATH.'member_update.class.php'; 7 W0 Z; l; s6 A$ n! f. X
$member_input = new member_input($this->memberinfo['modelid']); * \. @: K! P$ U$ M% z# ~/ @% m- |
$modelinfo = $member_input->get($_POST['info']); % n( n* ?0 e; A/ e
$this->db->set_model($this->memberinfo['modelid']); - \; @8 }# J$ ]8 W6 q: @0 N
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
: F1 y/ x3 B$ Z+ \ f: v! Q6 R if(!empty($membermodelinfo)) {
( `5 ?2 W$ W2 s& G, n5 p $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 [) n" k$ M& E& {1 L1 W } else {
- P" u+ g# P5 i2 S3 d- F4 | $modelinfo['userid'] = $this->memberinfo['userid']; + w: o; c( M: S8 M' M
$this->db->insert($modelinfo); " ~& K* u, O9 L7 h: @
}
" K$ b# U6 P: r* @3 A+ Z& t代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看," i! X* B6 f7 l4 U# ]
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
4 k. q+ S& U$ ^& @: B* Z2 i) j7 C5 ^3 c0 h+ k
) j; l# m4 k+ k$ c4 x
+ B3 `$ R$ V! d9 D; h* D" zfunction get($data) { + Y3 m. ~: h: v9 L. Y
$this->data = $data = trim_script($data);
8 f9 N6 s- @( n4 `% y $model_cache = getcache('member_model', 'commons');
- ~& Z" F- X# C' E; |) p9 q2 ^ $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
) G$ w3 E( v# d" A2 N $info = array();
( z+ j* M- n3 t $debar_filed = array('catid','title','style','thumb','status','islink','description');
' W/ m) I; o5 u. A if(is_array($data)) {
, f5 D2 Y( N6 _: |2 U, z foreach($data as $field=>$value) { - P+ B [1 H; U, T# B+ }5 W2 L$ p0 K
if($data['islink']==1 && !in_array($field,$debar_filed)) continue; * u- p$ h# y5 G$ P6 Y0 e
$name = $this->fields[$field]['name'];
" b7 `' T/ R- A1 a8 P1 y+ X' E ] $minlength = $this->fields[$field]['minlength'];
$ x" w* D2 p2 m& A8 l $maxlength = $this->fields[$field]['maxlength']; " G, C, i9 v8 P4 n
$pattern = $this->fields[$field]['pattern'];
' S7 T4 v& V+ f- B! M, R $errortips = $this->fields[$field]['errortips'];
( L! g# j% Y" Z& a if(empty($errortips)) $errortips = "$name 不符合要求!";
( H' X! C0 a- ?4 S. y. e, _ $length = empty($value) ? 0 : strlen($value); . e+ G! I/ ^& @- R0 q, P9 ]3 y: j
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
. k1 p/ ^% \: c9 C if($maxlength && $length > $maxlength && !$isimport) {
/ o5 n/ T$ C# y0 ^3 _$ G3 d showmessage("$name 不得超过 $maxlength 个字符!");
' J2 }- k* h- p: f } else { A6 M( O' _. u: C% M
str_cut($value, $maxlength); + C, o; l0 i ~+ e$ Y6 |8 [
}
. y1 x* W4 D) P$ m, C if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
$ Y {; `1 w- ` if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); ( U$ n' F) x4 r$ Z0 ]1 Z4 G
$func = $this->fields[$field]['formtype'];
+ |( x ?+ i, H$ W8 N1 w/ P, M if(method_exists($this, $func)) $value = $this->$func($field, $value);
6 a) g+ ~( {. e$ m/ [ $info[$field] = $value; , g) ?4 O7 Q% e3 {& v7 R
}
* G$ g: F; L6 `8 ^# w1 d }
) l, B9 ~# d6 f" @$ c" c return $info;
+ C, R. [% q+ ?4 j2 @' W5 h } ' U! l% j& B" U5 B( e2 Y/ F% w
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,+ A! s5 o6 X+ P" p; w- R
# n7 Q( o2 H% L; y4 c再到phpcms\modules\member\index.php 文件account_manage_info函数
! b# ^$ w Q2 Z8 r4 ~9 j过了get()函数之后。5 Q6 |" ^" D3 b; j# Q' [ B4 S
) O/ Z# c6 Y( `7 y6 u
5 H: P. M9 {) F) r$modelinfo = $member_input->get($_POST['info']);
' i9 p$ W% _2 U1 D- x $this->db->set_model($this->memberinfo['modelid']);
; t' o: K3 E' z! W& T $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); " `0 ^8 O" a5 C* I) m# ]7 ?1 L' r
if(!empty($membermodelinfo)) { : `+ F0 H( z" Z, N, }
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); * L# u" x; [% _1 P: K) g8 I
} else {
( A! o1 t0 D9 c1 Y直接带入数据库,update函数我们跟进看看" C, I! y! a& [" [
/ S, ]: B% d; i* w y 2 ^5 C: Y( j8 I7 X4 Z
public function update($data, $table, $where = '') { * h+ J' b/ Y( V W
if($table == '' or $where == '') { + N- ?0 ~ W) P. y
return false; + J# f- ^, Z3 S: ^# D T B! z* M
}
! S5 z; t0 ?' C9 X( ? $where = ' WHERE '.$where; : Q8 d5 |, C. R
$field = ''; 5 }/ L+ o- z5 w$ s0 O% A
if(is_string($data) && $data != '') { $ y" t! x, Z6 v8 S4 A
$field = $data; # x6 z1 J8 Q' h8 e3 f- L
} elseif (is_array($data) && count($data) > 0) {
$ T1 A S+ u0 T8 {9 X! p $fields = array(); : N5 }4 [# F2 Y: @5 Q6 d4 ]
foreach($data as $k=>$v) {
/ m7 t+ c7 z3 n; P8 f2 A5 u switch (substr($v, 0, 2)) {
$ g! g+ C5 d9 M9 u/ S+ x case '+=':
- w; U( U0 Y5 Y5 J$ G' f $v = substr($v,2); 9 Q/ h- o6 {! ]/ R* F9 t$ h( F( f
if (is_numeric($v)) {
1 @+ F5 s: j$ d' s7 c $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
8 R, _2 L, @- R" b3 E; | } else { 4 y! y3 ~; O5 N; D; l4 H
continue; , O9 ^- ^/ W y
} ; M: L2 h$ F( j- V. ]9 D& ?
break; 4 M; R% f* w5 C7 \: D
case '-=': ' r6 W$ j) o7 {/ {# j
$v = substr($v,2);
; `& M b, B3 p2 ^5 y if (is_numeric($v)) {
, C4 ] V k! F! X. a $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); # y- G0 g" S; o! V# F
} else { ! Y; T% o& W! ~+ e2 D
continue; . b, P5 q- h7 E- P: k, ^
}
( C+ b; p" ~% [8 m8 ? break;
7 z0 y: y7 C, t; q1 H% H0 r% g default: ( c' b- u% a- I
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
1 b+ _/ _" \3 D5 ]0 n } : X; H! X8 N! S: v! r
} ) s: `. U/ [2 D8 I7 H
$field = implode(',', $fields);
( E, B/ j; r! o# h } else { - E: a7 n! K$ y8 X3 a$ w4 P4 J, W
return false; ( P( H8 X. _6 E2 l, F7 k! F0 Q1 F% P
}
9 S8 ^9 f) h; {& J $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 3 N2 D- U4 H3 ]% w& w
print_r($sql);
1 A/ F8 T7 T, C return $this->execute($sql);
) B7 [4 n/ _4 |1 T7 B( p }
# {% m+ V' L- K# G! x从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。# @" a3 T: P, ~; P+ J/ j q
; l/ }- f& n, ?/ {' c* c& r0 F
攻击测试:2 p% M9 j9 Y8 \* M) W
测试地址http://localhost
/ w: B! A, K1 ]1 @; |1 F 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
5 ^/ m2 z, @( ]+ c
+ U! T& y+ u- g) m& s- h7 r1 X / J6 {, N7 w: G; q
' J8 ~$ E- E7 I0 q6 C$ j6 x( D& @. C
; U9 W, K5 \8 B
3 {6 i* H( N; N/ n# H1 a, z4 b |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|