Mpj dZddlmZddlZddlZddlZddlZddlZddlZejjejjejje ZejjejjedZeejvrejj#deddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZd Zej<dd k\reZ dZ!dZ"da#e!e"fDcgc]}d|z c}Z$e!e"fDcgc]}d|z c}Z%e!e"fDcgc]}d|z c}Z&e!e"fDcgc]}d|z c}Z'e!e"fDcgc]}d|z c}Z(e!e"fDcgc]}d|z c}Z)e!e"fDcgc]}d|z c}Z*dZ+dZ,ejZdej\Z/dZ0dZ1dZ2e1Z3dezZ4dZ5ejld Z7Gd!d"ejpZ9d#Z:dBd$Z;d%Zd(Z?d)Z@d*ZAd+ZBd,ZCdBd-ZDd.ZEejZd/ZFeGd0e,DZHejZd1ZId2ZJd3ZKGd4d5ejZMd6ZNdBd7ZOd8ZPejZd9ZQd:ZRd;ZSd<ZTeQZUd=ZVdBd>ZWdCd?ZXdBd@ZYeZdAk(rejeYyycc}wcc}wcc}wcc}wcc}wcc}wcc}w)Da? msodde.py msodde is a script to parse MS Office documents (e.g. Word, Excel, RTF), to detect and extract DDE links. Supported formats: - Word 97-2003 (.doc, .dot), Word 2007+ (.docx, .dotx, .docm, .dotm) - Excel 97-2003 (.xls), Excel 2007+ (.xlsx, .xlsm, .xlsb) - RTF - CSV (exported from / imported into Excel) - XML (exported from Word 2003, Word 2007+, Excel 2003, (Excel 2007+?) Author: Philippe Lagadec - http://www.decalage.info License: BSD, see source code or documentation msodde is part of the python-oletools package: http://www.decalage.info/python/oletools )print_functionNz..)ooxml) xls_parser)rtfobj)is_ppt)crypto)ensure_stdout_handles_unicode) log_helperz0.60.2z>( #(()C*0&*:< < Orfctd}|jddtd|jddd d |jd d d |jddddtd|jddtdd|j dd}|jdddd t d!"|jd#d$dd td%"|jd&d'dd td("|jt)|j|S)*zC parse command line arguments (given ones or per default sys.argv) z@A python tool to detect and extract DDE links in MS Office files) descriptionfilepathzpath of the file to be analyzedFILE)helptypemetavarz-jz--json store_truez.Output in json format. Do not use with -ldebug)actionrzz --nounquotezdon't unquote values)rzr~z-lz --loglevelloglevelstorezElogging level debug/info/warning/error/critical (default=%(default)s))destr~defaultrzz-pz --passwordappendz^if encrypted office files are encountered, try decryption with this password. May be repeated.)r{r~rzz0Filter which OpenXML field commands are returnedzOnly applies to OpenXML (e.g. docx) and rtf, not to OLE (e.g. .doc). These options are mutually exclusive, last option found on command line overwrites earlier ones.)titlerwz-dz --dde-only store_constfield_filter_modez"Return only DDE and DDEAUTO fields)r~rconstrzz-fz--filterz&Return all fields except harmless onesz-az --all-fieldsz1Return all fields, irrespective of their contentsr) r[ add_argumentruDEFAULT_LOG_LEVELstradd_argument_groupFIELD_FILTER_DDEFIELD_FILTER_BLACKLISTFIELD_FILTER_ALL set_defaultsFIELD_FILTER_DEFAULT parse_args) cmd_line_argsparser filter_groups re process_argsrse .HIF  )J*F< h|MO  ,B+- lG 156 lXOP,,@L-ML dL#6>N#GIdJ}#6$:#KMdN=#6>N$./ *>?   ] ++rfctjdj||jj j dr|S|jj j dr|Sy)zr check if field instructions start with DDE expects unicode input, returns unicode output (empty if not dde) zprocessing field '{0}'ddezdder )loggerdebugrslstriplower startswith)datas reprocess_doc_fieldr9sd LL,33D9: {{}''/  {{}''(>? rficd}d}d}g}d}d} |dz }|jd}t|dk(rnt|}|tk(r"|r|rtj dd}d}d}d}]|s`|t k(r|rtj d d}n|tk(r%t|}|r|j|d}d}d}n|s|rnt|tkDr+tj d jtd}nB|dk(r|t|z }n.|d vr|d z }n$|d kr|dz }n|dkr|t|z }n|dz }6|rtj dtj dj|t||S)z find dde links in single word ole stream since word ole file stream are subclasses of io.BytesIO, they are buffered, so reading char-wise is not that bad performanc-wise FNTrrz#big field was not a field after allr z*unexpected field: has multiple separators!z*field exceeds max size of {0}. Ignore rest)  ?z(Checked {0} characters, found {1} fields) readlenordOLE_FIELD_STARTrr OLE_FIELD_SEP OLE_FIELD_ENDrrOLE_FIELD_MAX_SIZErsunichr) stream have_starthave_sepfield_contents result_partsmax_size_exceededidxchar new_results reprocess_doc_streamrLs JHNL C  q{{1~ t9> t9D ? "/ BCJH %  N   =  IJH ] "*>:J##J/JH!N!^$'99 I$f%78:$(!&,.!%'$&&,.$&q t :; LL;&c,/02 rfc tjdg}t|jD]\}}|du}|r|j |}|j t jk(}tjdj||rdn |j|rdj|jndj|j |st|j|j|j}|r%tjd|jd||j|d j|S) a find dde links in word ole (.doc/.dot) file Checks whether files is ppt and returns empty immediately in that case (ppt files cannot contain DDE-links to my knowledge) like process_xml, returns a concatenated unicode string of dde links or empty if none were found. dde-links will still begin with the dde[auto] key word (possibly after some whitespace) process_docNzdirentry {:2d} {}: {}z[orphan]zis stream of size {}zno stream ({})zstream : r)rr enumerate direntries_load_direntry entry_typeolefile STGTY_STREAMrsnamesizer_open isectStartextendjoin)olelinkssiddirentry is_orphan is_stream new_partss rerrs LL E"3>>2$ X$ ))#.H''7+?+??  ,fS *x}} )4::8==I-44X5H5HIK L * (--x}}=?I  yIJ LL #!$& ::e rfcFg}d} tj|}|jD]}t|tjs|j D]}t|tj s|jtj jtj jfvs_|j|jjdddj|||jSS#||jwwxYw)z" find dde links in excel ole file N r)rXlsFile iter_streams isinstanceWorkbookStream iter_recordsXlsRecordSupBooksupport_link_typeLINK_TYPE_OLE_DDELINK_TYPE_EXTERNALr virt_pathreplacerclose)rxresultxls_filerrecords re process_xlsrs FH%%h/++- MFfj&?&?@ --/ M!&**E*EF++"33EE"33FF0HHMM&"2"2":":9d"KL  M Mzz&!   NN  8  NN  sB1D 8?D D c8tj|}g}d}d}|jttzD]\}}}|dk(rd}|j tvrc|j jtdxs"|j jtd} | |jt| |D]} d} | j tvr2| D]*} | j tvs| j tvs(| } n| J| } | tj|d| j jtdxs"| j jtd} | -| dk(r|dz }| dk(r|dz}|d vr|j|d}d}| j tvs| j |t| j z }t"j%d j'||t(dfvr|}nz|t*k(r%|Dcgc]}t,j/|r|}}nL|t0k(r)|Dcgc]}t3|j5s|}}nt7d j'|d j9|Scc}wcc}w) z7 find dde-links (and other fields) in Word 2007+ files rr )tagsrNz Got "None"-Element from iter_xmlbeginend)rrzfiltering with mode "{0}"#Unexpected field_filter_mode: "{0}"r)r XmlParseriter_xmlTAG_W_PTAG_W_FLDSIMPLEtagattribget ATTR_W_INSTRrunquoteTAG_W_R TAG_W_FLDCHARTAG_W_INSTRTEXTBadOOXMLATTR_W_FLDCHARTYPEtextrrrsrrFIELD_DDE_REGEXmatchrfield_is_blacklistedstrip ValueErrorr)rxrr all_fieldslevelddetext_subsdepth attrib_instr curr_elemelemchild attrib_type clean_fieldsfields re process_docxr s __X &FJ EG //w/H/I-.4 A:E 88 &;;??<?;<;;??<?; '!!',"78 ! .ID}}'&EyyM1!II8$  < |nnX%GII++//* &!235 5 ::l ##9Ds J"Jcd|vstr|S|jjd}d}|ddD]} tt |}||z }|S#t $r|}YwxYw)z0TODO: document what exactly is happening here...rIrr rN) NO_QUOTESrsplitchrintr)r partsddestrpart characters rerrs|ey KKM   $E Fab  CII )  M I sA A"!A"z "[^"]*"|\S+c#BK|]}|djyw)rN)r).0r s re r.sK%U1X^^-Ksz ^\\[\w#*@]$ctj|}|sy tj|dj }t jdj|t|t|\}}}}}}d} |ddD]} | ddk(rn| dz } | |kr't jdj| ||y| ||zkDr(t jdj| |||yd} g} |d| zdD]} | r2| r+| | vr't jd j| |yd} g} 7tj| s't jd j| |y| d} | |vr}| |vrd } | d k(rd |vrd } | dk(rd|vrd } | dk(rd } | ddgz } d|vr| gdz } d |vsg} t jdj| |yy #t $rYywxYw)a Check if given field contents matches any in FIELD_BLACKLIST A complete parser of field contents would be really complicated, so this function has to make a trade-off. There may be valid constructs that this simple parser cannot comprehend. Most arguments are not tested for validity since that would make this test much more complicated. However, if this parser accepts some field contents, then office is very likely to not complain about it, either. Frz.trying to match "{0}" to blacklist command {1}rN\z7too few args: found {0}, but need at least {1} in "{2}"z;too many args: found {0}, but need at most {1}+{2} in "{3}"z,Found invalid switch argument "{0}" in "{1}"z%expected switch, found "{0}" in "{1}"T#r@r* CHARFORMAT MERGEFORMATr)CapsFirstCapLowerUpperzunexpected switch {0} in "{1}") FIELD_WORD_REGEXfindallFIELD_BLACKLIST_CMDSindexrrrrrsFIELD_BLACKLISTFIELD_SWITCH_REGEXr)contentswordsr'rnargs_requirednargs_optional sw_with_argsw_solo sw_formatnargsword expect_arg arg_choicesswitchs rerr2sN  $ $X .E $**58>>+;< LLB&?5#9:< % GA~~{GY Eab  7d?    ~ OfUNH= ? ~.. fUNNHM OJKagh" K 7 L$fT846JK #))$/ LLA &x0 2a W   { "J s]yI5J s]zY6J s]J L-8 8K9$EE I% LL: &2 4E"J  s&G%% G10G1c Jg}tj|}|jD]\}}}|jj }|dk(s|j ds8g}d|j vr|j|j dd|j vr|j|j d|jdj|tjd|d|dt||jD]\}} } tjd j|| tj | | |D]} tjd j|| t#| tj$sC| j&tj$j(k(sk|j| j*dz| j,zdj|S#t.$r} | j1d s| j1d rtj2} n7| j1d s| dk(rtj} ntj} | dj|| t5| Yd} ~ d} ~ wwxYw)z< process an OOXML excel file (e.g. .xlsx or .xlsb or .xlsm) ddelinkz}ddelink ddeServiceddeTopicrz Found tag "z " in file rz1Parsing non-xml subfile {0} with content type {1}z{0}: {1}zapplication/vnd.ms-excel.zapplication/vnd.ms-office.zimage/zKapplication/vnd.openxmlformats-officedocument.spreadsheetml.printerSettingsz/Failed to parse {0} of content type {1} ("{2}")Nr)rrrrrendswithrrrrrrepr iter_non_xmlinforsrparse_xlsb_partrXlsbBeginSupBook link_type LINK_TYPE_DDEstring1string2 ExceptionrrXr)rx dde_linksr subfilenamerrr link_infosubfile content_typehandlerexclog_funcs re process_xlsxrMs6I __X &F & 1 ` T1hhnn ) s||J7It{{*  \!:;T[[(  Z!89   TYYy1 2 LLCdS\o^ _ `*0)<)<)>?%v ? KKK6 8$44V\5<> L Z..w?@fj&A&AB(("33AAB$$V^^c%9FNN%JK  L ?6 ::i  ! ?&&'BC&&'CD!>>((2l474"<<!;; FfWlCH= ? ? ?s&A>H'H-H J" B JJ"c4eZdZdZfdZdZdZdZxZS)RtfFieldParserzB Specialized RTF parser to extract fields such as DDEAUTO c:tt| |g|_yr])r`rO__init__fields)rbrrds rerQzRtfFieldParser.__init__s nd,T2 rfch|jdk(r#tjd|jzyy)Nfldinstz!*** Start field data at index %Xh)cwordrrstart)rb destinations reopen_destinationzRtfFieldParser.open_destinations2    * LL<&,,- . +rfcf|jdk(rtjd|jztjd|jz|jj ddj }tjd|z|jj|yy)NrTz!*** Close field data at index %XhzField text: %rs zCleaned Field text: %r) rUrrr'r translaterrRr)rbrW field_cleans reclose_destinationz RtfFieldParser.close_destinations    * LL5E5EFE%,,w'FJF LL>&*C O<>-t44! . .+59%*0079 9 4 4+5D%3EKKMBD D> &!235 5 ::l ###G9DsD35D8#"D=iz\s*"?[=+-@](.+)\|(.+)!(.*)\s*z, ;|^cg}tjjdkr td}n td}t |fi|5}t |t \}}|jtk}|rp|sntjd|jdt j|jd}|D]#} |jdt ||\}}%|r|stjd |jdt"j%|j't} | r1|j)d j+| j-d dd d d d j+|S#tj$r'tjdj!|YwxYw#1swYWxYw) a find dde in csv text finds text parts like =cmd|'/k ..\..\..\Windows\System32\calc.exe'! or =MSEXCEL|'\..\..\..\Windows\System32\regsvr32 [...] Hoping here that the :py:class:`csv.Sniffer` determines quote and delimiter chars the same way that excel does. Tested to some extend in unittests. This can only find DDE-links, no other "suspicious" constructs (yet). Cannot deal with unicode files yet (need more than just use uopen()). r^rb)moder )newlinez*small file, no results; try all delimitersrz(failed to csv-parse with delimiter {0!r}z5last attempt: take whole file as single unquoted cellrNr)sys version_infomajordictopenprocess_csv_dialectCSV_DELIMITERStellCSV_SMALL_THRESHrrseekr delimitercsvErrorrsCSV_DDE_FORMATrrrrgroups) rxresultsopen_argrhdialectis_small other_delimdelimrrs re process_csvrsG "T?# h #( #>{.{NK##%(88 G LLE F   Q (001B1BBGK$ 11$$Q'!4[%!HJGQ 1 G LL  !   Q "(()9)9:J)KLEtyy);<=3>6 ::g yy1LL!K"(&-11>>s2A:F= F BF=7F:7F=9F::F==Gc tjj|jt|}d|_t jdj|j|j|jdg}tj||}|D]R}|D]K}tj|}|s|jdj!|j#ddMT||fS)z= helper for process_csv: process with a specific csv dialect ) delimitersFz=sniffed csv dialect with delimiter {0!r} and quote char {1!r}rrNr^)rzSniffersniffrrwstrictrrrsry quotecharrxreaderr|rrrr})rhrrr~rrowcellrs rertrt/skkm!!+"2"23C"D-7"9GGN LL(&**G,=,=>@ QG ZZ W -F> >D"((.Etyy);<=  >> G rfcg}tj|}|jD] \}}}|jj }|dk7r|j ds9d}|j D]G}|j dk(s |j j ds6|j|}n|tjdj|tjt|}|s|jdj|j!dd d j|S) z find dde links in xml files created with excel 2003 or excel 2007+ TODO: did not manage to create dde-link in the 2007+-xml-format. Find out whether this is possible at all. If so, extend this function rz}cellNformulaz}formulazfound cell with formula {0}rr^r)rrrrrr:keysrrrrsrerXML_DDE_FORMATrrr}) rxrErrrrrkeyrs reprocess_excel_xmlrLs  I __X &Foo'< 4hhnn &=g!6 99; Cyy{i'399;+?+? +K((3-  ?  3::7CD1    TYYu||~bq'9: ;< ::i  rfcJtj|rtjdt j |r tjdt |St|rtjdytjdtj|d5}t|cdddSt|d5}|jd tk(r*tjd t||cdddS ddd tj|}tjd j!||tj$k(r tjd t'|S|tj(tj*fvr tjdt-|S|tj.tj0fvr tjdt3|S| tjdt5|Stjdt3||S#1swYxYw#1swYKxYw#t"$r1}tjd j!|d}Yd}~Id}~wwxYw)z2 decides which of the process_* functions to call z3Is OLE. Checking streams to see whether this is xlsz Process file as excel 2003 (xls)zis ppt - cannot have DDEr zProcess file as word 2003 (doc)N) path_encodingrlzProcess file as rtfzDetected file type: {0}z'Exception trying to xml-parse file: {0}z"Process file as excel 2007+ (xlsx)z)Process file as xml from excel 2003/2007+z(Process file as xml from word 2003/2007+zProcess file as csvz!Process file as word 2007+ (docx))r isOleFilerrris_xlsrr OleFileIOrrsrrerjrget_typersrD DOCTYPE_EXCELrMDOCTYPE_EXCEL_XMLDOCTYPE_EXCEL_XML2003rDOCTYPE_WORD_XMLDOCTYPE_WORD_XML2003r r)rxrrrhdoctyperKs re process_filerfs" JK   X & LL; <x( ( (  LL3 4 67   xt < $s# $ $ h ?   A ) + LL. /{,=> ?? +? ..* .55g>? %%%% 9:H%%5**E,G,GHH @A **5))5+E+EFF ?@H%% *+8$$ LL45 "3 44= $ $??  >EEcJKs0- I9I9I(II%( J"1&JJ"c zd} t|fi|}tj|s|S |tj k\rtj||d}|tj}nt|tjz} tj dtj||}|s*tjdtj|tjdt|||d zfi|} tj ||S#t$r1tj ddtj|sY)wxYw#t$rtj d dY|SwxYw# tj |w#t$rtj d dYwwxYwxYw) aA Process a file that might be encrypted. Calls :py:func:`process_file` and if that fails tries to decrypt and process the result. Based on recommendation in module doc string of :py:mod:`oletools.crypto`. :param str filepath: path to file on disc. :param passwords: list of passwords (str) to try for decryption or None :param int crypto_nesting: How many decryption layers were already used to get the given file. :param kwargs: same as :py:func:`process_file` :returns: same as :py:func:`process_file` r zIgnoring exception:T)exc_infoNzTrying to decrypt filez4Decrypt failed, run with debug output to get detailszAnalyze decrypted filerz*Ignoring exception closing decrypted file:)rr is_encryptedrDrrMAX_NESTING_DEPTHMaxCryptoNestingReachedDEFAULT_PASSWORDSlistdecryptraWrongEncryptionPasswordr=process_maybe_encryptedrnunlink)rx passwordscrypto_nestingkwargsrdecrypted_files rerrs"Fh1&1""8,M-111,,^XFFN,, Of&>&>> ( -.)< LLO P00: : ,-()7)9E=CE ( IIn % M=  *T :""8, -6 ( LLE"&  ( M ( ( IIn % ( LLE"&  ( (sT"DA=E<E6EE E98E9<F:>FF: F74F:6F77F:ct|}tj|j|jt j |jrdatjttjd|jzd}d} t|j|j|j}d}tjd |j'D]}tj|d tj(|S#t $r(}tj#t%|Yd}~d}~wwxYw) a Main function, called if this file is called as a script Optional argument: command line arguments to be forwarded to ArgumentParser in process_args. Per default (cmd_line_args=None), sys.argv is used. Option mainly added for unit-testing )rTzOpening file: %sr rrrNz DDE Links:zdde-link)r{)rr enable_loggingjsonrrostdout nounquoterr print_strr_rxrpasswordrrD exceptionr splitlines end_logging)rargsr return_coderKlinks remainrs   &D diiszzJ ~~  V '$--78 DK#& MM4=="446  \"!0J/0  #S""#s.D E "EE __main__r])Nr)\rj __future__rrqrnrorrzrronormpathabspathdirname__file___thismodule_dirr _parent_dirinsertoletoolsrrroletools.ppt_record_parserrroletools.common.io_encodingr oletools.common.log_helperr __version__rprrNS_WORD NS_WORD_2003rrrrrrrr LOCATIONSr(compileIrrrrrr_rget_or_create_silent_loggerrArgumentParserr[rurrrrrrrrrr rr$tupler&r)rrM RtfParserrOrerjrwr|rurrtrrrrrrgexit)nss0rers=`& ''""277??277??83L#MNggrww||OTBC chhHHOOA{#-E18 "A! F IE  3:L2IJB?R'J3:L2IJB?R'J/6 .EF#F #*L"9 :B7R< :#*L"9 :B7R< :,3\+BCR b C 8?7NO'",O 6 AF"**12448.-   0 / / 9 8(118#,p   L^ F.D$N "2::n-K?KKRZZ/Rj,!^BV%%B> $8<=-`4 !4)5^4n%P z CHHTV_KJF : :COs* J>. K K K - K K K