|
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
' N( c! A1 X" K9 C3 Z漏洞作者:skysheep+ P5 h& o1 ]9 c
分析作者:Seay
4 t& A( {( \. o1 ?! b4 m7 V3 a博客:http://www.cnseay.com// j( d1 `9 q1 p+ E0 u H3 I# |
漏洞分析:
6 }- I2 L+ c% i: ~# o 漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。* Z8 W7 B; Y7 m4 Z% W ^
, F e4 O7 y) P# G4 d4 m
2 u7 n$ }5 U. }/ p; r" N# J7 B. { , P- m2 N. I) Z' m
public function account_manage_info() {
6 _' ~) [8 D0 a5 p$ r/ C9 g if(isset($_POST['dosubmit'])) {
/ o, h* `. h( Z% m //更新用户昵称
& z% M2 f/ H7 G. C. H $nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : ''; 7 i& G$ v$ m! v: M& a F# w* ]
if($nickname) {
+ Y) K1 Y5 y& e3 _2 q# o0 g$ K $this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid'])); 2 {4 J: ~9 W! Q9 z6 B
if(!isset($cookietime)) {
; _- K+ `" V y* f/ [ $get_cookietime = param::get_cookie('cookietime');
+ e9 `' Z# W+ R+ a9 W4 } } ) c- _" ?+ ]- I! C0 c; O
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
3 e( j- a6 M& r2 ] $cookietime = $_cookietime ? TIME + $_cookietime : 0; 5 j, ^- p8 s" m& p( V
param::set_cookie('_nickname', $nickname, $cookietime);
2 I1 K* W" N9 |% u \! q+ u4 M9 a. L }
! m* o3 O ?' s0 U- S6 ` require_once CACHE_MODEL_PATH.'member_input.class.php';
6 d/ A2 h+ d8 P( V3 y! p. \& | require_once CACHE_MODEL_PATH.'member_update.class.php';
6 J5 |; K1 }" b$ v) a $member_input = new member_input($this->memberinfo['modelid']); R6 m3 A0 N z# s5 i
$modelinfo = $member_input->get($_POST['info']);
4 Y8 R/ I% G- O. b $this->db->set_model($this->memberinfo['modelid']); , \1 l5 V# K4 k: w3 e
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); 3 h, I: ]+ d& s+ H- Q
if(!empty($membermodelinfo)) { . R, C ^. \$ y' S# a3 l
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid'])); 5 R+ n1 n' S0 u; X# `
} else {
" ]# ?% P% F `( e $modelinfo['userid'] = $this->memberinfo['userid'];
0 q# c. e5 l7 i- g% o5 m $this->db->insert($modelinfo); ) z) H+ t! J1 r+ G# v' m0 f
}
0 _; b5 |! v' e8 Q& V代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,0 H, \* L7 B' y$ x5 t- R
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
( j0 Q5 @" G8 v1 j; G' C" d- }+ a, h* P$ c7 s
2 a! b3 h8 C. U- A9 i; B% R
/ ], ~# \$ {) ~* w: f6 A
function get($data) { 0 N$ \0 ]5 f( K) B2 e5 M
$this->data = $data = trim_script($data); ) g% ?' S: t$ t4 ]
$model_cache = getcache('member_model', 'commons'); , b$ b( c2 @3 G! t {2 d
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename']; " j) _- {0 |8 @- R
$info = array();
( P9 C! y4 ?" ^! ^ g $debar_filed = array('catid','title','style','thumb','status','islink','description');
9 w, }, H! y+ M if(is_array($data)) { , l6 G5 }6 z% h" t. `) x8 J
foreach($data as $field=>$value) {
/ J" Q/ @9 t+ w& a& w9 e if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
3 t6 e4 N6 F1 {4 _3 Z/ L $name = $this->fields[$field]['name']; ) C) u. G; u7 G
$minlength = $this->fields[$field]['minlength']; / o. w: @! S" ]/ E2 l6 ? g
$maxlength = $this->fields[$field]['maxlength']; ; w- R7 \! D% C& b! d5 @) R9 T
$pattern = $this->fields[$field]['pattern'];
+ c# |' X' \7 x9 p4 V7 [8 Z $errortips = $this->fields[$field]['errortips'];
! Q% ~6 p9 d( g if(empty($errortips)) $errortips = "$name 不符合要求!"; % i' c7 [3 O5 G1 o. a+ U
$length = empty($value) ? 0 : strlen($value);
. P) h! t. Z& g6 I3 t7 Q if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!"); ; Z( B& l, Y( O6 z
if($maxlength && $length > $maxlength && !$isimport) { , |. X0 {! U+ `0 N8 r4 t
showmessage("$name 不得超过 $maxlength 个字符!"); # d& ^" Y+ h1 @
} else {
) @& q, G M# | str_cut($value, $maxlength);
" ~6 H* @# W5 i( E; M }
9 w6 w7 V4 u! z9 n& @0 l0 W9 M+ L( f if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
( }3 Z& v! e, [+ X% K- G& y# f4 x if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
# G, i5 V! U( S5 f. Z- p3 } $func = $this->fields[$field]['formtype'];
3 C5 h, f+ u& }% Q: q' k if(method_exists($this, $func)) $value = $this->$func($field, $value);
a% E- c4 } l" }& G' ^1 n; i $info[$field] = $value;
, `, }6 K% y/ M( S }
& n& M1 {3 V$ q* i } 2 U, }, b# ?. i' V7 m
return $info;
0 @; D: |1 ~2 V) N; k8 p9 N7 l }
& J5 C2 U/ v! Rtrim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
. d. ?0 n) @. G4 d8 e! G5 H: m& w, u
再到phpcms\modules\member\index.php 文件account_manage_info函数
E+ {5 r! q# W过了get()函数之后。
8 m' W/ t/ y/ {
7 k8 ], F4 X$ H" `) U 0 J/ K' _* l7 \1 b) @( j3 I
$modelinfo = $member_input->get($_POST['info']); # p- e; T) X j2 m6 J: o: K7 J
$this->db->set_model($this->memberinfo['modelid']); % e' n0 b9 ^$ |6 |0 X U
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid'])); ' G' x7 c7 ]% C2 c5 C8 o9 v' N" t
if(!empty($membermodelinfo)) {
2 M, B. ~2 r. A- }+ I$ t( i $this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
/ ^& S+ q$ S3 i- r9 d } else { 3 M' z: i! T, s/ O$ v5 Z2 c
直接带入数据库,update函数我们跟进看看# P _4 P1 Q. w3 v% J
/ L! W2 H* s* D2 u( J" A
4 q+ q; w `0 F) G8 w
public function update($data, $table, $where = '') {
B+ q& r) w% j+ n7 Z F if($table == '' or $where == '') {
+ E( j; `) l' |0 R, t- Q) s' u: \ return false;
( m8 f7 n7 E5 @+ h+ i* x }
% U* X% v, s" y- q $where = ' WHERE '.$where; / j- q" ]) Y: V3 ^
$field = ''; 8 K0 R2 f5 d t8 \- N% e
if(is_string($data) && $data != '') { 8 g1 q$ ~" s' x9 Z
$field = $data;
u" X& p7 Q1 z9 h" G } elseif (is_array($data) && count($data) > 0) {
8 z& Y7 E: \& t# }& D" `* h7 T4 d $fields = array();
9 W1 e: s" D/ d. L L" C- S foreach($data as $k=>$v) {
" o& j5 h4 i5 E3 Y( d3 l5 w0 Q switch (substr($v, 0, 2)) { * T# \5 ?' o4 M! o
case '+=':
# t( J5 n, } ]: z8 O. A1 N5 k $v = substr($v,2);
2 O6 I" L- r9 l4 R( u' i if (is_numeric($v)) {
3 f( G. q4 e0 L. K( J. \ $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false); 7 i+ ]. Q% a; j
} else { ' I/ v$ m# c1 K9 O- O
continue; 1 w$ Q2 S6 S0 Y! G9 U
} ' p7 ?# | d4 N! B
break;
+ Y# J& a+ ]9 X" O5 F& N: v0 z case '-=': * ?& v2 ? ?$ z
$v = substr($v,2); . W7 S6 m* k% L' z0 k. J
if (is_numeric($v)) {
$ e; G3 x( d. x& @ s- { $fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false); 7 u% L) M! U1 @) P6 X, A$ F" l
} else {
8 ~; g% a! c5 I/ j" k" e continue; ' D4 M+ L+ q& c* r% b) r- w$ `/ s
} : _: v( {: F7 ?
break;
# ?# }: h5 g: |) O9 R3 r+ }3 @ default:
7 w1 L" n1 z9 S3 |+ S $fields[] = $this->add_special_char($k).'='.$this->escape_string($v); : r q' l* g* Y
} / o2 l9 e% Z8 I
} / t/ [( S3 a" x& Z
$field = implode(',', $fields); ' Y9 |' D V7 }
} else {
! Q9 Y" ~& A: Z+ A# h return false; 0 i; i/ x! x4 {2 q3 c- X+ F
}
+ q! \* k: v* X& j( _( ^ $sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
( H3 R: F4 a7 O print_r($sql);
+ D0 ]. p, y+ Q4 A7 C return $this->execute($sql);
: L' V- k) Y, T: }$ t9 n }
& m: [: G: v! B. L5 N从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
0 D$ w/ O* \- _# H+ K) V
# O5 j2 {/ ?' e攻击测试:% E' B1 k& n% x: \/ W
测试地址http://localhost0 n, ]. w* _, k
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
8 ^, a& p4 ^) R% h5 K/ S) g+ B
% b) C$ q5 ^( a5 o2 m7 J$ }7 L
; _& M6 e, k3 W3 O. [3 ]3 N- R6 s. J2 Y* x' S9 h- R
6 V* R! p0 \8 Q' v
- M# ? x8 ]: |$ M |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|