├── LICENSE ├── README.md ├── flareon4_challenge12 ├── README.md ├── challenge12_solver.py └── solver_output.txt └── flareon5_challenge11 ├── README.md ├── filtered_out.txt └── solution.py /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flareon 2 | Random stuff for FlareOn 3 | -------------------------------------------------------------------------------- /flareon4_challenge12/README.md: -------------------------------------------------------------------------------- 1 | # FlareOn4 Challenge 12: [missing] 2 | 3 | This directory contains files that were supposed to be included as part of the FireEye solution blog. 4 | 5 | The challenge 12 solver script expects to process a directory of flows generated by running tcpflow on the challenge pcap. Also provided is output from running the script. Package requirements are documented in-line with the import statements. Most of these are available via pip except for aplib, camellia, and vstructs (vivisect). 6 | 7 | Enjoy. 8 | 9 | -------------------------------------------------------------------------------- /flareon4_challenge12/challenge12_solver.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # Jay Smith (@jay_smif) 3 | # Solver script for Flare-On 4 (2017) Challenge 12 4 | # First run tcpflow on the given pcap to generate a set of TCP-stream files, 5 | # two per TCP stream. 6 | # Then run "python challenge12_solver.py " 7 | # where is the directory to write output files to 8 | # and is the directory containing tcpflow output files. 9 | 10 | import os 11 | import re 12 | import cmd 13 | import pdb 14 | import sys 15 | import time 16 | import zlib 17 | import base64 18 | import string 19 | import struct 20 | import pprint 21 | import hashlib 22 | import logging 23 | import binascii 24 | import cStringIO 25 | import traceback 26 | 27 | # pip: hexdump 28 | import hexdump 29 | 30 | # pip: python-lzo 31 | import lzo 32 | 33 | # pip: xtea 34 | import xtea 35 | 36 | # pip: M2Crypto 37 | import M2Crypto.RC4 38 | 39 | # pip: pycrypto 40 | import Crypto.Cipher.Blowfish 41 | import Crypto.Cipher.XOR 42 | 43 | #http://omake.accense.com/browser/camellia/trunk/pycamellia.py 44 | import pycamellia 45 | 46 | #https://github.com/snemes/kabopan/blob/master/kbp/comp/aplib.py 47 | import kbp.comp.aplib as c_aplib 48 | 49 | # we used vstruct heavily for declarative structures and parsing, and enums. quite nice 50 | #https://github.com/vivisect/vivisect 51 | import vstruct 52 | from vstruct.primitives import * 53 | import vstruct.defs.bmp as c_bmp 54 | import vstruct.defs.win32 as c_win32 55 | 56 | BITMAPFILEHEADER_SIZE = 0x0e 57 | ################################################################################ 58 | # stream constants 59 | IMPLANT_TO_SERVER = 0 60 | SERVER_TO_IMPLANT = 1 61 | 62 | DirStrings = { 63 | IMPLANT_TO_SERVER : 'I2S', 64 | SERVER_TO_IMPLANT : 'S2I', 65 | } 66 | 67 | logger = logging.getLogger() 68 | ################################################################################ 69 | 70 | def getFdMd5(fd): 71 | pos = fd.tell() 72 | md = hashlib.md5(fd.read()).hexdigest() 73 | fd.seek(pos) 74 | return md 75 | 76 | file_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-.' 77 | def cleanFileName(name): 78 | #make sure only allowed characters are in the name, and ends with '_' 79 | return ''.join([ b if b in file_letters else '_' for b in name]) + '_' 80 | 81 | 82 | ################################################################################ 83 | class Report(object): 84 | ''' 85 | Gathers parse data 86 | ''' 87 | def __init__(self, odir): 88 | self.events = [] 89 | self.odir = odir 90 | self.files = [] 91 | 92 | def addEvent(self, etype, edata): 93 | self.events.append( (etype, edata) ) 94 | 95 | def addFileFd(self, fd, name): 96 | md = getFdMd5(fd) 97 | cname = os.path.join(self.odir, '%s_%s' % (md, cleanFileName(name))) 98 | logger.debug('Adding file: %s', cname) 99 | with file(cname, 'wb') as ofile: 100 | ofile.write(fd.read()) 101 | self.files.append( (md, name) ) 102 | return md 103 | 104 | def addFileBytes(self, bytez, name): 105 | fd = cStringIO.StringIO() 106 | fd.write(bytez) 107 | fd.seek(0) 108 | ret = self.addFileFd(fd, name) 109 | fd.close() 110 | return ret 111 | 112 | ################################################################################ 113 | class TcpFlow(object): 114 | ''' 115 | Stores the two sides to a TCP flow as file-objects 116 | ''' 117 | def __init__(self, rpr, i2sFd, s2iFd): 118 | self.rpr = rpr 119 | self.fds = {} 120 | self.fds[IMPLANT_TO_SERVER] = i2sFd 121 | self.fds[SERVER_TO_IMPLANT] = s2iFd 122 | self.tastes = {} 123 | for k,v in self.fds.items(): 124 | self.tastes[k] = v.read(1024) 125 | v.seek(0) 126 | 127 | def __repr__(self): 128 | return self.rpr 129 | 130 | ################################################################################ 131 | 132 | 133 | 134 | CRYPTO_HEADER_SIZE = 36 135 | CMDSIG = 0x20170417 136 | 137 | CONNECT_TYPE = v_enum() 138 | CONNECT_TYPE.UNKNOWN = 0 139 | CONNECT_TYPE.CLIENT = 1 140 | CONNECT_TYPE.SERVER = 2 141 | 142 | ERROR = v_enum() 143 | ERROR.SUCCESS = 0 144 | ERROR.BASE = 0x100000 145 | ERROR.BAD_ARGUMENT = (ERROR.BASE + 1) 146 | ERROR.API_ERROR = (ERROR.BASE + 2) 147 | ERROR.MISSING_PLUGIN = (ERROR.BASE + 3) 148 | ERROR.INTERRUPTED_ALLOC_PLUGIN = (ERROR.BASE + 4) 149 | ERROR.MALLOC = (ERROR.BASE + 5) 150 | ERROR.LOAD_PLUGIN = (ERROR.BASE + 6) 151 | ERROR.NOT_AUTHENTICATED = (ERROR.BASE + 7) 152 | 153 | ERROR.SHELL_ERROR_BASE = (ERROR.BASE + 0x1000) 154 | ERROR.SHELL_NO_COMSPEC = (ERROR.SHELL_ERROR_BASE + 1) 155 | ERROR.SHELL_PIPE_ERROR = (ERROR.SHELL_ERROR_BASE + 2) 156 | ERROR.SHELL_CREATE_PROC_ERROR = (ERROR.SHELL_ERROR_BASE + 3) 157 | ERROR.SHELL_CREATE_THREAD_ERROR = (ERROR.SHELL_ERROR_BASE + 4) 158 | ERROR.SHELL_NOT_ACTIVE = (ERROR.SHELL_ERROR_BASE + 5) 159 | ERROR.SHELL_WRITE_ERROR = (ERROR.SHELL_ERROR_BASE + 6) 160 | ERROR.SHELL_READ_ERROR = (ERROR.SHELL_ERROR_BASE + 7) 161 | ERROR.PROXY_ERROR_BASE = (ERROR.BASE + 0x2000) 162 | ERROR.PROXY_CONN_CLOSED = (ERROR.PROXY_ERROR_BASE + 1) 163 | ERROR.PROXY_ERROR_DISCONNECT = (ERROR.PROXY_ERROR_BASE + 2) 164 | ERROR.FILE_ERROR_BASE = (ERROR.BASE + 0x3000) 165 | ERROR.FILE_EXISTING_FILE_PUT_ERROR = (ERROR.FILE_ERROR_BASE + 1) 166 | ERROR.FILE_CREATE_ERROR = (ERROR.FILE_ERROR_BASE + 2) 167 | ERROR.FILE_NOT_OPEN_ERROR = (ERROR.FILE_ERROR_BASE + 3) 168 | ERROR.FILE_GUID_MISMATCH_ERROR = (ERROR.FILE_ERROR_BASE + 4) 169 | ERROR.FILE_ADJUST_FILE_POINTER_ERROR = (ERROR.FILE_ERROR_BASE + 5) 170 | ERROR.FILE_WRITE_ERROR = (ERROR.FILE_ERROR_BASE + 6) 171 | ERROR.FILE_HASH_INCORRECT_ERROR = (ERROR.FILE_ERROR_BASE + 7) 172 | ERROR.FILE_THREAD_ERROR = (ERROR.FILE_ERROR_BASE + 8) 173 | ERROR.FILE_READ_ERROR = (ERROR.FILE_ERROR_BASE + 8) 174 | ERROR.FILE_NO_SUCH_DIRECTORY_ERROR = (ERROR.FILE_ERROR_BASE + 9) 175 | ERROR.SCREEN_ERROR_BASE = (ERROR.BASE + 0x4000) 176 | ERROR.SCREEN_GETDIBITS_ERROR = (ERROR.SCREEN_ERROR_BASE + 1) 177 | 178 | 179 | SHELLCMD = v_enum() 180 | SHELLCMD.BASE = 0 181 | SHELLCMD.ACTIVATE = (SHELLCMD.BASE + 1) 182 | SHELLCMD.DEACTIVATE = (SHELLCMD.BASE + 2) 183 | SHELLCMD.SHELLIN = (SHELLCMD.BASE + 3) 184 | SHELLCMD.SHELLOUT = (SHELLCMD.BASE + 4) 185 | 186 | SCREENCMD = v_enum() 187 | SCREENCMD.SCREEN_CMD_BASE = 0 188 | SCREENCMD.SCREEN_SCREENSHOT = (SCREENCMD.SCREEN_CMD_BASE + 1) 189 | SCREENCMD.SCREEN_BITMAPINFO = (SCREENCMD.SCREEN_CMD_BASE + 2) 190 | SCREENCMD.SCREEN_BITMAPDATA = (SCREENCMD.SCREEN_CMD_BASE + 3) 191 | 192 | MAINC2CMD = v_enum() 193 | MAINC2CMD.BASE = 0 194 | MAINC2CMD.HEARTBEAT = (MAINC2CMD.BASE + 1) 195 | MAINC2CMD.PING = (MAINC2CMD.BASE + 2) 196 | MAINC2CMD.HOSTINFO = (MAINC2CMD.BASE + 3) 197 | MAINC2CMD.QUERYPLUGINS = (MAINC2CMD.BASE + 4) 198 | MAINC2CMD.ALLOCPLUGIN = (MAINC2CMD.BASE + 5) 199 | MAINC2CMD.ADDPLUGINDATA = (MAINC2CMD.BASE + 6) 200 | MAINC2CMD.LOADPLUGIN = (MAINC2CMD.BASE + 7) 201 | MAINC2CMD.EXIT = (MAINC2CMD.BASE + 8) 202 | MAINC2CMD.GETCONFIG = (MAINC2CMD.BASE + 9) 203 | MAINC2CMD.SETCONFIG = (MAINC2CMD.BASE + 10) 204 | MAINC2CMD.MSGBOX = (MAINC2CMD.BASE + 11) 205 | MAINC2CMD.DISCONNECT = (MAINC2CMD.BASE + 12) 206 | MAINC2CMD.CANCELLOADPLUGIN = (MAINC2CMD.BASE + 13) 207 | MAINC2CMD.AUTHENTICATE = (MAINC2CMD.BASE + 14) 208 | 209 | FILECMD = v_enum() 210 | FILECMD.BASE = 0 211 | FILECMD.DRIVE_LIST = (FILECMD.BASE + 1) 212 | FILECMD.DIR_LIST = (FILECMD.BASE + 2) 213 | FILECMD.FILE_GET = (FILECMD.BASE + 3) 214 | FILECMD.FILE_GET_DATA = (FILECMD.BASE + 4) 215 | FILECMD.FILE_GET_DONE = (FILECMD.BASE + 5) 216 | FILECMD.FILE_PUT = (FILECMD.BASE + 6) 217 | FILECMD.FILE_PUT_DATA = (FILECMD.BASE + 7) 218 | FILECMD.CREATE_DIR = (FILECMD.BASE + 8) 219 | FILECMD.DEL_FILE = (FILECMD.BASE + 9) 220 | FILECMD.DEL_DIR = (FILECMD.BASE + 10) 221 | 222 | 223 | PROXYCMD = v_enum() 224 | PROXYCMD.CMD_BASE = 0 225 | PROXYCMD.CONNECT = (PROXYCMD.CMD_BASE + 1) 226 | PROXYCMD.DISCONNECT = (PROXYCMD.CMD_BASE + 2) 227 | PROXYCMD.DATA = (PROXYCMD.CMD_BASE + 3) 228 | PROXYCMD.QUERY_CONNECTIONS = (PROXYCMD.CMD_BASE + 4) 229 | 230 | 231 | NullCryptoPluginGuidBytes = '51298F741667D7ED2941950106F50545'.decode('hex') 232 | Rc4CryptoPluginGuidBytes = 'c30b1a2dcb489ca8a724376469cf6782'.decode('hex') 233 | LookupTableCryptoPluginGuidBytes = '38be0f624ce274fc61f75c90cb3f5915'.decode('hex') 234 | CustomBase64CryptoPluginGuidBytes = 'ba0504fcc08f9121d16fd3fed1710e60'.decode('hex') 235 | XteaCryptoPluginGuidBytes = 'b2e5490d2654059bbbab7f2a67fe5ff4'.decode('hex') 236 | BlowfishCryptoPluginGuidBytes = '2965e4a19b6e9d9473f5f54dfef93533'.decode('hex') 237 | SimpleXorCryptoPluginGuidBytes = '8746e7b7b0c1b9cf3f11ecae78a3a4bc'.decode('hex') 238 | Des3CryptoPluginGuidBytes = '46c5525904f473ace7bb8cb58b29968a'.decode('hex') 239 | CamelliaCryptoPluginGuidBytes = '9b1f6ec7d9b42bf7758a094a2186986b'.decode('hex') 240 | NullCompressPluginGuidBytes = 'f37126ad88a5617eaf06000d424c5a21'.decode('hex') 241 | ZlibCompressPluginGuidBytes = '5fd8ea0e9d0a92cbe425109690ce7da2'.decode('hex') 242 | LzoCompressPluginGuidBytes = '0a7874d2478a7713705e13dd9b31a6b1'.decode('hex') 243 | ApLibCompressPluginGuidBytes = '503b6412c75a7c7558d1c92683225449'.decode('hex') 244 | MainC2CommandPluginGuidBytes = '155bbf4a1efe1517734604b9d42b80e8'.decode('hex') 245 | FileCommandPluginGuidBytes = 'f47c51070fa8698064b65b3b6e7d30c6'.decode('hex') 246 | ShellCommandPluginGuidBytes = 'f46d09704b40275fb33790a362762e56'.decode('hex') 247 | ProxyCommandPluginGuidBytes = '77d6ce92347337aeb14510807ee9d7be'.decode('hex') 248 | ScreenCommandPluginGuidBytes = 'a3aecca1cb4faa7a9a594d138a1bfbd5'.decode('hex') 249 | 250 | PLUGIN_NAMES = { 251 | NullCryptoPluginGuidBytes : 'NullCrypto', 252 | Rc4CryptoPluginGuidBytes : 'Rc4Crypto', 253 | LookupTableCryptoPluginGuidBytes : 'LookupTableCrypto', 254 | CustomBase64CryptoPluginGuidBytes : 'CustomBase64Crypto', 255 | XteaCryptoPluginGuidBytes : 'XteaCrypto', 256 | BlowfishCryptoPluginGuidBytes : 'BlowfishCrypto', 257 | SimpleXorCryptoPluginGuidBytes : 'SimpleXorCrypto', 258 | Des3CryptoPluginGuidBytes : 'Des3Crypto', 259 | CamelliaCryptoPluginGuidBytes : 'CamelliaCrypto', 260 | NullCompressPluginGuidBytes : 'NullCompress', 261 | ZlibCompressPluginGuidBytes : 'ZlibCompress', 262 | LzoCompressPluginGuidBytes : 'LzoCompress', 263 | ApLibCompressPluginGuidBytes : 'ApLibCompress', 264 | MainC2CommandPluginGuidBytes : 'MainC2Command', 265 | FileCommandPluginGuidBytes : 'FileComamnd', 266 | ShellCommandPluginGuidBytes : 'ShellCommand', 267 | ProxyCommandPluginGuidBytes : 'ProxyCommand', 268 | ScreenCommandPluginGuidBytes : 'ScreenCommand', 269 | } 270 | CMD_PLUGIN_ENUMS = { 271 | MainC2CommandPluginGuidBytes : MAINC2CMD, 272 | FileCommandPluginGuidBytes : FILECMD, 273 | ShellCommandPluginGuidBytes : SHELLCMD, 274 | ProxyCommandPluginGuidBytes : PROXYCMD, 275 | ScreenCommandPluginGuidBytes : SCREENCMD, 276 | } 277 | 278 | g_LookupTable = [ 279 | 199, 25, 48, 12, 168, 16, 173, 213, 212, 22, 82, 252, 27, 130, 125, 280 | 50, 52, 1, 230, 76, 18, 8, 43, 247, 172, 139, 63, 103, 72, 114, 33, 281 | 220, 237, 246, 133, 184, 79, 95, 83, 10, 4, 40, 223, 216, 126, 6, 282 | 61, 3, 64, 54, 104, 115, 37, 183, 93, 30, 210, 13, 198, 195, 34, 242, 283 | 32, 14, 23, 204, 96, 92, 81, 194, 29, 74, 203, 51, 28, 248, 102, 131, 284 | 107, 62, 39, 227, 159, 245, 58, 170, 138, 38, 127, 90, 66, 207, 124, 285 | 7, 88, 113, 235, 5, 186, 41, 75, 122, 224, 236, 154, 123, 46, 55, 254, 286 | 164, 190, 73, 222, 0, 197, 187, 150, 233, 196, 121, 153, 135, 244, 19, 287 | 26, 21, 99, 249, 160, 209, 2, 214, 9, 31, 229, 146, 106, 231, 24, 67, 288 | 145, 110, 65, 200, 163, 178, 44, 238, 141, 166, 91, 239, 36, 185, 117, 289 | 87, 15, 111, 17, 71, 155, 59, 118, 225, 157, 100, 84, 167, 193, 85, 290 | 179, 137, 49, 253, 171, 177, 148, 182, 20, 47, 243, 188, 105, 191, 291 | 161, 128, 89, 11, 189, 201, 42, 215, 129, 60, 35, 211, 241, 250, 234, 292 | 57, 56, 158, 94, 181, 69, 97, 255, 78, 119, 77, 101, 156, 232, 217, 293 | 147, 175, 80, 162, 132, 136, 120, 152, 226, 134, 206, 221, 140, 142, 294 | 169, 149, 112, 174, 228, 202, 98, 205, 144, 192, 251, 176, 219, 180, 295 | 208, 151, 240, 45, 70, 218, 108, 109, 68, 116, 165, 143, 86, 53 296 | ] 297 | 298 | 299 | class CompressHeader(vstruct.VStruct): 300 | def __init__(self): 301 | vstruct.VStruct.__init__(self) 302 | self.headerSize = v_uint32() 303 | self.dataEncSize = v_uint32() 304 | self.dataDecSize = v_uint32() 305 | self.guid = v_bytes(16) 306 | 307 | class CryptoHeader(vstruct.VStruct): 308 | def __init__(self): 309 | vstruct.VStruct.__init__(self) 310 | self.sig = v_bytes(4) 311 | self.crc = v_uint32() 312 | self.headerSize = v_uint32() 313 | self.dataEncSize = v_uint32() 314 | self.dataDecSize = v_uint32() 315 | self.guid = v_bytes(16) 316 | 317 | class Rc4CryptoHeader(vstruct.VStruct): 318 | def __init__(self): 319 | vstruct.VStruct.__init__(self) 320 | self.head = CryptoHeader() 321 | self.key = v_bytes(16) 322 | 323 | class SimpleXorCryptoHeader(vstruct.VStruct): 324 | def __init__(self): 325 | vstruct.VStruct.__init__(self) 326 | self.head = CryptoHeader() 327 | self.key = v_bytes(4) 328 | 329 | class BlowfishCryptoHeader(vstruct.VStruct): 330 | def __init__(self): 331 | vstruct.VStruct.__init__(self) 332 | self.head = CryptoHeader() 333 | self.key = v_bytes(16) 334 | self.iv = v_bytes(8) 335 | 336 | class Des3CryptoHeader(vstruct.VStruct): 337 | def __init__(self): 338 | vstruct.VStruct.__init__(self) 339 | self.head = CryptoHeader() 340 | self.key = v_bytes(24) 341 | self.iv = v_bytes(8) 342 | 343 | class CamelliaCryptoHeader(vstruct.VStruct): 344 | def __init__(self): 345 | vstruct.VStruct.__init__(self) 346 | self.head = CryptoHeader() 347 | self.key = v_bytes(16) 348 | 349 | 350 | class XteaCryptoHeader(vstruct.VStruct): 351 | def __init__(self): 352 | vstruct.VStruct.__init__(self) 353 | self.head = CryptoHeader() 354 | self.key = v_bytes(16) 355 | self.iv = v_bytes(8) 356 | 357 | class CommandHeader(vstruct.VStruct): 358 | def __init__(self): 359 | vstruct.VStruct.__init__(self) 360 | self.sig = v_uint32() 361 | self.command = v_uint32() 362 | self.msgId = v_uint32() 363 | self.status = v_uint32() 364 | self.extendedStatus = v_uint32() 365 | self.guid = v_bytes(16) 366 | 367 | class AddPluginDataResp(vstruct.VStruct): 368 | def __init__(self): 369 | vstruct.VStruct.__init__(self) 370 | self.chead = CommandHeader() 371 | self.guid = v_bytes(16) 372 | self.type = v_uint32() 373 | self.offset = v_uint32() 374 | self.totalsize = v_uint32() 375 | self.chunksize = v_uint32() 376 | 377 | class DirListCmd(vstruct.VStruct): 378 | def __init__(self): 379 | vstruct.VStruct.__init__(self) 380 | self.chead = CommandHeader() 381 | self.directory = v_wstr(260) 382 | 383 | class DriveListItem(vstruct.VStruct): 384 | def __init__(self): 385 | vstruct.VStruct.__init__(self) 386 | self.driveName = v_wstr(4) 387 | self.drivetype = v_uint32() 388 | self.volSerialNumber = v_uint32() 389 | self.totalBytes = v_uint64() 390 | self.userFreeBytes = v_uint64() 391 | self.freeBytes = v_uint64() 392 | self.volumeName = v_wstr(128) 393 | self.volumeType = v_wstr(128) 394 | 395 | 396 | PLUGIN_TYPES = v_enum() 397 | PLUGIN_TYPES.Command = 0x20444d43 398 | PLUGIN_TYPES.Crypto = 0x54505243 399 | PLUGIN_TYPES.Compression = 0x504d4f43 400 | 401 | class QueryPluginsItem(vstruct.VStruct): 402 | def __init__(self): 403 | vstruct.VStruct.__init__(self) 404 | self.guid = v_bytes(16) 405 | self.plugtype = v_uint32(enum=PLUGIN_TYPES) 406 | self.name = v_str(64) 407 | self.version = v_str(64) 408 | 409 | class HostInfoResp(vstruct.VStruct): 410 | def __init__(self): 411 | vstruct.VStruct.__init__(self) 412 | self.chead = CommandHeader() 413 | self.hostid = v_bytes(16) 414 | self.version = v_str(64) 415 | self.computername = v_wstr(64) 416 | self.username = v_wstr(64) 417 | self.memo = v_wstr(256) 418 | self.isAdmin = v_uint32() 419 | self.connectType = v_uint32(enum=CONNECT_TYPE) 420 | self.defaultLcid = v_uint32() 421 | self.osVersionMajor = v_uint32() 422 | self.osVersionMinor = v_uint32() 423 | self.osVersionBuild = v_uint32() 424 | self.osVersionPlatformId = v_uint32() 425 | 426 | class ProxyConnectCmd(vstruct.VStruct): 427 | def __init__(self): 428 | vstruct.VStruct.__init__(self) 429 | self.chead = CommandHeader() 430 | self.port = v_uint32() 431 | self.hostname = v_str(256) 432 | 433 | class ProxyQueryItem(vstruct.VStruct): 434 | def __init__(self): 435 | vstruct.VStruct.__init__(self) 436 | self.index = v_uint32() 437 | self.runFlag = v_uint32() 438 | self.port = v_uint32() 439 | self.hostname = v_str(256) 440 | 441 | class FileGetDataResp(vstruct.VStruct): 442 | def __init__(self): 443 | vstruct.VStruct.__init__(self) 444 | self.chead = CommandHeader() 445 | self.guid = v_bytes(16) 446 | self.offset = v_uint64() 447 | self.totalLen = v_uint64() 448 | self.length = v_uint64() 449 | self.data = v_bytes() 450 | 451 | def pcb_length(self): 452 | self.vsGetField('data').vsSetLength(self.length) 453 | 454 | class BitmapInfoResp(vstruct.VStruct): 455 | def __init__(self): 456 | vstruct.VStruct.__init__(self) 457 | self.chead = CommandHeader() 458 | self.length = v_uint32() 459 | self.data = v_bytes() 460 | 461 | def pcb_length(self): 462 | self.vsGetField('data').vsSetLength(self.length) 463 | 464 | class AuthenticateCmd(vstruct.VStruct): 465 | def __init__(self): 466 | vstruct.VStruct.__init__(self) 467 | self.chead = CommandHeader() 468 | self.password = v_zwstr() 469 | 470 | class AddPluginDataCmd(vstruct.VStruct): 471 | def __init__(self): 472 | vstruct.VStruct.__init__(self) 473 | self.chead = CommandHeader() 474 | self.guid = v_bytes(16) 475 | self.pluginType = v_uint32() 476 | self.offset = v_uint32() 477 | self.totalFileLen = v_uint32() 478 | self.chunkLen = v_uint32() 479 | self.chunk = v_bytes() 480 | 481 | def pcb_chunkLen(self): 482 | self.vsGetField('chunk').vsSetLength(self.chunkLen) 483 | 484 | class BitmapDataResp(vstruct.VStruct): 485 | def __init__(self): 486 | vstruct.VStruct.__init__(self) 487 | self.chead = CommandHeader() 488 | self.offset = v_uint32() 489 | self.totalLength = v_uint32() 490 | self.length = v_uint32() 491 | self.data = v_bytes() 492 | 493 | def pcb_length(self): 494 | self.vsGetField('data').vsSetLength(self.length) 495 | 496 | 497 | class FilePutCmd(vstruct.VStruct): 498 | def __init__(self): 499 | vstruct.VStruct.__init__(self) 500 | self.chead = CommandHeader() 501 | self.guid = v_bytes(16) 502 | self.offset = v_uint64() 503 | self.totalLen = v_uint64() 504 | self.sha1sum = v_bytes(20) 505 | self.filename = v_wstr(256) 506 | 507 | class FilePutData(vstruct.VStruct): 508 | def __init__(self): 509 | vstruct.VStruct.__init__(self) 510 | self.chead = CommandHeader() 511 | self.guid = v_bytes(16) 512 | self.offset = v_uint64() 513 | self.totalLen = v_uint64() 514 | self.length = v_uint64() 515 | self.data = v_bytes() 516 | 517 | def pcb_length(self): 518 | self.vsGetField('data').vsSetLength(self.length) 519 | 520 | class FileGetCmd(vstruct.VStruct): 521 | def __init__(self): 522 | vstruct.VStruct.__init__(self) 523 | self.chead = CommandHeader() 524 | self.guid = v_bytes(16) 525 | self.filename = v_wstr(256) 526 | 527 | class ProxyQueryItem(vstruct.VStruct): 528 | def __init__(self): 529 | vstruct.VStruct.__init__(self) 530 | self.index = v_uint32() 531 | self.runFlag = v_uint32() 532 | self.port = v_uint32() 533 | self.hostname = v_str(256) 534 | 535 | ################################################################################ 536 | def decodeSecondStage(inBytes): 537 | key = struct.unpack_from('> 3) + 0x22334455)) 542 | v1 = 0xffffffff & (v1 + ((v1 >> 5) + 0x11223344)) 543 | v2 = 0xffffffff & (-127 * v2 + 0x44556677) 544 | v3 = 0xffffffff & (-511 * v3 + 0x33445566) 545 | b = 0xff & (v3 + v2 + v1 + v0) ^ ord(inBytes[i+4]) 546 | result.append(chr(b)) 547 | return ''.join(result) 548 | 549 | 550 | ################################################################################ 551 | 552 | class FileTransferChunk(object): 553 | def __init__(self, filename, guid, totalSize): 554 | self.filename = filename 555 | self.guid = guid 556 | self.totalSize = totalSize 557 | self.currentSize = 0 558 | self.data = [] 559 | 560 | def addData(self, data, offset): 561 | if offset != self.currentSize: 562 | logger.info('addData out of order: offset 0x%08x, expected 0x%08x', offset, self.currentSize) 563 | raise RuntimeError('Out of order file chunk') 564 | self.data.append(data) 565 | self.currentSize += len(data) 566 | 567 | def isComplete(self): 568 | return self.currentSize == self.totalSize 569 | 570 | def getFd(self): 571 | fd = cStringIO.StringIO() 572 | for dat in self.data: 573 | fd.write(dat) 574 | fd.seek(0) 575 | return fd 576 | 577 | 578 | ################################################################################ 579 | 580 | class MalarkeyParser(object): 581 | def __init__(self, flow, report): 582 | self.flow = flow 583 | self.report = report 584 | 585 | self.newAlph = 'B7wAOjbXLsD+S24/tcgHYqFRdVKTp0ixlGIMCf8zvE5eoN1uyU93Wm6rZPQaJhkn' 586 | self.origAlph = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 587 | self.b64DecryptTable = string.maketrans(self.newAlph, self.origAlph) 588 | 589 | self.bminfo = None 590 | self.bitmapdata = None 591 | self.bmpStruct = vstruct.defs.bmp.BITMAPINFOHEADER() 592 | self.proxyConns = {} 593 | self.proxyConnects = {} 594 | self.proxyConnectResps = {} 595 | self.files = {} 596 | self.fileGetCmds = {} 597 | 598 | def getBitmapFileHeader(self): 599 | bfSize = BITMAPFILEHEADER_SIZE + self.bmpStruct.biSize + (self.bmpStruct.biClrUser * 4) + self.bmpStruct.biSizeImage 600 | bitsOffset = BITMAPFILEHEADER_SIZE + self.bmpStruct.biSize + (self.bmpStruct.biClrUser * 4) 601 | return struct.pack('BI' -> 0xf0, decompressedLen 666 | t2 = struct.pack('>BI', 0xf0, comphead.dataDecSize) + idata 667 | return lzo.decompress(t2) 668 | 669 | def decrypt(self, crypthead, chead, encData): 670 | funcname = 'do_decrypt_%s' % PLUGIN_NAMES.get(crypthead.guid, 'UNKNOWN') 671 | func = getattr(self, funcname, None) 672 | if func is None: 673 | logger.warning('No crypto function implementation: %s', funcname) 674 | return None 675 | #logger.debug('Dispatching decrypt: %s', funcname) 676 | compData = func(crypthead, chead, encData) 677 | if compData is None: 678 | logger.warning('Crypto failed %s', funcname) 679 | return None 680 | return self.decompress(compData) 681 | 682 | def do_decrypt_NullCrypto(self, crypthead, chead, encData): 683 | return encData 684 | 685 | def do_decrypt_XteaCrypto(self, crypthead, chead, encData): 686 | xteaHead = XteaCryptoHeader() 687 | xteaHead.vsParse(chead) 688 | 689 | tea = xtea.new(xteaHead.key, mode=xtea.MODE_CBC, IV=xteaHead.iv) 690 | outBytes = tea.decrypt(encData) 691 | return outBytes[:xteaHead.head.dataDecSize] 692 | 693 | def do_decrypt_CustomBase64Crypto(self, crypthead, chead, encData): 694 | xlateBytes = encData[:crypthead.dataEncSize].translate(self.b64DecryptTable) 695 | return base64.b64decode(xlateBytes) 696 | 697 | def do_decrypt_LookupTableCrypto(self, crypthead, chead, encData): 698 | retList = [] 699 | for i in encData: 700 | retList.append(chr(g_LookupTable[ord(i)])) 701 | return ''.join(retList) 702 | 703 | def do_decrypt_SimpleXorCrypto(self, crypthead, chead, encData): 704 | xorHead = SimpleXorCryptoHeader() 705 | if len(xorHead) != len(chead): 706 | raise RuntimeError('Bad xor header') 707 | xorHead.vsParse(chead) 708 | cxor = Crypto.Cipher.XOR.new(xorHead.key) 709 | return cxor.decrypt(encData)[:xorHead.head.dataDecSize] 710 | 711 | def do_decrypt_BlowfishCrypto(self, crypthead, chead, encData): 712 | blowHead = BlowfishCryptoHeader() 713 | if len(blowHead) != len(chead): 714 | logger.warning('Bad blowfish header: 0x%x vs 0x%x', len(blowHead), len(chead)) 715 | hexdump.hexdump(chead) 716 | raise RuntimeError('Bad blowfish header') 717 | blowHead.vsParse(chead) 718 | bf = Crypto.Cipher.Blowfish.new(blowHead.key, Crypto.Cipher.Blowfish.MODE_CBC, blowHead.iv) 719 | outBytes = bf.decrypt(encData[:blowHead.head.dataEncSize]) 720 | return outBytes[:blowHead.head.dataDecSize] 721 | 722 | def do_decrypt_Rc4Crypto(self, crypthead, chead, encData): 723 | rc4Head = Rc4CryptoHeader() 724 | rc4Head.vsParse(chead) 725 | rc = M2Crypto.RC4.RC4(rc4Head.key) 726 | outdata = rc.update(encData) 727 | return outdata 728 | 729 | def do_decrypt_Des3Crypto(self, crypthead, chead, encData): 730 | deshead = Des3CryptoHeader() 731 | deshead.vsParse(chead) 732 | evp = M2Crypto.EVP.Cipher(alg='des_ede3_cbc', iv=deshead.iv, key=deshead.key, op=0, padding=0) 733 | outBytes = evp.update(encData) + evp.final() 734 | return outBytes[:crypthead.dataDecSize] 735 | 736 | def do_decrypt_CamelliaCrypto(self, crypthead, chead, encData): 737 | camHead = CamelliaCryptoHeader() 738 | camHead.vsParse(chead) 739 | camkey = pycamellia.Ekeygen(camHead.key) 740 | outArr = [] 741 | for i in range(camHead.head.dataEncSize/16): 742 | outArr.append(pycamellia.DecryptBlock(encData[16*i:16*i+16], camkey)) 743 | outBytes = ''.join(outArr) 744 | return outBytes[:crypthead.dataDecSize] 745 | 746 | def parseBinaryStream(self, dir, fd): 747 | logger.debug('parseBinaryStream %s', DirStrings[dir]) 748 | try: 749 | crypthead = CryptoHeader() 750 | off = fd.tell() 751 | chead = fd.read(len(crypthead)) 752 | while len(chead) == len(crypthead): 753 | #logger.debug('Working at off 0x%08x', off) 754 | crypthead.vsParse(chead) 755 | if crypthead.sig != '2017': 756 | raise RuntimeError('Missing 2017 sig') 757 | if len(chead) < crypthead.headerSize: 758 | chead = chead + fd.read(crypthead.headerSize - len(chead)) 759 | if len(chead) != crypthead.headerSize: 760 | raise RuntimeError('Bad header size now :(') 761 | encData = fd.read(crypthead.dataEncSize) 762 | if len(encData) != crypthead.dataEncSize: 763 | raise RuntimeError('Short read') 764 | #logger.debug('Trying to decrypt:%s', crypthead.tree()) 765 | #hexdump.hexdump(encData[:0x100]) 766 | data = self.decrypt(crypthead, chead, encData) 767 | if data is None: 768 | logger.debug('Stopping early due to empty decrypt') 769 | break 770 | crc32 = binascii.crc32(data) & 0xffffffff 771 | if crc32 != crypthead.crc: 772 | raise RuntimeError('Bad crc32 after decrypt/decompress') 773 | #logger.warning('Unhandled %s data:', DirStrings[dir]) 774 | #hexdump.hexdump(data) 775 | try: 776 | self.dispatchPayload(dir, data) 777 | except Exception, err: 778 | logger.exception('Error during dispatch: %s', str(err)) 779 | off = fd.tell() 780 | chead = fd.read(len(crypthead)) 781 | except Exception, err: 782 | logger.exception('Error during parse: %s', str(err)) 783 | 784 | def dispatchPayload(self, dir, data): 785 | cmdHeader = CommandHeader() 786 | if len(data) < len(cmdHeader): 787 | raise RuntimeError('Bad command header: too small') 788 | off = cmdHeader.vsParse(data) 789 | if cmdHeader.sig != CMDSIG: 790 | logger.warning('0x%08x vs 0x%08x', cmdHeader.sig, CMDSIG) 791 | hexdump.hexdump(data) 792 | raise RuntimeError('Bad command header: missing sig') 793 | pluginName = PLUGIN_NAMES.get(cmdHeader.guid, None) 794 | if pluginName is None: 795 | raise RuntimeError('Unknown command guid') 796 | cmdName = CMD_PLUGIN_ENUMS[cmdHeader.guid].vsReverseMapping(cmdHeader.command) 797 | funcname = 'do_%s_%s_%s' % (DirStrings[dir], pluginName, cmdName) 798 | func = getattr(self, funcname, None) 799 | if func is None: 800 | logger.warning('No command function implementation: %s', funcname) 801 | hexdump.hexdump(data) 802 | return None 803 | logger.debug('Dispatching command %s', funcname) 804 | return func(dir, data, cmdHeader) 805 | 806 | def do_I2S_MainC2Command_AUTHENTICATE(self, dir, data, cmdHeader): 807 | if cmdHeader.status == 0: 808 | logger.debug('I2S_MainC2Command_AUTHENTICATE: Success') 809 | else: 810 | logger.debug('I2S_MainC2Command_AUTHENTICATE: Failure') 811 | 812 | def do_I2S_MainC2Command_QUERYPLUGINS(self, dir, data, cmdHeader): 813 | off = len(cmdHeader) 814 | i = 0 815 | item = QueryPluginsItem() 816 | if cmdHeader.status != 0: 817 | logger.info('I2S_MainC2Command_QUERYPLUGINS: error 0x%08x 0x%08x', cmdHeader.status, cmdHeader.extendedStatus) 818 | return 819 | ret = [] 820 | logger.debug('I2S_MainC2Command_QUERYPLUGINS: %d items', cmdHeader.extendedStatus) 821 | while (i < cmdHeader.extendedStatus) and (off connid 915 | self.proxyConnectResps[cmdHeader.extendedStatus] = cmdHeader.msgId 916 | 917 | def do_I2S_ProxyCommand_DATA(self, dir, data, cmdHeader): 918 | 919 | if cmdHeader.status == ERROR.PROXY_CONN_CLOSED: 920 | return 921 | elif cmdHeader.status != 0: 922 | logger.warning('Bad proxy data: 0x%08x 0x%08x', cmdHeader.status, cmdHeader.extendedStatus) 923 | hexdump.hexdump(data) 924 | return 925 | pdata = data[len(cmdHeader):] 926 | #logger.debug('Proxy I2S %s', cmdHeader.tree()) 927 | self.addProxyData(dir, cmdHeader.extendedStatus, pdata) 928 | 929 | def addProxyData(self, dir, connid, data): 930 | conn = self.proxyConns.get(connid) 931 | if conn is None: 932 | conn = { 933 | IMPLANT_TO_SERVER : cStringIO.StringIO(), 934 | SERVER_TO_IMPLANT : cStringIO.StringIO(), 935 | } 936 | logger.debug('New proxy conn: 0x%08x', connid) 937 | self.proxyConns[connid] = conn 938 | conn[dir].write(data) 939 | 940 | def do_I2S_ShellCommand_DEACTIVATE(self, dir, data, cmdHeader): 941 | #not interesting 942 | pass 943 | 944 | def do_I2S_ProxyCommand_DISCONNECT(self, dir, data, cmdHeader): 945 | #not interesting 946 | pass 947 | 948 | def do_I2S_FileComamnd_FILE_GET(self, dir, data, cmdHeader): 949 | #not interesting 950 | pass 951 | 952 | def do_I2S_FileComamnd_FILE_GET_DATA(self, dir, data, cmdHeader): 953 | resp = FileGetDataResp() 954 | resp.vsParse(data) 955 | xfer = self.files.get(resp.guid) 956 | if xfer is None: 957 | filename = self.fileGetCmds.get(resp.guid) 958 | if filename is None: 959 | raise RuntimeError('Missing FileGet cmd') 960 | xfer = FileTransferChunk(filename, resp.guid, resp.totalLen) 961 | self.files[resp.guid] = xfer 962 | if resp.offset >= resp.totalLen: 963 | fd = xfer.getFd() 964 | sha1 = hashlib.sha1() 965 | sha1.update(fd.read()) 966 | if sha1.digest() != resp.data: 967 | raise RuntimeError('Sha1 mismatch in file get') 968 | fd.seek(0) 969 | if xfer.isComplete(): 970 | md = self.report.addFileFd(fd, xfer.filename) 971 | self.report.addEvent('file_get', dict(filename=xfer.filename, filemd5=md)) 972 | else: 973 | raise RuntimeError('File get not complete??') 974 | fd.close() 975 | 976 | else: 977 | xfer.addData(resp.data, resp.offset) 978 | 979 | def do_S2I_FileComamnd_FILE_GET(self, dir, data, cmdHeader): 980 | cmd = FileGetCmd() 981 | cmd.vsParse(data) 982 | self.fileGetCmds[cmd.guid] = cmd.filename 983 | 984 | def do_S2I_FileComamnd_FILE_GET_DONE(self, dir, data, cmdHeader): 985 | #not interesting 986 | pass 987 | 988 | def do_I2S_FileComamnd_FILE_GET_DONE(self, dir, data, cmdHeader): 989 | #not interesting 990 | pass 991 | 992 | ################################################################################ 993 | def do_S2I_MainC2Command_AUTHENTICATE(self, dir, data, cmdHeader): 994 | cmd = AuthenticateCmd() 995 | cmd.vsParse(data) 996 | logger.debug('Server authenticating using password %s', cmd.password) 997 | self.report.addEvent('c2_authenticate', dict(password=cmd.password)) 998 | 999 | def do_S2I_MainC2Command_HOSTINFO(self, dir, data, cmdHeader): 1000 | #not interesting 1001 | pass 1002 | 1003 | def do_S2I_MainC2Command_QUERYPLUGINS(self, dir, data, cmdHeader): 1004 | #not interesting 1005 | pass 1006 | 1007 | def do_S2I_MainC2Command_ALLOCPLUGIN(self, dir, data, cmdHeader): 1008 | #not interesting 1009 | pass 1010 | 1011 | def do_S2I_MainC2Command_ADDPLUGINDATA(self, dir, data, cmdHeader): 1012 | cmd = AddPluginDataCmd() 1013 | cmd.vsParse(data) 1014 | xfer = self.files.get(cmd.guid) 1015 | if xfer is None: 1016 | if PLUGIN_NAMES.get(cmd.guid) is None: 1017 | logger.warning('Missing plugin name:\n%s', cmd.tree()) 1018 | hexdump.hexdump(data) 1019 | xfer = FileTransferChunk('plugin_%s.bin' % PLUGIN_NAMES[cmd.guid], cmd.guid, cmd.totalFileLen) 1020 | self.files[cmd.guid] = xfer 1021 | logger.debug('Creating new plugin xfer: %s\n%s', xfer.filename, cmd.tree()) 1022 | #logger.warning('Trying to parse addplugin: %s 0x%08x of 0x%08x', PLUGIN_NAMES.get(cmd.guid, None), cmd.offset, cmd.totalFileLen) 1023 | logger.debug('Adding plugin %s: 0x%08x of 0x%08x: 0x%08x bytes', xfer.filename, cmd.offset, cmd.totalFileLen, len(cmd.chunk)) 1024 | #pdb.set_trace() 1025 | xfer.addData(cmd.chunk, cmd.offset) 1026 | 1027 | if xfer.isComplete(): 1028 | fd = xfer.getFd() 1029 | md = self.report.addFileFd(fd, xfer.filename) 1030 | info = { 1031 | 'filemd5' : md, 1032 | 'module_name' : xfer.filename, 1033 | 'module_id' : cmd.guid.encode('hex'), 1034 | } 1035 | self.report.addEvent('load_module', info) 1036 | fd.close() 1037 | self.files.pop(cmd.guid, None) 1038 | logger.debug('Adding module: %s %s', md, xfer.filename) 1039 | 1040 | 1041 | def do_S2I_MainC2Command_LOADPLUGIN(self, dir, data, cmdHeader): 1042 | #not interesting 1043 | pass 1044 | 1045 | def do_I2S_ShellCommand_ACTIVATE(self, dir, data, cmdHeader): 1046 | #not interesting 1047 | pass 1048 | 1049 | def do_I2S_ShellCommand_SHELLOUT(self, dir, data, cmdHeader): 1050 | self.report.addEvent('shell_out', dict(data=data[len(cmdHeader):])) 1051 | 1052 | def do_I2S_ShellCommand_SHELLIN(self, dir, data, cmdHeader): 1053 | #not interesting 1054 | pass 1055 | 1056 | def do_I2S_ScreenCommand_SCREEN_BITMAPINFO(self, dir, data, cmdHeader): 1057 | resp = BitmapInfoResp() 1058 | resp.vsParse(data) 1059 | self.bminfo = resp.data 1060 | self.bmpStruct.vsParse(self.bminfo) 1061 | 1062 | def do_I2S_ScreenCommand_SCREEN_BITMAPDATA(self, dir, data, cmdHeader): 1063 | resp = BitmapDataResp() 1064 | resp.vsParse(data) 1065 | #logger.warning('do_I2S_ScreenCommand_SCREEN_BITMAPDATA') 1066 | if self.bitmapdata is None: 1067 | self.bitmapdata = FileTransferChunk(None, None, resp.totalLength) 1068 | self.bitmapdata.addData(resp.data, resp.offset) 1069 | if self.bitmapdata.isComplete(): 1070 | logger.debug('Saving bitmap: 0x%08x 0x%08x size', self.bitmapdata.currentSize, self.bitmapdata.totalSize) 1071 | fd = self.bitmapdata.getFd() 1072 | bmp = cStringIO.StringIO() 1073 | bmp.write(self.getBitmapFileHeader()) 1074 | bmp.write(self.bminfo) 1075 | bmp.write(fd.read()) 1076 | bmp.seek(0) 1077 | fd.close() 1078 | md = self.report.addFileFd(bmp, 'screenshot.bmp') 1079 | self.report.addEvent('screenshot', dict(filemd5=md)) 1080 | bmp.close() 1081 | self.bitmapdata = None 1082 | 1083 | def do_S2I_FileComamnd_DRIVE_LIST(self, dir, data, cmdHeader): 1084 | #not interesing -> handled on the I2S side 1085 | pass 1086 | 1087 | def do_S2I_FileComamnd_DIR_LIST(self, dir, data, cmdHeader): 1088 | #not interesting -> handled completely on the I2S side 1089 | pass 1090 | 1091 | def do_S2I_ShellCommand_ACTIVATE(self, dir, data, cmdHeader): 1092 | #not interesting 1093 | pass 1094 | 1095 | def do_S2I_ShellCommand_SHELLIN(self, dir, data, cmdHeader): 1096 | sdata = data[len(cmdHeader):] 1097 | idx = sdata.find('\x00') 1098 | if idx >= 0: 1099 | sdata = sdata[:idx] 1100 | self.report.addEvent('shell_in', sdata) 1101 | 1102 | def do_S2I_ScreenCommand_SCREEN_SCREENSHOT(self, dir, data, cmdHeader): 1103 | #not interesting 1104 | pass 1105 | 1106 | def do_S2I_FileComamnd_FILE_PUT(self, dir, data, cmdHeader): 1107 | cmd = FilePutCmd() 1108 | cmd.vsParse(data) 1109 | xfer = FileTransferChunk(cmd.filename, cmd.guid, cmd.totalLen) 1110 | #duckpunch this in 1111 | xfer.sha1 = cmd.sha1sum 1112 | self.files[cmd.guid] = xfer 1113 | logger.debug('Adding new fileput: %s', cmd.filename) 1114 | 1115 | def do_S2I_FileComamnd_FILE_PUT_DATA(self, dir, data, cmdHeader): 1116 | cmd = FilePutData() 1117 | cmd.vsParse(data) 1118 | xfer = self.files.get(cmd.guid) 1119 | if xfer is None: 1120 | raise RuntimeError('Missing file put xfer') 1121 | xfer.addData(cmd.data, cmd.offset) 1122 | if xfer.isComplete(): 1123 | fd = xfer.getFd() 1124 | sha1 = hashlib.sha1() 1125 | sha1.update(fd.read()) 1126 | if sha1.digest() != xfer.sha1: 1127 | raise RuntimeError('Sha1 mismatch in file put') 1128 | fd.seek(0) 1129 | md = self.report.addFileFd(fd, xfer.filename) 1130 | self.report.addEvent('file_put', dict(filename=xfer.filename, filemd5=md)) 1131 | 1132 | def do_S2I_ProxyCommand_CONNECT(self, dir, data, cmdHeader): 1133 | cmd = ProxyConnectCmd() 1134 | cmd.vsParse(data) 1135 | logger.debug('Queueing proxy connect: 0x%08x: %s:%d', cmd.chead.msgId, cmd.hostname, cmd.port) 1136 | self.proxyConnects[cmd.chead.msgId] = (cmd.hostname, cmd.port) 1137 | 1138 | def do_S2I_ProxyCommand_DATA(self, dir, data, cmdHeader): 1139 | if cmdHeader.status == ERROR.PROXY_CONN_CLOSED: 1140 | pass 1141 | elif cmdHeader.status != 0: 1142 | logger.warning('Bad proxy data: 0x%08x 0x%08x', cmdHeader.status, cmdHeader.extendedStatus) 1143 | hexdump.hexdump(data) 1144 | return 1145 | pdata = data[len(cmdHeader):] 1146 | self.addProxyData(dir, cmdHeader.extendedStatus, pdata) 1147 | 1148 | def do_S2I_ShellCommand_DEACTIVATE(self, dir, data, cmdHeader): 1149 | #not interesting 1150 | pass 1151 | 1152 | def do_S2I_ProxyCommand_DISCONNECT(self, dir, data, cmdHeader): 1153 | #not interesting 1154 | pass 1155 | 1156 | def do_I2S_ProxyCommand_QUERY_CONNECTIONS(self, dir, data, cmdHeader): 1157 | i = 0 1158 | off = len(cmdHeader) 1159 | item = ProxyQueryItem() 1160 | ret = [] 1161 | while ((i < cmdHeader.extendedStatus) and (off < len(data))): 1162 | off = item.vsParse(data, off) 1163 | info = { 1164 | 'index' : item.index, 1165 | 'run_flag' : item.runFlag, 1166 | 'port' : item.port, 1167 | 'hostname' : item.hostname, 1168 | } 1169 | ret.append(info) 1170 | i += 1 1171 | self.report.addEvent('query_proxy_connections', ret) 1172 | 1173 | def do_S2I_ProxyCommand_QUERY_CONNECTIONS(self, dir, data, cmdHeader): 1174 | #logger.warning("Unimplemented: do_S2I_ProxyCommand_QUERY_CONNECTIONS\n %s", hexdump.hexdump(data, result='return')) 1175 | pass 1176 | 1177 | def do_S2I_MainC2Command_EXIT(self, dir, data, cmdHeader): 1178 | #not interesting 1179 | pass 1180 | 1181 | def do_I2S_ScreenCommand_SCREEN_SCREENSHOT(self, dir, data, cmdHeader): 1182 | #not interesting 1183 | pass 1184 | 1185 | ################################################################################ 1186 | 1187 | def handleHttpFlow(report, flow): 1188 | # from inspection there's only 1 request/response, so just pull out the body past the double newline 1189 | resp = flow.fds[SERVER_TO_IMPLANT].read() 1190 | idx = resp.index('\x0d\x0a\x0d\x0a') 1191 | body = resp[idx+4:] 1192 | md1 = report.addFileBytes(body, 'secondstage') 1193 | logger.debug('Add raw secondstage: %s %d bytes', md1, len(body)) 1194 | decbody = decodeSecondStage(body) 1195 | md2 = report.addFileBytes(decbody, 'decoded_secondstage') 1196 | logger.debug('Add decoded secondstage: %s %d bytes', md2, len(decbody)) 1197 | 1198 | def handleBinaryFlow(report, flow): 1199 | parser = MalarkeyParser(flow, report) 1200 | parser.parse() 1201 | 1202 | g_flowNameRe = re.compile(r"(\d{3}\.\d{3}\.\d{3}\.\d{3})\.(\d{5})-(\d{3}\.\d{3}\.\d{3}\.\d{3})\.(\d{5})") 1203 | g_serverPorts = set(['00080', '09443']) 1204 | 1205 | def parseFlow(report, flow): 1206 | logger.debug('Parsing flow %r', flow) 1207 | if flow.tastes[IMPLANT_TO_SERVER].startswith('GET /secondstage'): 1208 | return handleHttpFlow(report, flow) 1209 | elif flow.tastes[IMPLANT_TO_SERVER].startswith('2017'): 1210 | return handleBinaryFlow(report, flow) 1211 | else: 1212 | print('Unknown stream:') 1213 | hexdump.hexdump(flow.tastes[IMPLANT_TO_SERVER]) 1214 | #hexdump.hexdump(flow.tastes[SERVER_TO_IMPLANT]) 1215 | 1216 | ################################################################################ 1217 | 1218 | def loadFlowsFromDir(flowDir): 1219 | loadedNames = set() 1220 | ret = [] 1221 | for fname in os.listdir(flowDir): 1222 | isClient = False 1223 | if not os.path.isfile(os.path.join(flowDir, fname)): 1224 | #logger.debug('Skipping non-file name: %s', fname) 1225 | continue 1226 | m1 = g_flowNameRe.match(fname) 1227 | if m1 is None: 1228 | #logger.debug('Skipping non-flow name: %s', fname) 1229 | continue 1230 | if fname in loadedNames: 1231 | continue 1232 | #construct expected other side of the flow 1233 | otherSide = '%s.%s-%s.%s' % (m1.group(3), m1.group(4), m1.group(1), m1.group(2)) 1234 | if not os.path.isfile(os.path.join(flowDir, otherSide)): 1235 | logger.warning('Otherside of flow is missing: non-file name: %s', otherSide) 1236 | continue 1237 | loadedNames.add(fname) 1238 | loadedNames.add(otherSide) 1239 | isClient = m1.group(4) in g_serverPorts 1240 | if isClient: 1241 | i2sfd = file(os.path.join(flowDir, fname), 'rb') 1242 | s2ifd = file(os.path.join(flowDir, otherSide), 'rb') 1243 | flow = TcpFlow(fname, i2sfd, s2ifd) 1244 | ret.append(flow) 1245 | else: 1246 | s2ifd = file(os.path.join(flowDir, fname), 'rb') 1247 | i2sfd = file(os.path.join(flowDir, otherSide), 'rb') 1248 | flow = TcpFlow(otherSide, i2sfd, s2ifd) 1249 | ret.append(flow) 1250 | return ret 1251 | 1252 | ################################################################################ 1253 | 1254 | def main(): 1255 | if (len(sys.argv) != 3) or not os.path.isdir(sys.argv[2]): 1256 | print("Usage: %s " % sys.argv[0]) 1257 | print(" where is the directory to store results to") 1258 | print(" and is the output directory of running tcpflow on the given pcap") 1259 | return 1260 | #logging.basicConfig(level=logging.DEBUG) 1261 | logging.basicConfig(level=logging.INFO) 1262 | outDir = sys.argv[1] 1263 | flowDir = sys.argv[2] 1264 | print("Processing flows from directory %s" % sys.argv[2]) 1265 | flows = loadFlowsFromDir(flowDir) 1266 | print("Loaded %d flows" % len(flows)) 1267 | if not os.path.exists(outDir): 1268 | os.mkdir(outDir) 1269 | rep = Report(outDir) 1270 | for flow in flows: 1271 | parseFlow(rep, flow) 1272 | #now output data 1273 | print("Done parsing") 1274 | print('############################################################') 1275 | print('Events:') 1276 | for etype, edata in rep.events: 1277 | if etype == 'shell_out': 1278 | print(etype) 1279 | print(edata['data']) 1280 | else: 1281 | print("%s: %s" % (etype, pprint.pformat(edata))) 1282 | print('############################################################') 1283 | print('Files:') 1284 | for md, name in rep.files: 1285 | print("%s: %s" % (md, name)) 1286 | 1287 | 1288 | 1289 | if __name__ == '__main__': 1290 | main() 1291 | -------------------------------------------------------------------------------- /flareon4_challenge12/solver_output.txt: -------------------------------------------------------------------------------- 1 | Processing flows from directory flows/ 2 | Loaded 2 flows 3 | Done parsing 4 | ############################################################ 5 | Events: 6 | c2_authenticate: {'password': u'welcomepass1!1'} 7 | load_module: {'filemd5': 'f873a174ccb567670e222c1f37195cf2', 8 | 'module_id': 'c30b1a2dcb489ca8a724376469cf6782', 9 | 'module_name': 'plugin_Rc4Crypto.bin'} 10 | load_module: {'filemd5': '6e8866fc570d74ab21b99f687c108105', 11 | 'module_id': '38be0f624ce274fc61f75c90cb3f5915', 12 | 'module_name': 'plugin_LookupTableCrypto.bin'} 13 | load_module: {'filemd5': 'fb5acf29a468df13c2289f3e96027f12', 14 | 'module_id': 'ba0504fcc08f9121d16fd3fed1710e60', 15 | 'module_name': 'plugin_CustomBase64Crypto.bin'} 16 | load_module: {'filemd5': '59061be984a290dd9c9320edb569ac71', 17 | 'module_id': 'b2e5490d2654059bbbab7f2a67fe5ff4', 18 | 'module_name': 'plugin_XteaCrypto.bin'} 19 | load_module: {'filemd5': '114a99b58940c5f5dd41114fe340468e', 20 | 'module_id': '5fd8ea0e9d0a92cbe425109690ce7da2', 21 | 'module_name': 'plugin_ZlibCompress.bin'} 22 | load_module: {'filemd5': '4b05ff9bf7f59bf411a605b24c28a5b5', 23 | 'module_id': 'f47c51070fa8698064b65b3b6e7d30c6', 24 | 'module_name': 'plugin_FileComamnd.bin'} 25 | load_module: {'filemd5': 'ac9f33da50bb56522402bf01bb1df548', 26 | 'module_id': 'f46d09704b40275fb33790a362762e56', 27 | 'module_name': 'plugin_ShellCommand.bin'} 28 | shell_in: 'cd c:\\\r\n' 29 | shell_in: 'dir\r\n' 30 | shell_in: 'cd c:\\work\\\r\n' 31 | shell_in: 'dir\r\n' 32 | shell_in: 'cd c:\\work\\FlareOn2017\r\n' 33 | shell_in: 'dir\r\n' 34 | shell_in: 'cd c:\\work\\FlareOn2017\\Challenge_10\r\n' 35 | shell_in: 'dir\r\n' 36 | shell_in: 'type TODO.txt\r\n' 37 | shell_in: 'mkdir c:\\staging\r\n' 38 | shell_in: 'cd c:\\staging\r\n' 39 | load_module: {'filemd5': '3f002fa74b02da598f39a867e2d052a0', 40 | 'module_id': 'a3aecca1cb4faa7a9a594d138a1bfbd5', 41 | 'module_name': 'plugin_ScreenCommand.bin'} 42 | shell_in: 'ping larryjohnson-pc\r\n' 43 | file_put: {'filemd5': '27304b246c7d5b4e149124d5f93c5b01', 44 | 'filename': u'c:\\staging\\pse.exe'} 45 | file_put: {'filemd5': '27304b246c7d5b4e149124d5f93c5b01', 46 | 'filename': u'c:\\staging\\pse.exe'} 47 | shell_in: 'pse.exe -h\r\n' 48 | file_put: {'filemd5': 'bf0a86db982de1996c0dc49d681dbe81', 49 | 'filename': u'c:\\staging\\srv2.exe'} 50 | file_put: {'filemd5': 'bf0a86db982de1996c0dc49d681dbe81', 51 | 'filename': u'c:\\staging\\srv2.exe'} 52 | shell_in: 'pse.exe \\\\larryjohnson-pc -i -c -f -d -u larry.johnson -p n3v3rgunnag1veUup -accepteula srv2.exe\r\n' 53 | load_module: {'filemd5': '9e5ca05e6fe30a37056c290a9fc7177c', 54 | 'module_id': '77d6ce92347337aeb14510807ee9d7be', 55 | 'module_name': 'plugin_ProxyCommand.bin'} 56 | shell_in: 'dir\r\n' 57 | query_plugins: [' 00:155bbf4a1efe1517734604b9d42b80e8:1.2.0: Command:MainC2Command', 58 | ' 01:51298f741667d7ed2941950106f50545:1.0.2: Crypto:NullCrypto', 59 | ' 02:f37126ad88a5617eaf06000d424c5a21:1.0.1:Compression:NullCompress'] 60 | profile_host: {'default_locale': 1033, 61 | 'hostname': u'JOHNJACKSON-PC', 62 | 'malware_id': 'c5aaaeccf7f63d8312d673f01e2b042f', 63 | 'malware_note': u'feye2017 cli', 64 | 'malware_version': '2.1.0', 65 | 'os_version': '6.1.7601_2', 66 | 'username': u'john.jackson'} 67 | query_plugins: [' 00:155bbf4a1efe1517734604b9d42b80e8:1.2.0: Command:MainC2Command', 68 | ' 01:51298f741667d7ed2941950106f50545:1.0.2: Crypto:NullCrypto', 69 | ' 02:c30b1a2dcb489ca8a724376469cf6782:1.0.4: Crypto:Rc4Crypto', 70 | ' 03:38be0f624ce274fc61f75c90cb3f5915:1.1.8: Crypto:LookupTableCrypto', 71 | ' 04:ba0504fcc08f9121d16fd3fed1710e60:1.0.4: Crypto:CustomBase64Crypto', 72 | ' 05:b2e5490d2654059bbbab7f2a67fe5ff4:1.0.1: Crypto:XteaCrypto', 73 | ' 06:f37126ad88a5617eaf06000d424c5a21:1.0.1:Compression:NullCompress', 74 | ' 07:5fd8ea0e9d0a92cbe425109690ce7da2:1.2.11:Compression:ZlibCompress'] 75 | drive_list: [{'drive_letter': u'A:\\', 76 | 'filesystem': u'', 77 | 'free_space': 0, 78 | 'name': u'', 79 | 'total_space': 0}, 80 | {'drive_letter': u'C:\\', 81 | 'filesystem': u'NTFS', 82 | 'free_space': 55131119616, 83 | 'name': u'', 84 | 'total_space': 64422408192}, 85 | {'drive_letter': u'D:\\', 86 | 'filesystem': u'', 87 | 'free_space': 0, 88 | 'name': u'', 89 | 'total_space': 0}] 90 | dir_list: {'dir': u'c:\\\\\\', 91 | 'result': [{'accessed_time': 131401251571404950, 92 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 93 | 'created_time': 128920125750196134, 94 | 'filename': u'$Recycle.Bin', 95 | 'modified_time': 131401251571404950, 96 | 'size': 0}, 97 | {'accessed_time': 128920106441575908, 98 | 'attributes': ['ARCHIVE'], 99 | 'created_time': 128920106441575908, 100 | 'filename': u'autoexec.bat', 101 | 'modified_time': 128891437401258876, 102 | 'size': 24}, 103 | {'accessed_time': 130058786271306680, 104 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 105 | 'created_time': 130058786268186675, 106 | 'filename': u'Boot', 107 | 'modified_time': 130058786271306680, 108 | 'size': 0}, 109 | {'accessed_time': 130058786270058678, 110 | 'attributes': ['READONLY', 'HIDDEN', 'SYSTEM', 'ARCHIVE'], 111 | 'created_time': 130058786270058678, 112 | 'filename': u'bootmgr', 113 | 'modified_time': 129347621462815197, 114 | 'size': 383786}, 115 | {'accessed_time': 130058786272398682, 116 | 'attributes': ['READONLY', 'HIDDEN', 'SYSTEM', 'ARCHIVE'], 117 | 'created_time': 130058786272398682, 118 | 'filename': u'BOOTSECT.BAK', 119 | 'modified_time': 130058786272398682, 120 | 'size': 8192}, 121 | {'accessed_time': 128920106441731907, 122 | 'attributes': ['ARCHIVE'], 123 | 'created_time': 128920106441731907, 124 | 'filename': u'config.sys', 125 | 'modified_time': 128891437401258876, 126 | 'size': 10}, 127 | {'accessed_time': 128920208355488413, 128 | 'attributes': ['HIDDEN', 129 | 'SYSTEM', 130 | 'REPARSE_POINT', 131 | 'DIRECTORY', 132 | 'NOT_CONTENT_INDEXED'], 133 | 'created_time': 128920208355488413, 134 | 'filename': u'Documents and Settings', 135 | 'modified_time': 128920208355488413, 136 | 'size': 0}, 137 | {'accessed_time': 130058786667520320, 138 | 'attributes': ['HIDDEN', 'SYSTEM', 'ARCHIVE'], 139 | 'created_time': 130058786667520320, 140 | 'filename': u'pagefile.sys', 141 | 'modified_time': 131402123329880131, 142 | 'size': 1073741824}, 143 | {'accessed_time': 128920126252200916, 144 | 'attributes': ['DIRECTORY'], 145 | 'created_time': 128920126252200916, 146 | 'filename': u'PerfLogs', 147 | 'modified_time': 128920126252200916, 148 | 'size': 0}, 149 | {'accessed_time': 130058789050289949, 150 | 'attributes': ['READONLY', 'DIRECTORY'], 151 | 'created_time': 128920126252356915, 152 | 'filename': u'Program Files', 153 | 'modified_time': 130058789050289949, 154 | 'size': 0}, 155 | {'accessed_time': 130058789050289949, 156 | 'attributes': ['HIDDEN', 'DIRECTORY', 'NOT_CONTENT_INDEXED'], 157 | 'created_time': 128920126256100891, 158 | 'filename': u'ProgramData', 159 | 'modified_time': 130058789050289949, 160 | 'size': 0}, 161 | {'accessed_time': 130058788049308176, 162 | 'attributes': ['HIDDEN', 163 | 'SYSTEM', 164 | 'DIRECTORY', 165 | 'NOT_CONTENT_INDEXED'], 166 | 'created_time': 130058788048840175, 167 | 'filename': u'Recovery', 168 | 'modified_time': 130058788049308176, 169 | 'size': 0}, 170 | {'accessed_time': 130058794585911411, 171 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 172 | 'created_time': 130058786666896319, 173 | 'filename': u'System Volume Information', 174 | 'modified_time': 130058794585911411, 175 | 'size': 0}, 176 | {'accessed_time': 131402162184268785, 177 | 'attributes': ['DIRECTORY'], 178 | 'created_time': 131401276486610206, 179 | 'filename': u'temp', 180 | 'modified_time': 131402162184268785, 181 | 'size': 0}, 182 | {'accessed_time': 131401251555804923, 183 | 'attributes': ['READONLY', 'DIRECTORY'], 184 | 'created_time': 128920126257972879, 185 | 'filename': u'Users', 186 | 'modified_time': 131401251555804923, 187 | 'size': 0}, 188 | {'accessed_time': 130058795043304214, 189 | 'attributes': ['DIRECTORY'], 190 | 'created_time': 128920126259532869, 191 | 'filename': u'Windows', 192 | 'modified_time': 130058795043304214, 193 | 'size': 0}, 194 | {'accessed_time': 130140564865057976, 195 | 'attributes': ['DIRECTORY'], 196 | 'created_time': 131402864909305289, 197 | 'filename': u'work', 198 | 'modified_time': 130140564865057976, 199 | 'size': 0}]} 200 | dir_list: {'dir': u'c:\\work\\\\\\', 201 | 'result': [{'accessed_time': 130140564865057976, 202 | 'attributes': ['DIRECTORY'], 203 | 'created_time': 131402864909305289, 204 | 'filename': u'.', 205 | 'modified_time': 130140564865057976, 206 | 'size': 0}, 207 | {'accessed_time': 130140564865057976, 208 | 'attributes': ['DIRECTORY'], 209 | 'created_time': 131402864909305289, 210 | 'filename': u'..', 211 | 'modified_time': 130140564865057976, 212 | 'size': 0}, 213 | {'accessed_time': 130140561645188308, 214 | 'attributes': ['DIRECTORY'], 215 | 'created_time': 130140561645188308, 216 | 'filename': u'AX_Code', 217 | 'modified_time': 130140561645188308, 218 | 'size': 0}, 219 | {'accessed_time': 130140562567173940, 220 | 'attributes': ['DIRECTORY'], 221 | 'created_time': 130140562567173940, 222 | 'filename': u'EX_Code', 223 | 'modified_time': 130140562567173940, 224 | 'size': 0}, 225 | {'accessed_time': 130140564150888722, 226 | 'attributes': ['DIRECTORY'], 227 | 'created_time': 130140564150888722, 228 | 'filename': u'FlareOn2016', 229 | 'modified_time': 130140564150888722, 230 | 'size': 0}, 231 | {'accessed_time': 130140562308485474, 232 | 'attributes': ['DIRECTORY'], 233 | 'created_time': 130140561717572435, 234 | 'filename': u'FlareOn2017', 235 | 'modified_time': 130140562308485474, 236 | 'size': 0}, 237 | {'accessed_time': 130140564793921851, 238 | 'attributes': ['DIRECTORY'], 239 | 'created_time': 130140564793921851, 240 | 'filename': u'HX_Code', 241 | 'modified_time': 130140564793921851, 242 | 'size': 0}, 243 | {'accessed_time': 130140564849769949, 244 | 'attributes': ['DIRECTORY'], 245 | 'created_time': 130140564849769949, 246 | 'filename': u'Malware', 247 | 'modified_time': 130140564849769949, 248 | 'size': 0}, 249 | {'accessed_time': 130140564277716944, 250 | 'attributes': ['DIRECTORY'], 251 | 'created_time': 130140564277716944, 252 | 'filename': u'NX_Code', 253 | 'modified_time': 130140564277716944, 254 | 'size': 0}, 255 | {'accessed_time': 130140562360005576, 256 | 'attributes': ['DIRECTORY'], 257 | 'created_time': 130140562360005576, 258 | 'filename': u'RSA_factoring', 259 | 'modified_time': 130140562360005576, 260 | 'size': 0}]} 261 | shell_out 262 | Microsoft Windows [Version 6.1.7601] 263 | Copyright (c) 2009 Microsoft Corporation. All rights reserved. 264 | 265 | C:\Users\john.jackson\Downloads> 266 | shell_out 267 | cd c:\ 268 | 269 | c:\> 270 | shell_out 271 | dir 272 | Volume in drive C has no label. 273 | Volume Serial Number is ECAA-2B67 274 | 275 | Directory of c:\ 276 | 277 | 06/10/2009 02:42 PM 24 autoexec.bat 278 | 06/10/2009 02:42 PM 10 config.sys 279 | 07/13/2009 07:37 PM PerfLogs 280 | 02/20/2013 05:08 PM Program Files 281 | 05/25/2017 01:03 PM temp 282 | 05/24/2017 11:45 AM Users 283 | 02/20/2013 05:18 PM Windows 284 | 05/26/2013 08:41 AM work 285 | 2 File(s) 34 bytes 286 | 6 Dir(s) 55,131,119,616 bytes free 287 | 288 | c:\> 289 | shell_out 290 | cd c:\work\ 291 | 292 | c:\work> 293 | shell_out 294 | dir 295 | Volume in drive C has no label. 296 | Volume Serial Number is ECAA-2B67 297 | 298 | Directory of c:\work 299 | 300 | 05/26/2013 08:41 AM . 301 | 05/26/2013 08:41 AM .. 302 | 05/26/2013 08:36 AM AX_Code 303 | 05/26/2013 08:37 AM EX_Code 304 | 05/26/2013 08:40 AM FlareOn2016 305 | 05/26/2013 08:37 AM FlareOn2017 306 | 05/26/2013 08:41 AM HX_Code 307 | 05/26/2013 08:41 AM Malware 308 | 05/26/2013 08:40 AM NX_Code 309 | 05/26/2013 08:37 AM RSA_factoring 310 | 0 File(s) 0 bytes 311 | 10 Dir(s) 55,131,119,616 bytes free 312 | 313 | c:\work> 314 | shell_out 315 | cd c:\work\FlareOn2017 316 | 317 | c:\work\FlareOn2017> 318 | shell_out 319 | dir 320 | Volume in drive C has no label. 321 | Volume Serial Number is ECAA-2B67 322 | 323 | Directory of c:\work\FlareOn2017 324 | 325 | 05/26/2013 08:37 AM . 326 | 05/26/2013 08:37 AM .. 327 | 05/26/2013 08:36 AM Challenge_01 328 | 05/26/2013 08:36 AM Challenge_02 329 | 05/26/2013 08:36 AM Challenge_03 330 | 05/26/2013 08:36 AM Challenge_04 331 | 05/26/2013 08:36 AM Challenge_05 332 | 05/26/2013 08:36 AM Challenge_06 333 | 05/26/2013 08:36 AM Challenge_07 334 | 05/26/2013 08:36 AM Challenge_08 335 | 05/26/2013 08:36 AM Challenge_09 336 | 05/26/2017 08:46 AM Challenge_10 337 | 05/26/2013 08:37 AM Challenge_11 338 | 05/26/2013 08:37 AM SecretChallenge_12 339 | 0 File(s) 0 bytes 340 | 14 Dir(s) 55,131,119,616 bytes free 341 | 342 | c:\work\FlareOn2017> 343 | shell_out 344 | cd c:\work\FlareOn2017\Challenge_10 345 | 346 | c:\work\FlareOn2017\Challenge_10> 347 | shell_out 348 | dir 349 | Volume in drive C has no label. 350 | Volume Serial Number is ECAA-2B67 351 | 352 | Directory of c:\work\FlareOn2017\Challenge_10 353 | 354 | 05/26/2017 08:46 AM . 355 | 05/26/2017 08:46 AM .. 356 | 05/26/2017 08:46 AM 28 TODO.txt 357 | 1 File(s) 28 bytes 358 | 2 Dir(s) 55,131,119,616 bytes free 359 | 360 | c:\work\FlareOn2017\Challenge_10> 361 | shell_out 362 | type TODO.txt 363 | Check with Larry about this. 364 | c:\work\FlareOn2017\Challenge_10> 365 | shell_out 366 | mkdir c:\staging 367 | 368 | c:\work\FlareOn2017\Challenge_10> 369 | shell_out 370 | cd c:\staging 371 | 372 | c:\staging> 373 | screenshot: {'filemd5': 'df328417c4854dad1d1a6d1e939868c7'} 374 | shell_out 375 | ping larryjohnson-pc 376 | 377 | Pinging larryjohnson-pc [192.168.221.105] with 32 bytes of data: 378 | Reply from 192.168.221.105: bytes=32 time<1ms TTL=128 379 | 380 | shell_out 381 | Reply from 192.168.221.105: bytes=32 time<1ms TTL=128 382 | 383 | shell_out 384 | Reply from 192.168.221.105: bytes=32 time<1ms TTL=128 385 | 386 | shell_out 387 | Reply from 192.168.221.105: bytes=32 time<1ms TTL=128 388 | 389 | Ping statistics for 192.168.221.105: 390 | Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), 391 | Approximate round trip times in milli-seconds: 392 | Minimum = 0ms, Maximum = 0ms, Average = 0ms 393 | 394 | c:\staging> 395 | shell_out 396 | pse.exe -h 397 | 398 | PsExec v2.2 - Execute processes remotely 399 | Copyright (C) 2001-2016 Mark Russinovich 400 | Sysinternals - www.sysinternals.com 401 | 402 | PsExec executes a program on a remote system, where remotely executed console 403 | applications execute interactively. 404 | 405 | Usage: psexec [\\computer[,computer2[,...] | @file]][-u user [-p psswd][-n s][-r servicename][-h][-l][-s|-e][-x][-i [session]][-c [-f|-v]][-w directory][-d][-][-a n,n,...] cmd [arguments] 406 | -a Separate processors on which the application can run with 407 | commas where 1 is the lowest numbered CPU. For example, 408 | to run the application on CPU 2 and CPU 4, enter: 409 | "-a 2,4" 410 | -c Copy the specified program to the remote system for 411 | execution. If you omit this option the application 412 | must be in the system path on the remote system. 413 | -d Don't wait for process to terminate (non-interactive). 414 | -e Does not load the specified account's profile. 415 | -f Copy the specified program even if the file already 416 | exists on the remote system. 417 | -i Run the program so that it interacts with the desktop of the 418 | specified session on the remote system. If no session is 419 | specified the process runs in the console session. 420 | -h If the target system is Vista or higher, has the process 421 | run with the account's elevated token, if available. 422 | -l Run process as limited user (strips the Administrators group 423 | and allows only privileges assigned to the Users group). 424 | On Windows Vista the process runs with Low Integrity. 425 | -n Specifies timeout in seconds connecting to remote computers. 426 | -p Specifies optional password for user name. If you omit this 427 | you will be prompted to enter a hidden password. 428 | -r Specifies the name of the remote service to create or interact. 429 | 430 | shell_out 431 | with. 432 | -s Run the remote process in the System account. 433 | -u Specifies optional user name for login to remote 434 | computer. 435 | -v Copy the specified file only if it has a higher version number 436 | or is newer on than the one on the remote system. 437 | -w Set the working directory of the process (relative to 438 | remote computer). 439 | -x Display the UI on the Winlogon secure desktop (local system 440 | only). 441 | -arm Specifies the remote computer is of ARM architecture. 442 | -priority Specifies -low, -belownormal, -abovenormal, -high or 443 | -realtime to run the process at a different priority. Use 444 | -background to run at low memory and I/O priority on Vista. 445 | computer Direct PsExec to run the application on the remote 446 | computer or computers specified. If you omit the computer 447 | name PsExec runs the application on the local system, 448 | and if you specify a wildcard (\\*), PsExec runs the 449 | command on all computers in the current domain. 450 | @file PsExec will execute the command on each of the computers listed 451 | in the file. 452 | cmd Name of application to execute. 453 | arguments Arguments to pass (note that file paths must be 454 | absolute paths on the target system). 455 | -accepteula This flag suppresses the display of the license dialog. 456 | -nobanner Do not display the startup banner and copyright message. 457 | 458 | You can enclose applications that have spaces in their name with 459 | quotation marks e.g. psexec \\marklap "c:\long name app.exe". 460 | Input is only passed to the remote system when you press the enter 461 | key, and typing Ctrl-C terminates the remote process. 462 | 463 | If you omit a user name the process will run in the context of your 464 | account on the remote system, but will not have access to network 465 | resources (because it is impersonating). Specify a valid user n 466 | shell_out 467 | ame 468 | in the Domain\User syntax if the remote process requires access 469 | to network resources or to run in a different account. Note that 470 | the password and command is encrypted in transit to the remote system. 471 | 472 | Error codes returned by PsExec are specific to the applications you 473 | execute, not PsExec. 474 | 475 | 476 | c:\staging> 477 | shell_out 478 | pse.exe \\larryjohnson-pc -i -c -f -d -u larry.johnson -p n3v3rgunnag1veUup -accepteula srv2.exe 479 | 480 | shell_out 481 | 482 | PsExec v2.2 - Execute processes remotely 483 | Copyright (C) 2001-2016 Mark Russinovich 484 | Sysinternals - www.sysinternals.com 485 | 486 | 487 | shell_out 488 | Connecting to larryjohnson-pc... Starting PSEXESVC service on larryjohnson-pc... Connecting with PsExec service on larryjohnson-pc... Copying srv2.exe to larryjohnson-pc... Starting srv2.exe on larryjohnson-pc... 489 | srv2.exe started on larryjohnson-pc with process ID 3000. 490 | 491 | c:\staging> 492 | query_proxy_connections: [{'hostname': '192.168.221.105', 'index': 0, 'port': 16452, 'run_flag': 1}] 493 | shell_out 494 | dir 495 | Volume in drive C has no label. 496 | Volume Serial Number is ECAA-2B67 497 | 498 | Directory of c:\staging 499 | 500 | 08/01/2017 10:07 AM . 501 | 08/01/2017 10:07 AM .. 502 | 08/01/2017 10:07 AM 339,096 pse.exe 503 | 08/01/2017 10:07 AM 119,808 srv2.exe 504 | 2 File(s) 458,904 bytes 505 | 2 Dir(s) 55,130,656,768 bytes free 506 | 507 | c:\staging> 508 | proxy_connect: {'remote_host': '192.168.221.105', 'remote_port': 16452} 509 | c2_authenticate: {'password': u'welcomepass1!1'} 510 | load_module: {'filemd5': '935579cedacc17cc09096bb2fdf94b67', 511 | 'module_id': '2965e4a19b6e9d9473f5f54dfef93533', 512 | 'module_name': 'plugin_BlowfishCrypto.bin'} 513 | load_module: {'filemd5': 'bc9dd7421b8ac9494c63ca914dd20131', 514 | 'module_id': '8746e7b7b0c1b9cf3f11ecae78a3a4bc', 515 | 'module_name': 'plugin_SimpleXorCrypto.bin'} 516 | load_module: {'filemd5': '7a199d23e020cee581d01abdb656bb29', 517 | 'module_id': '46c5525904f473ace7bb8cb58b29968a', 518 | 'module_name': 'plugin_Des3Crypto.bin'} 519 | load_module: {'filemd5': '7ce8b4d35df8c7da55789ab8cf372f5f', 520 | 'module_id': '9b1f6ec7d9b42bf7758a094a2186986b', 521 | 'module_name': 'plugin_CamelliaCrypto.bin'} 522 | load_module: {'filemd5': 'e6895743ba3e996036d65f80fbae1827', 523 | 'module_id': '503b6412c75a7c7558d1c92683225449', 524 | 'module_name': 'plugin_ApLibCompress.bin'} 525 | load_module: {'filemd5': '5abd114aeb2af11c9b12c4918c5eb261', 526 | 'module_id': '0a7874d2478a7713705e13dd9b31a6b1', 527 | 'module_name': 'plugin_LzoCompress.bin'} 528 | load_module: {'filemd5': '3f002fa74b02da598f39a867e2d052a0', 529 | 'module_id': 'a3aecca1cb4faa7a9a594d138a1bfbd5', 530 | 'module_name': 'plugin_ScreenCommand.bin'} 531 | load_module: {'filemd5': '4b05ff9bf7f59bf411a605b24c28a5b5', 532 | 'module_id': 'f47c51070fa8698064b65b3b6e7d30c6', 533 | 'module_name': 'plugin_FileComamnd.bin'} 534 | load_module: {'filemd5': 'ac9f33da50bb56522402bf01bb1df548', 535 | 'module_id': 'f46d09704b40275fb33790a362762e56', 536 | 'module_name': 'plugin_ShellCommand.bin'} 537 | shell_in: 'cd c:\\\r\n' 538 | shell_in: 'dir\r\n' 539 | shell_in: 'cd work\r\n' 540 | shell_in: 'dir\r\n' 541 | shell_in: 'cd flareon2017\r\n' 542 | shell_in: 'dir\r\n' 543 | shell_in: 'type README.md\r\n' 544 | shell_in: 'cd package\r\n' 545 | shell_in: 'dir\r\n' 546 | shell_in: 'mkdir c:\\staging\r\n' 547 | file_put: {'filemd5': '22eef49edcaa9db5bdf90bb0147fb8b3', 548 | 'filename': u'c:\\staging\\cf.exe'} 549 | file_put: {'filemd5': '22eef49edcaa9db5bdf90bb0147fb8b3', 550 | 'filename': u'c:\\staging\\cf.exe'} 551 | shell_in: 'c:\\staging\\cf.exe lab10.zip tCqlc2+fFiLcuq1ee1eAPOMjxcdijh8z0jrakMA/jxg=\r\n' 552 | shell_in: 'dir\r\n' 553 | shell_in: 'del lab10*\r\n' 554 | shell_in: 'dir\r\n' 555 | query_plugins: [' 00:155bbf4a1efe1517734604b9d42b80e8:1.2.0: Command:MainC2Command', 556 | ' 01:51298f741667d7ed2941950106f50545:1.0.2: Crypto:NullCrypto', 557 | ' 02:f37126ad88a5617eaf06000d424c5a21:1.0.1:Compression:NullCompress'] 558 | profile_host: {'default_locale': 1033, 559 | 'hostname': u'LARRYJOHNSON-PC', 560 | 'malware_id': 'c5aaaeccf7f63d8312d673f01e2b042f', 561 | 'malware_note': u'feye2017 srv', 562 | 'malware_version': '2.1.0', 563 | 'os_version': '6.1.7601_2', 564 | 'username': u'larry.johnson'} 565 | query_plugins: [' 00:155bbf4a1efe1517734604b9d42b80e8:1.2.0: Command:MainC2Command', 566 | ' 01:51298f741667d7ed2941950106f50545:1.0.2: Crypto:NullCrypto', 567 | ' 02:2965e4a19b6e9d9473f5f54dfef93533:1.1.1: Crypto:BlowfishCrypto', 568 | ' 03:8746e7b7b0c1b9cf3f11ecae78a3a4bc:1.0.0: Crypto:SimpleXorCrypto', 569 | ' 04:46c5525904f473ace7bb8cb58b29968a:1.2.4: Crypto:Des3Crypto', 570 | ' 05:9b1f6ec7d9b42bf7758a094a2186986b:1.9.3: Crypto:CamelliaCrypto', 571 | ' 06:f37126ad88a5617eaf06000d424c5a21:1.0.1:Compression:NullCompress', 572 | ' 07:503b6412c75a7c7558d1c92683225449:1.1.1:Compression:ApLibCompress', 573 | ' 08:0a7874d2478a7713705e13dd9b31a6b1:2.0.6:Compression:LzoCompress'] 574 | screenshot: {'filemd5': '15801d18d54c9fa94010f48ab97096c6'} 575 | drive_list: [{'drive_letter': u'A:\\', 576 | 'filesystem': u'', 577 | 'free_space': 0, 578 | 'name': u'', 579 | 'total_space': 0}, 580 | {'drive_letter': u'C:\\', 581 | 'filesystem': u'NTFS', 582 | 'free_space': 55136497664, 583 | 'name': u'', 584 | 'total_space': 64422408192}, 585 | {'drive_letter': u'D:\\', 586 | 'filesystem': u'', 587 | 'free_space': 0, 588 | 'name': u'', 589 | 'total_space': 0}] 590 | dir_list: {'dir': u'c:\\\\\\', 591 | 'result': [{'accessed_time': 131401251571404950, 592 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 593 | 'created_time': 128920125750196134, 594 | 'filename': u'$Recycle.Bin', 595 | 'modified_time': 131401251571404950, 596 | 'size': 0}, 597 | {'accessed_time': 128920106441575908, 598 | 'attributes': ['ARCHIVE'], 599 | 'created_time': 128920106441575908, 600 | 'filename': u'autoexec.bat', 601 | 'modified_time': 128891437401258876, 602 | 'size': 24}, 603 | {'accessed_time': 130058786271306680, 604 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 605 | 'created_time': 130058786268186675, 606 | 'filename': u'Boot', 607 | 'modified_time': 130058786271306680, 608 | 'size': 0}, 609 | {'accessed_time': 130058786270058678, 610 | 'attributes': ['READONLY', 'HIDDEN', 'SYSTEM', 'ARCHIVE'], 611 | 'created_time': 130058786270058678, 612 | 'filename': u'bootmgr', 613 | 'modified_time': 129347621462815197, 614 | 'size': 383786}, 615 | {'accessed_time': 130058786272398682, 616 | 'attributes': ['READONLY', 'HIDDEN', 'SYSTEM', 'ARCHIVE'], 617 | 'created_time': 130058786272398682, 618 | 'filename': u'BOOTSECT.BAK', 619 | 'modified_time': 130058786272398682, 620 | 'size': 8192}, 621 | {'accessed_time': 128920106441731907, 622 | 'attributes': ['ARCHIVE'], 623 | 'created_time': 128920106441731907, 624 | 'filename': u'config.sys', 625 | 'modified_time': 128891437401258876, 626 | 'size': 10}, 627 | {'accessed_time': 128920208355488413, 628 | 'attributes': ['HIDDEN', 629 | 'SYSTEM', 630 | 'REPARSE_POINT', 631 | 'DIRECTORY', 632 | 'NOT_CONTENT_INDEXED'], 633 | 'created_time': 128920208355488413, 634 | 'filename': u'Documents and Settings', 635 | 'modified_time': 128920208355488413, 636 | 'size': 0}, 637 | {'accessed_time': 130058786667520320, 638 | 'attributes': ['HIDDEN', 'SYSTEM', 'ARCHIVE'], 639 | 'created_time': 130058786667520320, 640 | 'filename': u'pagefile.sys', 641 | 'modified_time': 131401464694700158, 642 | 'size': 1073741824}, 643 | {'accessed_time': 128920126252200916, 644 | 'attributes': ['DIRECTORY'], 645 | 'created_time': 128920126252200916, 646 | 'filename': u'PerfLogs', 647 | 'modified_time': 128920126252200916, 648 | 'size': 0}, 649 | {'accessed_time': 130058789050289949, 650 | 'attributes': ['READONLY', 'DIRECTORY'], 651 | 'created_time': 128920126252356915, 652 | 'filename': u'Program Files', 653 | 'modified_time': 130058789050289949, 654 | 'size': 0}, 655 | {'accessed_time': 130058789050289949, 656 | 'attributes': ['HIDDEN', 'DIRECTORY', 'NOT_CONTENT_INDEXED'], 657 | 'created_time': 128920126256100891, 658 | 'filename': u'ProgramData', 659 | 'modified_time': 130058789050289949, 660 | 'size': 0}, 661 | {'accessed_time': 130058788049308176, 662 | 'attributes': ['HIDDEN', 663 | 'SYSTEM', 664 | 'DIRECTORY', 665 | 'NOT_CONTENT_INDEXED'], 666 | 'created_time': 130058788048840175, 667 | 'filename': u'Recovery', 668 | 'modified_time': 130058788049308176, 669 | 'size': 0}, 670 | {'accessed_time': 130058794585911411, 671 | 'attributes': ['HIDDEN', 'SYSTEM', 'DIRECTORY'], 672 | 'created_time': 130058786666896319, 673 | 'filename': u'System Volume Information', 674 | 'modified_time': 130058794585911411, 675 | 'size': 0}, 676 | {'accessed_time': 131401301798756171, 677 | 'attributes': ['DIRECTORY'], 678 | 'created_time': 131401290722685153, 679 | 'filename': u'temp', 680 | 'modified_time': 131401301798756171, 681 | 'size': 0}, 682 | {'accessed_time': 131401251555804923, 683 | 'attributes': ['READONLY', 'DIRECTORY'], 684 | 'created_time': 128920126257972879, 685 | 'filename': u'Users', 686 | 'modified_time': 131401251555804923, 687 | 'size': 0}, 688 | {'accessed_time': 131460809090561681, 689 | 'attributes': ['DIRECTORY'], 690 | 'created_time': 128920126259532869, 691 | 'filename': u'Windows', 692 | 'modified_time': 131460809090561681, 693 | 'size': 0}, 694 | {'accessed_time': 131402110171404269, 695 | 'attributes': ['DIRECTORY'], 696 | 'created_time': 131402108897471776, 697 | 'filename': u'work', 698 | 'modified_time': 131402110171404269, 699 | 'size': 0}]} 700 | dir_list: {'dir': u'c:\\work\\\\', 701 | 'result': [{'accessed_time': 131402110171404269, 702 | 'attributes': ['DIRECTORY'], 703 | 'created_time': 131402108897471776, 704 | 'filename': u'.', 705 | 'modified_time': 131402110171404269, 706 | 'size': 0}, 707 | {'accessed_time': 131402110171404269, 708 | 'attributes': ['DIRECTORY'], 709 | 'created_time': 131402108897471776, 710 | 'filename': u'..', 711 | 'modified_time': 131402110171404269, 712 | 'size': 0}, 713 | {'accessed_time': 131402109503255096, 714 | 'attributes': ['DIRECTORY'], 715 | 'created_time': 131402109503255096, 716 | 'filename': u'bestinbreed', 717 | 'modified_time': 131402109503255096, 718 | 'size': 0}, 719 | {'accessed_time': 131402109374554870, 720 | 'attributes': ['DIRECTORY'], 721 | 'created_time': 131402109374554870, 722 | 'filename': u'coolstuff', 723 | 'modified_time': 131402109374554870, 724 | 'size': 0}, 725 | {'accessed_time': 131402109783899589, 726 | 'attributes': ['DIRECTORY'], 727 | 'created_time': 131402109783899589, 728 | 'filename': u'cyberdisruptor', 729 | 'modified_time': 131402109783899589, 730 | 'size': 0}, 731 | {'accessed_time': 131402110149720231, 732 | 'attributes': ['DIRECTORY'], 733 | 'created_time': 131402110149720231, 734 | 'filename': u'flareon2015', 735 | 'modified_time': 131402110149720231, 736 | 'size': 0}, 737 | {'accessed_time': 131402109311530759, 738 | 'attributes': ['DIRECTORY'], 739 | 'created_time': 131402109311530759, 740 | 'filename': u'flareon2016', 741 | 'modified_time': 131402109311530759, 742 | 'size': 0}, 743 | {'accessed_time': 131402109246410615, 744 | 'attributes': ['DIRECTORY'], 745 | 'created_time': 131402109011959978, 746 | 'filename': u'flareon2017', 747 | 'modified_time': 131402109246410615, 748 | 'size': 0}, 749 | {'accessed_time': 131402109442726989, 750 | 'attributes': ['DIRECTORY'], 751 | 'created_time': 131402109442726989, 752 | 'filename': u'gamechanger', 753 | 'modified_time': 131402109442726989, 754 | 'size': 0}, 755 | {'accessed_time': 131402109878123754, 756 | 'attributes': ['DIRECTORY'], 757 | 'created_time': 131402109878123754, 758 | 'filename': u'innovation', 759 | 'modified_time': 131402109878123754, 760 | 'size': 0}, 761 | {'accessed_time': 131402109566123206, 762 | 'attributes': ['DIRECTORY'], 763 | 'created_time': 131402109566123206, 764 | 'filename': u'paradigmshifter', 765 | 'modified_time': 131402109566123206, 766 | 'size': 0}]} 767 | shell_out 768 | Microsoft Windows [Version 6.1.7601] 769 | Copyright (c) 2009 Microsoft Corporation. All rights reserved. 770 | 771 | C:\Windows\system32> 772 | shell_out 773 | cd c:\ 774 | 775 | c:\> 776 | shell_out 777 | dir 778 | Volume in drive C has no label. 779 | Volume Serial Number is ECAA-2B67 780 | 781 | Directory of c:\ 782 | 783 | 06/10/2009 02:42 PM 24 autoexec.bat 784 | 06/10/2009 02:42 PM 10 config.sys 785 | 07/13/2009 07:37 PM PerfLogs 786 | 02/20/2013 05:08 PM Program Files 787 | 05/24/2017 01:09 PM temp 788 | 05/24/2017 11:45 AM Users 789 | 08/01/2017 10:08 AM Windows 790 | 05/25/2017 11:36 AM work 791 | 2 File(s) 34 bytes 792 | 6 Dir(s) 55,136,497,664 bytes free 793 | 794 | c:\> 795 | shell_out 796 | cd work 797 | 798 | c:\work> 799 | shell_out 800 | dir 801 | Volume in drive C has no label. 802 | Volume Serial Number is ECAA-2B67 803 | 804 | Directory of c:\work 805 | 806 | 05/25/2017 11:36 AM . 807 | 05/25/2017 11:36 AM .. 808 | 05/25/2017 11:35 AM bestinbreed 809 | 05/25/2017 11:35 AM coolstuff 810 | 05/25/2017 11:36 AM cyberdisruptor 811 | 05/25/2017 11:36 AM flareon2015 812 | 05/25/2017 11:35 AM flareon2016 813 | 05/25/2017 11:35 AM flareon2017 814 | 05/25/2017 11:35 AM gamechanger 815 | 05/25/2017 11:36 AM innovation 816 | 05/25/2017 11:35 AM paradigmshifter 817 | 0 File(s) 0 bytes 818 | 11 Dir(s) 55,136,497,664 bytes free 819 | 820 | c:\work> 821 | shell_out 822 | cd flareon2017 823 | 824 | c:\work\flareon2017> 825 | shell_out 826 | dir 827 | Volume in drive C has no label. 828 | Volume Serial Number is ECAA-2B67 829 | 830 | Directory of c:\work\flareon2017 831 | 832 | 05/25/2017 11:35 AM . 833 | 05/25/2017 11:35 AM .. 834 | 05/25/2017 11:35 AM build 835 | 08/01/2017 09:57 AM package 836 | 05/25/2017 11:39 AM 218 README.md 837 | 05/25/2017 11:35 AM src 838 | 1 File(s) 218 bytes 839 | 5 Dir(s) 55,136,497,664 bytes free 840 | 841 | c:\work\flareon2017> 842 | shell_out 843 | type README.md 844 | # GoChallenge 845 | Go Lang FlareOn Challenge 846 | 847 | To run the challenge: 848 | $ go run challenge.go 849 | 850 | To build the challenge: 851 | $ go build challenge.go 852 | 853 | Note: I think my password is good. Why do you guys want me to change it? 854 | c:\work\flareon2017> 855 | shell_out 856 | cd package 857 | 858 | c:\work\flareon2017\package> 859 | shell_out 860 | dir 861 | Volume in drive C has no label. 862 | Volume Serial Number is ECAA-2B67 863 | 864 | Directory of c:\work\flareon2017\package 865 | 866 | 08/01/2017 09:57 AM . 867 | 08/01/2017 09:57 AM .. 868 | 08/01/2017 09:54 AM 561,862 lab10.zip 869 | 1 File(s) 561,862 bytes 870 | 2 Dir(s) 55,136,538,624 bytes free 871 | 872 | c:\work\flareon2017\package> 873 | shell_out 874 | mkdir c:\staging 875 | 876 | c:\work\flareon2017\package> 877 | shell_out 878 | c:\staging\cf.exe lab10.zip tCqlc2+fFiLcuq1ee1eAPOMjxcdijh8z0jrakMA/jxg= 879 | 880 | shell_out 881 | 882 | c:\work\flareon2017\package>dir 883 | Volume in drive C has no label. 884 | Volume Serial Number is ECAA-2B67 885 | 886 | Directory of c:\work\flareon2017\package 887 | 888 | 08/01/2017 10:12 AM . 889 | 08/01/2017 10:12 AM .. 890 | 08/01/2017 09:54 AM 561,862 lab10.zip 891 | 08/01/2017 10:12 AM 561,972 lab10.zip.cry 892 | 2 File(s) 1,123,834 bytes 893 | 2 Dir(s) 55,135,965,184 bytes free 894 | 895 | c:\work\flareon2017\package> 896 | file_get: {'filemd5': '8bf72789dcc08e08b7ccf0ee879135e1', 897 | 'filename': u'c:\\work\\flareon2017\\package\\lab10.zip.cry'} 898 | shell_out 899 | del lab10* 900 | 901 | c:\work\flareon2017\package> 902 | shell_out 903 | dir 904 | Volume in drive C has no label. 905 | Volume Serial Number is ECAA-2B67 906 | 907 | Directory of c:\work\flareon2017\package 908 | 909 | 08/01/2017 10:13 AM . 910 | 08/01/2017 10:13 AM .. 911 | 0 File(s) 0 bytes 912 | 2 Dir(s) 55,137,095,680 bytes free 913 | 914 | c:\work\flareon2017\package> 915 | ############################################################ 916 | Files: 917 | f873a174ccb567670e222c1f37195cf2: plugin_Rc4Crypto.bin 918 | 6e8866fc570d74ab21b99f687c108105: plugin_LookupTableCrypto.bin 919 | fb5acf29a468df13c2289f3e96027f12: plugin_CustomBase64Crypto.bin 920 | 59061be984a290dd9c9320edb569ac71: plugin_XteaCrypto.bin 921 | 114a99b58940c5f5dd41114fe340468e: plugin_ZlibCompress.bin 922 | 4b05ff9bf7f59bf411a605b24c28a5b5: plugin_FileComamnd.bin 923 | ac9f33da50bb56522402bf01bb1df548: plugin_ShellCommand.bin 924 | 3f002fa74b02da598f39a867e2d052a0: plugin_ScreenCommand.bin 925 | 27304b246c7d5b4e149124d5f93c5b01: c:\staging\pse.exe 926 | 27304b246c7d5b4e149124d5f93c5b01: c:\staging\pse.exe 927 | bf0a86db982de1996c0dc49d681dbe81: c:\staging\srv2.exe 928 | bf0a86db982de1996c0dc49d681dbe81: c:\staging\srv2.exe 929 | 9e5ca05e6fe30a37056c290a9fc7177c: plugin_ProxyCommand.bin 930 | df328417c4854dad1d1a6d1e939868c7: screenshot.bmp 931 | 935579cedacc17cc09096bb2fdf94b67: plugin_BlowfishCrypto.bin 932 | bc9dd7421b8ac9494c63ca914dd20131: plugin_SimpleXorCrypto.bin 933 | 7a199d23e020cee581d01abdb656bb29: plugin_Des3Crypto.bin 934 | 7ce8b4d35df8c7da55789ab8cf372f5f: plugin_CamelliaCrypto.bin 935 | e6895743ba3e996036d65f80fbae1827: plugin_ApLibCompress.bin 936 | 5abd114aeb2af11c9b12c4918c5eb261: plugin_LzoCompress.bin 937 | 3f002fa74b02da598f39a867e2d052a0: plugin_ScreenCommand.bin 938 | 4b05ff9bf7f59bf411a605b24c28a5b5: plugin_FileComamnd.bin 939 | ac9f33da50bb56522402bf01bb1df548: plugin_ShellCommand.bin 940 | 22eef49edcaa9db5bdf90bb0147fb8b3: c:\staging\cf.exe 941 | 22eef49edcaa9db5bdf90bb0147fb8b3: c:\staging\cf.exe 942 | 15801d18d54c9fa94010f48ab97096c6: screenshot.bmp 943 | 8bf72789dcc08e08b7ccf0ee879135e1: c:\work\flareon2017\package\lab10.zip.cry 944 | 128321c4fe1dfc7ff25484d813c838b1: secondstage 945 | 6f53a0ed92c00f3e6fc83e0da28aaf19: decoded_secondstage 946 | -------------------------------------------------------------------------------- /flareon5_challenge11/README.md: -------------------------------------------------------------------------------- 1 | # FlareOn 2018 Challenge 11 solution 2 | 3 | You must first run tcpflow on the provided pcap to reconstruct TCP streams. 4 | Then run the tool passing in the path to the pcap and the flow directory: 5 | 6 | ``` 7 | $ python solution.py ./pcap.pcap ./flows/ 8 | ``` 9 | 10 | 11 | ## Requirements 12 | Install via pip the following packages: 13 | * hexdump 14 | * scapy 15 | * M2Crypto 16 | * pycrypto 17 | 18 | Install vivisect, either: 19 | * Clone https://github.com/vivisect/vivisect and place that in your PYTHONPATH 20 | * Install via pip: 21 | ``` 22 | $ pip install https://github.com/williballenthin/vivisect/zipball/master 23 | ``` 24 | 25 | 26 | -------------------------------------------------------------------------------- /flareon5_challenge11/filtered_out.txt: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | Events: 3 | authenticate: {'password': 'welcomepass1!1'} 4 | shell_in: 'cd c:\\' 5 | shell_in: 'dir' 6 | shell_in: 'cd c:\\work' 7 | shell_in: 'dir' 8 | shell_in: 'cd c:\\work\\FlareOn2018_Challenge9' 9 | shell_in: 'dir' 10 | file_put: {'filepath': u'c:\\work\\FlareOn2018_Challenge9\\Cryptor.exe', 11 | 'md5sum': '93cc547f9adbd6a4366d3d8b415a77f1'} 12 | shell_in: 'dir' 13 | shell_in: 'Cryptor level9.crypt level9.zip' 14 | shell_in: 'dir' 15 | shell_in: 'del level9.zip' 16 | shell_in: 'del level9.exe' 17 | shell_in: 'del level9.png' 18 | ftp_activate: {'hostname': u'52.0.104.200', 19 | 'password': u'', 20 | 'port': 21, 21 | 'username': u'anonymous'} 22 | ftp_upload: {'localpath': u'c:\\work\\FlareOn2018_Challenge9\\level9.crypt', 23 | 'remotepath': u'/upload/level9.crypt'} 24 | shell_in: 'del level9.crypt' 25 | shell_in: 'del Cryptor.exe' 26 | shell_in: 'dir' 27 | shell_deactiveate: None 28 | exit: None 29 | query_plugins: [{'id': '00000081', 30 | 'name': '', 31 | 'realname': 'MAINC2', 32 | 'type': 'CMD ', 33 | 'version': '1.5.0'}, 34 | {'id': '00000083', 35 | 'name': '', 36 | 'realname': 'SHELL', 37 | 'type': 'CMD ', 38 | 'version': '1.2.0'}, 39 | {'id': '00000082', 40 | 'name': '', 41 | 'realname': 'FILES', 42 | 'type': 'CMD ', 43 | 'version': '2.3.0'}, 44 | {'id': '00000084', 45 | 'name': '', 46 | 'realname': 'PROXY', 47 | 'type': 'CMD ', 48 | 'version': '2.5.0'}, 49 | {'id': '00000087', 50 | 'name': '', 51 | 'realname': 'FTP_EXFIL', 52 | 'type': 'CMD ', 53 | 'version': '1.1.0'}, 54 | {'id': '00000085', 55 | 'name': '', 56 | 'realname': 'LATERAL', 57 | 'type': 'CMD ', 58 | 'version': '1.1.0'}, 59 | {'id': '00000093', 60 | 'name': '', 61 | 'realname': 'CRYPTGENRANDOM', 62 | 'type': 'RAND', 63 | 'version': '1.0.3'}, 64 | {'id': '0000008e', 65 | 'name': '', 66 | 'realname': 'HASHSHA256', 67 | 'type': 'HASH', 68 | 'version': '1.0.6'}, 69 | {'id': '00000092', 70 | 'name': '', 71 | 'realname': 'AES128_CFB', 72 | 'type': 'CRPT', 73 | 'version': '1.0.9'}, 74 | {'id': '00000078', 75 | 'name': '', 76 | 'realname': 'ZLIB', 77 | 'type': 'COMP', 78 | 'version': '1.2.11'}, 79 | {'id': '0000008f', 80 | 'name': '', 81 | 'realname': 'HMACSHA256', 82 | 'type': 'HMAC', 83 | 'version': '1.0.9'}] 84 | host_survey: {'default_locale': 1033, 85 | 'host_id': '{f60f8b7b-63de-16f0-2448-02f52ae846c3}', 86 | 'hostname': u'LARRYJOHNSON-PC', 87 | 'malware_version': '3.0.8', 88 | 'memo': u'feye2018 pipe srv', 89 | 'os_version': '6.1.7601', 90 | 'username': u'SYSTEM'} 91 | shell_out 92 | Microsoft Windows [Version 6.1.7601] 93 | Copyright (c) 2009 Microsoft Corporation. All rights reserved. 94 | 95 | C:\Windows\system32> 96 | shell_out 97 | cd c:\ 98 | 99 | c:\> 100 | shell_out 101 | dir 102 | Volume in drive C has no label. 103 | Volume Serial Number is ECAA-2B67 104 | 105 | Directory of c:\ 106 | 107 | 06/10/2009 02:42 PM 24 autoexec.bat 108 | 06/10/2009 02:42 PM 10 config.sys 109 | 07/13/2009 07:37 PM PerfLogs 110 | 07/19/2018 03:02 PM Program Files 111 | 07/23/2018 09:29 PM temp 112 | 05/24/2017 11:45 AM Users 113 | 08/10/2018 08:21 AM Windows 114 | 07/23/2018 10:26 AM work 115 | 2 File(s) 34 bytes 116 | 6 Dir(s) 52,576,829,440 bytes free 117 | 118 | c:\> 119 | shell_out 120 | cd c:\work 121 | 122 | c:\work> 123 | shell_out 124 | dir 125 | Volume in drive C has no label. 126 | Volume Serial Number is ECAA-2B67 127 | 128 | Directory of c:\work 129 | 130 | 07/23/2018 10:26 AM . 131 | 07/23/2018 10:26 AM .. 132 | 07/23/2018 10:26 AM FlareOn2017_challenge10 133 | 08/10/2018 08:08 AM FlareOn2018_Challenge9 134 | 07/23/2018 10:26 AM Helix 135 | 07/23/2018 10:25 AM NX_Code 136 | 07/23/2018 10:26 AM X16 137 | 0 File(s) 0 bytes 138 | 7 Dir(s) 52,576,034,816 bytes free 139 | 140 | c:\work> 141 | shell_out 142 | cd c:\work\FlareOn2018_Challenge9 143 | 144 | c:\work\FlareOn2018_Challenge9> 145 | shell_out 146 | dir 147 | Volume in drive C has no label. 148 | Volume Serial Number is ECAA-2B67 149 | 150 | Directory of c:\work\FlareOn2018_Challenge9 151 | 152 | 08/10/2018 08:08 AM . 153 | 08/10/2018 08:08 AM .. 154 | 07/19/2018 02:24 PM 6,656 level9.exe 155 | 08/10/2018 07:35 AM 14,502 level9.png 156 | 08/10/2018 07:40 AM 10,223 level9.zip 157 | 3 File(s) 31,381 bytes 158 | 2 Dir(s) 52,576,034,816 bytes free 159 | 160 | c:\work\FlareOn2018_Challenge9> 161 | shell_out 162 | dir 163 | Volume in drive C has no label. 164 | Volume Serial Number is ECAA-2B67 165 | 166 | Directory of c:\work\FlareOn2018_Challenge9 167 | 168 | 08/10/2018 08:24 AM . 169 | 08/10/2018 08:24 AM .. 170 | 08/10/2018 08:24 AM 12,288 Cryptor.exe 171 | 07/19/2018 02:24 PM 6,656 level9.exe 172 | 08/10/2018 07:35 AM 14,502 level9.png 173 | 08/10/2018 07:40 AM 10,223 level9.zip 174 | 4 File(s) 43,669 bytes 175 | 2 Dir(s) 52,576,022,528 bytes free 176 | 177 | c:\work\FlareOn2018_Challenge9> 178 | shell_out 179 | Cryptor level9.crypt level9.zip 180 | 181 | shell_out 182 | Adding file level9.zip 183 | Done 184 | 185 | c:\work\FlareOn2018_Challenge9> 186 | shell_out 187 | dir 188 | Volume in drive C has no label. 189 | Volume Serial Number is ECAA-2B67 190 | 191 | Directory of c:\work\FlareOn2018_Challenge9 192 | 193 | 08/10/2018 08:24 AM . 194 | 08/10/2018 08:24 AM .. 195 | 08/10/2018 08:24 AM 12,288 Cryptor.exe 196 | 08/10/2018 08:24 AM 10,303 level9.crypt 197 | 07/19/2018 02:24 PM 6,656 level9.exe 198 | 08/10/2018 07:35 AM 14,502 level9.png 199 | 08/10/2018 07:40 AM 10,223 level9.zip 200 | 5 File(s) 53,972 bytes 201 | 2 Dir(s) 52,574,953,472 bytes free 202 | 203 | c:\work\FlareOn2018_Challenge9> 204 | shell_out 205 | del level9.zip 206 | 207 | c:\work\FlareOn2018_Challenge9> 208 | shell_out 209 | del level9.exe 210 | 211 | c:\work\FlareOn2018_Challenge9> 212 | shell_out 213 | del level9.png 214 | 215 | c:\work\FlareOn2018_Challenge9> 216 | shell_out 217 | del level9.crypt 218 | 219 | c:\work\FlareOn2018_Challenge9> 220 | shell_out 221 | del Cryptor.exe 222 | 223 | c:\work\FlareOn2018_Challenge9> 224 | shell_out 225 | dir 226 | Volume in drive C has no label. 227 | Volume Serial Number is ECAA-2B67 228 | 229 | Directory of c:\work\FlareOn2018_Challenge9 230 | 231 | 08/10/2018 08:26 AM . 232 | 08/10/2018 08:26 AM .. 233 | 0 File(s) 0 bytes 234 | 2 Dir(s) 52,574,937,088 bytes free 235 | 236 | c:\work\FlareOn2018_Challenge9> 237 | query_plugins: [{'id': '00000081', 238 | 'name': '', 239 | 'realname': 'MAINC2', 240 | 'type': 'CMD ', 241 | 'version': '1.5.0'}, 242 | {'id': '00000083', 243 | 'name': '', 244 | 'realname': 'SHELL', 245 | 'type': 'CMD ', 246 | 'version': '1.2.0'}, 247 | {'id': '00000082', 248 | 'name': '', 249 | 'realname': 'FILES', 250 | 'type': 'CMD ', 251 | 'version': '2.3.0'}, 252 | {'id': '00000084', 253 | 'name': '', 254 | 'realname': 'PROXY', 255 | 'type': 'CMD ', 256 | 'version': '2.5.0'}, 257 | {'id': '00000087', 258 | 'name': '', 259 | 'realname': 'FTP_EXFIL', 260 | 'type': 'CMD ', 261 | 'version': '1.1.0'}, 262 | {'id': '00000085', 263 | 'name': '', 264 | 'realname': 'LATERAL', 265 | 'type': 'CMD ', 266 | 'version': '1.1.0'}, 267 | {'id': '00000093', 268 | 'name': '', 269 | 'realname': 'CRYPTGENRANDOM', 270 | 'type': 'RAND', 271 | 'version': '1.0.3'}, 272 | {'id': '0000008e', 273 | 'name': '', 274 | 'realname': 'HASHSHA256', 275 | 'type': 'HASH', 276 | 'version': '1.0.6'}, 277 | {'id': '00000092', 278 | 'name': '', 279 | 'realname': 'AES128_CFB', 280 | 'type': 'CRPT', 281 | 'version': '1.0.9'}, 282 | {'id': '00000078', 283 | 'name': '', 284 | 'realname': 'ZLIB', 285 | 'type': 'COMP', 286 | 'version': '1.2.11'}, 287 | {'id': '0000008f', 288 | 'name': '', 289 | 'realname': 'HMACSHA256', 290 | 'type': 'HMAC', 291 | 'version': '1.0.9'}] 292 | host_survey: {'default_locale': 1033, 293 | 'host_id': '{f60f8b7b-63de-16f0-2448-02f52ae846c3}', 294 | 'hostname': u'JOHNJACKSON-PC', 295 | 'malware_version': '3.0.8', 296 | 'memo': u'feye2018 tcp cli', 297 | 'os_version': '6.1.7601', 298 | 'username': u'john.jackson'} 299 | drive_list: {'drives': [{'drive_letter': u'A:\\', 300 | 'filesystem': u'', 301 | 'free_space': 0, 302 | 'name': u'', 303 | 'total_space': 0, 304 | 'type': 2}, 305 | {'drive_letter': u'C:\\', 306 | 'filesystem': u'NTFS', 307 | 'free_space': 54842515456, 308 | 'name': u'', 309 | 'total_space': 64422408192, 310 | 'type': 3}, 311 | {'drive_letter': u'D:\\', 312 | 'filesystem': u'', 313 | 'free_space': 0, 314 | 'name': u'', 315 | 'total_space': 0, 316 | 'type': 5}, 317 | {'drive_letter': u'X:\\', 318 | 'filesystem': u'HGFS', 319 | 'free_space': 190915125248, 320 | 'name': u'Shared Folders', 321 | 'total_space': 1007057006592, 322 | 'type': 4}]} 323 | dir_list: {'contents': [{'filename': u'$Recycle.Bin'}, 324 | {'filename': u'autoexec.bat'}, 325 | {'filename': u'Boot'}, 326 | {'filename': u'bootmgr'}, 327 | {'filename': u'BOOTSECT.BAK'}, 328 | {'filename': u'config.sys'}, 329 | {'filename': u'Documents and Settings'}, 330 | {'filename': u'pagefile.sys'}, 331 | {'filename': u'PerfLogs'}, 332 | {'filename': u'Program Files'}, 333 | {'filename': u'ProgramData'}, 334 | {'filename': u'Recovery'}, 335 | {'filename': u'staging'}, 336 | {'filename': u'System Volume Information'}, 337 | {'filename': u'temp'}, 338 | {'filename': u'Users'}, 339 | {'filename': u'Windows'}, 340 | {'filename': u'work'}], 341 | 'dirname': u'c:\\'} 342 | dir_list: {'contents': [{'filename': u'.'}, 343 | {'filename': u'..'}, 344 | {'filename': u'AX_Code'}, 345 | {'filename': u'EX_Code'}, 346 | {'filename': u'FlareOn2016'}, 347 | {'filename': u'FlareOn2017'}, 348 | {'filename': u'FlareOn2018'}, 349 | {'filename': u'HX_Code'}, 350 | {'filename': u'Malware'}, 351 | {'filename': u'NX_Code'}, 352 | {'filename': u'RSA_factoring'}], 353 | 'dirname': u'c:\\work\\'} 354 | shell_out 355 | Microsoft Windows [Version 6.1.7601] 356 | Copyright (c) 2009 Microsoft Corporation. All rights reserved. 357 | 358 | C:\temp> 359 | shell_out 360 | cd c:\ 361 | 362 | c:\> 363 | shell_out 364 | dir 365 | Volume in drive C has no label. 366 | Volume Serial Number is ECAA-2B67 367 | 368 | Directory of c:\ 369 | 370 | 06/10/2009 02:42 PM 24 autoexec.bat 371 | 06/10/2009 02:42 PM 10 config.sys 372 | 07/13/2009 07:37 PM PerfLogs 373 | 02/20/2013 05:08 PM Program Files 374 | 08/01/2017 10:07 AM staging 375 | 07/23/2018 12:38 PM temp 376 | 05/24/2017 11:45 AM Users 377 | 02/20/2013 05:18 PM Windows 378 | 07/23/2018 07:45 AM work 379 | 2 File(s) 34 bytes 380 | 7 Dir(s) 54,842,515,456 bytes free 381 | 382 | c:\> 383 | shell_out 384 | cd c:\work\ 385 | 386 | c:\work> 387 | shell_out 388 | dir 389 | Volume in drive C has no label. 390 | Volume Serial Number is ECAA-2B67 391 | 392 | Directory of c:\work 393 | 394 | 07/23/2018 07:45 AM . 395 | 07/23/2018 07:45 AM .. 396 | 05/26/2013 08:36 AM AX_Code 397 | 05/26/2013 08:37 AM EX_Code 398 | 05/26/2013 08:40 AM FlareOn2016 399 | 05/26/2013 08:37 AM FlareOn2017 400 | 07/23/2018 07:53 AM FlareOn2018 401 | 05/26/2013 08:41 AM HX_Code 402 | 05/26/2013 08:41 AM Malware 403 | 05/26/2013 08:40 AM NX_Code 404 | 05/26/2013 08:37 AM RSA_factoring 405 | 0 File(s) 0 bytes 406 | 11 Dir(s) 54,842,515,456 bytes free 407 | 408 | c:\work> 409 | shell_out 410 | cd c:\work\flareon2018 411 | 412 | c:\work\FlareOn2018> 413 | shell_out 414 | dir 415 | Volume in drive C has no label. 416 | Volume Serial Number is ECAA-2B67 417 | 418 | Directory of c:\work\FlareOn2018 419 | 420 | 07/23/2018 07:53 AM . 421 | 07/23/2018 07:53 AM .. 422 | 07/23/2018 07:45 AM Challenge01 423 | 07/23/2018 07:45 AM Challenge02 424 | 07/23/2018 07:45 AM Challenge03 425 | 07/23/2018 07:46 AM Challenge04 426 | 07/23/2018 07:46 AM Challenge05 427 | 07/23/2018 07:46 AM Challenge06 428 | 07/23/2018 07:46 AM Challenge07 429 | 07/23/2018 07:46 AM Challenge08 430 | 07/23/2018 10:44 AM Challenge09 431 | 07/23/2018 07:46 AM Challenge10 432 | 07/23/2018 07:53 AM Challenge11 433 | 07/23/2018 07:53 AM Challenge12 434 | 0 File(s) 0 bytes 435 | 14 Dir(s) 54,842,515,456 bytes free 436 | 437 | c:\work\FlareOn2018> 438 | shell_out 439 | cd c:\work\flareon2018\Challenge09 440 | 441 | c:\work\FlareOn2018\Challenge09> 442 | shell_out 443 | dir 444 | Volume in drive C has no label. 445 | Volume Serial Number is ECAA-2B67 446 | 447 | Directory of c:\work\FlareOn2018\Challenge09 448 | 449 | 07/23/2018 10:44 AM . 450 | 07/23/2018 10:44 AM .. 451 | 07/23/2018 10:45 AM 119 README.md 452 | 1 File(s) 119 bytes 453 | 2 Dir(s) 54,842,515,456 bytes free 454 | 455 | c:\work\FlareOn2018\Challenge09> 456 | shell_out 457 | type README.md 458 | # TODO By Larry 459 | 460 | Larry is running late again. Check the wiki (http://wiki.flare.fireeye.com:8081) for latest updates. 461 | c:\work\FlareOn2018\Challenge09> 462 | shell_out 463 | ping wiki.flare.fireeye.com 464 | 465 | shell_out 466 | 467 | Pinging wiki.flare.fireeye.com [192.168.200.4] with 32 bytes of data: 468 | Reply from 192.168.200.4: bytes=32 time<1ms TTL=64 469 | 470 | shell_out 471 | Reply from 192.168.200.4: bytes=32 time<1ms TTL=64 472 | 473 | shell_out 474 | Reply from 192.168.200.4: bytes=32 time<1ms TTL=64 475 | 476 | shell_out 477 | Reply from 192.168.200.4: bytes=32 time<1ms TTL=64 478 | 479 | Ping statistics for 192.168.200.4: 480 | Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), 481 | Approximate round trip times in milli-seconds: 482 | Minimum = 0ms, Maximum = 0ms, Average = 0ms 483 | 484 | c:\work\FlareOn2018\Challenge09> 485 | query_proxy: [{'hostanme': 'wiki.flare.fireeye.com', 'index': 0, 'port': 8081, 'type': 0}] 486 | shell_out 487 | ping larryjohnson-pc 488 | 489 | Pinging larryjohnson-pc [fe80::905b:87b:5c8d:a243%11] with 32 bytes of data: 490 | Reply from fe80::905b:87b:5c8d:a243%11: time<1ms 491 | 492 | shell_out 493 | Reply from fe80::905b:87b:5c8d:a243%11: time<1ms 494 | 495 | shell_out 496 | Reply from fe80::905b:87b:5c8d:a243%11: time<1ms 497 | 498 | shell_out 499 | Reply from fe80::905b:87b:5c8d:a243%11: time<1ms 500 | 501 | Ping statistics for fe80::905b:87b:5c8d:a243%11: 502 | Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), 503 | Approximate round trip times in milli-seconds: 504 | Minimum = 0ms, Maximum = 0ms, Average = 0ms 505 | 506 | c:\work\FlareOn2018\Challenge09> 507 | query_proxy: [{'hostanme': 'wiki.flare.fireeye.com', 'index': 0, 'port': 8081, 'type': 0}, 508 | {'hostanme': u'192.168.221.105', 509 | 'index': 0, 510 | 'pipe': u'malaproppipe', 511 | 'type': 80}] 512 | authenticate: {'password': 'welcomepass1!1'} 513 | shell_in: 'cd c:\\' 514 | shell_in: 'dir' 515 | shell_in: 'cd c:\\work\\' 516 | shell_in: 'dir' 517 | shell_in: 'cd c:\\work\\flareon2018' 518 | shell_in: 'dir' 519 | shell_in: 'cd c:\\work\\flareon2018\\Challenge09' 520 | shell_in: 'dir' 521 | shell_in: 'type README.md' 522 | shell_in: 'ping wiki.flare.fireeye.com' 523 | shell_in: 'ping larryjohnson-pc' 524 | lateral_activate: {'hostname': u'192.168.221.105', 525 | 'password': u'n3v3rgunnag1veUup', 526 | 'username': u'larry.johnson'} 527 | lateral_install: {'args': u' -service', 528 | 'filename': u'launchaccelerator.exe', 529 | 'hostname': u'192.168.221.105', 530 | 'service': u'LaunchAccelerator'} 531 | lateral_config: {'commstype': 5, 532 | 'hostname': '', 533 | 'memo': u'feye2018 pipe srv', 534 | 'mutex': u'asdliugasldmgj', 535 | 'password': u'welcomepass1!1', 536 | 'pipename': u'malaproppipe', 537 | 'port': 0, 538 | 'servicename': u'LaunchAccelerator'} 539 | lateral_deactiveate: None 540 | lateral_activate: {'hostname': u'192.168.221.105', 541 | 'password': u'n3v3rgunnag1veUup', 542 | 'username': u'larry.johnson'} 543 | proxy_connect: {'hostname': 'wiki.flare.fireeye.com', 'port': 8081} 544 | ############################################################ 545 | Files: 546 | 05a3070492c6c9ca596997d3a79fe570: malaprop_stage3.dll_ 547 | 93cc547f9adbd6a4366d3d8b415a77f1: c:\work\FlareOn2018_Challenge9\Cryptor.exe 548 | 81ce35acb25c57257e0517ff0f379e8c: level9.crypt 549 | 378e1ac4fa4ab0329332d823b3448a62: / 550 | 5aecc6708beef1e98bc627c16644a4fb: /moin_static199/common/js/common.js 551 | 1e3633d772c2d8057e4ec2cba0630bd6: /moin_static199/common/flare_logo64.png 552 | 6dfc2390288b83f9b8cc619ac65a24f9: /moin_static199/modernized/css/print.css 553 | a7e8eb69c3314d556c23a85f366a3c86: /moin_static199/modernized/css/common.css 554 | 92e5379eafd4c4eebdda49e6c8d85986: /moin_static199/modernized/css/screen.css 555 | e42b5dc28457c35e38d9570d8bb22be8: /moin_static199/modernized/css/projection.css 556 | ef67a4e9689efda71625a2ef894fb700: /moin_static199/modernized/img/moin-www.png 557 | 824340baaf76d4442a9a47545061d464: /FlareProjects 558 | 58ae0d221fb2bb05f69a5a6b5ca2de30: /FlareOn2018 559 | dab1c5d6bb69c6bf716826364b898cc1: /FlareOn2018Challenge9 560 | e5660aeb0add65feb53179dfaf4a5c97: level9.zip 561 | -------------------------------------------------------------------------------- /flareon5_challenge11/solution.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # Jay Smith (@jay_smif) 3 | # Solver script for Flare-On 5 (2018) Challenge 11 4 | # First run tcpflow on the given pcap to generate a set of TCP-stream files, 5 | # two per TCP stream. 6 | # Then run "python solution.py " 7 | # where is the path to the input file 8 | # and is the directory containing tcpflow output files. 9 | 10 | 11 | import os 12 | import re 13 | import pdb 14 | import sys 15 | import copy 16 | import hmac 17 | import zlib 18 | import pprint 19 | import struct 20 | import hashlib 21 | import logging 22 | import os.path 23 | import binascii 24 | import cStringIO 25 | 26 | 27 | try: 28 | import hexdump 29 | import scapy.all 30 | import M2Crypto.EVP 31 | import M2Crypto.RC4 32 | import Crypto.Cipher.AES 33 | except ImportError, err: 34 | print('Failed to import: %s\nPlease install via pip the following:' % str(err)) 35 | print('hexdump') 36 | print('scapy') 37 | print('M2Crypto') 38 | print('pycrypto') 39 | sys.exit(-1) 40 | 41 | try: 42 | # we used vstruct heavily for declarative structures and parsing, and enums. quite nice 43 | #https://github.com/vivisect/vivisect 44 | import vstruct 45 | from vstruct.primitives import * 46 | import vstruct.defs.bmp as c_bmp 47 | import vstruct.defs.win32 as c_win32 48 | except ImportError, err: 49 | print('Failed to import vstruct. Please make sure that you clone https://github.com/vivisect/vivisect and place that in your PYTHONPATH') 50 | print('Alternatively you may be able to pip install via:') 51 | print(' pip install https://github.com/williballenthin/vivisect/zipball/master') 52 | sys.exit(-1) 53 | 54 | logger = logging.getLogger() 55 | 56 | def logUnknown(msg, *args, **kwargs): 57 | hexdata = kwargs.pop('hex', None) 58 | msg = msg % args 59 | if hexdata: 60 | return logger.warning('Unimp: ' + msg + '\n' + hd(hexdata)) 61 | return logger.warning('Unimp: ' + msg) 62 | 63 | 64 | ################################################################################ 65 | # stream constants 66 | IMPLANT_TO_SERVER = 0 67 | SERVER_TO_IMPLANT = 1 68 | 69 | DirStrings = { 70 | IMPLANT_TO_SERVER : 'I2S', 71 | SERVER_TO_IMPLANT : 'S2I', 72 | } 73 | 74 | 75 | DNS_TYPE_TEXT = 0x10 76 | 77 | logger = logging.getLogger() 78 | ################################################################################ 79 | def hd(bb): 80 | return hexdump.hexdump(bb, result='return') 81 | ################################################################################ 82 | def getFdSize(fd): 83 | pos = fd.tell() 84 | pos = fd.seek(0, 2) 85 | size = fd.tell() 86 | fd.seek(0) 87 | return size 88 | ################################################################################ 89 | 90 | def getFdMd5(fd): 91 | pos = fd.tell() 92 | md = hashlib.md5(fd.read()).hexdigest() 93 | fd.seek(pos) 94 | return md 95 | 96 | file_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-.' 97 | def cleanFileName(name): 98 | #make sure only allowed characters are in the name, and ends with '_' 99 | return ''.join([ b if b in file_letters else '_' for b in name]) + '_' 100 | 101 | ################################################################################ 102 | CRYPTO_KEY_SIZE = 0x10 103 | HMAC_KEY_SIZE = 0x20 104 | 105 | CMDSIG = 0x20180301 106 | 107 | 108 | PLUGINID = v_enum() 109 | PLUGINID.COMMS_TCP = 0x50 110 | PLUGINID.COMMS_NAMED_PIPE = 0x51 111 | PLUGINID.MAINC2 = 0x81 112 | PLUGINID.FILES = 0x82 113 | PLUGINID.SHELL = 0x83 114 | PLUGINID.PROXY = 0x84 115 | PLUGINID.LATERAL = 0x85 116 | PLUGINID.WGET = 0x86 117 | PLUGINID.FTP_EXFIL = 0x87 118 | PLUGINID.ZLIB = 0x78 119 | PLUGINID.HASHSHA256 = 0x8e 120 | PLUGINID.HMACSHA256 = 0x8f 121 | PLUGINID.AES128_CFB = 0x92 122 | PLUGINID.CRYPTGENRANDOM = 0x93 123 | 124 | 125 | COMMSTYPE_TCP = 2 126 | COMMSTYPE_PIPE = 4 127 | COMMSTYPE_CLIENT = 0 128 | COMMSTYPE_SERVER = 1 129 | 130 | COMMSTYPE = v_enum() 131 | COMMSTYPE.TCPCLIENT = (COMMSTYPE_TCP | COMMSTYPE_CLIENT) # 2 132 | COMMSTYPE.TCPSERVER = (COMMSTYPE_TCP | COMMSTYPE_SERVER) # 3 133 | COMMSTYPE.PIPECLIENT = (COMMSTYPE_PIPE | COMMSTYPE_CLIENT) # 4 134 | COMMSTYPE.PIPESERVER = (COMMSTYPE_PIPE | COMMSTYPE_SERVER) # 5 135 | 136 | 137 | 138 | ERROR = v_enum() 139 | ERROR.SUCCESS = 0 140 | #ERROR.BASE = 0x100000 141 | #ERROR.BAD_ARGUMENT = (ERROR.BASE + 1) 142 | #ERROR.API_ERROR = (ERROR.BASE + 2) 143 | #ERROR.MISSING_PLUGIN = (ERROR.BASE + 3) 144 | #ERROR.INTERRUPTED_ALLOC_PLUGIN = (ERROR.BASE + 4) 145 | #ERROR.MALLOC = (ERROR.BASE + 5) 146 | #ERROR.LOAD_PLUGIN = (ERROR.BASE + 6) 147 | #ERROR.NOT_AUTHENTICATED = (ERROR.BASE + 7) 148 | # 149 | #ERROR.SHELL_ERROR_BASE = (ERROR.BASE + 0x1000) 150 | #ERROR.SHELL_NO_COMSPEC = (ERROR.SHELL_ERROR_BASE + 1) 151 | #ERROR.SHELL_PIPE_ERROR = (ERROR.SHELL_ERROR_BASE + 2) 152 | #ERROR.SHELL_CREATE_PROC_ERROR = (ERROR.SHELL_ERROR_BASE + 3) 153 | #ERROR.SHELL_CREATE_THREAD_ERROR = (ERROR.SHELL_ERROR_BASE + 4) 154 | #ERROR.SHELL_NOT_ACTIVE = (ERROR.SHELL_ERROR_BASE + 5) 155 | #ERROR.SHELL_WRITE_ERROR = (ERROR.SHELL_ERROR_BASE + 6) 156 | #ERROR.SHELL_READ_ERROR = (ERROR.SHELL_ERROR_BASE + 7) 157 | #ERROR.PROXY_ERROR_BASE = (ERROR.BASE + 0x2000) 158 | #ERROR.PROXY_CONN_CLOSED = (ERROR.PROXY_ERROR_BASE + 1) 159 | #ERROR.PROXY_ERROR_DISCONNECT = (ERROR.PROXY_ERROR_BASE + 2) 160 | #ERROR.FILE_ERROR_BASE = (ERROR.BASE + 0x3000) 161 | #ERROR.FILE_EXISTING_FILE_PUT_ERROR = (ERROR.FILE_ERROR_BASE + 1) 162 | #ERROR.FILE_CREATE_ERROR = (ERROR.FILE_ERROR_BASE + 2) 163 | #ERROR.FILE_NOT_OPEN_ERROR = (ERROR.FILE_ERROR_BASE + 3) 164 | #ERROR.FILE_GUID_MISMATCH_ERROR = (ERROR.FILE_ERROR_BASE + 4) 165 | #ERROR.FILE_ADJUST_FILE_POINTER_ERROR = (ERROR.FILE_ERROR_BASE + 5) 166 | #ERROR.FILE_WRITE_ERROR = (ERROR.FILE_ERROR_BASE + 6) 167 | #ERROR.FILE_HASH_INCORRECT_ERROR = (ERROR.FILE_ERROR_BASE + 7) 168 | #ERROR.FILE_THREAD_ERROR = (ERROR.FILE_ERROR_BASE + 8) 169 | #ERROR.FILE_READ_ERROR = (ERROR.FILE_ERROR_BASE + 8) 170 | #ERROR.FILE_NO_SUCH_DIRECTORY_ERROR = (ERROR.FILE_ERROR_BASE + 9) 171 | #ERROR.SCREEN_ERROR_BASE = (ERROR.BASE + 0x4000) 172 | #ERROR.SCREEN_GETDIBITS_ERROR = (ERROR.SCREEN_ERROR_BASE + 1) 173 | 174 | SHELLCMD = v_enum() 175 | SHELLCMD.BASE = 0 176 | SHELLCMD.ACTIVATE = (SHELLCMD.BASE + 1) 177 | SHELLCMD.DEACTIVATE = (SHELLCMD.BASE + 2) 178 | SHELLCMD.SHELLIN = (SHELLCMD.BASE + 3) 179 | SHELLCMD.SHELLOUT = (SHELLCMD.BASE + 4) 180 | 181 | SCREENCMD = v_enum() 182 | SCREENCMD.SCREEN_CMD_BASE = 0 183 | SCREENCMD.SCREEN_SCREENSHOT = (SCREENCMD.SCREEN_CMD_BASE + 1) 184 | SCREENCMD.SCREEN_BITMAPINFO = (SCREENCMD.SCREEN_CMD_BASE + 2) 185 | SCREENCMD.SCREEN_BITMAPDATA = (SCREENCMD.SCREEN_CMD_BASE + 3) 186 | 187 | MAINC2CMD = v_enum() 188 | MAINC2CMD.BASE = 0 189 | MAINC2CMD.HEARTBEAT = (MAINC2CMD.BASE + 1) 190 | MAINC2CMD.PING = (MAINC2CMD.BASE + 2) 191 | MAINC2CMD.HOSTINFO = (MAINC2CMD.BASE + 3) 192 | MAINC2CMD.EXIT = (MAINC2CMD.BASE + 4) 193 | MAINC2CMD.MSGBOX = (MAINC2CMD.BASE + 5) 194 | MAINC2CMD.DISCONNECT = (MAINC2CMD.BASE + 6) 195 | MAINC2CMD.AUTHENTICATE = (MAINC2CMD.BASE + 7) 196 | MAINC2CMD.QUERYPLUGINS = (MAINC2CMD.BASE + 8) 197 | 198 | FILECMD = v_enum() 199 | FILECMD.BASE = 0 200 | FILECMD.DRIVE_LIST = (FILECMD.BASE + 1) 201 | FILECMD.DIR_LIST = (FILECMD.BASE + 2) 202 | FILECMD.CREATE_DIR = (FILECMD.BASE + 3) 203 | FILECMD.DEL_FILE = (FILECMD.BASE + 4) 204 | FILECMD.DEL_DIR = (FILECMD.BASE + 5) 205 | FILECMD.FILE_PUT_DATA = (FILECMD.BASE + 6) 206 | FILECMD.FILE_PUT_DONE = (FILECMD.BASE + 7) 207 | FILECMD.FILE_PUT = (FILECMD.BASE + 8) 208 | FILECMD.FILE_GET_DATA = (FILECMD.BASE + 9) 209 | FILECMD.FILE_GET_DONE = (FILECMD.BASE + 10) 210 | FILECMD.FILE_GET = (FILECMD.BASE + 11) 211 | 212 | 213 | PROXYCMD = v_enum() 214 | PROXYCMD.CMD_BASE = 0 215 | PROXYCMD.QUERY_CONNECTIONS = (PROXYCMD.CMD_BASE + 1) 216 | PROXYCMD.DATA = (PROXYCMD.CMD_BASE + 2) 217 | PROXYCMD.DISCONNECT = (PROXYCMD.CMD_BASE + 3) 218 | PROXYCMD.TCPCONNECT = (PROXYCMD.CMD_BASE + 4) 219 | PROXYCMD.PIPECONNECT = (PROXYCMD.CMD_BASE + 5) 220 | 221 | FTPEXFILCMD = v_enum() 222 | FTPEXFILCMD.CMD_BASE = 0 223 | FTPEXFILCMD.ACTIVATE = (FTPEXFILCMD.CMD_BASE + 1) 224 | FTPEXFILCMD.DEACTIVATE = (FTPEXFILCMD.CMD_BASE + 2) 225 | FTPEXFILCMD.UPLOAD = (FTPEXFILCMD.CMD_BASE + 3) 226 | 227 | LATERALCMD = v_enum() 228 | LATERALCMD.CMD_BASE = 0 229 | LATERALCMD.ACTIVATE = (LATERALCMD.CMD_BASE + 1) 230 | LATERALCMD.DEACTIVATE = (LATERALCMD.CMD_BASE + 2) 231 | LATERALCMD.INSTALL = (LATERALCMD.CMD_BASE + 3) 232 | LATERALCMD.STOPDELETE = (LATERALCMD.CMD_BASE + 4) 233 | 234 | 235 | ################################################################################ 236 | class RestartException(Exception): 237 | pass 238 | 239 | def getFieldOffset(vstr, seekname): 240 | for off, indent, name, field in vstr.vsGetPrintInfo(): 241 | if name == seekname: 242 | return off 243 | return None 244 | ################################################################################ 245 | 246 | 247 | SMB_MAGIC = "\xffSMB" 248 | SMB2_MAGIC = "\xfeSMB" 249 | 250 | SMB2_COMMANDS = v_enum() 251 | SMB2_COMMANDS._name = 'SMB2_COMMANDS' 252 | SMB2_COMMANDS.READ = 8 253 | SMB2_COMMANDS.WRITE = 9 254 | 255 | class Smb2PacketHeader(vstruct.VStruct): 256 | def __init__(self): 257 | vstruct.VStruct.__init__(self) 258 | self.protocol = v_bytes(4) 259 | self.hdrlen = v_uint16() 260 | self.credit_charge = v_uint16() 261 | self.status = v_uint32() 262 | self.command = v_uint16(enum=SMB2_COMMANDS) 263 | self.credit = v_uint16() 264 | self.flags = v_uint32() 265 | self.next_command = v_uint32() 266 | self.message_id = v_uint64() 267 | self.reserved = v_uint32() 268 | self.tree_id = v_uint32() 269 | self.session_id = v_uint64() 270 | self.signature = v_bytes(16) 271 | self.data = v_bytes(0) # capture maximum 272 | 273 | def vsParse(self, sbytes, offset=0): 274 | off = vstruct.VStruct.vsParse(self, sbytes, offset) 275 | if self.next_command == 0: 276 | # no next command, so take all the rest off 277 | self.data = v_bytes(vbytes=sbytes[off:]) 278 | off = len(sbytes) 279 | else: 280 | # take up to the next command 281 | datalen = self.next_command - len(self) 282 | self.data = v_bytes(vbytes=sbytes[off:off+datalen]) 283 | off = offset + self.next_command 284 | return off 285 | 286 | 287 | class Smb2WriteReq(vstruct.VStruct): 288 | def __init__(self, dataoff): 289 | vstruct.VStruct.__init__(self) 290 | self.struct_size = v_uint16() 291 | self.data_offset = v_uint16() 292 | self.data_length = v_uint32() 293 | self.offset = v_uint64() 294 | self.fid = GUID() 295 | self.channel = v_uint32() 296 | self.remaining_bytes = v_uint32() 297 | self.write_chan_offset = v_uint16() 298 | self.write_chan_length = v_uint16() 299 | self.flags = v_uint32() 300 | 301 | self.dataoff = dataoff 302 | 303 | def vsParse(self, sbytes, offset=0): 304 | vstruct.VStruct.vsParse(self, sbytes, offset) 305 | dataoff = offset + (self.data_offset - self.dataoff) 306 | self.data = sbytes[dataoff:dataoff+self.data_length] 307 | 308 | class Smb2ReadResp(vstruct.VStruct): 309 | def __init__(self, dataoff): 310 | vstruct.VStruct.__init__(self) 311 | self.struct_size = v_uint16() 312 | self.data_offset = v_uint8() 313 | self.reserved1 = v_uint8() 314 | self.data_length = v_uint32() 315 | self.read_remaining = v_uint32() 316 | self.reserved2 = v_uint32() 317 | 318 | self.dataoff = dataoff 319 | 320 | def vsParse(self, sbytes, offset=0): 321 | vstruct.VStruct.vsParse(self, sbytes, offset) 322 | dataoff = offset + (self.data_offset - self.dataoff) 323 | self.data = sbytes[dataoff:dataoff+self.data_length] 324 | 325 | ################################################################################ 326 | 327 | 328 | class FtpActivateCmd(vstruct.VStruct): 329 | def __init__(self): 330 | vstruct.VStruct.__init__(self) 331 | self.hostname = v_wstr(128) 332 | self.username = v_wstr(128) 333 | self.password = v_wstr(128) 334 | self.port = v_uint32() 335 | 336 | class FtpUploadCmd(vstruct.VStruct): 337 | def __init__(self): 338 | vstruct.VStruct.__init__(self) 339 | self.localpath = v_wstr(256) 340 | self.remotepath = v_wstr(256) 341 | 342 | 343 | 344 | class MalapropConfig(vstruct.VStruct): 345 | def __init__(self): 346 | vstruct.VStruct.__init__(self) 347 | self.sig = v_uint32() 348 | self.commsType = v_uint32() 349 | self.port = v_uint32() 350 | self.hostname = v_str(256) 351 | self.password = v_wstr(128) 352 | self.pipename = v_wstr(128) 353 | self.memo = v_wstr(128) 354 | self.mutex = v_wstr(128) 355 | self.servicename = v_wstr(128) 356 | 357 | # size of config + 0x20 byte key 358 | g_EncodedConfigLen = len(MalapropConfig()) + 0x20 359 | 360 | class FilePutCmd(vstruct.VStruct): 361 | def __init__(self): 362 | vstruct.VStruct.__init__(self) 363 | self.fileid = v_uint32() 364 | self.fileoff = v_uint64() 365 | self.filesize = v_uint64() 366 | self.filepath = v_wstr(256) 367 | 368 | class FilePutDataCmd(vstruct.VStruct): 369 | def __init__(self): 370 | vstruct.VStruct.__init__(self) 371 | self.fileid = v_uint32() 372 | self.fileoff = v_uint64() 373 | self.filesize = v_uint64() 374 | self.filechunksize = v_uint64() 375 | self.bytes = v_bytes() 376 | 377 | def pcb_filechunksize(self): 378 | self.vsGetField('bytes').vsSetLength(self.filechunksize) 379 | 380 | 381 | class TcpConnect(vstruct.VStruct): 382 | def __init__(self): 383 | vstruct.VStruct.__init__(self) 384 | self.port = v_uint32() 385 | self.hostname = v_str(256) 386 | 387 | class PipeConnectCmd(vstruct.VStruct): 388 | def __init__(self): 389 | vstruct.VStruct.__init__(self) 390 | self.hostname = v_wstr(256) 391 | self.pipename = v_wstr(256) 392 | 393 | 394 | class LateraralConnectCmd(vstruct.VStruct): 395 | def __init__(self): 396 | vstruct.VStruct.__init__(self) 397 | self.hostname = v_wstr(256) 398 | self.username = v_wstr(256) 399 | self.password = v_wstr(256) 400 | 401 | class LateraralInstallCmd(vstruct.VStruct): 402 | def __init__(self): 403 | vstruct.VStruct.__init__(self) 404 | self.interactive = v_uint32() 405 | self.hostname = v_wstr(256) 406 | self.service = v_wstr(256) 407 | self.filename = v_wstr(256) 408 | self.args = v_wstr(256) 409 | self.encodedConfig = v_bytes(g_EncodedConfigLen) 410 | 411 | class MsgEnvelope(vstruct.VStruct): 412 | def __init__(self): 413 | vstruct.VStruct.__init__(self) 414 | self.msgLen = v_uint32() # 0x00 415 | self.hmac = v_bytes(32) # 0x04 416 | self.cryptoType = v_uint8() # 0x24 417 | self.compressType = v_uint8() # 0x25 418 | self.decompressLen = v_uint32() # 0x26 419 | self.iv = v_bytes(16) # 0x 420 | 421 | # total size: 48 (0x30) 422 | class HelloChallenge(vstruct.VStruct): 423 | def __init__(self): 424 | vstruct.VStruct.__init__(self) 425 | self.magic00 = v_uint32() # 0x00 426 | self.padding04 = v_bytes(4) # 0x04 427 | self.magic08 = v_uint32() # 0x08 428 | self.padding0c = v_bytes(4) # 0x0c 429 | self.magic10 = v_uint32() # 0x10 430 | self.padding14 = v_bytes(28) # 0x14 431 | 432 | class CommandHeader(vstruct.VStruct): 433 | def __init__(self): 434 | vstruct.VStruct.__init__(self) 435 | self.crc32 = v_uint32() 436 | self.sig = v_uint32() 437 | self.plugId = v_uint32(enum=PLUGINID) 438 | self.command = v_uint32() 439 | self.msgId = v_uint32() 440 | self.status = v_uint32(enum=ERROR) 441 | self.extendedStatus = v_uint32() 442 | 443 | class DirListCmd(vstruct.VStruct): 444 | def __init__(self): 445 | vstruct.VStruct.__init__(self) 446 | self.chead = CommandHeader() 447 | self.directory = v_wstr(260) 448 | 449 | class DriveListItem(vstruct.VStruct): 450 | def __init__(self): 451 | vstruct.VStruct.__init__(self) 452 | self.totalBytes = v_uint64() 453 | self.freeBytes = v_uint64() 454 | self.userFreeBytes = v_uint64() 455 | self.drivetype = v_uint32() 456 | self.volSerialNumber = v_uint32() 457 | self.driveName = v_wstr(4) 458 | self.volumeName = v_wstr(128) 459 | self.volumeType = v_wstr(128) 460 | 461 | PLUGIN_TYPES = v_enum() 462 | PLUGIN_TYPES.Command = 0x20444d43 463 | PLUGIN_TYPES.Crypto = 0x54505243 464 | PLUGIN_TYPES.Compression = 0x504d4f43 465 | PLUGIN_TYPES.HMAC = 0x43414d48 466 | PLUGIN_TYPES.RAND = 0x444e4152 467 | PLUGIN_TYPES.HASH = 0x48534148 468 | PLUGIN_TYPES.NETWORK = 0x2054454e 469 | 470 | class QueryPluginsItem(vstruct.VStruct): 471 | def __init__(self): 472 | vstruct.VStruct.__init__(self) 473 | self.plugId = v_uint32(enum=PLUGINID) 474 | self.plugtype = v_uint32(enum=PLUGIN_TYPES) 475 | self.name = v_str(64) 476 | self.version = v_str(64) 477 | 478 | class HostInfoResp(vstruct.VStruct): 479 | def __init__(self): 480 | vstruct.VStruct.__init__(self) 481 | self.chead = CommandHeader() 482 | self.hostid = GUID() 483 | self.commsType = v_uint32(enum=COMMSTYPE) 484 | self.isAdmin = v_uint32() 485 | self.defaultLcid = v_uint32() 486 | self.osVersionMajor = v_uint32() 487 | self.osVersionMinor = v_uint32() 488 | self.osVersionBuild = v_uint32() 489 | self.osVersionPlatformId = v_uint32() 490 | self.computername = v_wstr(64) 491 | self.username = v_wstr(64) 492 | self.memo = v_wstr(256) 493 | self.version = v_str(64) 494 | 495 | class ProxyConnectCmd(vstruct.VStruct): 496 | def __init__(self): 497 | vstruct.VStruct.__init__(self) 498 | self.chead = CommandHeader() 499 | self.port = v_uint32() 500 | self.hostname = v_str(256) 501 | 502 | class ProxyQueryItem(vstruct.VStruct): 503 | def __init__(self): 504 | vstruct.VStruct.__init__(self) 505 | self.index = v_uint32() 506 | self.runFlag = v_uint32() 507 | self.type = v_uint32(enum=PLUGINID) 508 | 509 | class ProxyQueryItemPipe(vstruct.VStruct): 510 | def __init__(self): 511 | vstruct.VStruct.__init__(self) 512 | self.hostname = v_wstr(256) 513 | self.pipename = v_wstr(256) 514 | 515 | class ProxyQueryItemTcp(vstruct.VStruct): 516 | def __init__(self): 517 | vstruct.VStruct.__init__(self) 518 | self.hostname = v_str(256) 519 | self.port = v_uint32() 520 | self.padding = v_bytes(764) #union, so need to match proxyqueryitempipe 521 | 522 | 523 | class FileGetDataResp(vstruct.VStruct): 524 | def __init__(self): 525 | vstruct.VStruct.__init__(self) 526 | self.chead = CommandHeader() 527 | #self.fileId = v_uint32() 528 | self.fileId = v_bytes(4) 529 | self.offset = v_uint64() 530 | self.filesize = v_uint64() 531 | self.buffLen = v_uint64() 532 | 533 | 534 | ################################################################################ 535 | 536 | ROTATE_BITMASK = { 537 | 8 : 0xff, 538 | 16 : 0xffff, 539 | 32 : 0xffffffff, 540 | 64 : 0xffffffffffffffff, 541 | } 542 | 543 | 544 | def ror(inVal, numShifts, dataSize=32): 545 | '''rotate right instruction emulation''' 546 | if numShifts == 0: 547 | return inVal 548 | if (numShifts < 0) or (numShifts > dataSize): 549 | raise ValueError('Bad numShifts') 550 | if (dataSize != 8) and (dataSize != 16) and (dataSize != 32) and (dataSize != 64): 551 | raise ValueError('Bad dataSize') 552 | bitMask = ROTATE_BITMASK[dataSize] 553 | return bitMask & ((inVal >> numShifts) | (inVal << (dataSize-numShifts))) 554 | 555 | 556 | 557 | 558 | 559 | g_PluginMap = { 560 | PLUGINID.SHELL : SHELLCMD, 561 | PLUGINID.MAINC2 : MAINC2CMD, 562 | PLUGINID.PROXY : PROXYCMD, 563 | PLUGINID.FILES : FILECMD, 564 | PLUGINID.LATERAL : LATERALCMD, 565 | PLUGINID.FTP_EXFIL: FTPEXFILCMD, 566 | } 567 | 568 | class FileTransfer(object): 569 | def __init__(self, dir, fileId, filesize, remotePath): 570 | self.dir = dir 571 | self.fileId = fileId 572 | self.filesize = filesize 573 | self.remotePath = remotePath 574 | self.blocks = {} 575 | self.fd = cStringIO.StringIO() 576 | 577 | def addData(self, data, off): 578 | logger.debug('Adding 0x%08x bytes at 0x%08x', len(data), off) 579 | self.fd.seek(off) 580 | self.fd.write(data) 581 | 582 | def isComplete(self): 583 | return self.fd.tell() >= self.filesize 584 | 585 | def close(self): 586 | self.fd.close() 587 | 588 | 589 | 590 | class ProxySession(object): 591 | def __init__(self, connId, hostname = None, port = None): 592 | self.hostname = hostname 593 | self.port = port 594 | self.hasData = False 595 | self.streams = { 596 | SERVER_TO_IMPLANT : cStringIO.StringIO(), 597 | IMPLANT_TO_SERVER : cStringIO.StringIO(), 598 | } 599 | 600 | def setHost(self, hostname, port=None): 601 | if self.port is None: 602 | self.port = port 603 | 604 | if (self.hostname is None) or (len(self.hostname) == 0): 605 | self.hostname = hostname 606 | elif self.hostname != hostname: 607 | logger.debug('Changing hostname from %r to %r', self.hostname, hostname) 608 | raise RuntimeError('Trying to change hostname for ProxySession') 609 | 610 | def addData(self, dir, data): 611 | self.hasData = True 612 | self.streams[dir].write(data) 613 | 614 | def getStream(self, dir): 615 | return self.streams[dir] 616 | 617 | def close(self): 618 | for fd in self.streams.values(): 619 | fd.close() 620 | 621 | 622 | 623 | ################################################################################ 624 | 625 | class CustomRc4(object): 626 | def __init__(self, key, statelen=256, state=None): 627 | if state is not None: 628 | self.state = copy.copy(state) 629 | else: 630 | self.state = range(statelen) 631 | j = 0 632 | for i in range(statelen): 633 | j = (j + self.state[i] + ord(key[i % len(key)])) % statelen 634 | self.state[i], self.state[j] = self.state[j], self.state[i] 635 | 636 | def copy(self): 637 | return CustomRc4(None, state=self.state) 638 | 639 | def gen_random_bytes(self): 640 | i = 0 641 | j = 0 642 | while True: 643 | i = (i + 1) % len(self.state) 644 | j = (j + self.state[i]) % len(self.state) 645 | self.state[i], self.state[j] = self.state[j], self.state[i] 646 | yield self.state[(self.state[i] + self.state[j]) % len(self.state)] 647 | 648 | def update(self, text): 649 | cipher_chars = [] 650 | random_byte_gen = self.gen_random_bytes() 651 | for char in text: 652 | byte = ord(char) 653 | cipher_byte = byte ^ random_byte_gen.next() 654 | cipher_chars.append(chr(cipher_byte)) 655 | return ''.join(cipher_chars) 656 | 657 | 658 | 659 | ################################################################################ 660 | class Report(object): 661 | ''' 662 | Gathers parse data 663 | ''' 664 | def __init__(self, odir): 665 | self.events = [] 666 | self.odir = odir 667 | self.files = [] 668 | 669 | def addEvent(self, etype, edata): 670 | self.events.append( (etype, edata) ) 671 | 672 | def addFileFd(self, fd, name): 673 | md = getFdMd5(fd) 674 | cname = os.path.join(self.odir, '%s_%s' % (md, cleanFileName(name))) 675 | logger.debug('Adding file: %s', cname) 676 | with file(cname, 'wb') as ofile: 677 | bytez = fd.read() 678 | ofile.write(bytez) 679 | self.files.append( (md, name, bytez) ) 680 | return md 681 | 682 | def addFileBytes(self, bytez, name): 683 | fd = cStringIO.StringIO() 684 | fd.write(bytez) 685 | fd.seek(0) 686 | ret = self.addFileFd(fd, name) 687 | fd.close() 688 | return ret 689 | 690 | def getFileBytes(self, mdseek): 691 | for md, name, bytez in self.files: 692 | if mdseek == md: 693 | return (md, name, bytez) 694 | return None, None, None 695 | 696 | ################################################################################ 697 | class TcpFlow(object): 698 | ''' 699 | Stores the two sides to a TCP flow as file-objects 700 | ''' 701 | def __init__(self, rpr, i2sFd, s2iFd): 702 | self.rpr = rpr 703 | self.fds = {} 704 | self.fds[IMPLANT_TO_SERVER] = i2sFd 705 | self.fds[SERVER_TO_IMPLANT] = s2iFd 706 | self.tastes = {} 707 | for k,v in self.fds.items(): 708 | self.tastes[k] = v.read(1024) 709 | v.seek(0) 710 | 711 | def __repr__(self): 712 | return self.rpr 713 | 714 | ################################################################################ 715 | 716 | class MalapropDnsParser(object): 717 | def __init__(self, report, packets): 718 | self.report = report 719 | self.packets = packets 720 | self.parts = {} 721 | 722 | def addTxtRecord(self, prefix, rdata): 723 | #logger.debug('Adding prefix: %s: %s', prefix, hashlib.md5(rdata).hexdigest()) 724 | if prefix in self.parts: 725 | raise RuntimeError('Unexpected duplicate DNS TXT rec part') 726 | self.parts[prefix] = rdata 727 | 728 | def finishParse(self): 729 | fd = self.getAsciiStream() 730 | encData = self.asciiDecodeStream(fd) 731 | if len(encData) == 0: 732 | return 733 | #logger.debug('Decoded dns data to %d 0x%x bytes\n%s', len(encData),len(encData), hd(encData[:0x200])) 734 | rc4 = CustomRc4(encData[:0x10], 0xff) 735 | actualLen = struct.unpack_from('I', msglenbuff)[0] 819 | if msglen == 0: 820 | raise RuntimeError('Dropped packets') 821 | smbmsg = fd.read(msglen) 822 | if len(smbmsg) != msglen: 823 | raise RuntimeError('Missing data') 824 | nbhead = fd.read(4) 825 | if smbmsg[:4] == SMB_MAGIC: 826 | # just protocol negotiation 827 | logger.debug('Skipping SMBv1 message') 828 | continue 829 | if smbmsg[:4] != SMB2_MAGIC: 830 | raise RuntimeError('Unexpected smbv2 magic') 831 | smb2 = Smb2PacketHeader() 832 | try: 833 | smb2.vsParse(smbmsg) 834 | if (dir == SERVER_TO_IMPLANT) and (smb2.command == SMB2_COMMANDS.READ) and (smb2.status == 0) and (len(smb2.data) != 0): 835 | dataoff = getFieldOffset(smb2, 'data') 836 | writereq = Smb2WriteReq(dataoff) 837 | writereq.vsParse(smb2.data) 838 | newfd.write(writereq.data) 839 | elif (dir == IMPLANT_TO_SERVER) and (smb2.command == SMB2_COMMANDS.WRITE) and (len(smb2.data) != 0): 840 | dataoff = getFieldOffset(smb2, 'data') 841 | readresp = Smb2ReadResp(dataoff) 842 | readresp.vsParse(smb2.data) 843 | newfd.write(readresp.data) 844 | except Exception, err: 845 | logUnknown('Error during parse: %s', str(err)) 846 | logger.debug('Stopping smb stream') 847 | newfd.seek(0) 848 | return newfd 849 | 850 | def finishParse(self): 851 | for connId, proxy in self.proxySessions.items(): 852 | if proxy.hasData: 853 | self.report.addEvent('proxy_connect', dict(hostname=proxy.hostname, port=proxy.port)) 854 | #flip the directions: s2i is the clifd, i2s is the srvfd 855 | clifd = proxy.getStream(SERVER_TO_IMPLANT) 856 | srvfd = proxy.getStream(IMPLANT_TO_SERVER) 857 | clifd.seek(0) 858 | srvfd.seek(0) 859 | clifdsize = getFdSize(clifd) 860 | srvfdsize = getFdSize(srvfd) 861 | if (clifdsize == 0) and (srvfdsize == 0): 862 | #not sure -> restart issue? 863 | continue 864 | logger.debug('Now handling connId %d: %r %r (%d)(%d)', connId, proxy.hostname, proxy.port, clifdsize, srvfdsize) 865 | if proxy.hostname is None: 866 | logUnknown('Proxy destination unknown. Proceeding with default target host') 867 | logger.debug('Creating malaprop proxy flow') 868 | flow = TcpFlow(self.flow.rpr + 'proxy_' + proxy.hostname, clifd, srvfd) 869 | parseFlow(self.report, flow) 870 | logger.debug('Just handled inner stream: %r', flow) 871 | proxy.close() 872 | 873 | def handleKeyExchange(self, fd1, fd2): 874 | hello1 = HelloChallenge() 875 | hello2 = HelloChallenge() 876 | fd1r = fd1.read(len(hello1)) 877 | fd2r = fd2.read(len(hello1)) 878 | if (len(fd1r) != len(hello1)) or (len(fd2r) != len(hello2)): 879 | raise RuntimeError('Unable to do key exchange') 880 | hello1.vsParse(fd1r) 881 | hello2.vsParse(fd2r) 882 | logger.debug('Using hello1:\n%s', hd(fd1r)) 883 | logger.debug('Using hello2:\n%s', hd(fd2r)) 884 | if (hello2.magic08 != ror(hello2.magic00, 13)) or (hello2.magic10 != (0xffffffff & (~hello2.magic00))): 885 | raise RuntimeError('Bad hello2') 886 | if (hello1.magic08 != ror(hello1.magic00, 13)) or (hello1.magic10 != (0xffffffff & (~hello1.magic00))): 887 | raise RuntimeError('Bad hello1') 888 | 889 | tlist = [] 890 | for ii in xrange(len(fd1r)): 891 | bb = chr(ord(fd1r[ii]) ^ ord(fd2r[ii]) ^ 0xAA) 892 | tlist.append(bb) 893 | self.cryptoKey = ''.join(tlist[:CRYPTO_KEY_SIZE]) 894 | self.hmacKey = ''.join(tlist[CRYPTO_KEY_SIZE:]) 895 | 896 | logger.debug('Using cryptoKey:\n%s', hd(self.cryptoKey)) 897 | logger.debug('Using hmacKey:\n%s', hd(self.hmacKey)) 898 | 899 | def readMsg(self, fd, dir): 900 | msgLenBuff = fd.read(4) 901 | if len(msgLenBuff) != 4: 902 | logger.debug('Ending now') 903 | return '' 904 | msgLenVal = struct.unpack_from('>24) & 0xff 907 | recvBuff = msgLenBuff + fd.read(msgLen-4) 908 | if len(recvBuff) != msgLen: 909 | raise RuntimeError('Did not receive complete msg') 910 | msgEnv = MsgEnvelope() 911 | msgEnv.vsParse(recvBuff) 912 | dataToHash = recvBuff[0x24:] 913 | #logger.debug('Calculating hmac over data:\n%s', hd(dataToHash)) 914 | mhmac = hmac.new(self.hmacKey, dataToHash, digestmod=hashlib.sha256) 915 | calcHmac = mhmac.digest() 916 | #logger.debug('Calculated hmac:\n%s', hd(calcHmac)) 917 | #logger.debug('Received hmac:\n%s', hd(msgEnv.hmac)) 918 | if calcHmac != msgEnv.hmac: 919 | logUnknown('HMACs differ!') 920 | return '' 921 | #logger.debug('HMACs match') 922 | mungeMsgEnvelope(msgEnv) 923 | #logger.debug('Demunged header:\n%s', msgEnv.tree()) 924 | if msgEnv.cryptoType != PLUGINID.AES128_CFB: 925 | raise RuntimeError('Unexpected crypto id') 926 | if msgEnv.compressType != PLUGINID.ZLIB: 927 | raise RuntimeError('Unexpected compress id') 928 | #logger.debug('MsgEnvelope after munging:\n%s', msgEnv.tree()) 929 | evp = M2Crypto.EVP.Cipher(alg='aes_128_cfb', key=self.cryptoKey, iv=msgEnv.iv, op=0, padding=1) 930 | bytez1 = evp.update(recvBuff[len(msgEnv):]) 931 | #logger.debug('Decrypted data:\n%s', hd(bytez1)) 932 | 933 | bytez2 = zlib.decompress(bytez1) 934 | #logger.debug('Decompressed data:\n%s', hd(bytez2)) 935 | crc = struct.unpack_from(' len(msg)): 1091 | print('Warning: breaking early due to missing queryplugins data') 1092 | break 1093 | off = item.vsParse(msg, off) 1094 | #print('%s:%4s:%11s:%s' % (item.guid, item.version, PLUGIN_TYPES.vsReverseMapping(item.plugtype), item.name)) 1095 | #print('%08x:%4s:%11s:%s' % (item.plugId, item.version, PLUGIN_TYPES.vsReverseMapping(item.plugtype), PLUGINID.vsReverseMapping(item.plugId))) 1096 | info = { 1097 | 'id' : '%08x' % item.plugId, 1098 | 'type' : struct.pack(' %d mapping', chead.msgId, chead.extendedStatus) 1115 | self.proxyMap[chead.msgId] = chead.extendedStatus 1116 | psess = self.getProxySession(chead.extendedStatus) 1117 | else: 1118 | logUnknown('Bad PipeConnect resp') 1119 | 1120 | def do_I2S_PROXY_QUERY_CONNECTIONS(self, dir, msg, chead): 1121 | #logUnknown('do_I2S_PROXY_QUERY_CONNECTIONS', hex=msg) 1122 | if chead.status != 0: 1123 | logUnknown('proxy query connections failed :(') 1124 | return 1125 | off = len(chead) 1126 | connCount = chead.extendedStatus 1127 | item = ProxyQueryItem() 1128 | pipeItem = ProxyQueryItemPipe() 1129 | tcpItem = ProxyQueryItemTcp() 1130 | ret = [] 1131 | for i in range(connCount): 1132 | info = { 1133 | 'index' : item.index, 1134 | 'type' : item.type, 1135 | } 1136 | off = item.vsParse(msg, off) 1137 | if item.type == PLUGINID.COMMS_TCP: 1138 | off = tcpItem.vsParse(msg, off) 1139 | info['hostanme'] = tcpItem.hostname 1140 | info['port'] = tcpItem.port 1141 | elif item.type == PLUGINID.COMMS_NAMED_PIPE: 1142 | off = pipeItem.vsParse(msg, off) 1143 | info['hostanme'] = pipeItem.hostname 1144 | info['pipe'] = pipeItem.pipename 1145 | else: 1146 | raise RuntimeError('Unexpected proxy type') 1147 | ret.append(info) 1148 | self.report.addEvent('query_proxy', ret) 1149 | 1150 | 1151 | def do_I2S_PROXY_TCPCONNECT(self, dir, msg, chead): 1152 | #logUnknown('do_I2S_PROXY_TCPCONNECT', hex=msg) 1153 | if chead.status == 0: 1154 | logger.debug('do_I2S_PROXY_TCPCONNECT: adding %d -> %d mapping', chead.msgId, chead.extendedStatus) 1155 | self.proxyMap[chead.msgId] = chead.extendedStatus 1156 | psess = self.getProxySession(chead.extendedStatus) 1157 | else: 1158 | logUnknown('Bad TCPConnect resp') 1159 | 1160 | def do_I2S_SHELL_ACTIVATE(self, dir, msg, chead): 1161 | #logUnknown('do_I2S_SHELL_ACTIVATE', hex=msg) 1162 | pass 1163 | 1164 | def do_I2S_SHELL_DEACTIVATE(self, dir, msg, chead): 1165 | #logUnknown('do_I2S_SHELL_DEACTIVATE', hex=msg) 1166 | pass 1167 | 1168 | def do_I2S_SHELL_SHELLIN(self, dir, msg, chead): 1169 | #logUnknown('do_I2S_SHELL_SHELLIN', hex=msg) 1170 | pass 1171 | 1172 | def do_I2S_SHELL_SHELLOUT(self, dir, msg, chead): 1173 | #logUnknown('do_I2S_SHELL_SHELLOUT', hex=msg) 1174 | self.report.addEvent('shell_out', msg[len(chead):]) 1175 | 1176 | def do_S2I_FILES_DIR_LIST(self, dir, msg, chead): 1177 | #logUnknown('do_S2I_FILES_DIR_LIST', hex=msg) 1178 | pass 1179 | 1180 | def do_S2I_FILES_DRIVE_LIST(self, dir, msg, chead): 1181 | #logUnknown('do_S2I_FILES_DRIVE_LIST', hex=msg) 1182 | pass 1183 | 1184 | def do_S2I_LATERAL_ACTIVATE(self, dir, msg, chead): 1185 | #logUnknown('do_S2I_LATERAL_ACTIVATE', hex=msg) 1186 | latcmd = LateraralConnectCmd() 1187 | latcmd.vsParse(msg, len(chead)) 1188 | info = { 1189 | 'hostname' : latcmd.hostname, 1190 | 'username' : latcmd.username, 1191 | 'password' : latcmd.password, 1192 | } 1193 | self.report.addEvent('lateral_activate', info) 1194 | 1195 | def do_S2I_LATERAL_DEACTIVATE(self, dir, msg, chead): 1196 | #logUnknown('do_S2I_LATERAL_DEACTIVATE', hex=msg) 1197 | self.report.addEvent('lateral_deactiveate', None) 1198 | 1199 | def do_S2I_LATERAL_INSTALL(self, dir, msg, chead): 1200 | #logUnknown('do_S2I_LATERAL_INSTALL', hex=msg) 1201 | lcmd = LateraralInstallCmd() 1202 | lcmd.vsParse(msg, len(chead)) 1203 | info = { 1204 | 'hostname' : lcmd.hostname, 1205 | 'service' : lcmd.service, 1206 | 'filename' : lcmd.filename, 1207 | 'args' : lcmd.args, 1208 | } 1209 | self.report.addEvent('lateral_install', info) 1210 | decConfig = decodeConfig(lcmd.encodedConfig[0x20:], lcmd.encodedConfig[:0x20]) 1211 | malconf = MalapropConfig() 1212 | malconf.vsParse(decConfig) 1213 | #pdb.set_trace() 1214 | info = { 1215 | 'commstype' : malconf.commsType, 1216 | 'port' : malconf.port, 1217 | 'hostname' : malconf.hostname, 1218 | 'password' : malconf.password, 1219 | 'pipename' : malconf.pipename, 1220 | 'memo': malconf.memo, 1221 | 'mutex' : malconf.mutex, 1222 | 'servicename' :malconf.servicename, 1223 | } 1224 | self.report.addEvent('lateral_config', info) 1225 | 1226 | def do_S2I_MAINC2_AUTHENTICATE(self, dir, msg, chead): 1227 | #logUnknown('do_S2I_MAINC2_AUTHENTICATE', hex=msg) 1228 | password = v_zwstr() 1229 | password.vsParse(msg, len(chead)) 1230 | self.report.addEvent('authenticate', dict(password=str(password))) 1231 | 1232 | def do_S2I_MAINC2_EXIT(self, dir, msg, chead): 1233 | #logUnknown('do_S2I_MAINC2_EXIT', hex=msg) 1234 | self.report.addEvent('exit', None) 1235 | 1236 | def do_S2I_MAINC2_HOSTINFO(self, dir, msg, chead): 1237 | #logUnknown('do_S2I_MAINC2_HOSTINFO', hex=msg) 1238 | pass 1239 | 1240 | def do_S2I_MAINC2_PING(self, dir, msg, chead): 1241 | #logUnknown('do_S2I_MAINC2_PING', hex=msg) 1242 | pass 1243 | 1244 | def do_S2I_MAINC2_QUERYPLUGINS(self, dir, msg, chead): 1245 | #logUnknown('do_S2I_MAINC2_QUERYPLUGINS', hex=msg) 1246 | pass 1247 | 1248 | def do_S2I_PROXY_DATA(self, dir, msg, chead): 1249 | #logUnknown('do_S2I_PROXY_DATA', hex=msg) 1250 | psess = self.getProxySession(chead.extendedStatus) 1251 | psess.addData(dir, msg[len(chead):]) 1252 | 1253 | def do_S2I_PROXY_PIPECONNECT(self, dir, msg, chead): 1254 | #logUnknown('do_S2I_PROXY_PIPECONNECT', hex=msg) 1255 | tcpconn = PipeConnectCmd() 1256 | tcpconn.vsParse(msg, len(chead)) 1257 | connId = self.proxyMap.get(chead.msgId) 1258 | if connId is None: 1259 | raise RuntimeError('Bad msgid->connid map') 1260 | psess = self.getProxySession(connId) 1261 | psess.setHost(tcpconn.hostname, 445) 1262 | 1263 | def do_S2I_PROXY_QUERY_CONNECTIONS(self, dir, msg, chead): 1264 | #logUnknown('do_S2I_PROXY_QUERY_CONNECTIONS', hex=msg) 1265 | pass 1266 | 1267 | def do_S2I_PROXY_TCPCONNECT(self, dir, msg, chead): 1268 | #logUnknown('do_S2I_PROXY_TCPCONNECT', hex=msg) 1269 | tcpconn = TcpConnect() 1270 | tcpconn.vsParse(msg, len(chead)) 1271 | connId = self.proxyMap.get(chead.msgId) 1272 | if connId is None: 1273 | raise RuntimeError('Bad msgid->connid map') 1274 | psess = self.getProxySession(connId) 1275 | psess.setHost(tcpconn.hostname, tcpconn.port) 1276 | 1277 | def do_S2I_SHELL_ACTIVATE(self, dir, msg, chead): 1278 | #logUnknown('do_S2I_SHELL_ACTIVATE', hex=msg) 1279 | pass 1280 | 1281 | def do_S2I_SHELL_DEACTIVATE(self, dir, msg, chead): 1282 | #logUnknown('do_S2I_SHELL_DEACTIVATE', hex=msg) 1283 | self.report.addEvent('shell_deactiveate', None) 1284 | 1285 | def do_S2I_SHELL_SHELLIN(self, dir, msg, chead): 1286 | #logUnknown('do_S2I_SHELL_SHELLIN', hex=msg) 1287 | shelldata = msg[len(chead):].replace('\x00', '').strip() 1288 | self.report.addEvent('shell_in', shelldata) 1289 | 1290 | def do_I2S_FILES_FILE_PUT(self, dir, msg, chead): 1291 | #logUnknown('do_I2S_FILES_FILE_PUT', hex=msg) 1292 | pass 1293 | 1294 | def do_I2S_FILES_FILE_PUT_DATA(self, dir, msg, chead): 1295 | #logUnknown('do_I2S_FILES_FILE_PUT_DATA', hex=msg) 1296 | pass 1297 | 1298 | def do_I2S_FILES_FILE_PUT_DONE(self, dir, msg, chead): 1299 | #logUnknown('do_I2S_FILES_FILE_PUT_DONE', hex=msg) 1300 | pass 1301 | 1302 | def do_S2I_FILES_FILE_PUT(self, dir, msg, chead): 1303 | #logUnknown('do_S2I_FILES_FILE_PUT', hex=msg) 1304 | fput = FilePutCmd() 1305 | fput.vsParse(msg, len(chead)) 1306 | if self.currXfer is not None: 1307 | raise RuntimeError('Unexpected dual file xfer') 1308 | self.currXfer = FileTransfer(SERVER_TO_IMPLANT, fput.fileid, fput.filesize, fput.filepath) 1309 | logger.debug('Creating file put: %s', fput.filepath) 1310 | 1311 | def do_S2I_FILES_FILE_PUT_DATA(self, dir, msg, chead): 1312 | #logUnknown('do_S2I_FILES_FILE_PUT_DATA', hex=msg) 1313 | fpdata = FilePutDataCmd() 1314 | fpdata.vsParse(msg, len(chead)) 1315 | if self.currXfer is None: 1316 | raise RuntimeError('Unexpected missing xfer') 1317 | self.currXfer.addData(fpdata.bytes, fpdata.fileoff) 1318 | if self.currXfer.isComplete(): 1319 | self.currXfer.fd.seek(0) 1320 | md = self.report.addFileFd(self.currXfer.fd, self.currXfer.remotePath) 1321 | self.report.addEvent('file_put', dict(filepath=self.currXfer.remotePath, md5sum=md)) 1322 | self.currXfer.close() 1323 | self.currXfer = None 1324 | else: 1325 | logger.debug('CurrXfer not yet complete: 0x%x of 0x%x', self.currXfer.fd.tell(), self.currXfer.filesize) 1326 | 1327 | def do_S2I_FILES_FILE_PUT_DONE(self, dir, msg, chead): 1328 | #logUnknown('do_S2I_FILES_FILE_PUT_DONE', hex=msg) 1329 | if self.currXfer is not None: 1330 | #raise RuntimeError('Unexpected missing xfer') 1331 | self.currXfer.fd.seek(0) 1332 | md = self.report.addFileFd(self.currXfer.fd, self.currXfer.remotePath) 1333 | self.report.addEvent('file_put', dict(filepath=self.currXfer.remotePath, md5sum=md)) 1334 | self.currXfer.close() 1335 | self.currXfer = None 1336 | 1337 | def do_S2I_FTP_EXFIL_ACTIVATE(self, dir, msg, chead): 1338 | #logUnknown('do_S2I_FTP_EXFIL_ACTIVATE', hex=msg) 1339 | fcmd = FtpActivateCmd() 1340 | fcmd.vsParse(msg, len(chead)) 1341 | info = { 1342 | 'hostname' :fcmd.hostname, 1343 | 'username' :fcmd.username, 1344 | 'password' :fcmd.password, 1345 | 'port' :fcmd.port, 1346 | } 1347 | self.report.addEvent('ftp_activate', info) 1348 | 1349 | def do_S2I_FTP_EXFIL_DEACTIVATE(self, dir, msg, chead): 1350 | #logUnknown('do_S2I_FTP_EXFIL_DEACTIVATE', hex=msg) 1351 | pass 1352 | 1353 | def do_S2I_FTP_EXFIL_UPLOAD(self, dir, msg, chead): 1354 | #logUnknown('do_S2I_FTP_EXFIL_UPLOAD', hex=msg) 1355 | fcmd = FtpUploadCmd() 1356 | fcmd.vsParse(msg, len(chead)) 1357 | info = { 1358 | 'localpath' :fcmd.localpath, 1359 | 'remotepath' :fcmd.remotepath, 1360 | } 1361 | self.report.addEvent('ftp_upload', info) 1362 | 1363 | ################################################################################ 1364 | 1365 | 1366 | g_flowNameRe = re.compile(r"(\d{3}\.\d{3}\.\d{3}\.\d{3})\.(\d{5})-(\d{3}\.\d{3}\.\d{3}\.\d{3})\.(\d{5})") 1367 | g_serverPorts = set(['00080', '09443', '00445']) 1368 | 1369 | 1370 | ################################################################################ 1371 | def isMalapropTaste(taste): 1372 | if len(taste) < 0x20: 1373 | return False 1374 | hello = HelloChallenge() 1375 | hello.vsParse(taste) 1376 | if (hello.magic08 == ror(hello.magic00, 13)) and (hello.magic10 == (0xffffffff & (~hello.magic00))): 1377 | return True 1378 | return False 1379 | 1380 | 1381 | ################################################################################ 1382 | def decodeConfig(clearConfig, xorArray): 1383 | ret = [] 1384 | for ii, bb in enumerate(clearConfig): 1385 | cc = ret.append( chr( (0xff & ((ord(bb) ^ ord(xorArray[ii % len(xorArray)])) + ii)) ) ) 1386 | return ''.join(ret) 1387 | 1388 | ################################################################################ 1389 | def parseDns(report, packets): 1390 | parser = MalapropDnsParser(report, packets) 1391 | parser.parse() 1392 | 1393 | ################################################################################ 1394 | def generateHttpReqs(fd): 1395 | # first the headers 1396 | while True: 1397 | req = {} 1398 | line = fd.readline() 1399 | if len(line) == 0: 1400 | return 1401 | #logUnknown('HTTP path line:', hex=line) 1402 | if line.startswith('GET'): 1403 | #http request 1404 | req['urlpath'] = line.split()[1] 1405 | elif line.startswith('HTTP/1') and ('200' in line): 1406 | #http response 1407 | _, rcode, msg = line.split(' ', 2) 1408 | req['rcode'] = int(rcode) 1409 | req['status'] = msg 1410 | else: 1411 | raise RuntimeError('Only GET or 200-OK supported') 1412 | # now read all the headers 1413 | while True: 1414 | line = fd.readline().strip() 1415 | #logUnknown('Handle http header', hex=line) 1416 | if len(line) == 0: 1417 | break 1418 | hname, hval = line.split(':', 1) 1419 | hname = hname.lower() 1420 | req[hname] = hval 1421 | # now read the body (if any) 1422 | clen = req.get('content-length') 1423 | if clen is None: 1424 | req['body'] = '' 1425 | else: 1426 | req['body'] = fd.read(int(clen)) 1427 | #logger.debug('Yielding req:\n%s', pprint.pformat(req)) 1428 | yield req 1429 | 1430 | def parseSimpleHttp(report, flow): 1431 | logger.debug('Doing I2S http generate') 1432 | reqs = [req for req in generateHttpReqs(flow.fds[IMPLANT_TO_SERVER])] 1433 | logger.debug('Doing S2I http generate') 1434 | resps = [resp for resp in generateHttpReqs(flow.fds[SERVER_TO_IMPLANT])] 1435 | if len(reqs) != len(resps): 1436 | raise RuntimeError('Unmatched http reqs/resps') 1437 | for req, resp in zip(reqs, resps): 1438 | md = report.addFileBytes(resp['body'], req['urlpath']) 1439 | logger.debug('Added HTTP body: %s %s', md, req['urlpath']) 1440 | 1441 | 1442 | ################################################################################ 1443 | 1444 | def parseFlow(report, flow): 1445 | logger.debug('Parsing flow %r', flow) 1446 | #if flow.tastes[IMPLANT_TO_SERVER].startswith('GET /secondstage'): 1447 | # return handleHttpFlow(report, flow) 1448 | #elif flow.tastes[IMPLANT_TO_SERVER].startswith('2017'): 1449 | # return handleBinaryFlow(report, flow) 1450 | #else: 1451 | if 'wiki.flare.fireeye.com' in repr(flow): 1452 | parseSimpleHttp(report, flow) 1453 | return 1454 | if '00021' in repr(flow) and (flow.tastes[IMPLANT_TO_SERVER].startswith('USER')): 1455 | logger.debug('Ignoring FTP control channel') 1456 | #logUnknown('Handle ftp control: I2S\n%s', flow.tastes[IMPLANT_TO_SERVER]) 1457 | #logUnknown('Handle ftp control: S2I\n%s', flow.tastes[SERVER_TO_IMPLANT]) 1458 | return 1459 | if '49162' in repr(flow) and ('SMB' in flow.tastes[IMPLANT_TO_SERVER][0:0x10]): 1460 | parser = MalapropParser(flow, report) 1461 | parser.parseSmb() 1462 | return 1463 | if '00445' in repr(flow) and ('SMB' in flow.tastes[IMPLANT_TO_SERVER][0:0x10]): 1464 | logger.info('Skipping SMB flow with lateral spreading') 1465 | return 1466 | if '09443' in repr(flow): 1467 | parser = MalapropParser(flow, report) 1468 | parser.parseBinary() 1469 | return 1470 | if '00443' in repr(flow) and ('github.com' in flow.tastes[IMPLANT_TO_SERVER][:0x200]): 1471 | logger.debug('Skipping github TLS traffic') 1472 | return 1473 | if '54733' in repr(flow) and flow.tastes[IMPLANT_TO_SERVER].startswith('cryptar'): 1474 | #ftp data channel. just store the data 1475 | report.addFileFd(flow.fds[IMPLANT_TO_SERVER], 'level9.crypt') 1476 | return 1477 | logUnknown('Unknown stream: %r\n%s', flow, hd(flow.tastes[IMPLANT_TO_SERVER])) 1478 | 1479 | ################################################################################ 1480 | 1481 | def loadFlowsFromDir(flowDir): 1482 | loadedNames = set() 1483 | ret = [] 1484 | for fname in os.listdir(flowDir): 1485 | isClient = False 1486 | if not os.path.isfile(os.path.join(flowDir, fname)): 1487 | #logger.debug('Skipping non-file name: %s', fname) 1488 | continue 1489 | m1 = g_flowNameRe.match(fname) 1490 | if m1 is None: 1491 | #logger.debug('Skipping non-flow name: %s', fname) 1492 | continue 1493 | if fname in loadedNames: 1494 | continue 1495 | #construct expected other side of the flow 1496 | otherSide = '%s.%s-%s.%s' % (m1.group(3), m1.group(4), m1.group(1), m1.group(2)) 1497 | if not os.path.isfile(os.path.join(flowDir, otherSide)): 1498 | logUnknown('Assuming empty side of flow due to missing file: %s', otherSide) 1499 | # touch it to stop getting these errors 1500 | with file(os.path.join(flowDir, otherSide), 'wb') as ofile: 1501 | logger.info('Created empty file: %s', otherSide) 1502 | pass 1503 | loadedNames.add(fname) 1504 | loadedNames.add(otherSide) 1505 | isClient = m1.group(4) in g_serverPorts 1506 | if isClient: 1507 | i2sfd = file(os.path.join(flowDir, fname), 'rb') 1508 | s2ifd = file(os.path.join(flowDir, otherSide), 'rb') 1509 | flow = TcpFlow(fname, i2sfd, s2ifd) 1510 | ret.append(flow) 1511 | else: 1512 | s2ifd = file(os.path.join(flowDir, fname), 'rb') 1513 | i2sfd = file(os.path.join(flowDir, otherSide), 'rb') 1514 | flow = TcpFlow(otherSide, i2sfd, s2ifd) 1515 | ret.append(flow) 1516 | return ret 1517 | 1518 | ################################################################################ 1519 | def mungeMsgEnvelope(msgEnv): 1520 | #does the crappy xor of the header values 1521 | msgEnv.cryptoType = msgEnv.cryptoType ^ ord(msgEnv.iv[0]) 1522 | msgEnv.compressType = msgEnv.compressType ^ ord(msgEnv.iv[1]) 1523 | msgEnv.decompressLen = msgEnv.decompressLen ^ struct.unpack_from(' 3): 1552 | printUsage(sys.argv) 1553 | sys.exit(-1) 1554 | if len(sys.argv) > 1: 1555 | if os.path.exists(sys.argv[1]): 1556 | pcapPath = sys.argv[1] 1557 | else: 1558 | print('Bad pcap path') 1559 | printUsage(sys.argv) 1560 | sys.exit(-1) 1561 | if len(sys.argv) > 2: 1562 | if os.path.exists(sys.argv[2]): 1563 | flowPath = sys.argv[2] 1564 | else: 1565 | print('Bad flow path') 1566 | printUsage(sys.argv) 1567 | sys.exit(-1) 1568 | return 'dumpdir', pcapPath, flowPath 1569 | 1570 | def postProcess(report): 1571 | # now process the captured 1572 | md, name, bytez = report.getFileBytes('81ce35acb25c57257e0517ff0f379e8c') 1573 | if bytez is None: 1574 | logUnknown('Failed to post-process level9.crypt') 1575 | return 1576 | fd = cStringIO.StringIO(bytez) 1577 | decodeCryptarFile(report, fd) 1578 | fd.close() 1579 | 1580 | g_fileId = 'cryptar' 1581 | g_KeyData = { 1582 | "20180620" : 'UpIvmFvBUO/TfX3zoxcUkaldhBcxQMw0kpt+TYnwbWE=', 1583 | "20180621" : '2ffrzaquK+qmjepgNHu4EDnEjWGoxHetzwWn5fpV95c=', 1584 | "20180622" : "DKT8egTsPvSYoTvX+YGASr5yIyF9FuvZGywM0Agqc4k=", 1585 | "20180623" : "PAY3qjDtPfGDO88HhDSbl/PeeldDZ2nru1WoXHizKCY=", 1586 | "20180624" : "2ENxx3/8Pv+D4MyKwtJq8hEfOJjJtDCvVRvzT+V/Re0=", 1587 | "20180625" : "ubGLVkgjy1Z1V7O+Pw/RisF0ORQEtOXk9LvNQ/qLykI=", 1588 | "20180626" : "zJNUHUMYXgz8MxJ4QOuiC2A4EzAsq2JSXhe6t8JKmrA=", 1589 | "20180627" : "0uShUJvJ05kzT6ZEDaA4ZL+f56W0Z+60V1OCiKqb+UU=", 1590 | "20180628" : "gLp9tetjUsS/+GhmQZUOAFs2pL4Z9XiGDrgGQS/OoCY=", 1591 | "20180629" : "j1ybV7tCWNCOk5j9cble3wJmkpYJcyCrIyGlge3NJF0=", 1592 | "20180630" : "7EWkzKFD0mPrT0h0CPh6pVlEIGPO9xE2KqpRmLpNyHk=", 1593 | "20180701" : "UzLB6+CXfnUN2vZ7VMWlvFl1ic6fA1k2y5P5xVU+hjs=", 1594 | "20180702" : "a9ylQu0YDOP2ZQXgaaX61ER2gFsi89hrLQtxsEVT/sY=", 1595 | "20180703" : "AfMAC+wK5yeZwfM6r+3pIi5/9/RucZC8OHxqNzFJFE4=", 1596 | "20180704" : "P3TxMLTJ125QTclJjI4SSwzwTq5uVKB3Qla+dgxNvw0=", 1597 | "20180705" : "b4Z9ESNOuV3iVvnPDpGXhY0BxUm2FCLO/OEgsDsAJ2A=", 1598 | "20180706" : "ODSZG3zKCxGBbyGF08LcgfLPKy2EW5ltBtXY4i+FbFA=", 1599 | "20180707" : "55xG79FawZZySbuQapc7oVwdZo3A3aqmFmTmsffkYRI=", 1600 | "20180708" : "8IognEg//IUf3eijw2EKELYyMt0e/qk/hnAZZaP6s+8=", 1601 | "20180709" : "9WLpWLD9jd6cPUGZjqg6mhZPKWVOHL4+JSG/LN7uQU0=", 1602 | "20180710" : "6y2uJpyGLusOE4KyLLU9pe9lPWnTRdviURxFRi+wsP4=", 1603 | "20180711" : "JG5fEdVoqYZTTGt5lG35w+mtCX2vkFw3T707PrdR/80=", 1604 | "20180712" : "/F0izUakg4DSmJNpqFIExmgd4WTfC6d2sWxUxexH4QQ=", 1605 | "20180713" : "C4jSOcsslM5aX5aS61I2pecg4ogxA9DxktY8qjwVUyk=", 1606 | "20180714" : "L6gRiWQZhTDiipZYHrXjDYr+3GQO0KnTum7DMdBMcVA=", 1607 | "20180715" : "25CDpP6o73PWKdjDnWmQaypfH0UDHCNaaW8BzhdAIYI=", 1608 | "20180716" : "gj9CJC9oCYoYfNtc8HW1KtPMXvf9VPqMBFUS/qG0Lh4=", 1609 | "20180717" : "2DdyZE9tnvLqd3HZqSCKvjLe40H03OQt+OxxZ+6Sr6w=", 1610 | "20180718" : "XSTxORuqjZLcrEkA7k/uYo7xRU3fqrsy0T54ItKvdnE=", 1611 | "20180719" : "wm6gjVIzV0BrBdpMyCwrizlOYFjQBg638gr7UVgIE5Q=", 1612 | "20180720" : "oXB4QI9v86d77H2MKlxv8pVy8kna+il06fLiOvkpDjs=", 1613 | "20180721" : "tA9WuqydPg5rEw6bYA8T19bB5iBpsTGP16JgwtHxeNA=", 1614 | "20180722" : "DAuYxIYEMt6mDx/CGSPIp0okf/95KKVQWxiRogg295E=", 1615 | "20180723" : "YrUxgis2iBlRsFUSFHhWbZHgITN1D9N70arBt6wEiSE=", 1616 | "20180724" : "vVhN+Xangc4K+zJgkD1drBnWz8TA5a2B5ozP1sfh77s=", 1617 | "20180725" : "cHtwHsS4sI5r6y05j1W+vKPFEVuGcgdTd1TAy+ssR4g=", 1618 | "20180726" : "uiFtHOKOUho/Z643yEpGRRmgZ9BqfgPor3hviSFo8m8=", 1619 | "20180727" : "VnoGdNrCHls/q2D7yZ1hWMUH+/EprIxv+uuFTAdeAys=", 1620 | "20180728" : "guqonylNB4SiNEWXSd39sZO7RcexPArGSJtI0hQQjdw=", 1621 | "20180729" : "0QXIF/aKuMkoEpgFmYcZss0bxIN3zpzCGuWto+q+edw=", 1622 | "20180730" : "YH6VNUykJ0UzQSPlmC2V5/oBN40ku+3tFaoIByZS+ls=", 1623 | "20180731" : "s0T6cMfagWwshMPCuujREhhFFJXevsn3fDpRHUypLRM=", 1624 | "20180801" : "AFDC1Evq0+9WyAJE7C++fPOv09ELKSNynf+DGJAgfYY=", 1625 | "20180802" : "9pJ6I0pqoi3NRZepkKrWE2iQghsYjI+0IpdlOZVxO9I=", 1626 | "20180803" : "dZxm/xJd2rbWhs51J8La70+0Ry+5BvtOC1BthZ4o9Fs=", 1627 | "20180804" : "27wgWiPQ5ArJG+atqHYe5OgM9bxqjal5FGSb90yitz0=", 1628 | "20180805" : "T0UKU8d/yWfrMO9FdBbv8Uh4+GUl2biVuaTDj6zF+/k=", 1629 | "20180806" : "/wSJM2ruefYRFuIDpdBGDocQii7htFSh9wNUkgB+pMI=", 1630 | "20180807" : "odbz8AGA/1W56ath1WQRWUZ408NxmHNYVaCJe6fZLsc=", 1631 | "20180808" : "0ia/UPCsI01mFyTTbSVvOmno5NqjBgmEtVNGUV/uTVk=", 1632 | "20180809" : "YfX7ygEifLPd185HRo+Dp2O6e6Ode0D7KVJLblTygaY=", 1633 | "20180810" : "YFaxYE39D6Ko6MDe6VuyIB006rlsxqgVEQW81PwRMQo=", 1634 | "20180811" : "OMgvGqUK13X7LXjWnnh2FXdD0J2bKA/A+UaskCDMqao=", 1635 | "20180812" : "9RoyOCS0Ei0LzBw98n4A4YcaGkxWiW97zP+XvYfnTx8=", 1636 | "20180813" : "V05nC/ILP6kIf9ctNqoEWetJeeB9OcBjROpmwKUdS3Y=", 1637 | "20180814" : "3kE8+862EmeRFzIofOcaKm01lXlw2io8gyz8RhAlC4k=", 1638 | "20180815" : "4sbd/8TuqsgOP4Mehuoed6jOkXttPh0RXuRCqwMOXMw=", 1639 | "20180816" : "CkQGarc+Mve/bOT03DfpwNfY4fwlTo1YruvX4EkgECU=", 1640 | "20180817" : "j9KuzswQeHA93BpXTRKNNjU2hua1xJkOuacAlc2UaNc=", 1641 | "20180818" : "xx3hTbMw4Xl4QVgKacCsKvXwgfOG8NHtX9bG8p4Zk+Y=", 1642 | "20180819" : "aivNYPGTOzIZWPRp8xnnjDrUa0AOQTiBGHmOpCeoMFg=", 1643 | "20180820" : "kIe3obq7Ki5N6QPJyQj/YrQxK4CI5AUwUigpDcQR6hw=", 1644 | "20180821" : "nvaTJE2E58nU6fl8OGm9E61oBg5gFjZEo+CLBc49SFA=", 1645 | "20180822" : "mnahdz0c5dR/IRbbrzTY5Q/VSzvWJhlpaDHr2XGaOUI=", 1646 | "20180823" : "ZsC4G8T4AvGnXCwJRMNVa4FfHhmBMOfkIZBg0w2352g=", 1647 | "20180824" : "CD6545mUCJAjppHxeV/vrHG8FndIeq2I40NeiWdJoUo=", 1648 | "20180825" : "WjghDDhD7ZGIBLhP4T1PwuLBE6anx3LHLk15Fz9XS+A=", 1649 | "20180826" : "ugXMLlqLSJH3QegcNJ+J2Nqw5xeqwA0j17PjfX/UM9k=", 1650 | "20180827" : "hXJTgQOcQJA+E81GzykpdMEpH5Q4x6t6owRvX+74DJY=", 1651 | "20180828" : "hh2nfEdY0eE8aGk0G9KG/57T0KIVERtWtu7WacWz2yk=", 1652 | "20180829" : "aapFxQk22dgELgvSfIo6xZblFFIbtHMQIZJE+A23cMg=", 1653 | "20180830" : "2KVPxIMx5m+Q6dlJi0hyQoLkBTHpq5n9Osye0uIspIM=", 1654 | "20180831" : "BcUZckt2F9xOQCB+Trd5jeVi9gxwbeGMDr4NjRFYkB8=", 1655 | } 1656 | 1657 | 1658 | 1659 | def getKeyMaterial(istr): 1660 | rbytes = istr.decode('base64') 1661 | return(rbytes[:16], rbytes[16:]) 1662 | 1663 | def _unpad(s): 1664 | return s[:-ord(s[len(s)-1:])] 1665 | 1666 | def decryptFile(fd, key, iv): 1667 | idata = fd.read() 1668 | cipher = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv) 1669 | decData = _unpad(cipher.decrypt(idata)) 1670 | return decData 1671 | 1672 | def processCryptarSubFile(report, ibytes, off): 1673 | logger.debug('Processing file at 0x%08x' % off) 1674 | pathLen = struct.unpack_from('