Tuesday, September 5, 2017

Windows kernel pool spraying fun - Part 1 - Determine kernel object size

This is a series of posts I'm planning to write for about a year now, I did some research here and there, but sort of forget about the whole thing and never took notes properly. I wanted to explore what kind of objects we can use for kernel pool spraying, mainly how much space they consume, what attributes they have, and at the end come up with a code snippet that will take the 'pool hole size' as an input, and dynamically tell us what kind of objects to use for this in order to control the pool allocation for our overflow. So this went into my drawer for long time, and I got excited about this again, when I saw a Twitter post from @steventseeleyhttps://twitter.com/steventseeley/status/904443608216031233 and decided that I need to write this after all, partially for my own interest, and finally documenting it as well :)
At this point I don't know how many parts this series will have, and how fast I will progress due to lack of time, but I decided to start this, and not ignore it again.

Microsoft has a nice list of kernel objects we can create with calling user mode function, although it's not complete, it's still a good start: https://msdn.microsoft.com/library/windows/desktop/ms724485(v=vs.85).aspx

another important link, is a list of pool tags we can spot, which can also come handy when looking at the pool allocations:

In this post I want to explore the Mutex object, as that gave me a headache recently due to incomplete notes, and show how can we find and see the actual allocation in the pool space and some basic info about the object itself.

To setup the environment, we don't need to make remote kernel debugging, it's enough to do local kernel debugging, as we will only explore the kernel memory, and we don't need to setup any breakpoints for now. So a local debugging will be sufficient for our needs. For that we will need to enable debugging in Windows:

bcdedit -debug ON

After that we will need to restart the machine. Once it's done, we can fire up WinDBG, go to Kernel Debug, and select Local. It's recommended to issue the following commands to load symbols:

.symfix
.reload

At this point we can explore the kernel memory space. I will use a Win7 SP1 x86 for my demonstration.

First if we want we can get a more comprehensive list of objects with issuing the following command:
!object \ObjectTypes

which will give us something like this:
lkd> !object \ObjectTypes
Object: 8be05880  Type: (851466d8) Directory
    ObjectHeader: 8be05868 (new version)
    HandleCount: 0  PointerCount: 44
    Directory Object: 8be05ed0  Name: ObjectTypes

    Hash Address  Type                      Name
    ---- -------  ----                      ----
     00  851d6900 Type                      TpWorkerFactory
         851466d8 Type                      Directory
     01  8521a838 Type                      Mutant
         851cddb0 Type                      Thread
     03  857c7c40 Type                      FilterCommunicationPort
     04  8522a360 Type                      TmTx
     05  851d29c8 Type                      Controller
     06  8521d0b8 Type                      EtwRegistration
     07  851fe9c8 Type                      Profile
         8521a9c8 Type                      Event
         851467a0 Type                      Type
     09  8521cce0 Type                      Section
         8521a900 Type                      EventPair
         85146610 Type                      SymbolicLink
     10  851d69c8 Type                      Desktop
         851cdce8 Type                      UserApcReserve
     11  85221040 Type                      EtwConsumer
         8520e838 Type                      Timer
     12  8522a8f0 Type                      File
         851fe838 Type                      WindowStation
     14  860a6f78 Type                      PcwObject
     15  8521ceb0 Type                      TmEn
     16  851d2838 Type                      Driver
     18  8521db70 Type                      WmiGuid
         851fe900 Type                      KeyedEvent
     19  851d2900 Type                      Device
         851cd040 Type                      Token
     20  85214690 Type                      ALPC Port
         851cd568 Type                      DebugObject
     21  8522a9b8 Type                      IoCompletion
     22  851cde78 Type                      Process
     23  8521cf78 Type                      TmRm
     24  851d6838 Type                      Adapter
     26  852139a8 Type                      PowerRequest
         85218448 Type                      Key
     28  851cdf40 Type                      Job
     30  8521c940 Type                      Session
         8522a428 Type                      TmTm
     31  851cdc20 Type                      IoCompletionReserve
     32  8520e9c8 Type                      Callback
     33  85894328 Type                      FilterConnectionPort
     34  8520e900 Type                      Semaphore

This is a list of objects that can be allocated in the kernel space. We can explore several important attributes about them, by looking into them in more detail. With the dt nt!_OBJECT_TYPE <object> we can get some details about the object, like total handles, etc... but most importantly the offset to the _OBJECT_TYPE_INITIALIZER structure which will contain a whole lot of handy stuff for us. Let's see what it gives us for the Mutant object, what I want to explore here:

lkd> dt nt!_OBJECT_TYPE 8521a838
   +0x000 TypeList         : _LIST_ENTRY [ 0x8521a838 - 0x8521a838 ]
   +0x008 Name             : _UNICODE_STRING "Mutant"
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0xe ''
   +0x018 TotalNumberOfObjects : 0x15f
   +0x01c TotalNumberOfHandles : 0x167
   +0x020 HighWaterNumberOfObjects : 0xc4d7
   +0x024 HighWaterNumberOfHandles : 0xc4ed
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x6174754d
   +0x080 CallbackList     : _LIST_ENTRY [ 0x8521a8b8 - 0x8521a8b8 ]

and to read the _OBJECT_TYPE_INITIALIZER:

lkd> dt nt!_OBJECT_TYPE_INITIALIZER 8521a838+28
   +0x000 Length           : 0x50
   +0x002 ObjectTypeFlags  : 0 ''
   +0x002 CaseInsensitive  : 0y0
   +0x002 UnnamedObjectsOnly : 0y0
   +0x002 UseDefaultObject : 0y0
   +0x002 SecurityRequired : 0y0
   +0x002 MaintainHandleCount : 0y0
   +0x002 MaintainTypeList : 0y0
   +0x002 SupportsObjectCallbacks : 0y0
   +0x002 CacheAligned     : 0y0
   +0x004 ObjectTypeCode   : 2
   +0x008 InvalidAttributes : 0x100
   +0x00c GenericMapping   : _GENERIC_MAPPING
   +0x01c ValidAccessMask  : 0x1f0001
   +0x020 RetainAccess     : 0
   +0x024 PoolType         : 0 ( NonPagedPool )
   +0x028 DefaultPagedPoolCharge : 0
   +0x02c DefaultNonPagedPoolCharge : 0x50
   +0x030 DumpProcedure    : (null) 
   +0x034 OpenProcedure    : (null) 
   +0x038 CloseProcedure   : (null) 
   +0x03c DeleteProcedure  : 0x82afe453     void  nt!ExpDeleteMutant+0
   +0x040 ParseProcedure   : (null) 
   +0x044 SecurityProcedure : 0x82ca2936     long  nt!SeDefaultObjectMethod+0
   +0x048 QueryNameProcedure : (null) 
   +0x04c OkayToCloseProcedure : (null) 

This will give us two important things:
  • The pool type where this object is allocated - NonPagedPool in this case
  • Offset to functions (this is important during the actual exploitation part)
After this let's allocate a mutant, and find it in the kernel pool. I made a simple short python code, that will do this:


from ctypes import *
from ctypes.wintypes import *
import os, sys

kernel32 = windll.kernel32

def alloc_not_named_mutex():
        hHandle = HANDLE(0)
hHandle = kernel32.CreateMutexA(None, False, None)
if hHandle == None:
                print "[-] Error while creating mutex"
sys.exit()
print hex(hHandle)

if __name__ == '__main__':
        alloc_not_named_mutex()
variable = raw_input('Press any key to exit...')

This will allocate an unnamed mutex for us, print its handle and wait for exit. We need the wait, so we can explore the kernel pool in WinDBG, if the process exit, the mutex will be destroyed. I got a handle of 0x70, let's see how we can find it in WinDBG. First I need to find the Python process and switch context to it, which can be done this way:

lkd> !process 0 0 python.exe
PROCESS 86e80930  SessionId: 1  Cid: 0240    Peb: 7ffd4000  ParentCid: 0f80
    DirBase: bf3fd2e0  ObjectTable: a8282b30  HandleCount:  41.
    Image: python.exe

lkd> .process 86e80930  
Implicit process is now 86e80930

The first command will find the process for us, and the second will switch context. Then we need to query the handle, which will give us the address of the object in memory:

lkd> !handle 70

PROCESS 86e80930  SessionId: 1  Cid: 0240    Peb: 7ffd4000  ParentCid: 0f80
    DirBase: bf3fd2e0  ObjectTable: a8282b30  HandleCount:  41.
    Image: python.exe

Handle table at a8282b30 with 41 entries in use

0070: Object: 86e031a8  GrantedAccess: 001f0001 Entry: 8c0d80e0
Object: 86e031a8  Type: (8521a838) Mutant
    ObjectHeader: 86e03190 (new version)
        HandleCount: 1  PointerCount: 1

With that, we can find the pool location, and details:

lkd> !pool 86e031a8  
Pool page 86e031a8 region is Nonpaged pool
 86e03000 size:   98 previous size:    0  (Allocated)  IoCo (Protected)
 86e03098 size:   90 previous size:   98  (Allocated)  MmCa
 86e03128 size:   40 previous size:   90  (Allocated)  Even (Protected)
 86e03168 size:   10 previous size:   40  (Free)       Icp 
*86e03178 size:   50 previous size:   10  (Allocated) *Muta (Protected)
Pooltag Muta : Mutant objects
 86e031c8 size:   40 previous size:   50  (Allocated)  Even (Protected)
 86e03208 size:   40 previous size:   40  (Allocated)  Even (Protected)

It shows that it takes 0x50 bytes in the Nonpaged pool region. No matter how many times we repeat this, it will be consistently 0x50. The thing I didn't know is if we can allocate plenty of unnamed mutexes. It seems that we actually can. If we put our previous code into a loop, we can see that it will work, and that they can nicely spray the heap:

 851ef118 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef168 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef1b8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef208 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef258 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef2a8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef2f8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef348 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef398 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef3e8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef438 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef488 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef4d8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef528 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef578 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef5c8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef618 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef668 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef6b8 size:   50 previous size:   50  (Allocated)  Muta (Protected)
 851ef708 size:   50 previous size:   50  (Allocated)  Muta (Protected)

So what changes if we give a name to the Mutex? Here is another Python code for that:

def alloc_named_mutex(i):
        hHandle = HANDLE(0)
hHandle = kernel32.CreateMutexA(None, False, "Pool spraying is cool " + str(i))
if hHandle == None:
                print "[-] Error while creating mutex"
sys.exit()
print hex(hHandle)

I pass an argument, as that will be important if we want to use this for spraying, because we can't create two mutexes with the same name.

Once we create the mutex, and we follow the same logic as before, we can see a bit difference:
*871d39e8 size:   60 previous size:   30  (Allocated) *Muta (Protected)
Pooltag Muta : Mutant objects

This time it takes 0x60 bytes, and it will be consistent. We can do the same spraying etc... but with a different size. There is something here that will be important. If we take a look at the pool allocation, we can see that thee is a pointer at offset 0x20 from the beginning of the pool chunk, to the name of the Mutex:

lkd> dd 871d39e8 
871d39e8  040c0006 e174754d 00000000 00000050
871d39f8  00000000 00000000 9a06fb38 002e002e
871d3a08  aab50528 00000000 00000002 00000001
871d3a18  00000000 000a000e 86e0bd80 99a4fc07
871d3a28  0008bb02 00000001 871d3a30 871d3a30
871d3a38  00000001 00000000 00000000 01d10000
871d3a48  040b000c 6d4d6956 b299b8c8 9a087020
871d3a58  a8246340 00000000 00000000 85d4f0b0

lkd> dd aab50528
aab50528  006f0050 006c006f 00730020 00720070
aab50538  00790061 006e0069 00200067 00730069
aab50548  00630020 006f006f 0020006c 006f0031

lkd> dS aab50528
006c006f  "????????????????????????????????"
006c00af  "????????"

My WinDBG doesn't want to print the name, but if you take a look at the UNICODE in hex, this is the name we gave to the Mutex. If we check where this string is stored:

lkd> !pool aab50528
Pool page aab50528 region is Paged pool
 aab50000 size:   a8 previous size:    0  (Allocated)  CMDa
 aab500a8 size:   28 previous size:   a8  (Free)       3.7.
 aab500d0 size:   28 previous size:   28  (Allocated)  NtFs
 aab500f8 size:   28 previous size:   28  (Allocated)  MmSm
 aab50120 size:   38 previous size:   28  (Allocated)  CMnb Process: 86ef6760
 aab50158 size:  100 previous size:   38  (Allocated)  IoNm
 aab50258 size:   38 previous size:  100  (Allocated)  CMDa
 aab50290 size:   38 previous size:   38  (Allocated)  CMNb (Protected)
 aab502c8 size:   28 previous size:   38  (Allocated)  MmSm
 aab502f0 size:   20 previous size:   28  (Allocated)  CMNb (Protected)
 aab50310 size:   60 previous size:   20  (Allocated)  Key  (Protected)
 aab50370 size:   20 previous size:   60  (Allocated)  SeAt
 aab50390 size:   d8 previous size:   20  (Allocated)  FMfn
 aab50468 size:   28 previous size:   d8  (Allocated)  CMVa
 aab50490 size:   30 previous size:   28  (Allocated)  CMVa
 aab504c0 size:   60 previous size:   30  (Allocated)  Key  (Protected)
*aab50520 size:   38 previous size:   60  (Allocated) *ObNm
Pooltag ObNm : object names, Binary : nt!ob

It's in the paged pool! I will come back to this later, but I will give some spoiler here: We can create custom size allocations in the paged pool area with using named Mutexes, and the size will depend on the name we give. Super useful for spraying in the paged pool.






Saturday, October 22, 2016

Exploit generation and JavaScript analysis automation with WinDBG

The material for our talk "Exploit generation and JavaScript analysis automation with WinDBG" with Miklos Desbordes-Korcsev can be found in the below links.

hack.lu 2016 recording:


Tools we developed and used in the demos:

https://github.com/theevilbit/exploit_generator
https://github.com/szimeus/evalyzer

Slides will be available here:

http://archive.hack.lu/2016/

and on Hacktivity's website (https://hacktivity.com/en/). Will update this post when everything is online.

Enjoy!

Saturday, September 24, 2016

Offensive Security - Advanced Web Attacks and Exploitation (AWAE) review

I had the opportunity to attend OffSec's AWAE training this year at BlackHat. The challenge started with the registration, with monitoring past years events, I knew, that if I don't sign up in the first 24 hours, I need to wait one more year. I went for my employer approval way ahead of the registration opening, and luckily I had it a few days before. As soon as I got the BH newsletter about registration opened, I throw away everything and went to the computer to sign up. 21% of the course was already full!!! Luckily I could secure my place, and after that I read that this year the course filled up in 8!! hours. If you want to sign up, you have to be fast.

What background you need?

I'm still a guy working in incident response, so I don't do too much web application testing (I do exactly 0), all of my background knowledge comes from OSCP and OSCE. If you took those courses, you will be absolutely fine. What I missed is my lack of JavaScript coding experience. I can read JS, but can't write, which made things a bit harder, but it was still manageable. My advise is to learn some JS before this course.

The course:

There is one review of this course on OffSec's website, with the name "Story telling with muts", but that link is no longer valid. The title however is 100% right. There are 10+ case studies in this course to walk you through interesting techniques, chain of exploits, etc... and muts has a story to each of them, which makes the course really interesting, you not only get some in-depth knowledge, but also a couple of cool tales :)

I can't really split up the course into particular days, like I did with AWE, it's about the same level of difficulty through the entire 4 days. It does increase a bit, but overall it doesn't have big spikes. Compared to AWE this course is lighter and not in a negative sense. The fact that you don't need to build ROP chains manually, debug kernel, and hunt for bits in memory makes it much more brain friendly, and you don't fall apart after day 2 like at AWE :) You will learn / see plenty of examples of real hacker mindset, out-of-the-box thinking. You will see vectors, that maybe before you didn't even think before (e.g.: XSS via SNMP), some really cool exploit chains, where the exploits by themselv are not serious, but when applied together they give you remote code execution. Again, I think this course's main strength is not the techniques you learn or the bunch of 0-days you get (yes, you leave the course with a handful of them), but the mindset you get, you will look on webapps differently after this course. The course is 100% hands-on, they build upon the basics, and there is some theory covered on the fly, but it's fully practical, which you can't say to any other course generally. Typical OffSec course, and you will have plenty of chance to practice, practice and practice. You will be much more comfortable with testing web apps after this 4 days.

The bonus I got out of this training is that before it I hated playing with webapps, it simply didn't look interesting. This course changed my view and feelings, testing webapps can be really cool. :)

Mati (@muts) and Steven Seeley (@steventseeley) were the instructors, probably the best two people you can get for this kind of course.

The exam:

Well... it's not yet available.

Some closing thoughts:

I started my InfoSec journey back in 2012, and I quickly became aware of the Offensive Security trainings and exams, and after reading plenty of reviews, articles, I knew that time, that I want to be an OSCP and potentially go on with OSCE and the others. That time all of this looked nearly impossible to achieve, and were far far away in the big unknown, they were like a dream. I freaked out even from the OSCP reviews, not to talk about the rest. I started with OSWP in 2012, and then every year I managed to do one more, slowly progressing towards the end; 2013 - OSCP, 2014 - OSCE, 2015 - OSEE. I did other courses during the years, but definitely these were the most rewarding ones, especially that this was my big dream when I started. Even without OSWE at the moment, I'm very happy, and it feels really good, when you work hard (and in these cases really-really hard) towards some big goal, and you finally achieve it. This is not the end of the journey, but definitely a major milestone in my life.

Finally I want to say thank you:
1. To my family, who always supported me, and accepted the fact that I have less time for them when preparing for these courses / exams.
2. To Offensive Security for creating the trainings.
3. To my employer for paying the courses.

Thursday, June 23, 2016

Defend against malware with fake debugger windows

This post will be quite similar to my previous post about making your desktop look like a VM: https://theevilbit.blogspot.com/2015/10/make-your-desktop-fake-virtual-machine.html. The idea here is another trick malware commonly uses to detect malware analyst's machine: enumerating the windows and check if there are titles such as "OllyDBG", "WinDBG", "Wireshark", etc... it will frequently look for debuggers or various malware analysis tools.
I was thinking about, what if I place an empty window with this name, will malware detect it? I didn't wanted to hook into functions this time, but place an actual window. My other criteria was, that I want the window to be hidden, so it doesn't disturb people, but still can serve its purpose.

First let's see how malware detects / enumerates window titles. As described here: http://antukh.com/blog/2015/01/19/malware-techniques-cheat-sheet/ and many other places, it uses the "FindWindow" Windows API call to look for names. This is a quite simple function with two parameters:

HWND WINAPI FindWindow(
  _In_opt_ LPCTSTR lpClassName,
  _In_opt_ LPCTSTR lpWindowName
);

As per MSDN it will search for the string specified in lpClassName, which is the registered class name for the Window, or if that is NULL, it will search based on the window title, which is the second parameter. Malware typically will search for the class name. If you wonder how frequently this being used, here is a little help, to find malware which uses this technique. Cuckoo sandbox recently added some malware behaviour signatures to its feature set, and one of them is looking for this exactly, it can be found here:
https://github.com/cuckoosandbox/community/blob/master/modules/signatures/windows/antidbg_windows.py. It will add the description into the analysis page. https://malwr.com uses Cuckoo sandbox to perform malware analysis, and it's searchable on Google, with that simple search for this on Google, and you will get a bunch of samples:
"Checks for the presence of known windows from debuggers and forensic tools" site:malwr.com
For example:
Various malware will behave differently if it finds the window, it might exit, or will try to close the window, etc...

With this, let's create our window.

I still can't really do Win32 API programming, so I use the power of copy-paste. I found two very good articles about making your first, empty window in Windows: 
You can read through this, the only thing I want to highlight is that the window will not be visible by default, you either create it with the appropriate flag, or call ShowWindow, as specified in the example. What if we don't show it? Well, as you would expect, it's not seen, you don't see it on the tray, nowhere, the program runs, and you can find it in the task manager, but no window. What happens if we call FindWindow this way? Based on numerous tests I did, it will always find it, regardless if you search based on class name or title. This is great, because we can start a process, which is completely hidden to the average user, consumes about 500kB memory, and about 0% CPU resources, but a malware will find it. Cool! Now we can hope that it will actually exit if it finds it.
Next question, is: do we need multiple processes to create multiple windows with different names? Based on my tests: NO. You can create multiple windows with different names, and you can hide all of them, and the FindWindow function will find all of them.
I also tried, what happens if I start WinDBG, which will register the same class name, but apparently it doesn't matter. You can run both programs at the same time, you can close them, and it won't cause any conflicts.
There is another method to find windows with GetWindowText based on the title, described here: https://stackoverflow.com/questions/16530871/findwindow-does-not-find-the-a-window, and that also works, it will find the window as well.

If I was an AV developer, I would hook the FindWindow function, and check if a process is explicitly looking for these windows, and if yes, I would raise a flag, because it's definitely not normal. Not sure if anyone does it, but it would be a fairly easy thing to do.

I created a PoC code to create an OllyDBG and WinDBG window, and another one, which will use FindWindow to find them. Both of them are available from my Github page:

https://github.com/theevilbit/vaccination/

The related Visual Studio projects are:

  • FindWindow
  • FakeDebuggerWindows

Wednesday, June 8, 2016

About IOCs...

I usually stay away blogging about my opinion, but I so fed up with the IOC hype that I have to write it down. (My next topic might be threat intel, along the same lines).

First about sharing:
There is a huge number of possible IOC types, like IP, domain, registry modified, files created, etc... still what you can most commonly find in any malware analysis paper, or IOC feeds, or any generic sharing, are: IPs, domains, filenames, hashes. No more. Now there are multiple issues when it comes to sharing:
Why it can't be shared in a standard format, like STIX? Typically if you read a report, you will have this information at the end of the article as text, which might be OK as someone posting details doesn't necessarily have the option to upload files, but when it comes to big security vendors who often publish IOCs in a separate PDF(!!!!) there is no excuse. I really don't understand why it can't be in a CSV as a minimum, or more preferred in STIX XML format. Vendors should have the ability to generate these, and you could more easily feed it to some other tools, without making difficult copy-paste tricks from a PDF.
My second big headache is, why almost every big vendor shares only MD5 hashes of malware samples??? It's not just that MD5 is more and more subject to various collision attacks, but some logs you have, might only contain SHA-256 hashes of executables seen in an environment, so you have no chance at all to search that data. Why is it so much trouble for someone to calculate an additional SHA-1 and SHA-256 hashes besides and MD5 and sharing that? Why does it hurt anyone? As a backup you can hope that the sample gets uploaded to VirusTotal, and you can get the hashes from there, but that's an incredible big amount of additional, unnecessary work (even with a script) to get that information from another source - if you can at all.
Why vendors don't share other IOCs in a summarized form? Like registry entries created, etc... You might find them if you read through the 20+ page article, but who has the time to read through every single malware report?
With that my request to vendors, who commonly share plenty of IOCs:
  1. Please share them in STIX format but at a minimum in a CSV
  2. Please share SHA-1 and SHA-256 hashes as well beside MD5 (share all 3 not just 1 of them)
  3. Please summarize other IOC information as well, not just IP, domain, filenames and hashes
More on hashes:
Some tools allow you to search files across your environment. Guess what!? Some products use proper SHA-256 and some use MD5, but most products can search only one of them! If you consider how sharing is being done in the community (you only get one of the 3 popular hashes), this is setup for failure. By design. I don't want to write more about this.

On usefulness:
I don't think IOCs are from evil. It can be good, and you can potentially find some badness based on that, so it has its place in incident response, but it won't solve core security problems, and IOCs won't be the ultimate solution for everything. The problem comes when vendors start to rely 100% on this data. For example calling something a 'hunting' module, when it's only an advanced IOC search with a nice GUI, I think is really bad, and something conceptionally went wrong with the entire product.
These days vendors seem to think that IOCs will save the world, and they are extremely important, and everyone wants to sell you more and more IOCs for huge amount of money. I really mean huge! I could go into how the importance of threat intel in general is overrated, but that might be another post.
Just think about how these IOCs are really helpful:
  • hashes - considering the speed of sample generation (half million new samples / day), do people really think that 2-3 particular hashes are important? It's the oldest and dumbest signatures AVs can use. So if you honestly think about it, hashes are basically poor man's malware signatures.
  • IP - most of the time there are 100+ websites hosted on a single IP, if one site becomes infected, and there is a popular harmless site on the same IP, you could immediately flag significant part of your network traffic as malicious. Good luck in figuring out which one might be really bad in a big network... you will give up immediately as soon as you see the amount of data.
  • filenames - somewhat useful if the name is unique, but almost the same issue as with the hash, could be slightly better however.
  • domain names - probably the most useful ones in general.
In summary I think IOCs are just poor signatures, which are way too overrated by most vendors, especially when it comes around the topic threat intel. They can be useful, but the hype going around them these days is a shame. If you go and buy them, then they are also way too expensive if you compare it to standard AV signatures, and as noted above they are not better.

Tuesday, May 3, 2016

JavaScript deobfuscation: criminal case against you.wsf

A few months ago, I came across a malware dropper which was a javascript inside a Windows script file (WSF). The filename was: "criminal case against you.wsf". Typical... I'm a bit fed up with the naming, but anyhow... The file itself is somewhat interesting, because it can contain many types of scripts, and get them run in Windows if there is an interpreter. But this is not what I want to write about. The deobfuscation itself is not super hard, but after doing it I came across two really useful online tools, which can do this in a matter of seconds, and this is why making this quick post.

This one was new to me, and it is pretty handy:
http://deobfuscatejavascript.com/

I already knew about this, and it was useful in the last step:
http://jsbeautifier.org/

I really recommend everyone checking them out.

For completeness here is the file, which contains the original JS, and then each step of the decoding, it had 4 layers of obfuscation.
criminal case js deobfuscate.txt

Friday, February 19, 2016

CVE-2015-8285 - QuickHeal webssx.sys driver DOS vulnerability

A few months back I decided to practice my skills learned in the AWE course, in order to maintain it in my head, and keep it as an "active" knowledge. In general I don't have too much time these days, but I sacrificed some time for this. I also wanted to find a new vulnerability instead of writing an exploit code for an existing one, which didn't make things easier.
As I don't plan to do such kind of activity too often, I decided to look for bugs manually with reversing a kernel driver and look for possibly vulnerable IOCTL codes. I was probably lucky or these bugs are really frequent, but after some trials with a few products I found one in QuickHeal AV 16. There is a DOS vulnerability in the webssx.sys driver. Here is the document I made with all the details:


and here is my POC code:


Due to the reasons described in the document I didn't find a way to make a privilege escalation exploit out of this, so if someone see a possibility, please let me know :) With that it was still a very good experience, and I definitely learned new stuff with this.

This is also my first ever bug and CVE. This part was also a very interesting journey. How to report a bug, get CVE assigned, etc... It didn't went smoothly, and I had a few challenges initially to contact the vendor, but it all sorted out at the end. It took about 3 months from my initial trials of submitting the details to QuickHeal till they actually released a fix.