├── README.md └── rtfexploit_extract.py /README.md: -------------------------------------------------------------------------------- 1 | # rtf_exploit_extractor 2 | Script to extract malicious payload and decoy document from CVE-2015-1641 exploit documents 3 | 4 | usage: rtfexploit_extract.py [-h] [-o OUTFILE] [-d DECOY] [-l LENGTH] [-v] inputfile 5 | 6 | 7 | inputfile exploit document to examine 8 | 9 | optional arguments: 10 | 11 | -h, --help show this help message and exit 12 | 13 | -o OUTFILE, --outfile OUTFILE 14 | output filename for extracted payload 15 | 16 | -d DECOY, --decoy DECOY 17 | output filename for extracted decoy document 18 | 19 | -l LENGTH, --length LENGTH 20 | length of each marker to search for (def: 7) 21 | 22 | -v print debug messages 23 | 24 | 25 | All args are optional except for input filename. 26 | 27 | Ref: http://blog.malwareclipboard.com/2015/10/rtf-exploit-document-extraction.html 28 | -------------------------------------------------------------------------------- /rtfexploit_extract.py: -------------------------------------------------------------------------------- 1 | # rtfexploit_extract.py 2 | # 3 | # @cyberclues 4 | # ref: http://blog.malwareclipboard.com/2015/10/rtf-exploit-document-extraction.html 5 | # 6 | # 2015/10/22 7 | # Updated to support multiple input files 8 | 9 | import struct 10 | import sys 11 | import os 12 | import argparse 13 | 14 | def find_marker(fileref, marker, count): 15 | sect = 1 16 | while sect: 17 | sect = fileref.read(1) 18 | if sect == marker: 19 | for i in xrange(count-1): 20 | nextb = fileref.read(1) 21 | if nextb != marker: 22 | break 23 | else: 24 | if i == count-2: 25 | return fileref.tell() 26 | return "" 27 | 28 | def xor(data, key): 29 | return bytearray(( (data[i] ^ key[i % len(key)]) for i in range(0,len(data)))) 30 | 31 | def xorb(data,key): 32 | l = len(key) 33 | decodedbuff=bytearray() 34 | for i in range(0,len(data),4): 35 | if data[i:i+4] == bytearray([0x0,0x0,0x0,0x0]): 36 | decodedbuff[i:i+4]= bytearray([0x0,0x0,0x0,0x0]) 37 | else: 38 | decodedbuff[i:i+4] = xor(data[i:i+4],key) 39 | return decodedbuff 40 | 41 | if __name__ == "__main__": 42 | parser = argparse.ArgumentParser(description='Extract encrypted payload and decoy document from CVE-2015-1641 exploit documents. This will also work on other rtf exploit docs using a similar begin/end marker and xor cipher.') 43 | parser.add_argument('inputfile', nargs="+", help="exploit document to examine") 44 | parser.add_argument('-o' , '--outfile', help="output filename for extracted payload") 45 | parser.add_argument('-d', '--decoy', help="output filename for extracted decoy document") 46 | parser.add_argument('-l', '--length', type=int,help="length of each marker to search for (def: 7 to 4)", default=None) 47 | parser.add_argument('-v', action="store_true",help='print debug messages') 48 | args = parser.parse_args() 49 | 50 | MARKER_START = '\xba' 51 | MARKER_END = '\xbb' 52 | DECOY_END = '\xbc' 53 | marker_length= args.length 54 | 55 | for afile in args.inputfile: 56 | print 57 | print "# Processing {0}".format(afile) 58 | if os.path.isfile(afile): 59 | f = open(afile, "rb") 60 | else: 61 | print " could not open file, skipping.." 62 | continue 63 | 64 | # Check for magic byte variants 65 | magic = f.read(4) 66 | 67 | if magic == "{\\rt": 68 | badheadchr=f.read(1) 69 | if args.v: 70 | if badheadchr != 'f': 71 | print "# Incomplete rtf header found, continuing.." 72 | else: 73 | print "# Found rtf header.." 74 | 75 | else: 76 | print '# No header found. Skipping..' 77 | continue 78 | 79 | #Look for 0xBA 0xBA 0xBA 0xBA 0xBA 0xBA 0xBA 80 | if args.length is None: 81 | # No length provided, looking from 7 to 4 82 | found = False 83 | marker_length = 7 84 | while (not found and marker_length > 3): 85 | f.seek(0) 86 | start_pos = find_marker(f,MARKER_START,marker_length) 87 | if start_pos: 88 | found=True 89 | if args.v: 90 | print "# Found start marker of lengthh {0}".format(marker_length) 91 | print "# Found start marker at offset: 0x{:02X}".format(start_pos) 92 | else: 93 | if args.v: 94 | print "# Could not find marker of %i bytes" % marker_length 95 | marker_length = marker_length-1 96 | 97 | if not found: 98 | print "# Failed to find start marker, skipping file.." 99 | continue 100 | 101 | else: 102 | if args.v: 103 | print "# Looking for embedded payload and decoy markers of length {0}".format(args.length) 104 | 105 | start_pos = find_marker(f,MARKER_START,marker_length) 106 | if start_pos: 107 | if args.v: 108 | print "# Found start marker at offset: 0x{:02X}".format(start_pos) 109 | else: 110 | print "# Failed to find start marker, skipping file.." 111 | continue 112 | 113 | if args.length is None: 114 | found = False 115 | marker_length = 7 116 | while (not found and marker_length > 3): 117 | f.seek(start_pos) 118 | end_pos = find_marker(f,MARKER_END,marker_length) 119 | if end_pos: 120 | found=True 121 | if args.v: 122 | print "# Found end marker of length {0}".format(marker_length) 123 | print "# Found end marker at offset: 0x{:02X}".format(end_pos-(marker_length)) 124 | else: 125 | marker_length = marker_length-1 126 | 127 | if not found: 128 | print "# Failed to find end marker, skipping file.." 129 | continue 130 | else: 131 | #Look for 0xBB 0xBB 0xBB 0xBB 0xBB 0xBB 0xBB 132 | end_pos = find_marker(f,MARKER_END,marker_length) 133 | if end_pos: 134 | if args.v: 135 | print "# Found end marker at offset: 0x{:02X}".format(end_pos-(marker_length)) 136 | else: 137 | print "# Failed to find end marker, skipping file.." 138 | continue 139 | 140 | if args.length is None: 141 | found = False 142 | marker_length = 7 143 | while (not found and marker_length > 3): 144 | f.seek(end_pos) 145 | decoy_pos = find_marker(f,DECOY_END,marker_length) 146 | if decoy_pos: 147 | found=True 148 | decoy_found = True 149 | if args.v: 150 | print "# Found decoy document end marker at offset: 0x{:02X}".format(decoy_pos-(marker_length)) 151 | else: 152 | marker_length = marker_length -1 153 | if not found: 154 | print "# Failed to find decoy marker" 155 | decoy_found = False 156 | else: 157 | decoy_pos = find_marker(f,DECOY_END,marker_length) 158 | decoy_found=True 159 | if decoy_pos: 160 | if args.v: 161 | print "# Found decoy document end marker at offset: 0x{:02X}".format(decoy_pos-(marker_length)) 162 | else: 163 | print "# Failed to find decoy marker" 164 | decoy_found = False 165 | 166 | # open output files 167 | if args.outfile: 168 | outfile = open(args.outfile,"wb") 169 | else: 170 | outfile = open(afile + "_payload", "wb") 171 | 172 | if decoy_found: 173 | if args.decoy: 174 | decoyfile = open(args.decoy,"wb") 175 | else: 176 | decoyfile = open(afile + "_decoy","wb") 177 | 178 | #grab payload content 179 | f.seek(start_pos) 180 | total_bytes= (end_pos-(marker_length))-start_pos 181 | if args.v: 182 | print "# Grabbing 0x{:02X} bytes for payload decryption...".format(total_bytes) 183 | cooked_payload = bytearray(f.read(total_bytes)) 184 | 185 | 186 | #decrypt payload 187 | xor_key = bytearray([0xbe,0xba,0xfe,0xca]) 188 | if args.v: 189 | print "\t.. decrypting payload" 190 | 191 | decrypted = xorb(cooked_payload, xor_key) 192 | outfile.write(decrypted) 193 | 194 | #Grab and decrypt decoy 195 | if decoy_found: 196 | f.seek(end_pos) 197 | decoy_bytes = (decoy_pos-(marker_length))-end_pos 198 | if args.v: 199 | print "# Grabbing 0x{:02X} bytes for decoy decryption..".format(decoy_bytes) 200 | decoy_payload = bytearray(f.read(decoy_bytes)) 201 | 202 | decoy_xor_key = bytearray([0x0d,0xf0,0xad,0xba]) 203 | if args.v: 204 | print "\t.. decrypting decoy" 205 | 206 | decrypted_decoy = xorb(decoy_payload,decoy_xor_key) 207 | decoyfile.write(decrypted_decoy) 208 | decoyfile.close() 209 | 210 | f.close() 211 | 212 | outfile.close() 213 | if args.v: 214 | print("# Processing complete.") 215 | print("") 216 | --------------------------------------------------------------------------------