|
|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告3 a2 s9 A+ D0 f) y1 A# v
漏洞作者:skysheep/ s6 p$ a' S. F. P; g
分析作者:Seay
2 @* ^" p( H) e& R8 I: q博客:http://www.cnseay.com/+ _8 e) H8 h( c: T& {1 Q2 ^
漏洞分析:
8 Z* |! z3 s8 W- E! G 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
5 ?; Z, N, x* P. _* t) z4 u8 I' n3 ^# K
/ g1 _$ B3 M" | 2 b5 U6 e# c" |5 L9 d% U2 J/ ?9 `
public function account_manage_info() {
* ^8 ?8 O R) z8 h' z5 P if(isset($_POST['dosubmit'])) {
0 @2 @( Z. s4 i# K //更新用户昵称 2 X. k7 J2 N6 U) \; ~* U* S O
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 4 U8 p: ], y! o* Q
if($nickname) {
- J, p( _: O# b/ [/ W $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 5 [6 n' _7 B4 U' b: K: o0 Y9 n
if(!isset($cookietime)) {
4 u5 ]) C' b/ x$ c1 C8 n $get_cookietime = param::get_cookie('cookietime');
' f8 K$ T' U! z: B& c }
% k9 W6 s: S0 |. h8 B; n. @8 F3 } $_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
$ E# j0 A, w/ J4 J$ k. D0 y $cookietime = $_cookietime ? TIME + $_cookietime : 0;
2 e$ V- b! p7 b9 Y+ P( v: d/ e5 _3 U param::set_cookie('_nickname', $nickname, $cookietime); % Q4 K" ]7 u- X# a2 D
} ) ], H1 z: s2 l5 p8 v
require_once CACHE_MODEL_PATH.'member_input.class.php'; * M( s5 U* i: g. Y& ^( {
require_once CACHE_MODEL_PATH.'member_update.class.php'; " D. M2 C& z" D4 ^5 B, v( r
$member_input = new member_input($this->memberinfo['modelid']); ) Q2 q$ u# s+ f3 R) [
$modelinfo = $member_input->get($_POST['info']);
; x6 S1 b0 _7 a5 s4 V$ ` $this->db->set_model($this->memberinfo['modelid']);
) }6 D% m5 `2 h3 H( T. U: I: U $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
' ]# @ n- }: O# B% ]' y, ` if(!empty($membermodelinfo)) { 1 |+ w& P) ]9 B- ]: E) n! U$ r$ ^
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
. Z: R" U7 \* O& w } else {
% Z: t8 }. w! p' I# U $modelinfo['userid'] = $this->memberinfo['userid'];
; m' D' l: R) j( O1 n0 o- s $this->db->insert($modelinfo);
; q x/ L8 N. p0 _: g5 q6 b } 0 e5 V+ b% o/ Z
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,3 A& G; i/ c; _. ^' [/ J
在\caches\caches_model\caches_data\ member_input.class.php 文件中:4 @1 F. n' ?/ U1 r8 E7 y
4 L9 y5 @9 T7 i; c* r' F q0 n
4 e1 v3 b3 w; ^4 b' ]
* ?# W* h7 P2 F& V0 D- L! ~" m( Gfunction get($data) { # t0 y% X8 V7 V0 r1 e- ]
$this->data = $data = trim_script($data);
% Z# h0 t9 Q9 e $model_cache = getcache('member_model', 'commons');
7 |. N# t& j' p: y6 x $this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
4 }* ]/ Z) Z( V9 Q- d $info = array(); * q( N! q. d6 E" Z, W! _
$debar_filed = array('catid','title','style','thumb','status','islink','description');
d* i& [) j. w \ R if(is_array($data)) {
) u+ ^7 u6 s8 D, ]9 M foreach($data as $field=>$value) { : E: j/ w! z/ ]. D! p2 J
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
2 Y- p/ K5 W% Q+ A3 [( R $name = $this->fields[$field]['name']; + B5 J$ Q+ | |
$minlength = $this->fields[$field]['minlength'];
5 C# C8 b7 z& `2 r+ z4 f) c6 W6 T $maxlength = $this->fields[$field]['maxlength']; 6 ] a7 R+ Y' u8 U. ~3 }5 x
$pattern = $this->fields[$field]['pattern']; * p1 s4 Z; [, f0 ?1 I
$errortips = $this->fields[$field]['errortips'];
5 J$ e1 L; ]+ ] if(empty($errortips)) $errortips = "$name 不符合要求!";
' B0 W3 ~) W( Y7 I3 M M $length = empty($value) ? 0 : strlen($value);
: y' [" I5 s- Z7 \. O, m0 }" m+ G if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); 1 M8 c. L% T' `2 I) d% @3 `# M) |8 U
if($maxlength && $length > $maxlength && !$isimport) {
" h& c5 u& M' @ showmessage("$name 不得超过 $maxlength 个字符!");
9 N) V0 u- b$ C } else { 2 {. P) x$ T: R. Q. G* m
str_cut($value, $maxlength); 7 t) }1 c- N2 c7 h1 Y$ |
} 8 k8 E; |0 A2 B( K& s
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips); 9 J2 I3 C, z2 Y* F' N
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!"); - I0 g$ [5 K4 ^7 y- n' J. T
$func = $this->fields[$field]['formtype'];
; ~8 M# }! a: U! Q: v _+ \, Z* ^# ] if(method_exists($this, $func)) $value = $this->$func($field, $value);
# K' Z9 O Z( \8 h/ S $info[$field] = $value;
3 j4 y& l |" K: f }
% x6 F1 A9 a3 V% E% N } . e" E% H5 c; _2 W9 z9 p7 S
return $info;
# m! M0 Q* V5 y } , H# `) c- j5 L! i
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,- S, ?* S8 C4 t. G0 M
8 T3 H" Z# K. l& @
再到phpcms\modules\member\index.php 文件account_manage_info函数
1 T( P. F* z. }9 J过了get()函数之后。# ^, D: D/ e5 _& R, h6 _* h
: g. T" m% Y8 j& f+ B# S
2 V. L& [' V3 ~5 A3 H* D, \: ]$modelinfo = $member_input->get($_POST['info']);
3 A( L* m! Z, H# v1 ] $this->db->set_model($this->memberinfo['modelid']);
% L8 p0 O, j- ~5 k6 D, B W $membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); * J3 o: L$ o2 q$ n) |0 U
if(!empty($membermodelinfo)) { P6 g6 J3 a/ q: p4 ]0 C! X
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
5 B- D: T* x* E! D } else { / Y( |6 Y: d1 S5 G
直接带入数据库,update函数我们跟进看看
! e5 W: m# x/ b/ \& b. q9 L& M, [
' W4 y7 Z% T( _9 ]
0 t, u+ P/ [. C8 `( upublic function update($data, $table, $where = '') {
1 ~) V- J) \8 _1 A3 J if($table == '' or $where == '') { 2 t! s/ s; Y! ]9 k0 `2 ~; Q! G& C
return false;
. X+ t. L1 v) k" S }
$ |5 c& [9 Y- M $where = ' WHERE '.$where; ; J. `/ I2 u% Y3 g2 v
$field = ''; 3 O* T/ f7 S4 V. C/ U/ D7 l
if(is_string($data) && $data != '') {
9 T& s5 S% q" o+ ?3 x- A1 Q $field = $data;
6 \# Z& Y4 h6 i0 L( L } elseif (is_array($data) && count($data) > 0) { % f% l0 M0 S- D4 k2 K1 P
$fields = array();
5 v- ~, C; q |" W foreach($data as $k=>$v) {
( C8 O Z) d+ w- G5 f- z switch (substr($v, 0, 2)) {
, g. d( ~% A1 n; b, e8 z* x case '+=': ) o; j( G( _8 f8 F/ s( k$ V; p
$v = substr($v,2);
: l' ^" h7 v* \8 }% S; P( `% ` if (is_numeric($v)) { 0 z, c+ X8 O- z7 ^+ d
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 9 b7 n8 O/ Q: l0 j3 Y, `! s- Z; X; d+ z
} else { 5 d" L: k7 @' X3 p
continue;
. b8 d8 E% }1 |3 k% X& d }
) S+ f) o. ?8 T$ F/ { break; ( C4 X3 l; ]4 ~
case '-=': $ {' p8 A7 b: x) _8 x
$v = substr($v,2); + j1 I5 V+ [$ o2 a% V5 w: A
if (is_numeric($v)) {
+ S. I3 z- z7 K5 G4 F, A $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
( `1 A% a* X. s+ l } else { 1 q5 f+ g( p t. s: P* k( f: @4 _9 d/ @
continue; . C8 Z# q2 B/ f& f5 g
} + @4 T9 @( h5 c8 _3 g( w7 M( b3 @+ g
break; 9 J8 r, v7 q. T, T- H7 J& m5 P ~
default:
Y# w. w* V7 k2 R1 S $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); ' m( I: C3 n6 @$ {6 Q" x
}
9 n: ?4 L$ \& N2 N6 @: i" F* f% h } # c m$ h/ C4 n q1 y: g3 F
$field = implode(',', $fields); 2 \; o( s6 g% I; W
} else { . K- t0 p: `/ q) {
return false; 2 O) k1 o. E/ F; b l; x
}
P9 D8 w% e. R7 h( Z! F5 ] $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where; 2 X8 ^/ e0 _* u+ b2 D5 O/ @# N8 Z. s3 e
print_r($sql);
# J- ~: T- L4 f8 X4 W0 r0 z: { return $this->execute($sql);
" u! S6 j& z* M- Q } : _8 B! F, w) |, `4 B
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
# P, L; v4 B4 P1 _" q$ c( Y
* d. b1 i' c1 U% J5 x攻击测试:* E9 Q2 i( x$ t; S2 ~. c
测试地址http://localhost
8 U( _& i5 F) n) D 注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句# V7 H" |! S, t2 W2 P% X
4 g" @6 q }# D. N: z- P % B0 C" T) P$ }
! g O+ Y+ s; {; c; z
& q: {2 n/ a8 m, I' Q; ]$ H' I) ^
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|