|irvdZddlmZddlZddlZddlZddlZddlZddlZddl Z ddl m Z ddl Z ddl Z ejejejeZejejedZeejvrejdeddlmZddlmZmZmZddlmZdd lm Z d Z!d Z"ej#ej$ej%ej&ej'ej#d Z(Gd dej)Z*ej'dzfdZ+e+dZ,dZ-ej.ddkrdZ/ndZ/e0Z1dZ2dZ3dZ4ej5dZ6e6j7dksJej5dZ8e8j7dksJdZ9dZ:dZ;dZdZ?gdZ@dZAd ZBd!ZCd"ZDd#ZEd$ZFd%ZGGd&d'eHZIGd(d)eHZJd*ZKd+eAfd,ZLd-ZMd.ZNGd/d0e jOZPd8d1ZQd2ZRd3ZSd8d4ZTd5ZUd8d6ZVeWd7krejXeVdSdS)9a_ oleobj.py oleobj is a Python script and module to parse OLE objects and files stored into various MS Office file formats (doc, xls, ppt, docx, xlsx, pptx, etc) Author: Philippe Lagadec - http://www.decalage.info License: BSD, see source code or documentation oleobj is part of the python-oletools package: http://www.decalage.info/python/oletools )print_functionN) is_zipfile..)xglob)is_pptPptFilePptRecordExOleVbaActiveXAtom) XmlParser)ensure_stdout_handles_unicodez0.60.1warning)debuginfor errorcritical debug-olefileceZdZdZdZdS) NullHandlera( Log Handler without output, to avoid printing messages if logging is not configured by the main application. Python 2.7 has logging.NullHandler, but this is necessary for 2.6: see https://docs.python.org/2.6/library/logging.html section configuring-logging-for-a-library cdSN)selfrecords =C:\PYTHON\_runtimes\venv\Lib\site-packages\oletools/oleobj.pyemitzNullHandler.emits N)__name__ __module__ __qualname____doc__rrrrrrzs-     rrc |tjjjvr+tj|}|||Stj|}|t|||S)an Create a suitable logger object for this module. The goal is not to change settings of the root logger, to avoid getting other modules' logs on the screen. If a logger exists with same name, reuse it. (Else it would have duplicate handlers and messages would be doubled.) The level is set to CRITICAL+1 by default, to avoid any logging. )loggingLoggermanager loggerDict getLoggersetLevel addHandlerr)namelevelloggers r get_loggerr,s w~%000"4((  t $ $F kmm$$$ OOE MroleobjcNttjdS)z Enable logging for this module (disabled by default). This will set the module-specific logger level to NOTSET, which means the main application controls the actual logging level. N)logr'r"NOTSETrrrenable_loggingr1s LL     rzJ{http://schemas.openxmlformats.org/package/2006/relationships}Relationshipz>{http://schemas.microsoft.com/office/2006/01/customui}customUIz>{http://schemas.microsoft.com/office/2009/07/customui}customUIzrc|4t|dd}n0t|||dzd}|dz }||fS)a_ Read an unsigned integer from the 16 bits of data following index. :param data: bytes string or stream containing the data to be extracted. :param index: index to start reading from or None if data is stream :return: tuple (value, index) containing the read value (int), and the index to continue reading next time. Nr2r) STRUCT_UINT16rIrJrKs r read_uint16rSrPrct||\}}|dkrd|fS|.||dz }|d}n#||||zdz }|||zdz }||z }|tksJ||fS)aq Read a length-prefixed ANSI string from data. :param data: bytes string or stream containing the data to be extracted. :param index: index in data where string size start or None if data is stream :return: tuple (value, index) containing the read value (bytes string), and the index to start reading from next time. rNr )rOrJ NULL_CHAR)rLrMlength ansi_string null_chars rread_length_prefixed_stringrZs e,,MFE {{E{ }iiq)) IIaLL 5va/0 va(    ! ! ! !  rc dD][} ||d}td|||cS#t$rYXwxYwtd|ddS)z guess encoding of byte string to create unicode Since this is used to decode path names from ole objects, prefer latin1 over utf* codecs if ascii is not enough )asciilatin1utf8z utf-16-leutf16strict)errorszdecoded using {0}: "{1}"zGfailed to guess encoding for string, falling back to ascii with replacer\replace)decoder/r format UnicodeErrorr )rLencodingresults rguess_encodingrh1s D [[([;;F II1886JJ K K KMMM    D KK%&&& ;;wy; 1 11sAA AAc|t}ttD]R}t|d}|dkrt ||fcS||Std|d||tz}t ||||dzfS)a_ Read a zero-terminated string from data :param data: bytes string or stream containing an ansi string :param index: index at which the string should start or None if data is stream :return: tuple (unicode, index) containing the read string (unicode), and the index to start reading from next time. Nr rz&found no string-terminating zero-byte!) bytearrayxrange STR_MAX_LENordrJrhappend ValueErrorrM)rLrMrg_charend_idxs rread_zero_terminated_stringrtCs } $$  Atyy||$$Dqyy%f--u4444 MM$    ABBB**WeU;->??d5=122GAI==rc(eZdZdZdZdZddZdZdS) OleNativeStreamz OLE object contained into an OLENativeStream structure. (see MS-OLEDS 2.3.6 OLENativeStream) Filename and paths are decoded to unicode. r r2NFcd|_d|_d|_d|_d|_d|_d|_d|_||_d|_ d|_ || |dSdS)a' Constructor for OleNativeStream. If bindata is provided, it will be parsed using the parse() method. :param bindata: forwarded to parse, see docu there :param package: bool, set to True when extracting from an OLE Package object N)rL) filenamesrc_path unknown_shortunknown_long_1unknown_long_2 temp_path actual_sizerLr>is_linkdata_is_streamparse)rbindatar>s r__init__zOleNativeStream.__init__ks~  !""   "   JJGJ $ $ $ $ $  rc2t|dr d|_d}n d|_d}|jsJt||\|_}t d|jt||\|_ }t||\|_ }t||\|_ }t||\|_ }t||\|_}t||\|_} t||\|_}|jr||_n||||jz|_d|_dS#t&t(jf$r3t dd|_d|_d|_YdSwxYw)a> Parse binary data containing an OLENativeStream structure, to extract the OLE object it contains. (see MS-OLEDS 2.3.6 OLENativeStream) :param data: bytes array or stream, containing OLENativeStream structure containing an OLE object :return: None rJTNFrz*OLE native data size = {0:08X} ({0} bytes)z$data is not embedded but only a link)hasattrrr>rOnative_data_sizer/r rdrSrzrtrxryr{r|r}r~rLrIOErrorstructrrrLrMs rrzOleNativeStream.parses 4  "&D EE"'D E| 6+6tU+C+C (D !5 IIBvd344 6 6 6%0e$<$<!E:4GG u:4GG u%0u%=%="U%0u%=%="U ;D% H H &1$&>&> #D e" ?   uT-='=!=>  DLLL&    II< = = =DL D DIIII  sAE AFFNFrrrr TYPE_LINKED TYPE_EMBEDDEDrrrrrrvrv_sMKM%%%%.00000rrvc(eZdZdZdZdZddZdZdS) OleObjectzG OLE 1.0 Object see MS-OLEDS 2.2 OLE1.0 Format Structures r r2Ncd|_d|_d|_d|_d|_d|_d|_|||dSdS)aM Constructor for OleObject. If bindata is provided, it will be parsed using the parse() method. :param bindata: bytes, OLE 1.0 Object structure containing OLE object Note: Code can easily by generalized to work with byte streams instead of arrays just like in OleNativeStream. N) ole_version format_id class_name topic_name item_namerL data_sizer)rrs rrzOleObject.__init__s\     JJw       rcd}t||\|_}t||\|_}td|j|j|j|j|jfvsJt||\|_}t||\|_ }t||\|_ }td|j|j |j |j|jkrt||\|_ }td|j t||z ||||j z|_ t|j |j ksJ|||j zd|_dSdS)a Parse binary data containing an OLE 1.0 Object structure, to extract the OLE object it contains. (see MS-OLEDS 2.2 OLE1.0 Format Structures) :param data: bytes, OLE 1.0 Object structure containing an OLE object :return: rz!OLE version=%08X - Format ID=%08Xz,Class name=%r - Topic name=%r - Item name=%rz)Declared data size=%d - remaining size=%dN)rOrrr/r rrrZrrrrlenrL extra_datars rrzOleObject.parses"-dE":":% +D% 8 8 5"DN 4 4 4~$"4d6F!GGGGG!T/ / /%0e$<$< !DNE IIAnc$iio 7 7 7U5#778DIty>>T^3333"5#7#8#89DOOO 0 /rrrrrrrrsMKM    (!:!:!:!:!:rrc|s|St|}||kr|S|d}|dkr |d|S||z }||kr |d|S|d||z ||dzS)z@Create filename shorter than max_len, trying to preserve suffix..N)rrfind)fnamemax_lenname_lenidx suffix_lens rshorten_filenamers  5zzH' ++c  C byyXgXCJGXgX $'*$$ %cdd 33rrqcFtj|}t jd||}t |}d|vr|dd}d|vd|vr|dd}d|vt||S)a  Return filename that is save to work with. Removes path components, replaces all non-whitelisted characters (so output is always a pure-ascii string), replaces '..' and ' ' and shortens to given max length, trying to preserve suffix. Might return empty string z[^a-zA-Z0-9._ -]rrz  ) ospathbasenamestripresubstrrbr)rx replacementrbasepath sane_fnames rsanitize_filenamersw))//11H+[(CCJZJ *  ''c22  *   *  ''c22  *   J 0 00rc #Kg}g}|||fD]}t|d|d}||dzd}t||}|sk|d}|dkr|||t |dz kr|||||d|V|D]}|V|d t tD]]} |D]X} |t | z } | dkrd tj d td | } | | zVY^d |zVdS) aA Get some sane filenames out of path information, preserving file suffix. Returns several canddiates, first with suffix, then without, then random with suffix and finally one last attempt ignoring max_len using arg `noname_index`. In some malware examples, filename (on which we relied sofar exclusively for this) is empty or " ", but src_path and tmp_path contain paths with proper file names. Try to extract filename from any of those. Preservation of suffix is especially important since that controls how windoze treats the file. /\r NrrrrUabcdefghijklmnopqrstuvwxyzz oleobj_%03d) maxrrrrorrangeMAX_FILENAME_ATTEMPTSjoinrandomsamplemin) rxrytmp_pathr noname_indexsuffixescandidates_without_suffix candidaterrqsuffix leftover_lenr)s rget_sane_embedded_filenamesr's H "(3 )//#&& (=(=>>c!eff%++-- &iAAA   ooc"" "99 % , ,Y 7 7 7  3y>>!# # # % , ,Y 7 7 7   #$$(((/  OOB ( ) )    F"S[[0La776=)E),R)>)>@@AAD-       , &&&&&&rc#Kd} t|}|D]}t|D]e\}}t |t rId} t |}|dttj tj kr || t d|j||}|Vn^#t"$rQtd|jt ddYnwxYw|| I#|| wwxYwg || dSdS#|| wwxYw)a( find ole streams in ppt This may be a bit confusing: we get an ole file (or its name) as input and as output we produce possibly several ole files. This is because the data structure can be pretty nested: A ppt file has many streams that consist of records. Some of these records can contain data which contains data for another complete ole file (which we yield). This embedded ole file can have several streams, one of which can contain the actual embedded file we are looking for (caller will check for these). NzJFound record with embedded ole object in ppt (stream "{0}", record no {1})zCError reading data from {0} stream or interpreting it as OLE objectrUTexc_info)r iter_streams enumerate iter_records isinstancer nextiter_uncompressedrolefileMAGICcloser/r rdr)get_data_as_olefilerr )rxppt_filestream record_idxrole data_starts rfind_ole_in_pptrfsH8$$++-- ( (F&/0C0C0E0E&F&F ( (" Ff&BCC(C(%)&*B*B*D*D%E%E %&9s7='9'9&9:gmKK$?IIKKK #F#)6&+z#B#BDDD%88::! "555 %D%+VFK%8%8::: "t 44444 5 ?IIKKK?IIKKKK+'( ( (0   NN      8  NN     sQA%G,AD =GA D F AE;8F:E;;F>GF//GG*c\eZdZdZfdZdZdZdZdZd dZ e j fd Z d Z xZS) FakeFilea create file-like object from data without copying it BytesIO is what I would like to use but it copies all the data. This class does not. On the downside: data can only be read and seeked, not written. Assume that given data is bytes (str in py2, bytes in py3). See also (and maybe can put into common file with): ppt_record_parser.IterStream, ooxml.ZipSubFile ctt|||_d|_t ||_dS)z' create FakeFile with given bytes data rN)superrrrLposrsize)rrL __class__s rrzFakeFile.__init__s> h&&((( II rcdSNTrrs rreadablezFakeFile.readabletrcdSrrrs rwritablezFakeFile.writablesurcdSrrrs rseekablezFakeFile.seekablerrctt||j|jz }|dkrdS|j|j|j|z|d|<|xj|z c_|S)z read into pre-allocated target rN)rrrrrL)rtargetn_datas rreadintozFakeFile.readintosdS[[$)DH"455 Q;;1)DHTXf_$<=ww F rrc|j|jkrtS|dkr|j|jz }|j|j|j|z}|xj|z c_|S)z read and return data r)rrbytesrL)rrrgs rrJz FakeFile.reads_ 8ty 77N R<<Y)F48DHVO34 F rc|tjkr|}nX|tjkr |j|z}n=|tjkr |j|z}n"t d||dkrtd||_dS)z" jump to another position in file z(invalid offset {0}, need SEEK_* constantrz%Seek beyond start of file not allowedN) ioSEEK_SETSEEK_CURrSEEK_ENDrrprdr)rroffsetnew_poss rseekz FakeFile.seeks R[ GG r{ " "hnGG r{ " "i#oGGG$fVnn.. . Q;;ABB Brc|jS)z& tell where in file we are positioned )rrs rtellz FakeFile.tells xr)r)rrrrrrrrrrJrrrr __classcell__)rs@rrrs   "{ rrc#K|,td|}t|}ntd|}|}d} tj|r{t |r5td|zt|D]}|Vd} td|ztj|}|Vn|t|r|&t|}| D]}td|z| D]p\}}} | ttj} n1#t $r$td|zdVYbwxYw| tjkr|dtd |z tj|}|VnZ#t&$rMtd ||td d YnwxYw||d}6#||d}wxYwtd|zrn1td|dVnC#t.$r6td|d dVYnwxYw||dSdS#||wwxYw)z try to open somehow as zip/ole/rtf/... ; yield None if fail If data is given, filename is (mostly) ignored. yields embedded ole streams in form of OleFileIO. Nz*working on data, file is not touched belowzworking on file by namez is ppt file: z is ole file: z is zip file: zzip is encrypted: rz unzipping ole: z@Error reading data from {0}/{1} or interpreting it as OLE objectrUTrz unzip skip: z5open failed: {0} (or its data) is neither zip nor OLEzCaught exception opening {0})r/r rr isOleFilerrr OleFileIOrr iter_xml iter_non_xmlrJrr RuntimeErrorrrrr rdr Exception) rxrL xml_parser arg_for_ole arg_for_ziprrqsubfile file_handleheads rfind_olers  >??? tnn  +,,,  C:  [ ) )2 k"" 83444*;77CIIICC HH_x/ 0 0 0#K00CIIII  #z+'>'> #!&{33 #,,..A HH_x/ 0 0 0,6+B+B+D+D 8 8'K&++C ,>,>??DD#II2X=>>>JJJH 7=(($$Q'''HH07:;;; '%/ <<! "555 %D%+VHg%>%>@@@ "t 44444 5 ?IIKKK"&C?IIKKK"&CJJJJIInw677771 84 KKO)) + + +JJJ  077AA  ! ! !  ? IIKKKKK ?3? IIKKKK sDK1,FK1+F/,K1.F//AK15H JAI%"J$I%%J(K1JAK10M1=L1.M0L11MM'c#$K|ddtD]p\}}} |jddkrA|jdddd}|tvr||jdfVZ#t t f$rYmwxYwdS) zE iterate XML files looking for relationships to external objects NF TargetModeExternalTyperr Target)rOOXML_RELATIONSHIP_TAGattribrsplitBLACKLISTED_RELATIONSHIP_TYPESAttributeErrorKeyError)rrqelemrelationship_types rfind_external_relationshipsr's!))$7MNN   4 {<(J66$(K$7$>$>sA$F$Fq$I!$(FFF+T[-BBBBB)    D   sAA99B  B c#K|ddttfD]!\}}}|d}||V"dS)ap iterate XML files looking for customUI to external objects or VBA macros Examples of malicious usage, to load an external document or trigger a VBA macro: https://www.trellix.com/en-us/about/newsroom/stories/threat-labs/prime-ministers-office-compromised.html https://www.netero1010-securitylab.com/evasion/execution-of-remote-vba-script-in-excel NFonLoad)rTAG_CUSTOMUI_2007TAG_CUSTOMUI_2010get)rrqrcustomui_onloads r find_customUIr7sd!))$8IK\7]^^"" 4(++  %! ! ! !""rc  t|tdz pd}|rotj|s/t d|tj|tj||}n?tj |}tj||}tdtd|zd}d}d}d} d } t|rt d t|} t| D]@\} } d } td | d | | drtdAt| D]} d } td| zt!||| D]F} | | D]*}d|}t d||ddkrd } | |}td|ztdt+|}nB#t,$r5t dd }||YwxYw|jrt dtd|jztd|jztd|jzt;|j|j|jtt=|z dz |D]+}|dz|z}tj|sn, td|ztA|d5}d}tCtD|j#}|r|$|}|%||t=|z }t=||kr > L&H E\\\SYSYZ [ [ [  ** ><===#J// y yFH nqww x x x x $ 33>> ; ++--: : J((:..K II*K 8 8 8"~##%%):::  ^^J77FO'()))/000*622DD KK ;<<>%00#-5666eT** If#$$'9I$J$J ' I#);;y#9#9D"LL...$D 1H"4yyI55 # ,I-3VIs4yy-I-I!K!K!K %(+O,0, 7>>( # #P()=)D)DX)N)NOOO Orcttdtztdtdtdd}tj|}|ddd d |d t d dd|dddt dd|dddt dd|ddddtd |d!dtd"d#$|d%d&t d"d'(|d)d*dd+,| |}|j r|xj |j gz c_ |j rd-|_ |j s|tSt!jt$|j t&jd./t*t j|j d0krt1jd1}d1}d1}t5j|j |j|j|j2D]G\}}} |r|d3rtA|| |j!\} } } || z}|| z}|| z}HtD} |r | tFz } |r | tHz } |r | tJz } | S)4z main function, called when running this as script Per default (cmd_line_args=None) uses sys.argv. For testing, however, can provide other arguments. z)oleobj %s - http://decalage.info/oletoolsz3THIS IS WORK IN PROGRESS - Check updates regularly!zGPlease report any issue at https://github.com/decalage2/oletools/issuesrUz4usage: %(prog)s [options] [filename2 ...])usagez-r store_true recursivez)find files recursively in subdirectories.)actiondesthelpz-dr.Nz(use specified directory to output files.)typerGdefaultrHz-zz--zip zip_passwordziif the file is a zip archive, open first file from it, using the provided password (requires Python 2.6+))rGrIrJrHz-fz --zipfname zip_fname*zoif the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)z-lz --loglevelloglevelstorezElogging level debug/info/warning/error/critical (default=%(default)s))rGrFrJrHinputFILEz"Office files to parse (same as -i))nargsrImetavarrHz-iz --more-inputz7Additional file to parse (same as positional arguments))rIrSrHz-vz --verbosez2verbose mode, set logging to DEBUG (overwrites -l))rFrHr z%(levelname)-8s %(message)s)r*rrdrF)rErKrLr)&r r% __version__r?ArgumentParser add_argumentrDEFAULT_LOG_LEVELrA parse_args more_inputrPverboserN print_helpRETURN_ERR_ARGSr" basicConfig LOG_LEVELSsysstdoutr/r'r0rr1r iter_filesrErKrLendswithr=r.RETURN_NO_DUMPRETURN_DID_DUMPRETURN_ERR_STREAMRETURN_ERR_DUMP) cmd_line_argsrCparseroptionsany_err_streamany_err_dumping any_did_dump containerrxrLr1r2r3 return_vals rmainros"### 5 CDDD ?@@@  9::: "III BE  $5 1 1 1F  \ HJJJ 3\4GIII gN $,---  l3 #6777  lG 15666 sACCC n3*+++ k,/000 ..G0 ',// #" =  j)9:3:<>>>>LL   ?**   NOL  W]g6G*1*>'.'8 : : : ! !! 8T  **3//   4); < < * K*$;&  J&o% ('' &o% r__main__r)Yr __future__rr"rr?rrr_rzipfilerrrrnormpathabspathr$__file___thismodule_dirr _parent_dirinsertoletools.thirdpartyroletools.ppt_record_parserrrr oletools.ooxmlr oletools.common.io_encodingr rTrWDEBUGINFOWARNINGERRORCRITICALr^Handlerrr,r/r1 version_inforVrrlrrrStructrHrrRrmr,rcrdr\rerfrr!rrOrSrZrhrtobjectrvrrrrr RawIOBaserrrrr=rArorexitrrrrs  T&%%%%%   '""27??27??83L3L#M#MNNgrw||OTBBCC chHOOA{###%%%%%%FFFFFFFFFF$$$$$$EEEEEE& (!-!,!/!-!*&} ..       '/    #+A-8j!!!A!III FeTT  d## Q d## Q """& ""   8222$>>>8SSSSSfSSSlA:A:A:A:A:A:A:A:H444*-0111112<'<'<'~(((VAAAAAr|AAAHOOOOd    " " "q-q-q-q-n````F z CHTTVVr