|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告! q* Q i2 v; D2 Y, t. m
漏洞作者:skysheep
' D3 [$ C( d# k. p1 k分析作者:Seay
_# H% U% } Y3 d) M. }博客:http://www.cnseay.com/
, O+ v6 t$ L" d( M漏洞分析:! v% T2 X) f3 `- X
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
( w p7 @3 q5 {- ^! F1 w/ S0 w2 T
* t; ?4 c3 n9 ?9 X$ a, Z
" J! J/ i) g+ T w: i: g * S1 r+ C5 ]& u( X9 m
public function account_manage_info() {
( P4 _ b, i4 l5 G if(isset($_POST['dosubmit'])) { 6 w1 A' Z4 O, E- V
//更新用户昵称 5 A! _) b6 }& B( B/ `2 C
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
3 l8 |2 R" M+ P+ x' B& w if($nickname) { ) | q3 c- m# [+ d1 U- c x; k, U
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
% _% e) J. b7 Y8 ^/ d if(!isset($cookietime)) { 1 \+ o D' G) Y m5 _, h1 |
$get_cookietime = param::get_cookie('cookietime'); / U) B2 c9 [5 y* b1 Z
}
2 U& Y& C; @6 H% m: \: V5 M) p $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
1 E% s4 E& [9 X" _$ h5 B, P $cookietime = $_cookietime ? TIME + $_cookietime : 0;
4 G5 }7 r. N6 J param::set_cookie('_nickname', $nickname, $cookietime);
/ q. {) Q0 x8 l } 0 T+ _4 y7 ?6 o7 Y8 h; F
require_once CACHE_MODEL_PATH.'member_input.class.php'; * Z* _* g3 J5 z
require_once CACHE_MODEL_PATH.'member_update.class.php'; 7 p! T! H8 _: z @: x
$member_input = new member_input($this->memberinfo['modelid']);
6 n( H s! ~/ V1 [. K' T$ |4 n $modelinfo = $member_input->get($_POST['info']); H: X. x8 w0 p1 A# r
$this->db->set_model($this->memberinfo['modelid']);
7 T7 e3 c5 H7 m( [7 ?) P $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
0 W4 y7 C8 g& A* B6 [ if(!empty($membermodelinfo)) { 6 | U, I, ^0 E2 Z! |& }& @/ j) R
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); ( r( w+ O5 e- p6 \' M9 d' v
} else { 4 J# C3 `+ n+ j. Q) n
$modelinfo['userid'] = $this->memberinfo['userid'];
8 Z5 n0 W6 G; j/ H3 Y- g $this->db->insert($modelinfo);
* t6 g( V* d" q) m1 R }
. X" V2 \5 {2 e6 C* N代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,7 u0 C, w1 z" z- ? @$ v! l1 \
在\caches\caches_model\caches_data\ member_input.class.php 文件中:0 n1 j7 D6 X, O7 s7 ~* L; b: E3 v
4 E, ]6 b) n" V, C$ \, q6 t& F
0 X0 C- Y( w9 H6 R1 ~, g, ~ V: S& V& u; ~3 x* f/ b! [2 r
function get($data) { ' W3 ^/ t- z$ c; [( v
$this->data = $data = trim_script($data);
8 H' ]6 F' C7 c9 T0 _ $model_cache = getcache('member_model', 'commons'); 5 y& J% Z% L6 _' D" s2 \
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; ' Y5 _/ [* { V- h( g
$info = array();
. @& [3 u \+ u2 ?$ R( ?: C: u $debar_filed = array('catid','title','style','thumb','status','islink','description');
/ s9 b' `8 [- z+ I. s if(is_array($data)) {
5 G2 f. P9 f- O- @- I$ S0 h foreach($data as $field=>$value) { $ H* j4 \: L% j8 J Y G
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
# k* U* a- ~8 \( l' n. [* Z $name = $this->fields[$field]['name']; 7 a: {- |6 c$ p1 Q! u
$minlength = $this->fields[$field]['minlength'];
% |" X( U6 D- r# J3 ]/ p, F# X $maxlength = $this->fields[$field]['maxlength']; ( c' D% m S' O. H# ?3 G
$pattern = $this->fields[$field]['pattern'];
a6 y) J4 L. a O1 b& Q $errortips = $this->fields[$field]['errortips'];
3 I \* X! j0 f3 a: A if(empty($errortips)) $errortips = "$name 不符合要求!";
$ Q/ U9 b1 ?7 i& c $length = empty($value) ? 0 : strlen($value); $ P( w( a. D& z+ @
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
, y& k- P: w1 a% @# s1 D G2 Z8 Y1 O if($maxlength && $length > $maxlength && !$isimport) {
5 T- X9 f% d$ h' E% k' x showmessage("$name 不得超过 $maxlength 个字符!");
! g# U: ]* A; k# b) G, M# }, q } else { 4 r" L C, I2 b6 Z; F+ Q
str_cut($value, $maxlength); 0 O( [# h% Z" @
}
3 |4 j7 @' t2 k" W! i& E ] if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); $ N, R/ [! E) Z% j( C
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
, V4 D, e9 P% J& \+ R. j $func = $this->fields[$field]['formtype']; # N; F' ?- x9 e0 C9 G# T x8 T
if(method_exists($this, $func)) $value = $this->$func($field, $value);
* V, d& n" |2 d8 a% o' m2 U $info[$field] = $value;
' u0 y, u1 ]5 @9 M% i+ P } ' s% h% `7 i/ _3 U& t. [/ @$ {
} 2 h) I* J/ D0 u! h* x
return $info; 7 u- Q* E( o; B0 {# P' n
}
/ O! Y, ?2 P0 `4 ftrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,& v4 `& V f* l+ P& E) z K
4 U9 y/ M% ~$ [4 O% x: l
再到phpcms\modules\member\index.php 文件account_manage_info函数
& Q6 C" ~& b9 u0 \+ W a过了get()函数之后。
3 k5 i: d, Q0 ?% ~/ F. ?5 c$ Y/ N \8 L y
4 ?& X5 I5 C$ e; G# e( r+ S
$modelinfo = $member_input->get($_POST['info']);
/ N% L* ^* u: j. T' a" _ $this->db->set_model($this->memberinfo['modelid']); # E# |- J/ x& W: t
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
! S1 M( D4 Z+ V% G8 ]: v if(!empty($membermodelinfo)) { 9 q( U4 B+ K7 z2 o- m [+ s' G
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); " _1 u' Q: {1 u p+ \' w" ^
} else { + @. j" M ]% P/ a
直接带入数据库,update函数我们跟进看看$ C5 R- @3 ? l* `& q: ?) ]
4 q/ u- z. l5 x9 m/ t3 o4 G6 @
3 @8 @% s1 D3 e/ [$ X6 M: M
public function update($data, $table, $where = '') { / `# S j" E9 P! w$ B$ d5 L
if($table == '' or $where == '') { O* q5 `4 s4 {8 S* g# X& X6 i& l$ B! v
return false;
6 N9 `( P0 T0 b3 ?+ C }
1 {& F+ q. U1 r- a7 ]/ W $where = ' WHERE '.$where;
& _' U6 @& Q0 k+ v! I $field = ''; , G, w, @* ]- |
if(is_string($data) && $data != '') { ! b9 {' |4 @) X" Z5 _9 A
$field = $data; 3 E) b1 S3 d; m- B( m( X
} elseif (is_array($data) && count($data) > 0) { 0 A( `4 Z( f% H7 x5 J+ t
$fields = array(); 4 V$ c" y* A9 t* a( ]
foreach($data as $k=>$v) {
; T* X' y; j" }$ P4 E( N( F5 ` switch (substr($v, 0, 2)) {
! X( Z: i( C; D* t. E& O, o. T case '+=': 5 F0 P! z9 L& x" B6 l5 {/ {
$v = substr($v,2); - ]1 I4 L" R8 I9 \" F* a& t9 Y+ i
if (is_numeric($v)) {
) [# c; \" J# C! e/ v $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
5 ]8 x k0 ] o' _9 j } else {
( G; x7 M; i& Q6 [. A7 p! k continue; 1 ~# \ w2 S; [
}
1 ?+ o6 |- v) o break;
8 P1 \ g+ X6 a# h6 F5 I) E/ I8 n" y case '-=': ' o& W& @% v; y7 F5 Y4 N3 x
$v = substr($v,2); 5 d% f. S, }5 w( M
if (is_numeric($v)) {
+ G9 {4 A6 Y. F, _! E $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
( i/ F, a) S: r' S5 a5 s; d } else { ; N4 O# J0 b0 l$ C
continue;
/ ?+ A- x1 c- d: u } 9 R& q- w0 ]' P1 A
break;
* @. P) F0 Q. J! g: _! f default: 6 u P8 w- N( I$ q: W6 R7 @
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
$ n! v( r0 t4 o3 T }
* k, h9 [6 Z3 t+ d1 m3 s6 P0 B } & W1 @, u( |/ B2 b
$field = implode(',', $fields); 7 Y6 C- K' a7 j- M
} else {
/ m* R1 [! d/ o: y. [ return false; 1 ? W0 Y1 A8 `: ]: \
}
4 y& e& c& J* V' X- z $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; * v# `3 Q' F1 D4 x5 m
print_r($sql);
+ D) P2 t1 l8 ]( r return $this->execute($sql); 6 X8 O: C8 Z4 M/ J- b0 l7 d E! M! h
}
+ n& b! e6 r& y# p6 g: }) D从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ @- R+ U0 f3 |6 y: W& H8 y$ }7 O. ~5 [' K& _, _+ s# F! G
攻击测试:/ l: N8 L, \( V/ l7 G" i. m/ n
测试地址http://localhost
- t) `. }1 {0 S- R" _, }" ?' R 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
L2 B6 y2 g6 J! w) B
7 `6 U- L5 u. F, f6 {* T- b " V8 [' v0 K! A% U- h3 G
+ C# E7 M5 ~4 i( _
4 K0 n" j' Z) ?& ]( ~/ c# D
6 d( ~" i# L. y4 {! `' o: B
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|