From: Stephanie Wehner [_@R4K.NET] Sent: Tuesday, November 07, 2000 8:37 AM To: VULN-DEV@SECURITYFOCUS.COM Subject: windows scripting encoder Hi, Since a friend of mine told me they use the windows scripting encoder to obfuscate their asp code before giving it to customers, I decided to take a look at it. It turned out to be extremely trivial. Of course this is just encoding and is, as ms says themselves, not a real protection of asp code. However I find it a bit misleading to present this as an option to 'protect your sourcecode from prying eyes', since it really is totally simple. I think it creates a false sense of 'security' by hiding your source code that just encourages sloppy coding practises. I'd be curious to know if anyone is actually using this or ever looked at it. There's some info at: http://msdn.microsoft.com/library/periodic/period99/scriptengine.htm You can get the encoder from: http://msdn.microsoft.com/scripting/vbscript/download/x86/sce10en.exe Anyway, this is how it works: The encoder will translate each character in the input, to one of three possible other characters depending on the position in the input. There is basically a pattern which determines which of the three characters will replace the input character at a certain position. This pattern repeats after 64 chars. This pattern is the same for all characters. There's some exceptions to this rule, namely \n and \r will be encoded to 2 characters each. Futhermore this encoding is not dependant on the machine it is done on. That's all there is to it. So in order to write a decoder one needs to find the pattern and the character combinations. In order to do this, I just made a sample file like: <% '**Start Encode** aaa %> If one encoded this, one basically knows the 3 letters a maps to. Therefore it is easy to just to this for all ascii characters (after installing a nice shell on windows :) ) and make a little script which will create some perl hash, which will give me the outcome of each letter seen in the encoded string, cause the original character is known. I couldn't find any obvious connection between the the input character and the output, hence the table. Next step is to extract the pattern. This is also very simple by encoding the same file as above with a lot of a's, and then have a script dump this as an array to include in the perl code. With this information one can write a perl script, like the one below, which will decode the asp file. For example the encoded file: %@ LANGUAGE = VBScript.Encode %> <% '**Start Encode**#@~^JQAAAA==@#@&K4b/,k/,L!dY,/G:^?PdO!wk9~Y^?/Y@#@&FQsAAA==^#~@%> The output of the decoder will be: I've tried this on some real asp code as well. I assume that in some dll, there has to be the code to do this for you, since IIS will run this script and I suppose it has to be decoded first. But I'm not really a windows person. I searched the ms site, but couldn't find anything. I'm not really sure how serious this is. I suppose no sane person would use this scripting encoder and assume noone could read the code on their website or that customers can't read the code you wrote for them. However I'm kind of wondering why this functionality is offered at all then. bye, Stephanie ----------------------------<> _@r4k.net <>-----------------<> FreeBSD <>--- jx : yeah im a net-monk and i pray every day for the soul of internet - #unix,07/08/00 #!/usr/bin/perl # Windows Scripting Decoder # # 16/11/2000, Stephanie Wehner, _@r4k.net # # Note that this does not attempt to be a nice decoder and produce # nicely formatted output. It's just a demo, your milage may vary. # # Usage: cat file | ./dec.pl use strict; # pattern, auto generated by make_pat my @pat = (1,2,0,1,2,0,2,0,0,2,0,2,1,0,2,0,1,0,2,0,1,1,2,0,0,2,1,0,2,0,0,2,1,1,0,2,0,2,0,1,0,1,1,2,0,1,0,2,1,0,2,0,1,1,2,0,0,1,1,2,0,1,0,2); # list auto generated by make_list, some of this probably newer occurs # but oh well my %code = ( 0 => [0,0,0], 1 => [1,1,1], 2 => [2,2,2], 3 => [3,3,3], 4 => [4,4,4], 5 => [5,5,5], 6 => [6,6,6], 7 => [7,7,7], 8 => [8,8,8], 9 => [123,87,110], 10 => [0,0,0], 11 => [11,11,11], 12 => [12,12,12], 13 => [0,0,0], 14 => [14,14,14], 15 => [15,15,15], 16 => [16,16,16], 17 => [17,17,17], 18 => [18,18,18], 19 => [19,19,19], 20 => [20,20,20], 21 => [21,21,21], 22 => [22,22,22], 23 => [23,23,23], 24 => [24,24,24], 25 => [25,25,25], 26 => [26,26,26], 27 => [27,27,27], 28 => [28,28,28], 29 => [29,29,29], 30 => [30,30,30], 31 => [31,31,31], 32 => [50,46,45], 33 => [48,71,117], 34 => [33,122,82], 35 => [41,86,96], 36 => [91,66,113], 37 => [56,106,94], 38 => [51,47,73], 39 => [61,38,92], 40 => [88,73,98], 41 => [58,65,125], 42 => [53,52,41], 43 => [101,50,54], 44 => [57,91,32], 45 => [92,118,124], 46 => [86,114,122], 47 => [115,67,0], 48 => [102,56,107], 49 => [78,57,99], 50 => [69,112,51], 51 => [107,69,43], 52 => [98,104,104], 53 => [89,113,81], 54 => [120,79,102], 55 => [94,9,118], 56 => [125,98,49], 57 => [74,68,100], 58 => [109,35,84], 59 => [113,117,67], 60 => [0,0,0], 61 => [96,126,58], 62 => [0,0,0], 63 => [83,94,126], 64 => [0,0,0], 65 => [66,119,69], 66 => [39,74,44], 67 => [72,97,42], 68 => [114,93,116], 69 => [117,34,39], 70 => [49,75,55], 71 => [55,111,68], 72 => [77,78,121], 73 => [82,59,89], 74 => [34,76,47], 75 => [84,80,111], 76 => [106,103,38], 77 => [71,42,114], 78 => [100,125,106], 79 => [45,116,57], 80 => [32,84,123], 81 => [0,43,63], 82 => [46,45,56], 83 => [76,44,119], 84 => [93,48,103], 85 => [126,110,83], 86 => [108,107,71], 87 => [111,102,52], 88 => [121,53,120], 89 => [116,37,93], 90 => [67,33,48], 91 => [38,100,35], 92 => [118,77,90], 93 => [37,82,91], 94 => [36,99,108], 95 => [43,63,72], 96 => [40,123,85], 97 => [35,120,112], 98 => [65,41,105], 99 => [52,40,46], 100 => [9,115,76], 101 => [42,89,33], 102 => [68,51,36], 103 => [63,0,78], 104 => [119,109,80], 105 => [59,85,9], 106 => [85,83,86], 107 => [105,124,115], 108 => [97,58,53], 109 => [99,95,97], 110 => [80,101,75], 111 => [103,70,88], 112 => [81,88,59], 113 => [73,49,87], 114 => [79,105,34], 115 => [70,108,109], 116 => [104,90,77], 117 => [124,72,37], 118 => [54,39,40], 119 => [112,92,70], 120 => [110,61,74], 121 => [122,36,50], 122 => [47,121,65], 123 => [95,55,61], 124 => [75,96,95], 125 => [90,81,79], 126 => [44,32,66], 127 => [87,54,101] ); my $encode = 0; my($l); while(<>) { # if only <% on line, don't look further if(/^\s*<%\s*$/) { next; } # Check if this line contains things that need decoding if(($encode == 1) && ((/<%/) || (/Start Encode/))){ # Cut out all actual encoded parts $l = $_; while($l =~ /^(.*?)(<%)*('\*\*Start Encode\*\*)*(=*)#@~\^[0-9a-zA-Z]+==(.*?).{3}AAA==\^#~@%>(.*)$/) { print $1 . "<%" . $4; decode_string($5); print "%>"; $l = $6; } print $l . "\n"; } elsif((//i) || (/<%@*\s*language\s*=\s*(\S+)\.encode\s*%>/i)) { # encoding header found, start to look for encoded parts $encode = 1; # make a new header print "\n"; # This function does the actual decoding. For each char found, determine # which char it needs to be decoded to, depending on the current position # in the pattern sub decode_string { my($in) = @_; my($i, $c, $pos, $mod); my $length = length($in); my $s = 0; if($in eq "") { print "Regexp Failed\n"; return; } for($i = 0;$i < $length;$i++) { $c = substr($in,$i,1); if($s == 0) { # encountering @ is a special situation # cause it's the start of a two letter # encoding (for \r and \n) if ($c eq "@") { $s = 1; } else { $mod = $pos % 64; print chr($code{ord($c)}[$pat[$mod]]); $pos++; } } elsif ($s == 1) { if($c eq "#") { print "\r"; } elsif($c eq "&") { print "\n"; } # other @ not of interest $s = 0; $pos++; } } }