Storm Worm Vigenère Sample

This page plays with the Storm/Zhelatin/Nuwar worm Vigenère cipher code. For details looking at "view source" is essential, this page contains live javascript that generates some of the data shown. I've whacked this into a separate page since several AV engines will probably dislike the content! This is related to my write-up here: Storm Worm Vigenère

Update: This might create garbage in Internet Explorer. According to this information it seems IE implements things slightly differently to Firefox. Since all the inputs are generated dynamically it might work though… I don't have an IE handy to test with right at this moment.

First, the "encrypt" function is used to encode a little javascript program, don't worry — it's just a call to "alert". This "encrypted" string is our "payload":

        979e9aa9ba6a6d9aa5a7559daba7b261aa9a9a57b2b1bca657595e72
    

In our next act we provide this "payload" string to the function "decrypt" which both decrypts and 'eval's the resulting string. Rather than just do this here's a button with appropriate "onclick" logic bound to it:

Do you feel the love?

I've only tested this page in Firefox, IE might not feel the love. There's much more information in my write-up and in the comments embedded in the code.

Key Material Generation

Here's the source of the decrypt function as returned by the decrypt.toString() call. Note that my enlightening comments are removed and the formatting is normalised (at least this is the case in Firefox), to see the commented version use "view source" or look at the textfile version of the code.

function decrypt(cipherText)
{
    // The most interesting part, this grabs this function body code as a 
    // string.  Later on this string is used to generate the decryption key.
    var funText = arguments.callee.toString().replace(/\W/g, '').toUpperCase();
    // Used as counters throughout the code.
    var j; var i;
    // Initially the length of the condensed function body text, this is
    // later used to store the length of other strings as well.
    var funTextLen = funText.length;
    // Used to calculate and stringigy the "magic" value that is turned into
    // the final key array.
    var magic;
    // The ciphertext is decrypted into this before being eval-ed.
    var plainText = '';

    // Create zeroed 256 element array
    var array256 = new Array();
    for (i = 0; i < 256; i++) {
        array256[i] = 0;
    }

    // Generate values for 256 element array.  The main idea here seems to be to
    // try and maintain significant bits in most nibbles (so, I guess, the final
    // key is unlikely to contain 0x0 nibbles).
    var j = 1;
    for (i = 128; i; i >>= 1) {
        j = (j >>> 1) ^ ((j & 1) ? 0xEDB88320 : 0);
        for (k = 0; k < 256; k += i * 2) {
            array256[k + i] = (array256[k] ^ j);
            if (array256[k + i] < 0) {
                array256[k + i] += 0x100000000;
            }
        }
    }

    // Generate the initial key value by combining elements from 'array256' 
    // selected using ascii values from 'funText'.  I.e. key value derived from
    // function text!
    magic = 0xFFFFFFFF;
    for (j = 0; j < funTextLen; j++) {
        magic = array256[(magic ^ funText.charCodeAt(j)) & 255] ^ ((magic >> 8) & 16777215);
    }
    var uselessArray = new Array();  // Red herring?
    magic = magic ^ 0xFFFFFFFF;
    if (magic < 0) {
        // I'm not sure about integer sizes in ECMA.  Based on this useage int
        // values are interpreted as 32 bit signed yet immediate int values can
        // be >32 bits?  Note that in original code 4294967296 is used here,
        // not hex.
        magic += 0x100000000;
    }

    // Turn the "magic" 32 bit value into an upper case hex string.
    magic = magic.toString(16).toUpperCase();
    var key = new Array();
    // NOTE!!!! funTextLen is now magicLen!! 
    var funTextLen = magic.length;
    for (i = 0; i < 8; i++) {
        var lengthGuard = funTextLen + i;
        uselessArray[i] = 1;
        if (lengthGuard >= 8) {
            // The lengthGuard value will handle the case where the "magic"
            // value happens to have less than 8 nibbles of significant
            // content.
            lengthGuard = lengthGuard - 8;
            key[i] = magic.charCodeAt(lengthGuard);
        } else {
            key[i] = 48;
        }
    }

    // The following loop implements the Vigenere decrypt.
    var keyIdx = 0; // Index into the key value array.
    var cryptByte;
    var plainByte;
    var redHerring;
    // Note!!! funTextLen now used as ciphertext length!
    funTextLen = cipherText.length;
    redHerring = funTextLen;
    // Loop over bytes (two ascii hex chars) in the input hex ciphertext.
    for (i = 0; i < funTextLen; i += 2) {
        // Get two character "byte" of the ciphertext.
        var cryptHex = cipherText.substr(i, 2);
        // Interpret to integer from base-16 (hex) notation.
        cryptByte = parseInt(cryptHex, 16);

        // Shift the byte value by subtracting the current key value.
        plainByte = cryptByte - key[keyIdx];
        if (plainByte < 0) {
            // If result of shift is -ve flip to +ve 
            plainByte = plainByte + 256;
        }

        // Translate the calculated byte value to an ASCII character and append
        // to the plaintext string.
        plainText += String.fromCharCode(plainByte);

        redHerring++; // This seems to have no significance!

        // This if increments or wraps the key, it's an obfuscated:
        //    keyIdx = ((keyIdx + 1) % 8)
        if (keyIdx < key.length - 1) {
            uselessArray[i] = 2;  // This also seems to have no significance!
            keyIdx++; 
        } else {
            keyIdx = 0;
        }
    }

    // Do the wicked deed!
    eval(plainText);
}
    

The following string is what the function uses as the key input material, it has non-word characters removed and is upper-cased using decrypt.toString().replace(/\W/g,'').toUpperCase() (yep, it's a long string, my apoligies to your browser width):

FUNCTIONDECRYPTCIPHERTEXTTHEMOSTINTERESTINGPARTTHISGRABSTHISFUNCTIONBODYCODEASASTRINGLATERONTHISSTRINGISUSEDTOGENERATETHEDECRYPTIONKEYVARFUNTEXTARGUMENTSCALLEETOSTRINGREPLACEWGTOUPPERCASEUSEDASCOUNTERSTHROUGHOUTTHECODEVARJVARIINITIALLYTHELENGTHOFTHECONDENSEDFUNCTIONBODYTEXTTHISISLATERUSEDTOSTORETHELENGTHOFOTHERSTRINGSASWELLVARFUNTEXTLENFUNTEXTLENGTHUSEDTOCALCULATEANDSTRINGIGYTHEMAGICVALUETHATISTURNEDINTOTHEFINALKEYARRAYVARMAGICTHECIPHERTEXTISDECRYPTEDINTOTHISBEFOREBEINGEVALEDVARPLAINTEXTCREATEZEROED256ELEMENTARRAYVARARRAY256NEWARRAYFORI0I256IARRAY256I0GENERATEVALUESFOR256ELEMENTARRAYTHEMAINIDEAHERESEEMSTOBETOTRYANDMAINTAINSIGNIFICANTBITSINMOSTNIBBLESSOIGUESSTHEFINALKEYISUNLIKELYTOCONTAIN0X0NIBBLESVARJ1FORI128II1JJ1J10XEDB883200FORK0K256KI2ARRAY256KIARRAY256KJIFARRAY256KI0ARRAY256KI0X100000000GENERATETHEINITIALKEYVALUEBYCOMBININGELEMENTSFROMARRAY256SELECTEDUSINGASCIIVALUESFROMFUNTEXTIEKEYVALUEDERIVEDFROMFUNCTIONTEXTMAGIC0XFFFFFFFFFORJ0JFUNTEXTLENJMAGICARRAY256MAGICFUNTEXTCHARCODEATJ255MAGIC816777215VARUSELESSARRAYNEWARRAYREDHERRINGMAGICMAGIC0XFFFFFFFFIFMAGIC0IMNOTSUREABOUTINTEGERSIZESINECMABASEDONTHISUSEAGEINTVALUESAREINTERPRETEDAS32BITSIGNEDYETIMMEDIATEINTVALUESCANBE32BITSNOTETHATINORIGINALCODE4294967296ISUSEDHERENOTHEXMAGIC0X100000000TURNTHEMAGIC32BITVALUEINTOANUPPERCASEHEXSTRINGMAGICMAGICTOSTRING16TOUPPERCASEVARKEYNEWARRAYNOTEFUNTEXTLENISNOWMAGICLENVARFUNTEXTLENMAGICLENGTHFORI0I8IVARLENGTHGUARDFUNTEXTLENIUSELESSARRAYI1IFLENGTHGUARD8THELENGTHGUARDVALUEWILLHANDLETHECASEWHERETHEMAGICVALUEHAPPENSTOHAVELESSTHAN8NIBBLESOFSIGNIFICANTCONTENTLENGTHGUARDLENGTHGUARD8KEYIMAGICCHARCODEATLENGTHGUARDELSEKEYI48THEFOLLOWINGLOOPIMPLEMENTSTHEVIGENEREDECRYPTVARKEYIDX0INDEXINTOTHEKEYVALUEARRAYVARCRYPTBYTEVARPLAINBYTEVARREDHERRINGNOTEFUNTEXTLENNOWUSEDASCIPHERTEXTLENGTHFUNTEXTLENCIPHERTEXTLENGTHREDHERRINGFUNTEXTLENLOOPOVERBYTESTWOASCIIHEXCHARSINTHEINPUTHEXCIPHERTEXTFORI0IFUNTEXTLENI2GETTWOCHARACTERBYTEOFTHECIPHERTEXTVARCRYPTHEXCIPHERTEXTSUBSTRI2INTERPRETTOINTEGERFROMBASE16HEXNOTATIONCRYPTBYTEPARSEINTCRYPTHEX16SHIFTTHEBYTEVALUEBYSUBTRACTINGTHECURRENTKEYVALUEPLAINBYTECRYPTBYTEKEYKEYIDXIFPLAINBYTE0IFRESULTOFSHIFTISVEFLIPTOVEPLAINBYTEPLAINBYTE256TRANSLATETHECALCULATEDBYTEVALUETOANASCIICHARACTERANDAPPENDTOTHEPLAINTEXTSTRINGPLAINTEXTSTRINGFROMCHARCODEPLAINBYTEREDHERRINGTHISSEEMSTOHAVENOSIGNIFICANCETHISIFINCREMENTSORWRAPSTHEKEYITSANOBFUSCATEDKEYIDXKEYIDX18IFKEYIDXKEYLENGTH1USELESSARRAYI2THISALSOSEEMSTOHAVENOSIGNIFICANCEKEYIDXELSEKEYIDX0DOTHEWICKEDDEEDEVALPLAINTEXT