phpcmsV9某sql注射漏洞及修复
- phpcmsV9某sql注射漏洞及修复
-
别人说最危险的地方最安全,我说最安全的地方最危险。。。
相信你们也没有想到这个最常见,常常会在各种教程出现的地方会存在SQL注射。。。
需 GPC OFF
首先,我们看登陆的地方。。最常见了吧。。。http://127.0.0.1/index.php?m=member&c=index&a=login
默认安装情况下,会使有 V9自带的用户中心。
phpcms\modules\member\index.php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public
function login() {
$
this
->_session_start();
//获取用户siteid
$siteid = isset($_REQUEST[
'siteid'
]) && trim($_REQUEST[
'siteid'
]) ? intval($_REQUEST[
'siteid'
]) :
1
;
//定义站点id常量
if
(!defined(
'SITEID'
)) {
define(
'SITEID'
, $siteid);
}
if
(isset($_POST[
'dosubmit'
])) {
if
(empty($_SESSION[
'connectid'
])) {
//判断验证码
$code = isset($_POST[
'code'
]) && trim($_POST[
'code'
]) ? trim($_POST[
'code'
]) : showmessage(L(
'input_code'
), HTTP_REFERER);
if
($_SESSION[
'code'
] != strtolower($code)) {
showmessage(L(
'code_error'
), HTTP_REFERER);
}
}
$username = isset($_POST[
'username'
]) && trim($_POST[
'username'
]) ? trim($_POST[
'username'
]) : showmessage(L(
'username_empty'
), HTTP_REFERER);
$password = isset($_POST[
'password'
]) && trim($_POST[
'password'
]) ? trim($_POST[
'password'
]) : showmessage(L(
'password_empty'
), HTTP_REFERER);
$cookietime = intval($_POST[
'cookietime'
]);
$synloginstr =
''
;
//同步登陆js代码
if
(pc_base::load_config(
'system'
,
'phpsso'
)) {
$
this
->_init_phpsso();
$status = $
this
->client->ps_member_login($username, $password);
$memberinfo = unserialize($status);
可以看到用户名密码交给了1$
this
->client->ps_member_login($username, $password);
我们跟进。
phpcms\modules\member\classes\client.class.php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071$
return
= $
this
->_ps_send(
'login'
, array(
'username'
=>$username,
'password'
=>$password));
private
function _ps_send($action, $data =
null
) {
return
$
this
->_ps_post($
this
->ps_api_url.
"/index.php?m=phpsso&c=index&a="
.$action,
500000
, $
this
->auth_data($data));
}
public
function auth_data($data) {
$s = $sep =
''
;
foreach($data as $k => $v) {
if
(is_array($v)) {
$s2 = $sep2 =
''
;
foreach($v as $k2 => $v2) {
$s2 .=
"$sep2{$k}[$k2]="
.$
this
->_ps_stripslashes($v2);
$sep2 =
'&'
;
}
$s .= $sep.$s2;
}
else
{
$s .=
"$sep$k="
.$
this
->_ps_stripslashes($v);
}
$sep =
'&'
;
}
$auth_s =
'v='
.$
this
->ps_vsersion.
'&appid='
.APPID.
'&data='
.urlencode($
this
->sys_auth($s));
return
$auth_s;
}
_ps_stripslashes
private
function _ps_stripslashes($string) {
!defined(
'MAGIC_QUOTES_GPC'
) && define(
'MAGIC_QUOTES_GPC'
, get_magic_quotes_gpc());
if
(MAGIC_QUOTES_GPC) {
return
stripslashes($string);
}
else
{
return
$string;
}
}
还原了 GPC,传参数给 API。我们再看看 API 方的处理方式
phpsso_server\phpcms\modules\phpsso\classes\phpsso.class.php
1234567891011121314151617if
(isset($_POST[
'data'
])) {
parse_str(sys_auth($_POST[
'data'
],
'DECODE'
, $
this
->applist[$
this
->appid][
'authkey'
]), $
this
->data);
if
(!is_array($
this
->data)) {
exit(
'0'
);
}
}
else
{
exit(
'0'
);
}
parse_str 函数默认是根据 GPC情况过滤。再到
phpsso_server\phpcms\modules\phpsso\index.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263public
function login() {
$
this
->password = isset($
this
->data[
'password'
]) ? $
this
->data[
'password'
] :
''
;
$
this
->email = isset($
this
->data[
'email'
]) ? $
this
->data[
'email'
] :
''
;
if
($
this
->email) {
$userinfo = $
this
->db->get_one(array(
'email'
=>$
this
->email));
}
else
{
$userinfo = $
this
->db->get_one(array(
'username'
=>$
this
->username));
}
phpsso_server\phpcms\libs\classes\model.
class
.php
final
public
function get_one($where =
''
, $data =
'*'
, $order =
''
, $group =
''
) {
if
(is_array($where)) $where = $
this
->sqls($where);
return
$
this
->db->get_one($data, $
this
->table_name, $where, $order, $group);
}
/**
* 将数组转换为SQL语句
* @param array $where 要生成的数组
* @param string $font 连接串。
*/
final
public
function sqls($where, $font =
' AND '
) {
if
(is_array($where)) {
$sql =
''
;
foreach ($where as $key=>$val) {
$sql .= $sql ?
" $font `$key` = '$val' "
:
" `$key` = '$val'"
;
}
return
$sql;
}
else
{
return
$where;
}
}
可以看到全程没有对字符串进行过滤。。。因此,在GPC为 OFF 时,存在SQL注入。
可能没说清楚问题在那里
两个:
1、auth_data 参数拼接
2、api中没有对数据进行过滤
可以做什么?盲注,任意用户登陆。。。其实还有很多利用的地方。。
测试如下:
修复方案:
过滤