|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
3 F2 ?( c8 c S N3 v9 Y7 M% ^% e, U6 Z, \漏洞作者:skysheep6 [7 w7 M6 s/ j; B. w+ F5 y
分析作者:Seay
8 o0 N4 ]9 y8 ?- t. u* r博客:http://www.cnseay.com/
9 c0 C2 _' i. T. p2 q漏洞分析:6 Q- n/ e M( C/ |! U7 k
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。' o1 U) g8 s. q
1 O& }' d0 L0 j1 s7 r/ r
: z+ c" k" Z' P/ T# @, [9 w
/ F- q& D- k" L8 a% j z) S
public function account_manage_info() {
; D/ X9 h* k7 [% p4 O if(isset($_POST['dosubmit'])) {
1 {1 z3 f3 ^# W& n, ]# b //更新用户昵称
2 j- Q# U# ?+ m' O $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; ) K9 r$ @4 ^" X; I
if($nickname) {
0 H z! K2 R$ f, ~- E+ e5 i$ E2 n $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 4 {7 T+ @; ?. {- f+ Z
if(!isset($cookietime)) {
$ C" }+ C2 U) T% j $get_cookietime = param::get_cookie('cookietime'); 0 S" l5 r4 ]" \6 q/ f% A
}
C& b A. f _8 R* B $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
2 R6 @1 C! {& B; k $cookietime = $_cookietime ? TIME + $_cookietime : 0; 2 y- j& ~6 m4 }$ _
param::set_cookie('_nickname', $nickname, $cookietime); , U# L, g' h. I+ [8 w; t& d) M
} . Q/ G2 B$ [8 S8 \ y( l
require_once CACHE_MODEL_PATH.'member_input.class.php';
, @8 D! z" n$ J/ C/ N require_once CACHE_MODEL_PATH.'member_update.class.php';
! x6 w2 d' Q3 x& G* m3 v" y $member_input = new member_input($this->memberinfo['modelid']); ) P" v' L4 O& H2 b5 h
$modelinfo = $member_input->get($_POST['info']); 5 x6 l8 | e! R" H6 K3 T
$this->db->set_model($this->memberinfo['modelid']); 0 X4 D7 N$ [# Q+ i& }4 X% O5 e
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
4 |" o2 Y( W6 g if(!empty($membermodelinfo)) {
! ]/ @6 q$ G, ~& N $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. p2 L* Z) D" B, F# } } else { 1 U3 K# x. G! F$ f l1 \
$modelinfo['userid'] = $this->memberinfo['userid'];
* p$ a c8 H% U- d; C/ v: B/ o $this->db->insert($modelinfo); & K) z2 y- U! P9 D8 j' M
} ' x3 p. ^1 j9 g1 ^
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
+ z u- u8 Y& b4 [0 Q在\caches\caches_model\caches_data\ member_input.class.php 文件中:
, Z6 Y% W. ~$ e: \$ q2 `+ m% o5 n+ D0 i: |8 b5 q6 B( x5 G
) b. X! j4 t1 K
4 m* t$ C5 a: y0 C. qfunction get($data) {
( h7 B6 T7 y5 e+ W: x/ ?1 y8 s $this->data = $data = trim_script($data); E# v' J' k. s7 ]
$model_cache = getcache('member_model', 'commons');
8 ?* K/ y) a* n; d, @ t' T' F $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 3 k. H( @- h. [0 \7 Z
$info = array();
4 D @8 O& e) R/ } $debar_filed = array('catid','title','style','thumb','status','islink','description'); : w! q7 z2 U, N0 `5 E! F
if(is_array($data)) {
& k3 _) S- n$ i$ [* W' t" u foreach($data as $field=>$value) {
h1 v$ S8 C- J7 Z if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ' z: F& K$ i, \6 A [5 Y
$name = $this->fields[$field]['name'];
+ {# q5 `. I5 {7 U6 F) U- H6 V $minlength = $this->fields[$field]['minlength'];
8 n& J" L1 |! ~" L; i $maxlength = $this->fields[$field]['maxlength'];
m: D O; R* o/ i+ E $pattern = $this->fields[$field]['pattern'];
' Q$ z: H9 E7 i( }. @9 i$ Y2 U" D $errortips = $this->fields[$field]['errortips']; # y7 k) d; p( y9 s. \" [, _( N
if(empty($errortips)) $errortips = "$name 不符合要求!";
# s, y0 J/ v% l- W $length = empty($value) ? 0 : strlen($value); 9 d. D% g5 [0 N; Y) ~7 q. J( q
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ; F# C8 r; K0 r7 K+ @% h: m
if($maxlength && $length > $maxlength && !$isimport) { : P- x) v) P {! F2 s
showmessage("$name 不得超过 $maxlength 个字符!");
. U. ?! c9 m! B9 o! q } else { 4 ?6 ~3 O! c# k, V2 t
str_cut($value, $maxlength);
, n0 k8 J0 n, d6 S }
2 T. w2 h J) T% \$ x/ g( H7 X if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
2 \( i! ]6 T. y if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); 9 F) Y2 c; [/ H% J% Z
$func = $this->fields[$field]['formtype'];
1 ^5 L6 m* c) ^2 D' E! O* } if(method_exists($this, $func)) $value = $this->$func($field, $value); : l) Z. q N, J+ x+ @
$info[$field] = $value;
' i l/ v, |, m% t3 `1 a! r7 W t }
# Y/ U( ~2 u' B9 k2 Q! J: e } * w2 b& L& h' o# p
return $info;
4 C1 k n' |% q8 ` } + q; J$ J8 P* a3 o
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& \" ~/ L0 R% h0 I+ l9 v _
" T* T" w: c. V& c# \, n3 S再到phpcms\modules\member\index.php 文件account_manage_info函数
" B% E/ @/ r5 m9 o# {: m M/ u4 _过了get()函数之后。0 a$ I! E5 m {7 ]* B" W7 P
5 B$ D7 r/ `0 U
7 l0 N1 r- k# D6 ]# a D$modelinfo = $member_input->get($_POST['info']);
8 f" }' ?$ z' b1 k d $this->db->set_model($this->memberinfo['modelid']); . W5 z- I/ @& q" N9 h9 _
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 9 _5 W: v# B2 D* ]* S
if(!empty($membermodelinfo)) {
6 `* ]! W/ B1 }9 h7 B* _ $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); % n' \; k% Z2 L3 _. w% ^, z+ @8 k
} else {
* Y5 l S) c5 Q, j" b' E直接带入数据库,update函数我们跟进看看
: I* ?! b+ M& [& l+ c
6 M* I$ o( A2 t0 k* \ _3 g; X * B3 @1 j: V, |! l0 H( `* z
public function update($data, $table, $where = '') { * f* a1 K7 D, l* f* a' V
if($table == '' or $where == '') {
/ \( ?0 Z% |& m return false;
( i! ?# y, f, v& n' ^5 X. C }
" d0 J/ e8 H/ x; [( A $where = ' WHERE '.$where;
+ a: o ^# }! ? $field = ''; , B$ B; N/ s! u' D
if(is_string($data) && $data != '') {
. h$ Z1 y* \; U $field = $data; ( ~2 t6 }4 a* g" D- e; u0 n" s
} elseif (is_array($data) && count($data) > 0) {
5 M3 G) F$ B, } $fields = array(); , y7 H; q4 }, ]. {% D- j
foreach($data as $k=>$v) {
- j! p$ e& u- b. Q" M/ I switch (substr($v, 0, 2)) { ! \0 i+ Z; L: v! \
case '+=': 3 R# x. a1 R7 H6 B8 F
$v = substr($v,2); % h# V6 Y0 `1 {" c) L. S, S
if (is_numeric($v)) {
/ r5 p+ w+ C" f- o6 a8 { $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
2 S' C$ L, _. \- N! k9 | } else {
$ ?! u0 p4 p9 n, N3 Z continue; , @1 o0 a! X2 T% B' n+ A& ?6 T
} 6 C$ S6 c- h! p1 p" c
break;
\8 b) C) l$ u4 A, t case '-=':
+ c8 B- b3 K8 W( {/ y8 `1 z8 E3 ? $v = substr($v,2); ; N0 B8 R+ i; I$ X V
if (is_numeric($v)) {
* {- M2 u: `( X1 |8 q5 c# o5 { W" K $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); - O I: }" n( P5 z Z
} else { # J5 q, W+ S7 I& c/ g$ g- f
continue;
& w- ?3 z! r9 Y `) `7 v }
8 W8 z: s X! I. | break; , k) b3 `! u6 W: K+ s8 s
default: # p/ v( N% A6 R
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
$ y% f1 R3 i6 {" Z$ z4 Z2 T }
0 ]* A, `: O; \3 k- f8 s/ ? }
" a; C: ~ ]# P4 r $field = implode(',', $fields); " M) P1 x4 \; p6 b2 f
} else { & B8 S! K5 Q, }
return false;
( H T1 P/ f( O+ j" |6 b5 H }
5 ~6 H0 G. m' x- R$ j- R0 r5 Y $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
! L2 F& w; U" U$ ?! {# K print_r($sql);
" g* Z5 _1 h$ R1 y; T; r' g' V4 D return $this->execute($sql); ' `& k! x9 k+ ~2 w
}
, y8 ?: w9 W' v! e1 ^' ]" N% C" c从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
% j* j+ j8 G1 O# C/ M o
( }) J- Q% Z& `1 y; d+ p* L攻击测试:9 U; y0 n# R% A- V: ?7 y
测试地址http://localhost
5 u' C) Q" j5 e 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句; U3 |/ q' o7 J
0 l5 ~; p# Q4 u" o7 D , y: m# F5 T9 @, E* @, K3 h
7 M# I: h7 t' V8 G' m
- T& }5 o, ~5 k5 e
1 {( W, Y: b2 e |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|