|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告1 }' S- }! b J7 A: m% f
漏洞作者:skysheep1 ^9 v$ _) x5 k! d4 f5 ]
分析作者:Seay( Y' k5 s) r3 X7 U8 t3 K, k9 N
博客:http://www.cnseay.com/
+ o+ @" W; ] ~5 v4 z/ `漏洞分析:
- w4 Y5 ?; M5 v3 ~( O8 f 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
4 {3 Y4 j2 n2 j& _6 ?* E1 s
9 P. Z9 {2 i; O9 h. d- A2 K1 e$ R, ^/ G, @
/ S7 f( I# q* D$ E+ }public function account_manage_info() {
& O8 U2 q- C" f X5 o, e# a# J+ R if(isset($_POST['dosubmit'])) {
5 u* ~" P; f( @3 S //更新用户昵称
6 A+ \ `2 M9 j9 g# |* z) p $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
% }! C6 s7 P& M" }2 s5 z if($nickname) {
1 c l( D' K, Q2 j( d5 w $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); * m Q9 h$ d$ b# I
if(!isset($cookietime)) { - i+ W! ~4 S4 }- h
$get_cookietime = param::get_cookie('cookietime');
( z. L& Y$ _4 i/ S* k# u4 B }
. H* n+ k0 r5 L7 }+ Q% E7 T6 U; x $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); + z3 E7 S/ ~9 Z$ C1 l8 W
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
. `' T% N: a( R- X8 x param::set_cookie('_nickname', $nickname, $cookietime);
3 _2 N0 {0 A+ S. D. R' p! c } ! I+ l; Z* I( y! X! c
require_once CACHE_MODEL_PATH.'member_input.class.php';
& C! n! A1 a6 m L require_once CACHE_MODEL_PATH.'member_update.class.php';
7 s4 Z6 a% W( e+ D $member_input = new member_input($this->memberinfo['modelid']);
/ t% U, h4 l' ~5 A) ~* a $modelinfo = $member_input->get($_POST['info']); & \( X( L' P- @0 R: n
$this->db->set_model($this->memberinfo['modelid']);
. O( x8 S$ ~. _/ J$ W $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 6 l7 P2 F8 w$ i
if(!empty($membermodelinfo)) { 5 E# q3 J6 F- k+ d( w) A w
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); h! b t1 F1 ^4 z; X
} else {
( `1 n5 B& v% d: v $modelinfo['userid'] = $this->memberinfo['userid']; : u3 |" J( X# ?8 I1 u7 i
$this->db->insert($modelinfo); j4 o! s. F. P+ X* O
} " c& F1 v6 g1 h9 Z) V" k
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
3 K# ^! W0 L5 G5 Q( X9 n( A" a2 w在\caches\caches_model\caches_data\ member_input.class.php 文件中:' D0 {: X* B- g
# F4 t( Y0 S, _0 r1 w+ A
* j: a* e: d" ]9 X5 o7 J; g) ?
; K0 X9 Z" f7 y8 b$ e% l) ?) v/ T/ n
function get($data) {
2 N) N% A3 p# `0 X8 U $this->data = $data = trim_script($data);
/ b N4 x; ^' f $model_cache = getcache('member_model', 'commons');
h$ v( D: z: V5 U- `) N2 D& Z' z $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
* O0 n% A* R$ x; U9 E $info = array(); 1 h: M$ p. C1 p, Y, ?2 o
$debar_filed = array('catid','title','style','thumb','status','islink','description'); + {* b- }% ^# I1 q( h Z) N, @
if(is_array($data)) { O- H2 K: u! U6 f- y
foreach($data as $field=>$value) {
, Y( ^# `3 Z* g! u2 \- _4 Y if($data['islink']==1 && !in_array($field,$debar_filed)) continue; ( ]* g' o# }' T6 {/ M
$name = $this->fields[$field]['name']; , c2 I) I4 \: w
$minlength = $this->fields[$field]['minlength'];
& \# e! H# S" Z $maxlength = $this->fields[$field]['maxlength']; 4 a2 _ P5 u. ^* S2 S
$pattern = $this->fields[$field]['pattern'];
6 \# z9 u F" |7 n) S3 I1 s $errortips = $this->fields[$field]['errortips'];
s3 f/ b! }- W/ s/ @7 S if(empty($errortips)) $errortips = "$name 不符合要求!";
% x9 ?7 Z& M' ~! k& f# @) g3 a $length = empty($value) ? 0 : strlen($value);
2 N. P6 f' T: {, `$ ^" ` if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); x, R _5 s. h# @; `( k6 H* K' b3 X
if($maxlength && $length > $maxlength && !$isimport) { " U( f/ o2 b# P# m: y
showmessage("$name 不得超过 $maxlength 个字符!");
* h; x2 u" c- K% \7 ^* w/ ]( m } else { ' E, u4 f" M" ~! y
str_cut($value, $maxlength); # ?% F- n3 S) _3 h- {% O% ?' n" d
}
/ `- f' O3 S! | if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); - J3 D* P3 z Y" k( K
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
. `: ]' D; h; U5 |" J7 V $func = $this->fields[$field]['formtype']; 4 \: y2 j# Q8 d- \1 Y) V0 R
if(method_exists($this, $func)) $value = $this->$func($field, $value);
7 t8 }& @. R- L6 Q9 S% o/ t1 a w $info[$field] = $value; : a0 e$ J9 d5 v
}
6 d- ^3 B) u* {8 [2 a+ i: ]2 B }
2 |# ]7 E3 K2 T; z$ \) x# d' b+ z1 W% U return $info;
, s4 g7 K$ _1 [ } " D$ Q8 f- i$ V& K
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组," E$ u5 a% y0 |7 P2 Z
, y k2 Y. ]- Y) j, _6 `再到phpcms\modules\member\index.php 文件account_manage_info函数9 f( `- `$ A8 s) M: p% K
过了get()函数之后。! T" D& G+ {/ `# x+ b
0 @3 v3 A1 `) E$ E. C" m F% |4 H% x6 U+ L" P9 L
$modelinfo = $member_input->get($_POST['info']); / E' u9 c8 w: O4 Y' Z% K1 V4 i
$this->db->set_model($this->memberinfo['modelid']);
2 J1 j8 Y: H# Z5 g9 n $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
% t7 C( k1 B( b+ o( a5 v9 ~5 k% [ if(!empty($membermodelinfo)) {
! u" [& B% _( t" g $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); , \/ ?6 j5 Z Z! k$ j9 V
} else { 5 l2 B* r% a- O/ z
直接带入数据库,update函数我们跟进看看+ m w& `* O! k5 o5 Y' T% ]) Q
5 G; M0 `3 L( W. G3 j$ B3 E
C( V: L8 T: c+ cpublic function update($data, $table, $where = '') {
1 m* d* z% o0 c8 {4 U if($table == '' or $where == '') { ' E, k+ \0 i# D/ w1 u$ y/ v! U7 u. V
return false;
1 X( \ T2 |0 C# \ } / B& E7 t# M' z& \" n2 q" c& M
$where = ' WHERE '.$where; & U5 f9 H. Q! P% @, ]
$field = '';
2 \, P. n- l0 w! U* B if(is_string($data) && $data != '') {
0 F# P. ^& N% l- u( T4 { $field = $data; % f! K+ i; p9 R1 }
} elseif (is_array($data) && count($data) > 0) { 7 w4 m' A# Y" l# N5 R+ w
$fields = array(); $ K3 \! G0 l& ~6 h& h
foreach($data as $k=>$v) {
6 N+ p4 i5 H" l$ P; S switch (substr($v, 0, 2)) { * T. A' t& q2 H% i B
case '+=':
5 @( j& a7 R c: Y) G; h: r $v = substr($v,2);
4 ?4 @8 N2 k) y9 }' ~& W if (is_numeric($v)) { 7 i. p9 k$ Y9 G0 _; Z) u8 n( [* D
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); : x% N2 a; D x9 G3 C( H
} else {
! u5 y& }) S# S7 [) I$ Y4 s5 ~ continue; ; L" k: I. c" m; s: ^0 r- F! U
}
5 A9 t8 y3 [6 e$ M break;
8 M+ p& x( {6 s; z# j: o case '-=':
% q; U0 w4 I& ]8 ]' {4 [ $v = substr($v,2);
3 f {& C5 X8 |/ v, w if (is_numeric($v)) {
4 J3 T* B+ L% }! n2 i# ]% a $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); M1 P$ s/ o. z" X( a& s2 T
} else {
7 R; @" y% l5 M5 K& x3 r6 r continue; ; c: k2 G6 [1 h7 ~1 s1 v* G0 c' g& t
} 7 X0 t5 a( U' L5 |$ N
break; ! w1 ` R/ p) c+ F: x
default: ; X' Q) Z* Y& e5 `4 v' C5 b! j
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
+ G0 N' E+ k8 p- v; M; V }
% ^8 l8 ~8 n; Y) T } : ~3 O, m, y, y, u
$field = implode(',', $fields); & I; p6 p f% n
} else { 1 C, O. r8 E8 {# ]5 g7 i
return false;
7 ]& P- y$ |! K- S+ ` }
, `7 m+ _) S3 G' x- t4 | $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
: x& A# ~: r$ k! k' f print_r($sql); : m2 I4 X v5 K& i+ z5 J3 X
return $this->execute($sql); 3 v# z o) @8 x* p' `; E `
} S( d% R, c/ b4 t. q# B9 g4 N
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。2 Q! p# M( T8 I( c% u
2 ~4 q( r% N/ j0 M0 Z攻击测试:
; W- |$ Z/ `( V _" z% }/ P测试地址http://localhost! s7 Z; |& F1 G1 ^3 n/ R3 c
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
9 h0 ^/ {4 K1 h- L$ M; _& B. c: W2 ]
+ P' _+ L8 R9 n ! ]0 G) f- c4 U( g, W4 J$ n$ b
3 e6 ?9 [$ v1 r. Y) ?; U: g
7 F! `+ X0 @4 m
1 o# S. @6 H: L- X; Z% v |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|