中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
$ m. w% I3 X% \5 {3 [, d
漏洞作者:skysheep
( D8 J: T' s4 G+ W, G; a/ a
分析作者:Seay
4 j2 @3 ~4 d/ E: z7 j# Y; h
博客:
http://www.cnseay.com/
f) ^- P9 S* \* k
漏洞分析:
( Y, ?/ Z* z @) [
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
& h- ]7 H8 Y4 y) y
& M- ?. P" e8 [$ W
& O( G+ R, P) ?/ U; h
G) a3 F- C: t" Z5 s2 ]
public function account_manage_info() {
1 c9 r) B5 x! E0 E& I; |
if(isset($_POST['dosubmit'])) {
% [' w& S2 d. C% S
//更新用户昵称
$ Y0 p+ Y( x' Z
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
4 v4 Q; a4 Z6 _$ E5 e
if($nickname) {
( d; E" r9 ^4 x1 ]( K
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
& f- N) k, {" g9 w+ x8 T5 N
if(!isset($cookietime)) {
. B; x( {4 a! d v; b8 i* ]
$get_cookietime = param::get_cookie('cookietime');
. u: j! _' j: {
}
" _9 e) k, U5 u1 ^' m
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
! f3 R. ?7 D+ Y/ F
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
; o3 K% b- g7 `* o
param::set_cookie('_nickname', $nickname, $cookietime);
% C; O6 q# s9 Y; A. {" Q
}
, s2 g" F, R# e: Z! B
require_once CACHE_MODEL_PATH.'member_input.class.php';
^+ o: o- ?5 d! A# \
require_once CACHE_MODEL_PATH.'member_update.class.php';
0 ~* b7 r: j" [2 s/ X+ \
$member_input = new member_input($this->memberinfo['modelid']);
% [! Z# g5 w# S- Q* e
$modelinfo = $member_input->get($_POST['info']);
3 n* v! J* j4 m% T, d# X
$this->db->set_model($this->memberinfo['modelid']);
3 _. [. n. r% c9 t- k" F
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
" S$ {7 A9 {3 y& H+ }
if(!empty($membermodelinfo)) {
" A; R1 W7 I; p$ z7 j
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
, s' E* s7 R. I( k3 j
} else {
* B- }6 `4 [6 p+ d/ g1 d
$modelinfo['userid'] = $this->memberinfo['userid'];
& q( _5 F) m5 X; e3 Y$ p
$this->db->insert($modelinfo);
$ T1 M5 f: U, I- J
}
) F: ?9 ?- m5 F2 ^+ i% b
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
1 D2 W! I, j# \) w! _
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
8 P2 {" w. N6 Y/ g
9 [7 ]' y. C: U7 v8 i1 o
) D+ B# q( i: r8 F* C* Z2 f
( @6 o3 ]' R/ R& |
function get($data) {
; g E' R3 I1 n8 t
$this->data = $data = trim_script($data);
0 i6 k+ K" g1 c3 O9 G2 |% |
$model_cache = getcache('member_model', 'commons');
- s. w+ Z! a% X& I% V: g2 E4 v3 M
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
/ H5 q' K$ e3 }+ y# J
$info = array();
$ i; Y( q6 ~# s7 I. R& F5 i
$debar_filed = array('catid','title','style','thumb','status','islink','description');
5 D# a V, s, t
if(is_array($data)) {
; b. ^# g1 f u/ e
foreach($data as $field=>$value) {
* g5 \7 V! j @, }8 f& s' t
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
{, G5 O5 V4 C; [
$name = $this->fields[$field]['name'];
& L9 a" A* u1 v2 E
$minlength = $this->fields[$field]['minlength'];
' g5 g2 ~% A. s3 ]
$maxlength = $this->fields[$field]['maxlength'];
5 ?1 Z* d2 L* v6 q7 d5 W
$pattern = $this->fields[$field]['pattern'];
5 M* {; ?& i% v: }0 `" F W
$errortips = $this->fields[$field]['errortips'];
3 L C1 X' g2 L: u5 b5 X9 L
if(empty($errortips)) $errortips = "$name 不符合要求!";
; Y& v+ ^; B" ]: x/ l
$length = empty($value) ? 0 : strlen($value);
2 O! m" Q, Q3 O6 Q- F& u8 T
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
: N( v, l( J# j: t2 l9 ^+ }
if($maxlength && $length > $maxlength && !$isimport) {
* k' R' y. q: v; |* @
showmessage("$name 不得超过 $maxlength 个字符!");
" @, _ X# L: X. D
} else {
) T; ^+ ]6 S- w% J
str_cut($value, $maxlength);
}+ A' `% c k: M2 Y p: n
}
1 u2 @, v: C( j; ]6 t
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
1 F) s' `4 w9 M- u# ?
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
+ u! S: Z* Q8 _( ~, N6 C
$func = $this->fields[$field]['formtype'];
) {$ X9 _ n7 `6 ?) }* ^; D
if(method_exists($this, $func)) $value = $this->$func($field, $value);
! m9 j+ U5 v" u) m/ y) C% G$ G+ |+ c
$info[$field] = $value;
1 t5 X8 t; V' P# s- U9 C; `: A
}
) w6 J2 ] H: a$ t
}
* O' s, q- ~9 r5 t
return $info;
* F( [1 b+ \" v1 e
}
4 k/ L; X0 B) L3 {% [1 ]( v: D
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
. L! n; Z# s" A; Z4 R1 F9 V
" O) k; C/ A3 g1 d4 v
再到phpcms\modules\member\index.php 文件account_manage_info函数
6 N1 d) C3 A7 d/ r0 Z! t( O
过了get()函数之后。
( f2 L$ J, W& L5 Y1 {+ V
# Q4 S$ _9 K- z" _6 X# y1 F
. v; r2 C; c4 w
$modelinfo = $member_input->get($_POST['info']);
& r6 }$ K, v2 S9 _& K: ]) w
$this->db->set_model($this->memberinfo['modelid']);
7 q2 _5 _) B1 L' x" q. {( \, z
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
1 Q, {7 v/ } L- @% l4 j
if(!empty($membermodelinfo)) {
1 }6 p* l( [; W q6 {
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
' S" T! H e7 Y. P% `
} else {
4 M/ }4 ^' X; @: C8 s
直接带入数据库,update函数我们跟进看看
0 v8 j) ]" _) U5 s) r0 h7 W
6 _0 R3 R/ \' v" u" U
" }" Z9 r6 h' N
public function update($data, $table, $where = '') {
: e r. @9 x0 Y* T* T$ \
if($table == '' or $where == '') {
- X2 d; s' o$ t& }, ?
return false;
3 A: K6 ~2 R4 q4 q7 M$ M5 Q5 _+ u
}
% t% e/ `5 o4 w& Y. _
$where = ' WHERE '.$where;
1 _- G4 K$ e9 _* g! i/ a1 `
$field = '';
2 }- Z+ |2 e" w* i- E: W
if(is_string($data) && $data != '') {
! I3 w3 v+ n# d: n: \1 j
$field = $data;
2 o3 h9 W% r( Q2 A: b' m& G
} elseif (is_array($data) && count($data) > 0) {
1 g8 U0 @! {* M) b; Y5 b$ w
$fields = array();
0 q# G6 D1 y( X! A% I' r( g& L
foreach($data as $k=>$v) {
% T! q+ O O7 T* r8 h" e
switch (substr($v, 0, 2)) {
# o- H' D; s/ N& s) `6 b/ j
case '+=':
1 t/ o' O9 v& r2 d. ~4 _; v8 Z3 x9 P
$v = substr($v,2);
: v0 V' |4 k# d( `- {
if (is_numeric($v)) {
1 t/ w! ?) j/ {+ a8 Z+ n
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
0 Z, Y7 q' |8 K' R4 z* A
} else {
; h$ }* W$ V, ~+ n" e2 W5 k
continue;
! G! ^! N3 G2 k" F8 E+ E: i1 X! m
}
' q" S8 I0 A2 ^6 @
break;
: |: x5 Z) }% ?" G( K4 g
case '-=':
y% a' B9 v0 E' a2 @1 V
$v = substr($v,2);
) K, W$ V- |* o2 f) l
if (is_numeric($v)) {
6 {: E7 [1 _4 ~' L, n
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 f& k1 S8 c: @" E+ |; {$ l8 c% b. y
} else {
2 z7 T* k2 Q' ?' j+ M7 i+ d
continue;
+ x! r, Q$ h" o2 u1 p/ n: ^
}
0 x. q6 E s! G- W
break;
# s5 X! @0 k2 J! e9 j
default:
6 ^3 C* n$ H1 {3 q
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
: M. @/ X/ m% L* F
}
- x# f. j9 J4 [* H/ K* h" \3 H! Z+ B
}
/ ?# @( m- o1 u
$field = implode(',', $fields);
# E1 @; }5 \, K% D
} else {
2 p! B. n) P, P
return false;
" E9 g( P$ Y; u7 Q% o0 s5 U6 x9 f, ^
}
$ `- I* T7 T' q- z7 P3 b2 d
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
4 q/ d+ K4 l, Z5 M# g/ J
print_r($sql);
) E s" p7 y# s6 u3 z" ], X, |
return $this->execute($sql);
* m, T6 W q1 F H% I" i" q
}
+ P, L1 U1 T$ {& S' j! s
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
& C2 r4 e5 |6 d3 V2 r
% b2 @# M2 ` W% ^/ H
攻击测试:
- x* _9 o3 V" V& J
测试地址
http://localhost
+ Z% y' g0 ?6 i& h" p
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
G5 o, w0 i3 t! R! V
/ k% o' D3 q+ V, j5 I
[attach]179[/attach]
. C: o# p# s1 g+ C* ~* L- S
5 t* |& [8 Y6 I4 j7 ~. R; }
6 e% H0 a' ~: c+ H5 D9 [" m; e, \% m
[attach]180[/attach]
' d8 }! w2 v/ r- s) v
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/)
Powered by Discuz! X3.2