|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告; L2 \, u. R6 ~0 l7 l
漏洞作者:skysheep
# \ @5 d0 W5 R8 i9 k分析作者:Seay# p+ o$ }& t1 N8 ?' C) {- o
博客:http://www.cnseay.com/; `8 b) u: Y. o9 s9 M8 p
漏洞分析:
! Z) Y" e; o3 L- z 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
2 }' Q3 [3 ?/ [) j/ Y! W, M* m& M, u2 w5 H" e7 [0 @5 p' k
- L! s* O) K7 Q; _9 H
) @8 _& E8 E5 ~% a/ npublic function account_manage_info() {
! x w; c3 m3 ~7 D% T- x& Y! Z if(isset($_POST['dosubmit'])) { : q4 A( E4 y- a' B
//更新用户昵称 0 X% L# a3 M6 J. Y6 J5 `% ~2 M5 q
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
! z) k' f& ?" i+ y0 D, g if($nickname) {
: \: w# ]9 N# q $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
" I& G$ D( w- a2 | if(!isset($cookietime)) {
: b! u0 r5 f$ r( Q9 V& O $get_cookietime = param::get_cookie('cookietime'); ) }: e# N3 V9 O
} % D. o% ^6 c' t2 b# y. f
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0); & n, v7 S$ B! p3 M& m; L
$cookietime = $_cookietime ? TIME + $_cookietime : 0; / r |: o0 F) y7 X) e
param::set_cookie('_nickname', $nickname, $cookietime); 1 z) u. [) B2 E: K4 z# U$ F' l
}
: f. q: l) J% \ require_once CACHE_MODEL_PATH.'member_input.class.php';
# }2 y& S$ \: p, m require_once CACHE_MODEL_PATH.'member_update.class.php';
+ u1 o8 ~# I0 r! p $member_input = new member_input($this->memberinfo['modelid']);
0 l% [* F! L. [9 \5 ^ $modelinfo = $member_input->get($_POST['info']);
6 l& x; w5 Q+ l2 ]: B* |2 g $this->db->set_model($this->memberinfo['modelid']); $ a' W. i2 L' h2 ?8 S+ y
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
; ?* k |& E5 H+ C$ _' | if(!empty($membermodelinfo)) { 2 p, [# H/ J9 ]" V' t- |, y
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); $ a3 K! M. f P9 Y) X% i
} else {
& s/ L f( @, a $modelinfo['userid'] = $this->memberinfo['userid'];
4 r5 W( ` x* U7 a4 [& z* K $this->db->insert($modelinfo); ' l# W( u/ g; S/ g: p/ |9 {3 }
}
4 f% `5 F! ]- U6 E8 \: Q代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
y- c+ X: W3 ~9 t- ~在\caches\caches_model\caches_data\ member_input.class.php 文件中:
& F8 w p9 ~ k/ l# G7 h) ?' ^- \+ K, Y4 f9 V3 y% W, M
3 A0 c9 j8 v7 \( E- s) F+ {, E
1 _5 C- U- N. x
function get($data) {
! E6 D, x: T/ W/ C2 n $this->data = $data = trim_script($data); * o+ r# ?2 O2 l
$model_cache = getcache('member_model', 'commons');
/ R4 T& @) a1 ]/ u! A6 h7 w4 p/ \ $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; 8 [5 V5 J( C/ c y1 f6 T7 Z
$info = array(); " y9 ?2 L$ U. q0 C6 ^
$debar_filed = array('catid','title','style','thumb','status','islink','description');
6 ]+ \5 h! C& |3 u if(is_array($data)) { * x$ H* k4 `. m. d$ q. e3 ~8 P+ A
foreach($data as $field=>$value) {
! m! c& {5 X$ [. z3 y if($data['islink']==1 && !in_array($field,$debar_filed)) continue; + R' R. q! f3 J; G4 N/ t9 S6 e
$name = $this->fields[$field]['name']; j# F4 t$ w9 o: J& Q$ k% a. F
$minlength = $this->fields[$field]['minlength']; 5 W8 t" w/ }6 Y3 P6 O. ~% T. Y: d
$maxlength = $this->fields[$field]['maxlength'];
1 }7 b$ ^! o7 u; o0 ^9 ` $pattern = $this->fields[$field]['pattern'];
2 A+ [0 |: C1 e. W7 @, t4 f $errortips = $this->fields[$field]['errortips']; % N/ A" v4 J8 J8 w9 k
if(empty($errortips)) $errortips = "$name 不符合要求!";
. ]$ A/ S- c) ^2 p9 I& t2 Y! ~ $length = empty($value) ? 0 : strlen($value); 2 V+ w5 ^# i0 a3 S# @8 F) S1 G
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); & ?7 @+ ]8 n* ^- j
if($maxlength && $length > $maxlength && !$isimport) { # }- Q6 x/ M/ A
showmessage("$name 不得超过 $maxlength 个字符!"); 3 ~0 M+ U; h' r1 z8 ]/ z
} else {
; |( {" v* V+ B: O5 E- q str_cut($value, $maxlength); ! H }- k. m. f
} 4 H; R/ v4 G2 F+ ?$ Q& Y E
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
5 h. i$ t8 s2 K+ b3 Q" ^* i V6 D if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
& o: t) a* I/ O" ~9 P0 d4 c o $func = $this->fields[$field]['formtype']; ; h$ a( B. b4 M7 A* V7 }; ^
if(method_exists($this, $func)) $value = $this->$func($field, $value);
( {! [8 O. v6 P" c' X# S $info[$field] = $value;
( C7 b4 ]+ L/ \6 ]7 F' w- j }
+ N/ d* \6 v( a9 D1 f8 r7 v } 9 E8 X( X8 z5 B& j4 O; Q
return $info;
+ J3 m' Z$ S4 V } ) Q) R" {! j/ X& I/ @7 r( }
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,/ G' g, ~. y$ p7 Y
9 m/ I5 M$ |) [+ n; q再到phpcms\modules\member\index.php 文件account_manage_info函数
# O# g& n; e5 r; I9 t过了get()函数之后。+ p3 S: ]1 q* L1 u8 s. O2 U
8 k0 m+ g0 W" X1 o, ]/ `) z
' P0 j0 v$ f0 n; x" g$modelinfo = $member_input->get($_POST['info']);
2 F! W( l( |; g# j* q% P, o $this->db->set_model($this->memberinfo['modelid']); ! W* |8 {& M! r8 g+ |( ]0 s
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
3 U. P2 p7 C/ R/ C' \+ y if(!empty($membermodelinfo)) { ) P& q- [& s0 y: I, z1 |
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
* J) i/ X) }, b% ?. w" S2 @ } else { 8 T1 q. {' B+ r
直接带入数据库,update函数我们跟进看看
8 V: s% o( z( r2 C0 x+ {$ J- D' ]3 ^, j+ s: H
# p4 D, ~! _% I) c! m7 h L* Wpublic function update($data, $table, $where = '') { - s$ \. ?% Q+ y y$ ^- L
if($table == '' or $where == '') {
; x. Y% ~* ?2 U return false;
& s8 P2 L6 p& v) F } , D# {( n R) C9 {
$where = ' WHERE '.$where;
, o& l5 e. k. Q# ^$ Z5 l9 Z/ O: G $field = '';
9 j8 T2 y( H% c* l, ^ if(is_string($data) && $data != '') { # j* V. ]+ U5 K( i3 T
$field = $data; 7 U3 `) U$ v9 n6 q1 ^- D( [
} elseif (is_array($data) && count($data) > 0) {
/ M% H% o5 @5 C$ \* Q $fields = array(); 6 V( N: x' p8 I7 s
foreach($data as $k=>$v) {
) H+ C# e2 P/ t+ D switch (substr($v, 0, 2)) {
# U+ ^: C/ j6 |+ B# B& e* ? case '+=': 3 z3 p1 c B" f6 t8 B6 h
$v = substr($v,2); & k* S: M# b5 |
if (is_numeric($v)) {
$ C# o% n/ A, k $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
& C0 S9 H3 o* N) u& F! I9 G; b } else { 9 }* e: I) u& N2 J* ~
continue; 7 p( U" l- G/ N
}
8 }% Y! U' i- v2 J( R. }3 _ break;
$ D2 I- l- f" O+ s4 U% J case '-=':
! |9 |- B, ~2 M& t0 y $v = substr($v,2); & j/ @( l0 e0 G; t! k4 B
if (is_numeric($v)) { : P0 V; V( O. g- c( j
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
( y& j, F1 O) h5 v } else { ) J$ S# F5 C, i9 j- Z
continue;
9 Z8 C* A% f3 J9 `% t$ w } 2 F' N9 @5 i3 X3 J' ~. t# i
break; ( k# W' z7 A4 O# U* `4 F
default:
[3 a; l7 k8 T! i; R7 O: t8 V $fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
' C6 Q D$ t- ^- X+ a3 X } 5 R4 P7 g- a: }" V+ ?! i) Y, D6 {0 c
} . N% t& T8 v1 V, c: G4 E& {
$field = implode(',', $fields);
2 W1 }9 _" M/ H; e% T; s. `1 R6 B } else { 4 z: p* M* X" F# M
return false;
1 k4 \( G+ p5 s) Q }
@9 z7 z$ i. ~/ n8 f% X; v1 N/ h $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; ) d( G) x! N2 Y2 r& h& O
print_r($sql); # ?5 }" L/ H* r" i' z
return $this->execute($sql);
7 X9 P0 H, i$ v9 m6 u! u- Y9 t } : j5 Q* i! Z8 {/ ^: ^. D
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
/ A! @# w/ ~& a$ W6 r4 ?+ N4 p5 b
9 I: u& v8 o. B+ m, a攻击测试:8 C0 p c4 i* ?! b, w
测试地址http://localhost
x0 u$ a0 Q9 A' g 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
H/ B1 u4 v( D0 v/ g% M$ j& `+ i0 \+ G' n6 c. w
. e0 z& Y, c% g- S1 o1 `1 M& d) C) i) z/ n# Y3 {8 {* j0 W
7 p1 O. ~& H# v2 i! q0 Q
! y+ L0 O( a n |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|