Advertisement
Guest User

Untitled

a guest
Mar 14th, 2018
7,545
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 37.59 KB | None | 0 0
  1. Rootkit analysis
  2. Use case on HideDRV
  3. Version 1.6
  4. 2016-10-27
  5. Author: Paul Rascagnères
  6.  
  7. HideDRV – Rootkit analysis
  8. 2
  9. INDEX
  10. Preparation ........................................................................................... 5
  11. Driver signature enforcement & Patch guard........................................................ 6
  12. Global architecture of the driver ...................................................................... 7
  13. File-system mini-filter...................................................................................13
  14. Registry callbacks ........................................................................................21
  15. Process creation callbacks..............................................................................24
  16. Payload injection.........................................................................................25
  17. Chapter abstract..........................................................................................26
  18. Context ..........................................................................................28
  19. Device and Symbolic link ...............................................................................28
  20. SSDT hooks ..........................................................................................30
  21. Chapter abstract..........................................................................................32
  22. HideDRV – Rootkit analysis
  23. 3
  24. DISCLAIMER
  25. The purpose of this article is to provide a first good overview of the kernel mechanisms and how to
  26. handle a rootkit analysis.
  27. This document is issued by Sekoia Cybersecurity and intends to share reverse engineering best practices
  28. in order to help organizations in their security jobs. If you are interested in reverse engineering, malware
  29. analysis, advanced rootkit analysis or Windows internal working, CERT Sekoia can assist you either for
  30. investigations or for trainings.
  31. Contact us at [email protected]
  32. HideDRV – Rootkit analysis
  33. 4
  34. INTRODUCTION
  35. ESET’s experts published a complete and interesting white paper on the toolkit of a well-known APT
  36. actor identified as Sednit (aka APT28, Fancy Bear, Sofacy, STRONTIUM, Tsar Team…). The paper can be
  37. downloaded there. This group seems to be the author of several major media hacks such as the attack of
  38. the German parliament in December 2014 or the compromise of TV5Monde in April 2015. Thanks to a
  39. great collaboration with ESET, CERT Sekoia got access to the rootkit samples in order to perform its own
  40. investigation.
  41. The name of the rootkit discovered by ESET is HIDEDRV. This name was chosen by the developer and is
  42. present in several comments in the driver file (FsFlt.sys). CERT Sekoia frequently deals with malware and
  43. rootkits analysis. Sometimes, several people ask us for tricks for kernel analysis and debugging. After a
  44. quick overview of the drivers, we found out that this sample was a perfect case study for beginners.
  45. Therefore, we decided to publish a deep analysis on this rootkit. People interested in discovering and
  46. practising Windows kernel analysis and rootkit debugging might like it. Indeed, this sample is wonderful
  47. for beginners:
  48. - no packer;
  49. - no obfuscation;
  50. - no advanced or undocumented trick;
  51. - really small (< 100 functions);
  52. - all the classical features (such as registries hiding, files hiding, code injection from the kernel).
  53. And there is the icing on the cake: the developers let a lot of comments for helping and guiding the
  54. analyst ;).
  55. This article describes how to deal with rootkit analysis step by step: laboratory setup, Windows kernel
  56. architecture and API, Windows protection, Windows 10 64 bits… The purpose is to provide a tutorial of
  57. the “state of the art” of rootkit analysis on modern x64 Windows systems.
  58. As usual we love feedbacks, so don’t be afraid to contact us!
  59. HideDRV – Rootkit analysis
  60. 5
  61. X64 ROOTKIT ANALYSIS
  62. PREPARATION
  63. This article was created based on the following configuration:
  64. - Host: Windows 10 TH2 – x64
  65. - Guest: Windows 10 TH2 – x64
  66. - Hypervisor: Hyper-V
  67. - Sample hash: 4bfe2216ee63657312af1b2507c8f2bf362fdf1d63c88faba397e880c2e39430
  68. - Sample file name: fsflt.sys
  69. - Debugger: WinDBG x64
  70. - Disassembler: IDA Pro
  71. WinDBG is the Microsoft debugger. It allows userland and kernel debugging on Windows environments.
  72. If you are not familiar with WinDBG, we recommend reading this article first: http://windbg.info/doc/1-
  73. common-cmds.html.
  74. To perform Windows kernel analysis, we need a specific setup of the Virtual Machine and WinDBG in
  75. order to remotely debug the VM from the host. We strongly recommend the following Microsoft
  76. documentation available there: https://msdn.microsoft.com/enus/library/windows/hardware/hh439378(v=vs.85).aspx.
  77. Additionnaly, we need:
  78. - To disable the driver signature enforcement (see next chapter):
  79. Bcdedit.exe -set TESTSIGNING ON
  80. - To create some registry keys in order to load the rootkit on demand (we will see why later):
  81. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt]
  82. "Description"="FsFtl minifilter"
  83. "DependOnService"="FltMgr"
  84. "Group"="FSFilter Content Screener"
  85. "ImagePath"="system32\\Drivers\\fsflt.sys"
  86. "DisplayName"="FsFlt"
  87. "Tag"=dword:00000001
  88. "ErrorControl"=dword:00000001
  89. "Type"=dword:00000002
  90. "Start"=dword:00000003
  91. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Enum]
  92. "NextInstance"=dword:00000001
  93. "Count"=dword:00000001
  94. "0"="Root\\\\LEGACY_minifilter\\\\0000"
  95. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Instances]
  96. "DefaultInstance"="minifilter Instance"
  97. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Instances\minifilter
  98. Instance]
  99. "Flags"=dword:00000000
  100. "Altitude"="262100"
  101. HideDRV – Rootkit analysis
  102. 6
  103. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Parameters]
  104. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Parameters\c1]
  105. @="\\Device\\HarddiskVolume1\\Sekoia\\ApplicationDummy.dll"
  106. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Parameters\c2]
  107. @="\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\services\\FsFlt"
  108. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Parameters\c3]
  109. @="\\Device\\HarddiskVolume1\\Sekoia\\ApplicationDummy.dll"
  110. [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FsFlt\Parameters\c4]
  111. @=\\Device\\HarddiskVolume1\\Sekoia
  112. Note: you must adapt the HarddiskVolume1 to your system and point to the C drive. The directory
  113. C:\Sekoia must exist and must contain an ApplicationDummy.dll file.
  114. DRIVER SIGNATURE ENFORCEMENT & PATCH GUARD
  115.  
  116. Driver signature enforcement
  117. Since Windows 7 x64, Microsoft implemented the driver signature enforcement. When this feature is
  118. enabled, the drivers must be signed by a trusted vendor in order to be loaded by the system. More
  119. information about this topic can be found on the Microsoft web site: https://msdn.microsoft.com/enus/library/windows/hardware/ff544865%28v=vs.85%29.aspx.
  120. This validation is performed by CI.dll
  121. (Code Integrity). The rootkit developers need to sign their rootkits or to bypass the signature control. If
  122. you are interested in the driver signature bypasses, we recommend watching our talk during SyScan360
  123. there: https://www.youtube.com/watch?v=lVEaw5SW2DE. In our case, we didn’t have the dropper of
  124. the driver so we could not identify the signature bypass technique that was used. At this stage, to
  125. perform the analysis, we have to disable the driver signature as explained previously.
  126.  
  127. Patch guard
  128. Since Windows 7 x64, Microsoft implemented the Patch Guard (aka Kernel Patch Protection – KKP). The
  129. purpose of the security feature is to protect the integrity of the kernel’s code and to avoid modification
  130. of sensitive tables such as the System Service Dispatch Table (SSDT), the Interrupt Descriptor Table (IDT)
  131. or Global Descriptor Table (GDT). A lot of rootkits performed inline hooks or table modifications in order
  132. to modify the behaviour of the kernel. If a driver performs this kind of thing with Patch Guard enabled,
  133. the system crashes and the user gets a Blue Screen of Death. Generally, in the 64 bits version of modern
  134. Windows rootkit, the developers use legitimate callback procedures provided by Microsoft (the rootkit
  135. Turla is an exception however where the developers bypassed Patch Guard and performed inline hooks
  136. in the kernel’s code directly).
  137. HideDRV – Rootkit analysis
  138. 7
  139. GLOBAL ARCHITECTURE OF THE DRIVER
  140.  
  141. First steps
  142. WinDBG can be automatically stopped when the driver is loaded (after pressing CTRL+Break to break
  143. the system):
  144. kd> sxe ld FsFlt.sys
  145. Using a previously described setup, you can easily load the driver by starting the FsFlt service:
  146. C:\Windows\System32> sc start FsFlt
  147. The debugger will be automatically stopped at the driver loading:
  148. kd> !lmi FsFlt
  149. Loaded Module Info: [fsflt]
  150. Module: fsflt
  151. Base Address: fffff80198af0000
  152. Image Name: fsflt.sys
  153. Machine Type: 34404 (X64)
  154. Time Stamp: 5305a705 Thu Feb 20 07:56:05 2014
  155. Size: d000
  156. CheckSum: b745
  157. Characteristics: 22
  158. Debug Data Dirs: Type Size VA Pointer
  159. CODEVIEW 58, 82f8, 72f8 RSDS - GUID: {5DBF95F0-1907-435C-996B1A16AE85070C}
  160. Age: 1e, Pdb:
  161. d:\!work\etc\hideinstaller_kis2013\Bin\Debug\win7\x64\fsflt.pdb
  162. Symbol Type: DEFERRED - No error - symbol load deferred
  163. Load Report: no symbols loaded
  164. You can notice the name of the .pdb path: kis2013. This could be a reference to Kaspersky Internet
  165. Security 2013. At this point, you can set a breakpoint on the DriverEntry() function of the driver:
  166. HideDRV – Rootkit analysis
  167. 8
  168. kd> bp fsflt+0xb064
  169. kd> g
  170. Breakpoint 0 hit
  171. fsflt+0xb064:
  172. fffff801`98afb064 4883ec28 sub rsp,28h
  173. Note: the default base address in IDA Pro is 0x10000. The base address of the loading module is
  174. 0xfffff800198af0000 as you can see in the !lmi command output (so the offset in argument to the
  175. breakpoint is IDA Pro Address – 0x10000).
  176. Static overview of the rootkit design
  177. As explained, the entry point of the rootkit is at offset 0x1b064 (DriverEntry()). The core of the
  178. driver is located at 0x11010 (Core()). This chapter will focus on the workflow of this Core() function.
  179. Note: If you are lost, do not hesitate to read the debug provided by the developer:
  180. To display the debug, you can use DebugView from SysInternals: https://technet.microsoft.com/enus/sysinternals/bb896647.aspx
  181.  
  182. HideDRV – Rootkit analysis
  183. 9
  184. Step 1
  185. First, (offset 0x11500), the driver gets the registry stored value in
  186. \REGISTRY\MACHINE\SYSTEM\CurrentControlSet\services\FsFlt\Parameters\c4 thanks to the
  187. ZwOpenKey(), ZwQueryKey() and ZwEnumerateKey() APIs. Then the driver checks if the
  188. value is an existing directory with the ZwCreateFile() function with the CreateOptions
  189. argument at “FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NOALERT”:
  190. If the directory does not exist, the driver stops itself. (That's why we created the C:\Sekoia directory).
  191. In kernel space, the most common way to allocate memory is to use ExAllocatePoolWithTag()
  192. function. Here is the prototype:
  193. PVOID ExAllocatePoolWithTag(
  194. _In_ POOL_TYPE PoolType,
  195. _In_ SIZE_T NumberOfBytes,
  196. _In_ ULONG Tag
  197. );
  198. The first argument is the pool type (paged, non-paged, etc.), the second argument is the size of the pool
  199. and the last argument is the tag name. The tag name is a four characters’ value. We identified two
  200. different tag names in the rootkit:
  201. - DCBA
  202. - rbRN
  203. HideDRV – Rootkit analysis
  204. 10
  205. Step 2
  206. The rootkit registers a file-system minifilter. This feature will be described in detail later.
  207. Step 3
  208. The rootkit parses the configuration stored in the registry in order to get the value of the files, the
  209. directories and the registry keys to hide and the path of the library to inject in explorer.exe. The
  210. parsing is performed at offset 0x12e30 and the values are stored in global variables (FilePath,
  211. RegPath and DLLPath in the screenshot):
  212. HideDRV – Rootkit analysis
  213. 11
  214. In each callback (registry/file/directory), these global variables are used to check if the accessed element
  215. returned by the callback matches one of the rules stored in registry. Here is the debug output of the
  216. configuration parsing left by the developer:
  217. HIDEDRV: >>>>>>>>Hide rules>>>>>>>> rules
  218. HIDEDRV: File rules: \Device\HarddiskVolume1\Sekoia\ApplicationDummy.dll
  219. HIDEDRV: Registry rules: \REGISTRY\MACHINE\SYSTEM\CurrentControlSet\services\FsFlt
  220. HIDEDRV: Inject dll: \Device\HarddiskVolume1\Sekoia\ApplicationDummy.dll
  221. HIDEDRV: Folder rules: \Device\HarddiskVolume1\Sekoia
  222. HIDEDRV: <<<<<<<<XXXXX<<<<<<<< rules
  223. HIDEDRV: <<<<<<<<Hide rules<<<<<<<< rules
  224. The driver callbacks use the full volume path, that’s why the content in registry must start by
  225. \Device\HarddiskVolumeX (the kernel view) and not by c:\ (the userland view). The comparison
  226. function is at the offset 0x13360. This function has 2 arguments:
  227. - a string (the name to be checked returned by the callback)
  228. - an integer:
  229. o 1: check if the string matches the file rules global variable;
  230. o 2: check if the string matches the registry rules global variable;
  231. o 3: check if the string matches the folder rules global variable.
  232.  
  233. HideDRV – Rootkit analysis
  234. 12
  235. Step 4
  236. The rootkit starts the file-system minifilter registered previously. This feature will be described in detail
  237. later.
  238. Step 5
  239. The rootkit registers and starts the registry callbacks at offset 0x144a0. This feature will be described in
  240. detail later.
  241. Step 6
  242. The rootkit registers and starts the process creation callbacks at offset 0x15030. This feature will be
  243. described in detail later. However, this function starts with an interesting action:
  244. The rootkit deletes the file C:\Windows\System32\sysprep\CRYPTBASE.dll. We don’t
  245. exactly know why the driver removes this file. However, the library and this path are frequently used to
  246. bypass UAC. You can find in Metasploit the code of this kind of privilege escalation:
  247. https://github.com/rapid7/metasploitframework/blob/master/external/source/exploits/bypassuac/Win7Elevate/Win7Elevate_Inject.cpp.
  248. We
  249. assume that the rootkit removes the trace of a privilege escalation previously realised.
  250. Step 7
  251. Finally, the rootkit defines an event that will be used to perform the library injection in the process
  252. explorer.exe. This features will be described in detail later.
  253. HideDRV – Rootkit analysis
  254. 13
  255. FILE-SYSTEM MINI-FILTER
  256. Static analysis
  257. A file-system mini-filter is basically registered and started using two functions:
  258. - FltRegisterFilter() (MSDN documentation: https://msdn.microsoft.com/enus/library/windows/hardware/ff544305(v=vs.85).aspx)
  259. - FltStartFiltering() (MSDN documentation: https://msdn.microsoft.com/enus/library/windows/hardware/ff544569(v=vs.85).aspx)
  260. From a reverse engineering point of view, the first one in the most interesting, in particular the second
  261. argument (structure) used in the function: FLT_REGISTRATION. Here is the prototype of the
  262. structure:
  263. typedef struct _FLT_REGISTRATION {
  264. USHORT Size;
  265. USHORT Version;
  266. FLT_REGISTRATION_FLAGS Flags;
  267. const FLT_CONTEXT_REGISTRATION *ContextRegistration;
  268. const FLT_OPERATION_REGISTRATION *OperationRegistration;
  269. PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback;
  270. PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback;
  271. PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback;
  272. PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback;
  273. PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback;
  274. PFLT_GENERATE_FILE_NAME GenerateFileNameCallback;
  275. PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback;
  276. PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback;
  277. #if FLT_MGR_LONGHORN
  278. PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback;
  279. PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback;
  280. #endif
  281. #ifdef FLT_MFG_WIN8
  282. PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
  283. #endif
  284. } FLT_REGISTRATION, *PFLT_REGISTRATION;
  285. Here is the view of this structure in IDA Pro:
  286. HideDRV – Rootkit analysis
  287. 14
  288. The 5 callbacks functions do not contain relevant codes. The interesting code in located in the
  289. FLT_OPERATION_REGISTRATION structure. Here is the prototype of the structure:
  290. typedef struct _FLT_OPERATION_REGISTRATION {
  291. UCHAR MajorFunction;
  292. FLT_OPERATION_REGISTRATION_FLAGS Flags;
  293. PFLT_PRE_OPERATION_CALLBACK PreOperation;
  294. PFLT_POST_OPERATION_CALLBACK PostOperation;
  295. PVOID Reserved1;
  296. } FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
  297. The content can also be viewedin IDA Pro:
  298. The PreOperation()function (0x14100) contains the code used to get the file/directory name
  299. returned by the callback in order to compare it with the path stored in the registry. Here is the assembly
  300. code:
  301. HideDRV – Rootkit analysis
  302. 15
  303. If the callback name returned matches a value in registry, the code modifies the contents of the callback
  304. data structure by setting STATUS_NOT_FOUND via the FltSetCallbackDataDirty() API. The
  305. file or the directory will finally be hidden to the user:
  306.  
  307. HideDRV – Rootkit analysis
  308. 16
  309. Overview of the minifilter structure
  310. The Windows kernel frequently uses linked lists. The file system minifilters uses circular doubly linked
  311. list. Here is the definition of this kind of list:
  312. typedef struct _LIST_ENTRY {
  313. struct _LIST_ENTRY *Flink;
  314. struct _LIST_ENTRY *Blink;
  315. } LIST_ENTRY, *PLIST_ENTRY;
  316. The schema of the linked filters can be seen below:
  317.  
  318. HideDRV – Rootkit analysis
  319. 17
  320. Dynamic analysis with WinDBG
  321. Thanks to the previous schema, we can analyse and identify the file-system minifilters with WinDBG. The
  322. fltkd extension allows to list the registered filter (and get the address of the ListHead in red) and
  323. the configuration of these filters:
  324. kd> !fltkd.filters
  325. Filter List: ffffe001b9e730c0 "Frame 0"
  326. FLT_FILTER: ffffe001b9e8ba90 "WdFilter" "328010"
  327. FLT_INSTANCE: ffffe001ba3cd780 "WdFilter Instance" "328010"
  328. FLT_INSTANCE: ffffe001ba47f010 "WdFilter Instance" "328010"
  329. FLT_INSTANCE: ffffe001ba8c8640 "WdFilter Instance" "328010"
  330. FLT_INSTANCE: ffffe001ba982640 "WdFilter Instance" "328010"
  331. FLT_FILTER: ffffe001b9b42010 "FsFlt" "262100"
  332. FLT_INSTANCE: ffffe001ba8366f0 "minifilter Instance" "262100"
  333. FLT_INSTANCE: ffffe001ba8306f0 "minifilter Instance" "262100"
  334. FLT_INSTANCE: ffffe001ba83c6f0 "minifilter Instance" "262100"
  335. FLT_INSTANCE: ffffe001ba8416f0 "minifilter Instance" "262100"
  336. FLT_FILTER: ffffe001b9240320 "storqosflt" "244000"
  337. FLT_FILTER: ffffe001b954a5e0 "FileCrypt" "141100"
  338. FLT_FILTER: ffffe001bb694720 "luafv" "135000"
  339. FLT_INSTANCE: ffffe001bb69b010 "luafv" "135000"
  340. FLT_FILTER: ffffe001ba4b2cb0 "npsvctrig" "46000"
  341. FLT_INSTANCE: ffffe001ba98c710 "npsvctrig" "46000"
  342. FLT_FILTER: ffffe001b9e85010 "FileInfo" "45000"
  343. FLT_INSTANCE: ffffe001ba3cdb40 "FileInfo" "45000"
  344. FLT_INSTANCE: ffffe001ba46e420 "FileInfo" "45000"
  345. FLT_INSTANCE: ffffe001baa45310 "FileInfo" "45000"
  346. FLT_INSTANCE: ffffe001ba721b40 "FileInfo" "45000"
  347. FLT_FILTER: ffffe001b9e88580 "Wof" "40700"
  348. FLT_INSTANCE: ffffe001ba462b90 "Wof Instance" "40700"
  349. FLT_INSTANCE: ffffe001ba73c640 "Wof Instance" "40700"
  350. kd> !fltkd.filter ffffe001b9b42010
  351. FLT_FILTER: ffffe001b9b42010 "FsFlt" "262100"
  352. FLT_OBJECT: ffffe001b9b42010 [02000000] Filter
  353. RundownRef : 0x000000000000000a (5)
  354. PointerCount : 0x00000001
  355. PrimaryLink : [ffffe001b9240330-ffffe001b9e8baa0]
  356. Frame : ffffe001b9e73010 "Frame 0"
  357. Flags : [00000002] FilteringInitiated
  358. HideDRV – Rootkit analysis
  359. 18
  360. DriverObject : ffffe001bc5768d0
  361. FilterLink : [ffffe001b9240330-ffffe001b9e8baa0]
  362. PreVolumeMount : 0000000000000000 (null)
  363. PostVolumeMount : 0000000000000000 (null)
  364. FilterUnload : fffff80198af13f0 fsflt+0x13f0
  365. InstanceSetup : fffff80198af14b0 fsflt+0x14b0
  366. InstanceQueryTeardown : fffff80198af14d0 fsflt+0x14d0
  367. InstanceTeardownStart : fffff80198af14f0 fsflt+0x14f0
  368. InstanceTeardownComplete : fffff80198af14f0 fsflt+0x14f0
  369. ActiveOpens : (ffffe001b9b421c8) mCount=0
  370. Communication Port List : (ffffe001b9b42218) mCount=0
  371. Client Port List : (ffffe001b9b42268) mCount=0
  372. VerifierExtension : 0000000000000000
  373. Operations : ffffe001b9b422c0
  374. OldDriverUnload : 0000000000000000 (null)
  375. SupportedContexts : (ffffe001b9b42140)
  376. VolumeContexts : (ffffe001b9b42140)
  377. InstanceContexts : (ffffe001b9b42140)
  378. FileContexts : (ffffe001b9b42140)
  379. StreamContexts : (ffffe001b9b42140)
  380. StreamHandleContexts : (ffffe001b9b42140)
  381. TransactionContext : (ffffe001b9b42140)
  382. (null) : (ffffe001b9b42140)
  383. InstanceList : (ffffe001b9b42078)
  384. FLT_INSTANCE: ffffe001ba8366f0 "minifilter Instance" "262100"
  385. FLT_INSTANCE: ffffe001ba8306f0 "minifilter Instance" "262100"
  386. FLT_INSTANCE: ffffe001ba83c6f0 "minifilter Instance" "262100"
  387. FLT_INSTANCE: ffffe001ba8416f0 "minifilter Instance" "262100"
  388. We can see few callbacks to the FsFlt driver. Sadly, the fltkd extension hides few elements and we
  389. cannot find the PreOperation() function. We used a trick to get it by parsing the kernel structures
  390. as described in the previous schema:
  391. kd> dt _FLT_FILTER ffffe001b9b42010
  392. FLTMGR!_FLT_FILTER
  393. +0x000 Base : _FLT_OBJECT
  394. +0x030 Frame : 0xffffe001`b9e73010 _FLTP_FRAME
  395. +0x038 Name : _UNICODE_STRING "FsFlt"
  396. +0x048 DefaultAltitude : _UNICODE_STRING "262100"
  397. +0x058 Flags : 2 ( FLTFL_FILTERING_INITIATED )
  398. +0x060 DriverObject : 0xffffe001`bc5768d0 _DRIVER_OBJECT
  399. +0x068 InstanceList : _FLT_RESOURCE_LIST_HEAD
  400. +0x0e8 VerifierExtension : (null)
  401. HideDRV – Rootkit analysis
  402. 19
  403. +0x0f0 VerifiedFiltersLink : _LIST_ENTRY [ 0x00000000`00000000 -
  404. 0x00000000`00000000 ]
  405. +0x100 FilterUnload : 0xfffff801`98af13f0 long +0
  406. +0x108 InstanceSetup : 0xfffff801`98af14b0 long +0
  407. +0x110 InstanceQueryTeardown : 0xfffff801`98af14d0 long +0
  408. +0x118 InstanceTeardownStart : 0xfffff801`98af14f0 void +0
  409. +0x120 InstanceTeardownComplete : 0xfffff801`98af14f0 void +0
  410. +0x128 SupportedContextsListHead : (null)
  411. +0x130 SupportedContexts : [7] (null)
  412. +0x168 PreVolumeMount : (null)
  413. +0x170 PostVolumeMount : (null)
  414. +0x178 GenerateFileName : (null)
  415. +0x180 NormalizeNameComponent : (null)
  416. +0x188 NormalizeNameComponentEx : (null)
  417. +0x190 NormalizeContextCleanup : (null)
  418. +0x198 KtmNotification : (null)
  419. +0x1a0 SectionNotification : (null)
  420. +0x1a8 Operations : 0xffffe001`b9b422c0 _FLT_OPERATION_REGISTRATION
  421. +0x1b0 OldDriverUnload : (null)
  422. +0x1b8 ActiveOpens : _FLT_MUTEX_LIST_HEAD
  423. +0x208 ConnectionList : _FLT_MUTEX_LIST_HEAD
  424. +0x258 PortList : _FLT_MUTEX_LIST_HEAD
  425. +0x2a8 PortLock : _EX_PUSH_LOCK
  426. kd> dt _FLT_OPERATION_REGISTRATION 0xffffe001`b9b422c0
  427. FLTMGR!_FLT_OPERATION_REGISTRATION
  428. +0x000 MajorFunction : 0 ''
  429. +0x004 Flags : 0
  430. +0x008 PreOperation : 0xfffff801`98af4100 _FLT_PREOP_CALLBACK_STATUS +0
  431. +0x010 PostOperation : (null)
  432. +0x018 Reserved1 : (null)
  433. kd> u 0xfffff801`98af4100
  434. fsflt+0x4100:
  435. fffff801`98af4100 4c89442418 mov qword ptr [rsp+18h],r8
  436. fffff801`98af4105 4889542410 mov qword ptr [rsp+10h],rdx
  437. fffff801`98af410a 48894c2408 mov qword ptr [rsp+8],rcx
  438. fffff801`98af410f 4883ec38 sub rsp,38h
  439. fffff801`98af4113 c744242000000000 mov dword ptr [rsp+20h],0
  440. fffff801`98af411b 48c744242800000000 mov qword ptr [rsp+28h],0
  441. fffff801`98af4124 488b442440 mov rax,qword ptr [rsp+40h]
  442. fffff801`98af4129 488b4010 mov rax,qword ptr [rax+10h]
  443. HideDRV – Rootkit analysis
  444. 20
  445. We can confirm that the defined PreOperation() function is at FsFlt+0x4100 as mentioned
  446. previously.
  447. Limitations
  448. The implementation made by the rootkit developer has several limitations. The most interesting is the
  449. fact that files and directories implementing hiding feature is performed using their full volume paths (for
  450. example: \Device\HarddiskVolume1\Sekoia\ApplicationDummy.dll). By changing the
  451. volume name, we bypass the protection and we can access to the hidden artefacts. An easy way to
  452. change the volume name is to create a Shadow Copy of the drive. The volume path pattern of a Shadow
  453. Copy is \Device\HarddiskVolumeShadowCopyX. By mounting it, we are able to access to the
  454. protection files and directories. If you use live forensics tools with Shadow Copy feature to retrieve the
  455. artefacts (such as FastIR Collector: https://github.com/SekoiaLab/Fastir_Collector) the rootkit is simply
  456. inefficient.
  457. HideDRV – Rootkit analysis
  458. 21
  459. REGISTRY CALLBACKS
  460. Static analysis
  461. A registry access callback is basically registered and started with one function:
  462. - CmRegisterCallbackEx() (MSDN documentation: https://msdn.microsoft.com/enus/library/windows/hardware/ff541921(v=vs.85).aspx)
  463.  
  464. From a reverse engineering point of view, the first argument of this function is the most interesting. It
  465. contains the function to be executed by the callback:
  466. The callback function is RegCallBacksFunction() (0x4870). This function checks if the accessed
  467. registry name matches the value to be hidden. If the result is successful, the function performs a second
  468. check on the process path that tries to access to this registry:
  469. HideDRV – Rootkit analysis
  470. 22
  471. If the process path that ends with services.exe is available to the hidden registry, the rootkit does
  472. not hide it. However, the callback function changes the contents of the callback data structure to
  473. STATUS_NOT_FOUND:
  474.  
  475. Overview of the registry callbacks structure
  476. The schema of the registry callback linked list looks like this:
  477.  
  478.  
  479. HideDRV – Rootkit analysis
  480. 23
  481. Dynamic analysis with WinDBG
  482. We can list the registry callbacks thanks to WinDBG. The first step is to get the number of defined
  483. callbacks:
  484. kd> dd nt!CmpCallBackCount L1
  485. fffff803`6db02be0 00000002
  486. On our virtual machine, we have 2 registry callbacks listed into a _LIST_ENTRY list:
  487. kd> dps nt!CallbackListHead L2
  488. fffff803`6dafa700 ffffc000`92eb3bb0
  489. fffff803`6dafa708 ffffc000`9605c710
  490. kd> dt nt!_LIST_ENTRY fffff803`6dafa700
  491. [ 0xffffc000`92eb3bb0 - 0xffffc000`9605c710 ]
  492. +0x000 Flink : 0xffffc000`92eb3bb0 _LIST_ENTRY [ 0xffffc000`9605c710 -
  493. 0xfffff803`6dafa700 ]
  494. +0x008 Blink : 0xffffc000`9605c710 _LIST_ENTRY [ 0xfffff803`6dafa700 -
  495. 0xffffc000`92eb3bb0 ]
  496. kd> dps 0xffffc000`9605c710 L8
  497. ffffc000`9605c710 fffff803`6dafa700 nt!CallbackListHead
  498. ffffc000`9605c718 ffffc000`92eb3bb0
  499. ffffc000`9605c720 0069006e`00000000
  500. ffffc000`9605c728 01d1d67d`e400c771
  501. ffffc000`9605c730 ffffc000`952b4a80
  502. ffffc000`9605c738 fffff801`98af4870 fsflt+0x4870
  503. ffffc000`9605c740 00650065`000c000c
  504. ffffc000`9605c748 ffffc000`9498ca70
  505. We can see that one of the callbacks refers to FsFlt+0x4870. It matches the offset mentioned
  506. previously.
  507. Limitations
  508. The implementation by the rootkit developer has several limitations. By simply copying regedit.exe
  509. on the desktop and by renaming it to services.exe, the user can execute it and see the hidden
  510. registry key because the executable file path will end with services.exe and the rootkit will think
  511. that it’s the real services.exe process.
  512. HideDRV – Rootkit analysis
  513. 24
  514. PROCESS CREATION CALLBACKS
  515.  
  516. Static analysis
  517. A process creation callback is basically registered and started with one function:
  518. - PsSetCreateProcessNotifyRoutine() (MSDN documentation:
  519. https://msdn.microsoft.com/en-us/library/windows/hardware/ff559951(v=vs.85).aspx)
  520. Once again, from a reverse engineering point of view, the first argument of the function is the most
  521. interesting since it contains the function name to be executed when a process is created or deleted:
  522. In our sample, the function is ProcessCallbacks() (0x15280). This function checks if the process
  523. name is explorer.exe. If so, the rootkit sets an event in order to inject the library (.dll) configured in
  524. the registry.
  525. Dynamic analysis with WinDBG
  526. We can list the process creation and deletion callbacks thanks to WinDBG. The first step is to get the
  527. number of callbacks:
  528. kd> dd nt!PspCreateProcessNotifyRoutineCount L1
  529. fffff803`6defadcc 00000006
  530. kd> dd nt!PspCreateProcessNotifyRoutineExCount L1
  531. fffff803`6defadc8 00000002
  532. On the system, we have 8 process callbacks. With a small script, we can list them all:
  533. kd> .for (r $t0=0; $t0 < 8; r $t0=$t0+1) { r $t1=poi($t0 * 8 +
  534. nt!PspCreateProcessNotifyRoutine); .if ($t1 == 0) { .continue }; r $t1 = $t1 &
  535. 0xFFFFFFFFFFFFFFF0; dps $t1+8 L1;}
  536. ffffe001`b9248688 fffff803`6d8db7e0 nt!ViCreateProcessCallback
  537. ffffe001`b9239348 fffff801`96687290 cng!CngCreateProcessNotifyRoutine
  538. ffffe001`b95f7df8 fffff801`96c970a0 WdFilter!MpCreateProcessNotifyRoutineEx
  539. ffffe001`b9e8e1f8 fffff801`96488da0 ksecdd!KsecCreateProcessNotifyRoutine
  540. ffffe001`ba312748 fffff801`96fc30d0 tcpip!CreateProcessNotifyRoutineEx
  541. ffffe001`ba372f58 fffff801`9633d7b0 CI!I_PEProcessNotify
  542. HideDRV – Rootkit analysis
  543. 25
  544. ffffe001`b92cb2e8 fffff801`988daba0 peauth+0x2aba0
  545. ffffe001`bada52d8 fffff801`98af5280 fsflt+0x5280
  546. On the results above, the last callback points to the ProcessCallbacks() function mentioned
  547. previously. As WinDBG scripting language is not really user-friendly, here is the explanation:
  548. .for (r $t0=0; $t0 < 8; r $t0=$t0+1) #a loop of 8 iterations (our 8 callbacks)
  549. {
  550. r $t1=poi($t0 * 8 + nt!PspCreateProcessNotifyRoutine);
  551. #For each callback, get the pointer to the process callback structure
  552. .if ($t1 == 0) {
  553. .continue
  554. };
  555. r $t1 = $t1 & 0xFFFFFFFFFFFFFFF0; #Apply a mask on the pointer
  556. dps $t1+8 L1; #the callback function is at the offset 0x8
  557. }
  558. PAYLOAD INJECTION
  559. To perform the library injection in the process explorer.exe, the rootkit uses the APC (Asynchronous
  560. Procedure Calls). You can find a document on the feature on MSDN: https://msdn.microsoft.com/frfr/library/windows/desktop/ms681951(v=vs.85).Aspx.
  561. The technique was used in the well-known rootkit TLD3/TLD4. The driver uses the API
  562. KeStackAttachProcess() and KeUnstackDetachProcess() in order to attach the current
  563. driver thread to the address space of the explorer.exe process. The driver gets the base address of
  564. kernel32.dll and particularly the address of LoadLibrary() in explorer.exe. Then, the
  565. driver allocates memory in the process to copy the library path. Finally, the rootkit executes
  566. KeInitializeApc() and KeInsertQueueAPC() to execute LoadLibrary() in order to load
  567. and execute the library.
  568. As a tutorial, you can read the source code of TLD3 in order to understand the techniques in use:
  569. http://pastebin.com/UpvGUw19.
  570. HideDRV – Rootkit analysis
  571. 26
  572. CHAPTER ABSTRACT
  573. With one driver, we browsed explanations on file-system, registry callbacks, process creation callbacks
  574. and code injection via APC. These techniques are really common in rootkit analysis. The biggest missing
  575. piece concerns the network capabilities. This rootkit does not implement NDIS Filter or WFP (Windows
  576. Filtering Platform). The network capabilities are really common; for example, this mechanism was
  577. implemented in the Turla rootkit. In order to be as complete as possible, here is the way to investigate
  578. and to list the WPF callbacks with WinDBG:
  579. kd> dp netio!gWfpGlobal L1
  580. fffff801`96a63258 ffffe001`b9e025b0
  581. kd> u netio!FeInitCalloutTable L10
  582. NETIO!FeInitCalloutTable:
  583. fffff801`96a22490 4053 push rbx
  584. fffff801`96a22492 4883ec20 sub rsp,20h
  585. fffff801`96a22496 488b05bb0d0400 mov rax,qword ptr [NETIO!gWfpGlobal (fffff801`96a63258)]
  586. fffff801`96a2249d 33c9 xor ecx,ecx
  587. fffff801`96a2249f ba57667043 mov edx,43706657h
  588. fffff801`96a224a4 48898848010000 mov qword ptr [rax+148h],rcx
  589. fffff801`96a224ab 48898850010000 mov qword ptr [rax+150h],rcx
  590. fffff801`96a224b2 b900400100 mov ecx,14000h
  591. fffff801`96a224b7 4c8b059a0d0400 mov r8,qword ptr [NETIO!gWfpGlobal (fffff801`96a63258)]
  592. fffff801`96a224be 4981c050010000 add r8,150h
  593. fffff801`96a224c5 e8223dfeff call NETIO!WfpPoolAllocNonPaged (fffff801`96a061ec)
  594. fffff801`96a224ca 488bd8 mov rbx,rax
  595. kd> dps ffffe001`b9e025b0+0x150 L1
  596. ffffe001`b9e02700 ffffe001`b9e07000
  597. kd> !pool ffffe001`b9e07000
  598. Pool page ffffe001b9e07000 region is Nonpaged pool
  599. *ffffe001b9e07000 : large page allocation, tag is WfpC, size is 0x14000 bytes
  600. Pooltag WfpC : WFP callouts, Binary : netio.sys
  601. kd> u NETIO!InitDefaultCallout
  602. NETIO!InitDefaultCallout:
  603. fffff801`96a2251c 4053 push rbx
  604. fffff801`96a2251e 4883ec20 sub rsp,20h
  605. fffff801`96a22522 4c8d051f150400 lea r8,[NETIO!gFeCallout (fffff801`96a63a48)]
  606. fffff801`96a22529 ba57667043 mov edx,43706657h
  607. fffff801`96a2252e b950000000 mov ecx,50h
  608. fffff801`96a22533 e8b43cfeff call NETIO!WfpPoolAllocNonPaged
  609. HideDRV – Rootkit analysis
  610. 27
  611. fffff801`96a22538 488bd8 mov rbx,rax
  612. fffff801`96a2253b 4885c0 test rax,rax
  613. kd> r $t0=ffffe001b9e07000; .for( r $t1=0; @$t1 < 0x30; r $t1=@$t1+1) {dps
  614. @$t0+2*@$ptrsize L2; r $t0=@$t0+0x50;}
  615. ffffe001`b9e07010 00000000`00000000
  616. ffffe001`b9e07018 00000000`00000000
  617. ffffe001`b9e07060 fffff801`971ab5c0 tcpip!IPSecInboundTransportFilterCalloutClassifyV4
  618. ffffe001`b9e07068 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  619. ffffe001`b9e070b0 fffff801`971ab700 tcpip!IPSecInboundTransportFilterCalloutClassifyV6
  620. ffffe001`b9e070b8 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  621. ffffe001`b9e07100 fffff801`971aaf70 tcpip!IPSecOutboundTransportFilterCalloutClassifyV4
  622. ffffe001`b9e07108 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  623. ffffe001`b9e07150 fffff801`971b30d0 tcpip!IPSecOutboundTransportFilterCalloutClassifyV6
  624. ffffe001`b9e07158 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  625. ffffe001`b9e071a0 fffff801`971b2990 tcpip!IPSecInboundTunnelFilterCalloutClassifyV4
  626. ffffe001`b9e071a8 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  627. ffffe001`b9e071f0 fffff801`971b2a50 tcpip!IPSecInboundTunnelFilterCalloutClassifyV6
  628. ffffe001`b9e071f8 fffff801`9712b060 tcpip!IPSecAleConnectCalloutNotify
  629. […]
  630. ffffe001`b9e07c90 fffff801`97037500 tcpip!WfpAlepSetOptionsCalloutClassify
  631. ffffe001`b9e07c98 fffff801`9707ce80 tcpip!FllAddGroup
  632. ffffe001`b9e07ce0 00000000`00000000
  633. HideDRV – Rootkit analysis
  634. 28
  635. BONUS: DIFFERENCE BETWEEN
  636. THE X86 & THE X64 VERSIONS
  637. CONTEXT
  638. We decided to add a small chapter concerning the x86 version of HIDEDRV. This version contains 2 major
  639. difference with the x64 version:
  640. - The driver creates a device and a symbolic link;
  641. - Instead of using file system minifilters to hide elements, the driver defines 3 SSDT (System Service
  642. Dispatch Table) hooks.
  643. As we wrote, the SSDT hooks are not possible in Windows x64 (except when bypassing Patch Guard).
  644. This constraint does not exist in x86. This approach is not popular anymore but it’s interesting to keep it
  645. in mind and be able to analyse it.
  646. For those interested in this x86 sample, the associated hash is:
  647. b1900cb7d1216d1dbc19b4c6c8567d48215148034a41913cc6e59958445aebde
  648. DEVICE AND SYMBOLIC LINK
  649. On the x86 version of the rootkit, the developer created a driver device and a symbolic link:
  650. HideDRV – Rootkit analysis
  651. 29
  652. In the analysed sample, the device and symbolic link are not used. The symbolic links are usually created
  653. in order to receive notification (IOCTL) from the user space via the DeviceIoControl() API.
  654. We can list the device thanks to WinDBG:
  655. kd> !object \Device
  656. Object: 88e0f030 Type: (85253e90) Directory
  657. ObjectHeader: 88e0f018 (new version)
  658. HandleCount: 0 PointerCount: 232
  659. Directory Object: 88e010e8 Name: Device
  660. Hash Address Type Name
  661. ---- ------- ---- ----
  662. 00 85fb9738 Device KsecDD
  663. 862092d8 Device Beep
  664. 86004388 Device Ndis
  665. […]
  666. 28 854762e8 Device dfsflt
  667. 86214578 Device Null
  668. 852d77f0 Device 00000010
  669. 852c7030 Device 00000003
  670. […]
  671. HideDRV – Rootkit analysis
  672. 30
  673. SSDT HOOKS
  674. Overview of the SSDT
  675. The SSDT is the table that contains the addresses of the functions to be executed when a syscall is made.
  676. Here is the schema of the table:
  677. To understand how it works, we can look the assembly code of the function NtQueryKey():
  678. kd> u ntdll!NtQueryKey
  679. ntdll!ZwQueryKey:
  680. 77ca60e8 b8f4000000 mov eax,0F4h
  681. 77ca60ed ba0003fe7f mov edx,offset SharedUserData!SystemCallStub
  682. 77ca60f2 ff12 call dword ptr [edx]
  683. 77ca60f4 c21400 ret 14h
  684. 77ca60f7 90 nop
  685. The function executes a system call with the argument 0xF4. We can get the function executed when this
  686. system call is performed:
  687. kd> dps KiServiceTable+0xf4*4 L1
  688. 826b816c 82886cae nt!NtQueryKey
  689. Note: a second table (KeServiceDescriptorTableShadow) exists. The table contains a pointer to
  690. KiServiceTable (the same as previously) and to W32perviceTable (syscall for the GUI threads).
  691.  
  692. HideDRV – Rootkit analysis
  693. 31
  694. Static analysis
  695. In the function SSDT_Hook() (0x13490), the rootkit replaces 3 functions addresses in the
  696. KiServiceTable. These functions are used to read registry value, get file information and get
  697. directory information:
  698. The malicious code functions have the same purpose as the callbacks previously described:
  699. - if the accessed file, directory or registry must be hidden, the rootkit returns that the element does
  700. not exist;
  701. - if the accessed file, directory or registry is not in the list, the rootkit executes the original function
  702. of the SSDT (previously saved in a global variable).
  703. -
  704. Dynamic analysis with WinDBG
  705. The SSDT hook can be directly identified with WinDBG:
  706. kd> dps nt!KeServiceDescriptorTable L3
  707. 827a39c0 826b7d9c nt!KiServiceTable
  708. 827a39c4 00000000
  709. 827a39c8 00000191
  710. kd> .shell -ci "dps nt!KiServiceTable L0x191" find "FsFlt"
  711. 826b7f6c 92af9160 FsFlt+0x2160
  712. 826b8118 92afac00 FsFlt+0x3c00
  713. 826b82c0 92afa9c0 FsFlt+0x39c0
  714. .shell: Process exited
  715. HideDRV – Rootkit analysis
  716. 32
  717. CHAPTER ABSTRACT
  718. The SSDT hooks are becoming rarer. The same approach can be performed in the IDT (Interrupt
  719. Description Table). The table is used to identify the function address to execute when an interrupt is
  720. called. We can display the table thanks to WinDBG:
  721. kd> !idt -a
  722. Dumping IDT: 80b95400
  723. 3255d61800000000:82677fc0 nt!KiTrap00
  724. 3255d61800000001:82678150 nt!KiTrap01
  725. 3255d61800000002:Task Selector = 0x0058
  726. 3255d61800000003:826785c0 nt!KiTrap03
  727. 3255d61800000004:82678748 nt!KiTrap04
  728. […]
  729. As explained previously, this approach was popular a few years ago on x86 platform but it tends to
  730. become rarer today due to Patch Guard.
  731. HideDRV – Rootkit analysis
  732. 33
  733. CONCLUSION
  734. This document has been written as a “hands on” for reverse engineering beginners and willing to
  735. leverage his experience on rootkits.
  736. The use case of the document is a very interesting case study for basic rootkit techniques. Using
  737. different tools and tricks, we overviewed the main features of the rootkit such as filesystem
  738. manipulation, registry and process callbacks, code injection and even network manipulation.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement