|i dZddlmZddlZddlZddlZddlZddlZddlZej ej ej e Zej ej edZeej vrej deddlmZddlmZddlmZddlmZdd lmZdd lmZdd lmZd Zejdd kreZ dZ!dZ"da#de!e"fDZ$de!e"fDZ%de!e"fDZ&de!e"fDZ'de!e"fDZ(de!e"fDZ)de!e"fDZ*dZ+dZ,ej-dej.Z/dZ0dZ1dZ2e1Z3dezZ4dZ5ej6d Z7Gd!d"ej8Z9d#Z:dBd$Z;d%Zd(Z?d)Z@d*ZAd+ZBd,ZCdBd-ZDd.ZEej-d/ZFeGd0e,DZHej-d1ZId2ZJd3ZKGd4d5ejLZMd6ZNdBd7ZOd8ZPej-d9ZQd:ZRd;ZSd<ZTeQZUd=ZVdBd>ZWdCd?ZXdBd@ZYeZdAkrej[eYdSdS)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.2zrJJJB?R'JJJcg|]}d|zS)z {%s}fldSimpler rs rrrrrcg|]}d|zS)z {%s}fldCharr rs rrrsFFF#FFFrcg|]}d|zS)z{%s}pr rs rrr : : :B7R< : : :rcg|]}d|zS)z{%s}rr rs rrrrrcg|]}d|zS)z {%s}instrr rs rrrsCCCR b CCCrcg|]}d|zS)z{%s}fldCharTyper rs rrrsOOO'",OOOr)zword/document.xmlzword/endnotes.xmlzword/footnotes.xmlzword/header1.xmlzword/footer1.xmlzword/header2.xmlzword/footer2.xmlzword/comments.xml)-) CREATEDATErrhsdatetime)DATErrrhlsr)EDITTIMErrrrnumeric) PRINTDATErrrrr)SAVEDATErrrrr)TIMErrrrr)AUTHORrrrstring)COMMENTSrr(rrr)) DOCPROPERTYr(rrrzstring/numeric/datetime)FILENAMErrrpr))FILESIZErrrkmr#)KEYWORDSrr(rrr)) LASTSAVEDBYrrrrr))NUMCHARSrrrrr#)NUMPAGESrrrrr#)NUMWORDSrrrrr#)SUBJECTrr(rrr))TEMPLATErrrr-r))TITLErr(rrr))ADVANCErrdlruxyrr)SYMBOLr(rfsahjur) FORMCHECKBOXrrrrr) FORMDROPDOWNrrrrr)FORMTEXTrrrrr)INDEXrr bcdefghklpszryr)TArrclrsbir)TCr(rflnr)TOArrbcdeglsfhpr)TOCrr abcdflnopsthuwxzr)XEr(rfrtyrEr) BIBLIOGRAPHYrrlfmrr)CITATIONr(rlfspvmntyr)NOTEREFr(rrrKr)PAGEREFr(rrhpr)QUOTEr(rrrr)STYLEREFr(rrlnprtwr)LISTNUMrr(lsrr)PAGErrrrr#)REVNUMrrrrr)SECTIONrrrrr#) SECTIONPAGESrrrrr#)SEQr(r(rschnr#) USERADDRESSrr(rrr)) USERINITIALSrr(rrr))USERNAMErr(rrr)z^\s*dde(auto)?\s+zonly ddezexclude blacklistedzkeep allzmsodde %s - http://decalage.info/python/oletools THIS IS WORK IN PROGRESS - Check updates regularly! Please report any issue at https://github.com/decalage2/oletools/issues warningmsoddec"eZdZdZfdZxZS)ArgParserWithBannerz' Print banner before showing any error ctttt||dSN)printBANNERsuperrkerror)selfmessage __class__s rrqzArgParserWithBanner.errors3 f  !4((..w77777r)__name__ __module__ __qualname____doc__rq __classcell__rts@rrkrks>11888888888rrkctj|s'tjd||S)z< called by argument parser to see whether given file exists zFile {0} does not exist.)ospathexistsargparseArgumentTypeErrorformat)filenames r existing_filersB 7>>( # #<()C*0&*:*:<< < Orctd}|ddtd|ddd d |d d d |ddddtd|ddtdd|dd}|dddd t d!"|d#d$dd td%"|d&d'dd td("| t)| |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)actionrz --nounquotezdon't unquote values)rrz-lz --loglevelloglevelstorezElogging level debug/info/warning/error/critical (default=%(default)s))destrdefaultrz-pz --passwordappendz^if encrypted office files are encountered, try decryption with this password. May be repeated.)rrrz0Filter 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.)titlerz-dz --dde-only store_constfield_filter_modez"Return only DDE and DDEAUTO fields)rrconstrz-fz--filterz&Return all fields except harmless onesz-az --all-fieldsz1Return all fields, irrespective of their contentsr) rk add_argumentrDEFAULT_LOG_LEVELstradd_argument_groupFIELD_FILTER_DDEFIELD_FILTER_BLACKLISTFIELD_FILTER_ALL set_defaultsFIELD_FILTER_DEFAULT parse_args) cmd_line_argsparser filter_groups r process_argsrs .HIIIF  )J*F<<< h|MOOO  ,B+--- lG 15666 lXOPPP,,@L-MML dL#6>N#GIIIdJ}#6$:#KMMMdN=#6>N$./// *>???   ] + ++rcLtd||dr|S|dr|SdS)zr check if field instructions start with DDE expects unicode input, returns unicode output (empty if not dde) zprocessing field '{0}'ddezdder)loggerdebugrlstriplower startswith)datas rprocess_doc_fieldr9s LL,33D99::: {{}}''//  {{}}''(>?? 3ricd}d}d}g}d}d} |dz }|d}t|dkrnGt|}|tkr'|r|rtdd}d}d}d}q|st|t kr|rtd d}n|tkr-t|}|r| |d}d}d}n|s|rnt|tkr5td td}nM|dkr|t|z }n4|d vr|d z }n*|d kr|dz }n|dkr|t|z }n|dz }v|rtdtd |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 FNTr(rz#big field was not a field after allrz*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_SIZErunichr) stream have_starthave_sepfield_contents result_partsmax_size_exceededidxchar new_results rprocess_doc_streamrLsJ JHNL C8' q{{1~~ t99>> t99D ? " " D/ D BCCCJH %  N    =  K IJJJHH ] " "*>::J 0##J///JH!NN '! '^$$'999 I$f%788:::$(!!&,,.!!%'$&&,,.$&q8't< :;;; LL;&c,//00222 rc tdg}t|jD] \}}|du}|r||}|jt jk}td||rdn|j |rd|j nd|j|rit| |j |j }|r%td|j d|||d |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_STREAMrnamesizer_open isectStartextendjoin)olelinkssiddirentry is_orphan is_stream new_partss rrrs\ LL E"3>22$$ X$  /))#..H'7+??  ,fS "L**x} )J3::8=III-44X5HIIKK L L L  $* (-x}==??I K  yyIJJJ LL # # # ::e  rc:g}d} tj|}|D]}t|tjs|D]t}t|tjs|jtjjtjj fvr.| |j ddud |||SS#||wwxYw)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)rresultxls_filerrecords r process_xlsrs5FH%h//++-- M MFfj&?@@  --// M M!&**EFF+"3E"3F0HHHMM&"2":":9d"K"KLLL  Mzz&!!   NN     8  NN     s C#DDctj|}g}d}d}|ttzD]\}}}|dkrd}|jtvro|jtdp$|jtd} | "| t| |D]} d} | jtvr)| D]"} | jtvs| jtvr| } n#| ;n| } | tj|d| jtdp$| jtd} | 3| dkr|dz }| dkr"|dz}|d vr| |d}d}| jtvr| j|t| jz }t"d ||t(dfvr|}nR|t*kr d |D}n:|t,kr d |D}n"t/d |d|S)z7 find dde-links (and other fields) in Word 2007+ files rr)tagsr(Nz Got "None"-Element from iter_xmlbeginend)rrzfiltering with mode "{0}"cFg|]}t||Sr FIELD_DDE_REGEXmatchrfields rrz process_docx..:999%*00779999rcTg|]%}t|#|&Sr field_is_blacklistedstriprs rrz process_docx..CDDD%3EKKMMBBDDDDr#Unexpected field_filter_mode: "{0}"r)r XmlParseriter_xmlTAG_W_PTAG_W_FLDSIMPLEtagattribget ATTR_W_INSTRrunquoteTAG_W_R TAG_W_FLDCHARTAG_W_INSTRTEXTBadOOXMLATTR_W_FLDCHARTYPEtextrrrrrr ValueErrorr)rrr all_fieldslevelddetext_subsdepth attrib_instr curr_elemelemchild attrib_type clean_fieldss r process_docxr!s _X & &FJ EG //w/H/II-.-.4 A::E 8 & &;??<?;;<;??<?;; '!!',"7"7888 ! .! .ID}''&EyM11!I88$9< !|nX%GIII+//* &!23355 5 ::l # ##rcd|vstr|S|d}d}|ddD]7} tt |}n#t $r|}YnwxYw||z }8|S)z0TODO: document what exactly is happening here...rYrrr(N) NO_QUOTESrsplitchrintr)rpartsddestrpart characters rr r sey KKMM   $ $E Fabb  CIIII   III ) MsA!! A0/A0z "[^"]*"|\S+c#JK|]}|dVdS)rN)rrs r r,.s0KK%U1X^^--KKKKKKrz ^\\[\w#*@]$ct|}|sdS t|d}n#t $rYdSwxYwt d|t|t|\}}}}}}d} |ddD]} | ddkrn| dz } | |kr1t d| ||dS| ||zkr2t d| |||dSd} g} |d| zdD] } | r<| r5| | vr1t d | |dSd} g} At | s1t d | |dS| d} | |vr| |vrd } | d krd |vrd } | dkrd|vrd } | dkrd } | ddgz } d|vr| gdz } d |vrg} t d| |dSd S)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}r(N\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_CMDSindexrrrrrFIELD_BLACKLISTFIELD_SWITCH_REGEXr)contentswordsr;rnargs_requirednargs_optional sw_with_argsw_solo sw_formatnargsword expect_arg arg_choicesswitchs rrr2s  $ $X . .E u$**58>>+;+;<< uu LLB&?5#9::<<< % GA~~{GY Eabb  7d?? E   ~ OfUNH== ? ? ?u ~... fUNNHMM O O OuJKaghh""   K 7 7 L$fT844666uuJK #))$//  LLA &x00 2 2 255a W    { " "JJ s]]yI55JJ s]]zY66JJ s]]J L-8 8K9$$EEEE I%% LL: &22 4 4 455 4s2A A! A!c g}tj|}|D]\}}}|j}|dks|drg}d|jvr ||jdd|jvr ||jd|d|t d|d|dt|| D]\}} } t d || tj| | |D]} t d || t#| tjr?| jtjjkr%|| jdz| jz#t.$r} | d s| d r tj} n4| d s| dkr tj } n tj } | d|| t5| Yd} ~ d} ~ wwxYwd|S)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)rrrr rendswithr rrrrrepr iter_non_xmlinforrparse_xlsb_partrXlsbBeginSupBook link_type LINK_TYPE_DDEstring1string2 Exceptionrrhr)r dde_linksr subfilenamerrr  link_infosubfile content_typehandlerexclog_funcs r process_xlsxrasI _X & &F & 1 1 ` ` T1hnn )  s||J77 It{**  \!:;;;T[((  Z!8999   TYYy11 2 2 2 LLLCCCdS\ooo^ _ _ _*0)<)<)>)>??%v ? KKK66 8 8 8$4V\5<>> L L Z..w??@@@fj&ABBL("3ABB$$V^c%9FN%JKKK  L ? ? ?&&'BCC '&&'CDD '">((22 'l47474"<!; HFfWlCHH== ? ? ? ? ? ? ? ? ? ::i  s.CG== J*BJ%%J*c4eZdZdZfdZdZdZdZxZS)RtfFieldParserzB Specialized RTF parser to extract fields such as DDEAUTO cftt||g|_dSrm)rprc__init__fields)rrrrts rrezRtfFieldParser.__init__s, nd##,,T222 rcd|jdkr$td|jzdSdS)Nfldinstz!*** Start field data at index %Xh)cwordrrstart)rr destinations ropen_destinationzRtfFieldParser.open_destinationsF   * * LL<&,- . . . . . + *rcp|jdkrtd|jztd|jz|jdd}td|z|j|dSdS)Nrhz!*** Close field data at index %XhzField text: %rs zCleaned Field text: %r) rirrr;r translaterrfr)rrrk field_cleans rclose_destinationz RtfFieldParser.close_destinations   * * LL.s$FFFE%,,w''FFFrz+found {1} fields, filtering with mode "{0}"NcFg|]}t||Sr rrs rrzprocess_rtf..rrcTg|]%}t|#|&Sr rrs rrzprocess_rtf..rrrr) RTF_STARTrrrcparserfrrrrrrrrr) file_handlerrr rtfparserr s r process_rtfrs=J {'')) )Dt$$I OOFFY5EFFFJ LL>&*C OO<<>>>-t444! . . .99:999 4 4 4DD:DDD > &!23355 5 ::l # ##riz\s*"?[=+-@](.+)\|(.+)!(.*)\s*z, ;|^c(g}tjjdkrtd}ntd}t |fi|5}t |t \}}|tk}|r|st d| dt |j d}|D]m} | dt ||\}},#tj$r0t d|YjwxYw|r|st d | dt"|t} | rB|d | d dd d d n #1swxYwYd |S) 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()). rrrb)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 delimitercsvErrorrCSV_DDE_FORMATrrrrgroups) rresultsopen_argrdialectis_small other_delimdelimrrs r process_csvrsOG ""T???### h # #( # #>{.{NKK##%%(88  1G 1 LLE F F F   Q   (001BBGGK$ 1 11$$Q'''!4[%!H!HJGQQy111LL!K"(&--111111  >G > LL  ! ! !   Q   "(()9)9:J)K)KLLE >tyy);<<===3>>>>>>>>>>>>>>>6 ::g  s8B G4(C=<G4=>@@@ QG Z W - -F>> > >D"((..E >tyy);<<===  > G rcg}tj|}|D]9\}}}|j}|dkr|ds> ???????? +??????????????? .** .55g>>????  >EEcJJKKK%%%% 9:::H%%%5*E,GHHH @AAA ***5)5+EFFF ?@@@H%%% *+++8$$$ LL4555 "3 4 44s>C##C'*C'>AE  E$'E$,AF.. G18/G,,G1c d} t|fi|}tj|s|SnA#t$r4tddtj|sYnwxYw|tjkrtj||d}| tj}nt|tjz} tdtj ||}|s.t dtj |t dt|||d zfi|} tj|nr#t$rtd dYnJwxYw# tj|w#t$rtd dYwwxYwxYw|S) 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` rzIgnoring exception:T)exc_infoNzTrying to decrypt filez4Decrypt failed, run with debug output to get detailszAnalyze decrypted filer(z*Ignoring exception closing decrypted file:)rr is_encryptedrXrrMAX_NESTING_DEPTHMaxCryptoNestingReachedDEFAULT_PASSWORDSlistdecryptrqWrongEncryptionPasswordrQprocess_maybe_encryptedr|unlink)r passwordscrypto_nestingkwargsrdecrypted_files rrrs8"Fh11&11"8,, M   *T :::"8,,    111,^XFFFN, OOf&>> ( -...)<< ; LLO P P P0:: : ,---()7)9EE=CEE ( In % % % % ( ( ( LLE"&  ( ( ( ( ( ( ( In % % % % ( ( ( LLE"&  ( ( ( ( ( ( MsR"(;A&%A&#t $r1}tt%|Yd}~nd}~wwxYwt d |D]}t |d tj|S) 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: %srr(rrNz DDE Links:zdde-link)r)rr enable_loggingjsonrrstdout nounquoter#r print_strrorrpasswordrrX exceptionr splitlines end_logging)rargsr return_coder_links rmainrsS  & &D diszJJJJ ~  V '$-7888 DK#& M4="4666 ###S""""""""# \"""!!00J//// s #B.. C)8'C$$C)__main__rm)Nr)\rx __future__rrr|rrrrr}normpathabspathdirname__file___thismodule_dirr _parent_dirinsertoletoolsrrroletools.ppt_record_parserrroletools.common.io_encodingr oletools.common.log_helperr __version__rr%rNS_WORD NS_WORD_2003r#rrrrrr r LOCATIONSr<compileIrrrrrrorget_or_create_silent_loggerrArgumentParserrkrrrrrrrrrrr!r r8tupler:r=rra RtfParserrcr}rrrrrrrrrrrruexitr rrrs`&%%%%% '""27??27??83L3L#M#MNNgrw||OTBBCC chHOOA{###------EEEEEE1111118 "A! F IE  JJ7L2IJJJJJ7L2IJJJFFw .EFFF : :7L"9 : : : : :7L"9 : : :CCG\+BCCC OO7NOOO 6 AF"*12488.-   0 / 9 9 88888(1888#,#,#,#,p     LLL^   F.D$D$D$D$N   "2:n--uKK?KKKKKRZ//RRRj,!,!,!^BBBBBV%BBB> $$$$8<==---`4 !!!4)5)5)5)5^4444n%%%%P z CHTTVVr