中国网络渗透测试联盟
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
[打印本页]
作者:
admin
时间:
2013-2-4 16:17
标题:
phpcms v9 2013-02-01 会员中心注入漏洞分析报告
报告名称:phpcms v9 2013-02-01 会员中心注入漏洞分析报告
- {) m6 u7 Z& ]" u/ j' M
漏洞作者:skysheep
/ D5 s5 |$ t' o' W9 q
分析作者:Seay
0 ~' I) u9 o: a! i
博客:
http://www.cnseay.com/
1 ?, `6 I8 R2 g) w2 z
漏洞分析:
3 N. I; ~+ I1 ?! m5 ~' b0 y m8 S
漏洞存在于 phpcms\modules\member\index.php 文件account_manage_info函数,其功能是更新会员信息。
: c* @9 j3 W; k' J* {( A6 ?" y! R
2 @. p: G$ a( J
6 [' L7 U# n2 Z2 q' m* B
! }" \) m9 g5 d8 I
public function account_manage_info() {
f" u) T! D7 m- A3 ?! A/ V
if(isset($_POST['dosubmit'])) {
" g" A) o) S! o$ O0 Z
//更新用户昵称
6 N* |- m5 | P: _* _
$nickname = isset($_POST['nickname']) && trim($_POST['nickname']) ? trim($_POST['nickname']) : '';
$ |3 R% Q1 G4 @$ C7 r
if($nickname) {
) D7 S( Y/ g Y; _+ x4 J8 Q9 l' j3 p
$this->db->update(array('nickname'=>$nickname), array('userid'=>$this->memberinfo['userid']));
' _4 r \* o$ S/ R6 c3 D
if(!isset($cookietime)) {
3 q; I9 T7 I$ D; S' z2 P' I
$get_cookietime = param::get_cookie('cookietime');
( \" r! [5 ^2 T% [0 K$ S* R! [
}
( E0 ]) d+ ~0 i
$_cookietime = $cookietime ? intval($cookietime) : ($get_cookietime ? $get_cookietime : 0);
# N% T# g" R$ Q$ R2 X6 K! J
$cookietime = $_cookietime ? TIME + $_cookietime : 0;
6 f1 s0 z' P) v9 W( x e
param::set_cookie('_nickname', $nickname, $cookietime);
2 |' i# U* w" W6 @2 O; |
}
" C* f! w) d- a/ o% |
require_once CACHE_MODEL_PATH.'member_input.class.php';
# j( z2 L4 W) z! X" _
require_once CACHE_MODEL_PATH.'member_update.class.php';
( m5 _2 d' i v, Q
$member_input = new member_input($this->memberinfo['modelid']);
, F% s0 C' K) F; Y N5 ?3 J
$modelinfo = $member_input->get($_POST['info']);
+ D! H8 y3 C# q9 f( @; m# U, y
$this->db->set_model($this->memberinfo['modelid']);
0 ]0 S$ M; e% ^& `5 U' _
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
2 V4 p5 r! N3 |
if(!empty($membermodelinfo)) {
' ~! d% h1 g- r/ p- r. }5 d
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
, ]* w* m# v4 S; v# h& X5 R
} else {
/ ^! B# n# S$ G& B4 m- a
$modelinfo['userid'] = $this->memberinfo['userid'];
; o2 ~0 i" [. `+ y
$this->db->insert($modelinfo);
" I* O% w( B: D0 O
}
- K9 L/ s/ f* z! s; f6 E& _+ v4 S
代码中:$modelinfo = $member_input->get($_POST['info']);取得提交上来的会员模型中的字段,我们跟进member_input类中的get()函数看看,
+ G# @" Z: s5 l5 W5 ~, c% O
在\caches\caches_model\caches_data\ member_input.class.php 文件中:
. l# j2 g8 ~; }# M! E6 y
/ f( ?; R _8 p$ ]* s
1 p; V6 M( ~) V. C) O% P
7 r/ S- y- V7 l, x6 J6 a) r
function get($data) {
( j7 y+ c% n" G4 f: j/ F6 k
$this->data = $data = trim_script($data);
4 O: h9 ~" H9 C0 s/ E
$model_cache = getcache('member_model', 'commons');
( n2 w1 X5 J/ t" I2 Y: [" o8 p
$this->db->table_name = $this->db_pre.$model_cache[$this->modelid]['tablename'];
" n+ f" g: X$ Y% N' A U! R
$info = array();
9 R* U2 z" s! } U
$debar_filed = array('catid','title','style','thumb','status','islink','description');
+ t( Y0 g8 j9 V8 @" O. ^
if(is_array($data)) {
% v+ a L5 o3 ?% z- W* E2 J
foreach($data as $field=>$value) {
s d' U$ I$ ~
if($data['islink']==1 && !in_array($field,$debar_filed)) continue;
8 g6 f5 S/ B/ ?. |$ a( }( d
$name = $this->fields[$field]['name'];
+ L% v3 d( G0 X7 z' s
$minlength = $this->fields[$field]['minlength'];
4 H9 k- i$ T( R* p9 W4 T$ d
$maxlength = $this->fields[$field]['maxlength'];
4 i% b/ F) t* q/ \3 j; Y. p
$pattern = $this->fields[$field]['pattern'];
3 i$ X! X9 N! c; r- w
$errortips = $this->fields[$field]['errortips'];
& z8 `- ^. ~! x0 K* S: e2 j
if(empty($errortips)) $errortips = "$name 不符合要求!";
* k* `7 }7 [: o
$length = empty($value) ? 0 : strlen($value);
+ V) x/ N" Q& F, _( j9 _+ |8 E
if($minlength && $length < $minlength && !$isimport) showmessage("$name 不得少于 $minlength 个字符!");
& H3 L$ w6 E; d7 N+ \3 b4 g7 i
if($maxlength && $length > $maxlength && !$isimport) {
$ l) J5 f* ]8 J) c2 S
showmessage("$name 不得超过 $maxlength 个字符!");
+ M# ` _5 f( W5 z
} else {
+ {6 Q5 Q8 {5 |$ M
str_cut($value, $maxlength);
+ \! Y- r5 h" E5 B
}
" y5 B7 b/ U' _
if($pattern && $length && !preg_match($pattern, $value) && !$isimport) showmessage($errortips);
/ `) N7 m0 m; E/ X) ^ x. I
if($this->fields[$field]['isunique'] && $this->db->get_one(array($field=>$value),$field) && ROUTE_A != 'edit') showmessage("$name 的值不得重复!");
) T4 K8 W$ y$ g, b
$func = $this->fields[$field]['formtype'];
$ P, k& a" _$ r0 o' @
if(method_exists($this, $func)) $value = $this->$func($field, $value);
0 G+ |+ I+ N+ \6 i+ g4 w I
$info[$field] = $value;
o' t" y3 j3 _ Y Z/ R1 F. n- K
}
! Z: K0 P, j. ^3 L) V2 H, z
}
9 F6 t R! p9 R& Q$ r4 a0 A0 ^- X" g
return $info;
! c& A4 w D9 f, [3 J1 s9 {
}
. p( h$ @- n1 C5 t
trim_script函数是过滤XSS的,上面get函数一段代码干的事就是取提交上来的字段和值重新赋值到数组,
7 [. b `, K I: U. ?
; B A6 C; U$ z" W' n% u
再到phpcms\modules\member\index.php 文件account_manage_info函数
' a7 V9 _9 S# [' ~ m2 t
过了get()函数之后。
% n; F% p& P" y3 z; F
: c" N- o+ e$ D1 _' z5 q
) v4 {1 [: G/ W
$modelinfo = $member_input->get($_POST['info']);
a2 X8 d# C& ^. F, u: R
$this->db->set_model($this->memberinfo['modelid']);
* Q* j( B$ O: h; L2 s2 Q
$membermodelinfo = $this->db->get_one(array('userid'=>$this->memberinfo['userid']));
& G7 r! a: }9 r. M+ n5 X: o1 ?
if(!empty($membermodelinfo)) {
5 N+ i0 x: {1 F9 @
$this->db->update($modelinfo, array('userid'=>$this->memberinfo['userid']));
( O) c. f2 t& s! W
} else {
6 o0 J$ c; A- R: @0 {- H
直接带入数据库,update函数我们跟进看看
) Y5 f" J+ {. q O0 C7 Z* T5 B
x" ]6 N2 W1 g+ M" d
+ G6 W6 m0 r5 f( r
public function update($data, $table, $where = '') {
7 C# A% ]# |0 _' W7 e8 I
if($table == '' or $where == '') {
7 ]2 f1 w/ e) c8 \
return false;
0 a8 A, B( K6 X/ k z7 c) g! _
}
& A" b2 T" [! W+ c- m
$where = ' WHERE '.$where;
- q9 z3 o9 j- Y% U+ r
$field = '';
" N( A8 B8 ^& Y/ o
if(is_string($data) && $data != '') {
' @9 |, F% B/ x: `7 f: {- D
$field = $data;
0 c( T( w! u: l$ B+ c V. a
} elseif (is_array($data) && count($data) > 0) {
( I# D9 S+ n7 z$ G. W9 ~' k
$fields = array();
' j% d6 e2 U" d0 I# @
foreach($data as $k=>$v) {
: i' ~" r/ Y3 h6 D1 l( X9 p% R
switch (substr($v, 0, 2)) {
/ Z8 C; k/ F5 t
case '+=':
% ~2 r" t: k: @0 N
$v = substr($v,2);
4 z2 m1 P' M3 W/ {) Y
if (is_numeric($v)) {
' c; u; c4 Y2 ~8 w& h! M" o
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'+'.$this->escape_string($v, '', false);
4 R8 @3 Z1 `' H. r1 ]* }
} else {
6 G. k! C2 w2 H C
continue;
7 c) A3 {# w# F" o
}
1 I! K+ \1 l7 Q1 V$ d6 t- Q5 J0 \
break;
, R0 z5 c, f* ? p1 R+ Q
case '-=':
4 x1 T5 [6 n6 |9 f; g- j
$v = substr($v,2);
2 {/ j0 p# p* n a% E8 D
if (is_numeric($v)) {
* j! d4 i) Z! ]; W M
$fields[] = $this->add_special_char($k).'='.$this->add_special_char($k).'-'.$this->escape_string($v, '', false);
0 k: u( f" \1 {0 M# c
} else {
* C8 i' v7 U" ^$ H+ E/ s6 D, X4 |
continue;
, P9 t: V0 A2 `( s p4 ~
}
: B# v: F* u9 `, Y9 x' t
break;
0 a, ~( y) T- |" Q6 ]6 T2 U& T* J
default:
. n$ D5 U& r' f) V2 e; f5 P" p
$fields[] = $this->add_special_char($k).'='.$this->escape_string($v);
. F9 K: J: [) `2 Y5 Z
}
6 b1 z6 t5 u/ f, y2 w3 L/ W- _ C/ r
}
! I6 t* z: z8 \! A2 r
$field = implode(',', $fields);
2 c8 d" ^. ^& k; a
} else {
* Z7 ^& e4 A2 _" {
return false;
% N& \8 X3 E5 i& ^
}
3 i; {9 x7 t% k! A5 \) y5 n5 E
$sql = 'UPDATE `'.$this->config['database'].'`.`'.$table.'` SET '.$field.$where;
2 w, s& B2 f/ f$ G& b4 B
print_r($sql);
" M. A5 ]) K0 A) a, [
return $this->execute($sql);
5 W- L4 a9 f$ j; ?
}
! L( Q5 m6 U4 r
从头到尾也是没有验证数据库是否存在数组中的字段,然后直接执行SQL语句。也就是说SQL语句中的字段我们可控,导致注入。
9 s* v- J8 \ u* B2 }2 w7 f/ n( Z
' r% e& i! S1 G6 v
攻击测试:
d! T: P! [% H3 K9 A
测试地址
http://localhost
5 X! r0 `7 { p8 e! |, A8 O. ]
注册会员seay并登陆。打开firebug工具HTML选项。修改birthday的name值为注入语句
' ~/ J8 V3 Q! L# {
% q- Q( |* x5 \! P6 F2 D) K1 f1 F
[attach]179[/attach]
- o2 S+ Q$ t/ v" y, W# a3 a
) m% [+ e7 V' _; V( {6 C. @ [+ ~
: H8 ^/ L$ R2 c2 ]9 p, G
[attach]180[/attach]
: l( U3 L! J- {0 X# F
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/)
Powered by Discuz! X3.2