08 - FLARE-ON 2018

The 2018 FLARE-ON Challenge


I've been trying to write two posts per month, though it's been faltering lately, obviously.  Over the past few days / week, I've been working on the FLARE-ON challenge, so my attention hasn't really been on blogging, but I thought I'd take some time to make a placeholder for when the current FLARE-ON challenge ends-- then I can freely blog about the challenges and how far I got. 

In the event you haven't heard of FLARE, it's an acronym for FireEye Labs Advanced Reverse Engineering.  Apparently this is the fifth year this challenge has run.  I'd never heard of it until recently, but it looked fun and so far it has proved to be entertaining, as well as quite challenging.  I'm not really well versed in RE...  I'm interested in it, more for the exploit dev side of things than malware analysis, but more or less I'm just trying to get better at it.  Little CTF challenges like this one are a great time and help stretch those RE muscles.

I'll update this when the challenge ends, but for the time being, if you'd like to participate go to the challenge website, here: FLARE-ON

Good luck!

Time's up, fools

Definitely felt like a fool going through FLAREON 2018.  You could say that reverse engineering definitely isn't my forte.  All in all, good fun.  I found the first two challenges to be a breeze, the third to start to pick up steam and get some creativity flowing, and the fourth, I needed some hints.

Previous experience, albeit limited in regards to reverse engineering, with debuggers and lots of CTFs definitely helped, but I ended up learning quite a bit.  Obfuscation techniques, forensics and RE in general are all skills that are a bit foreign to me.  I've never practiced much of any of it beyond surface level stuff and the small amounts of RE involved in OSCE.

All in all, I really look forward to doing next years.

01 - Minesweeper Championship


The first challenge for the FLAREON competition was 'cracking' a Minesweeper Championship registration key.  The premise was that the registration had closed for the 'Minesweeper World Championship' but you wanted in.

FireEye provided a binary to download with the goal of obtaining a key.  Once the key was found, it could be entered on the FLAREON website, giving you access to challenge two.  This process effectively remained the same for the other challenges.

The target file is a java binary, named 'MinesweeperChampionshipRegistration.jar'.  When started, the following screen appears:



Pretty simple.  Upon entering an invalid code, a dialogue box appears telling us that the code was incorrect, and good luck next year.  Nice.

Anyway, since Java is an open source language it's not all too difficult to look at what's happening behind the scenes.  Lots of applications exist to decompile jar files.  I chose to open this one in Javasnoop. 

Launch Javasnoop and select 'A new process'.  After that, you'll be prompted with a screen with multiple options.  Just click 'Add', then 'Start Process'



Once opened, click OK or cancel, then 'Class Management' -> 'Decompile Class' and select 'InviteValidator'.  Within this window will be the decompiled class, containing the key we need:

/* Decompiled InviteValidator */
import javax.swing.JOptionPane;

public class InviteValidator
{

    public InviteValidator()
    {
    }

    public static void main(String args[])
    {
        String response = JOptionPane.showInputDialog(null, "Enter your invitation code:", "Minesweeper Championship 2018", 3);
        if(response.equals("GoldenTicket2018@flare-on.com"))
            JOptionPane.showMessageDialog(null, (new StringBuilder("Welcome to the Minesweeper Championship 2018!\nPlease enter the following code to the ctfd.flare-on.com website to compete:\n\n")).append(response).toString(), "Success!", -1);
        else
            JOptionPane.showMessageDialog(null, "Incorrect invitation code. Please try again next year.", "Failure", 0);
    }
}

Pretty clear that the key is contained within that.


02 - Ultimate Minesweeper Championship


Alright, with the first challenge completed, the second becomes available.  This time it's not the minesweeper registration, but the World Championship.  Hell yeah.

FireEye provides us with the 'UltimateMinesweeper.exe' PE file.  Running file against the PE on a linux box lets us know that it's a .NET assembly.  In my initial naivety I thought I'd be able to just run it via Immunity and see what's happening.  Unfortunately, what I learned is that .NET compiles down to IL language that isn't normal assembly.  This is then translated by mscoree.dll into machine code.  Now, what this means is that instead of being able to use a typical debugger, i.e. Immunity, you'd need to use something like dnSpy.

dnSpy allows us to take a .NET executable and decompile it.  It also contains a debugger, so you can decompile, set breakpoints, debug, and even make changes to the code (haven't had much luck with this).

Opening the Ultimate Minesweeper game gives us a 30x30 playing field, totaling 900 squares.  I quickly found that clicking just about anywhere loses the game.  Sure, I could click each square again and again until I find the flags, if there are any, but that's boooring.

Anyway, open the exe in dnSpy:



Now, this isn't an obfuscated binary.  The Minesweeper Championship Committee works on an honor system, so they're not expecting anyone to... cheat?  In the above screen, I've set a breakpoint on the initialization of the application.

After starting the game, playing, watching in dnSpy, etc, I eventually found where the field was constructed:



As mentioned previously, the field is 900 squares, a 30x30 array ([0x1E,0x1E]).  A function called 'MineField' is... telling.  We know that there are 897 mines present, leaving three non-mine squares.  Continuing to explore the construction of the minefield is useful, but following a click event revealed even more information:



Now those variables are interestingly named.  What's in 'MinesPresent', I wonder?



Thought so.  A bit of scrolling reveals the first safe tile, at position [7,20], below:



After some trial and error counting the right squares, victory is attained:


Honestly, there's probably a great way to automate the clicking, and may even be other ways of pulling the key out, but I'm not sure.  I thought it was fun 'reversing' the Minefield, so I'm satisfied.


03 - FLEGGO


This one was a fresh breath of air from the typical 'reversing' mentality.  It felt more like a puzzle, and of the four I did / tried, this one was the most fun.

For starters, FireEye provides a zip file for this challenge.  Within this zip are 48 binaries, each with a seemingly randomized name.  Upon running the exe's a command prompt is opened with a 'What is the password?' dialogue.  Entering incorrect stuff results in a 'Go step on a brick!' message:



Fun, but not what we want.  So clearly, we need some sort of password to advance this dialogue.  With my limited reversing experience, I like to start puzzles like these by opening the executable up within Immunity.  Unlike the Ultimate Minesweeper executable, this isn't a .NET binary and Immunity can debug it without issue.

This appears to be some sort of... 'authentication'.  Typically with janky authentication like this, a password will be stored somewhere in memory.  Obviously, this isn't always the case, but it's a good starting point.  Launching the app in Immunity, I did 'Search for' -> 'All referenced text strings' within the loaded module.

Note: If you use this search functionality, make sure you're within the proper module.  If I'm trying to search for all referenced text strings within the executable, I want to make sure I'm selecting that options from within the CPU window for that module.  At times you may find yourself searching within system memory, usually because ntdll.dll or something else was called.  Seems simple, but once you know it's just a QoL improvement.

Searching for the strings reveals the following:



There are a lot of unicode entries in there that seem like they could be passwords...  After trying a few I found that the application has differing responses for different values in the above screenshot, but none seemed to get any progress.

I decided to open another of the 48 binaries in another Immunity session and noted a discrepancy.  I searched for all referenced text strings on application load.  I didn't run it, though.  Look at the following screenshot and find the difference:



There are a few lines that stand out in that.  Although this is a different binary, it became apparent that they all exhibited this behavior.  Passing this string to the application has... unexpected results:



Well, a few things happened here.  First, it seems like the password was accepted.  Although we have received other strings back, for instance sending 'IronManSucks' results in the application returning 'Oh, hello Batman...', we receive a message and an additional clue when sending the seemingly random value.  The application actually spits out an image file, called 6732218.png with a weird '=> _ ' symbol.  Unsure of what that means, I noted it and took a look at the image:



Interesting.

Alright, well that wasn't expected.  I repeated the same thing for another binary and had a similar result.  An image was extracted, and a '=>' symbol followed by a letter was sent to stdout.  The other interesting thing is the number in the top left of each image changes... This gave me an idea.

It seems like the images don't follow a particular pattern.  As in, after viewing some I realized they probably won't be 'stitched' together.  Some are single lego men, or blocks, while others start to display a completed lego 'set', you could say.  I decided to disregard the images themselves, but decided the numbers in the top left must be important.

After looking at a few of the binaries, the offset of the actual 'passwords' appeared to remain static, too.  Understanding what happened was easy, it was clear the correct password spits out a file.  That file has a number which likely means it should be ordered in a certain way.  This is when I realized the '=>' symbol wasn't 'an equal to or greater than' symbol, but an arrow, indicating a letter associated with that particular image.  Now, it's not that the letter is associated exactly with the image, but the image's importance stems from the number in the top left.

With all these clues, I wrote a python script to extract the password from each file, enter it via stdin, retrieve and parse the stdout for the file and the associated character:
#!/usr/bin/env python

import os
import sys
from subprocess import Popen, PIPE, STDOUT

dir = sys.argv[1]
fileList = [None] * 48
values = [None] * 48

def addFiles(d):
  global fileList
  x = 0
  for f in os.listdir(d):
    fileList[x] = f
    x += 1
 
def fileRead(list):
  global values
  offset = int('2AB0', base=16)
  x = 0
  for f in list:
    with open(dir + "\\" + f, 'r+b') as fin:
      fin.seek(offset)
      r = fin.read(32)
      values[x] = r
      x += 1

def cleaner():
  x = 0
  for v in values:
    s = ""
    for c in v:
      if not c == '\x00':
      s += c
    values[x] = s
    x += 1
  
def solver():
  x = 0
  t = ""
    for f in fileList:
      p = Popen([dir + "\\" + f], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
      out = p.communicate(input=values[x])[0]
      s = out.decode()
      print s
      t += s[-3:]
      x += 1
  print chr(int(t))
 
addFiles(dir)
fileRead(fileList)
cleaner()
solver()

The script run, generates the files and ends up spitting the data out.  Funny enough, it errors out at the end but spits out what I wanted... so I just left it.  It's hacked together and does the job :]

Here's some screenshots of it running, the subsequent error and the end result:






As you can see, it does the job.  Like I said, it errors, but whatever.  All we really wanted were the images extracted and the chars from each file.  I decided I'd just manually look at each file and correlate it to the character / number.  Basically, this means image 67782682.jpg (which has the number one in the top left) is character one of the final key, or: 'm'

I'd assume I could easily use python to leverage functionality from OCR libraries (someone must have written one) to automate this process, but it wasn't hard.  Eventually I ended up with the final key:

mor3_awes0m3_th4n_an_awes0me_p0ssum@flare-on.com


04 - binstall

This is where the difficulty levels starts to pick up drastically.  The first three challenges are
  • Minesweeper Registration
    • Easy, demonstrate you understand basic principals
  • Ultimate Minesweeper
    • Easy, demonstrate you can think creatively and apply basic principals
  • FLEGGO
    • Easy to slightly harder than easy, this isn't a large step up in difficulty, though it does require some concepts that aren't required for the previous two to automate it.
  • binstall
    • Uh, oh.  This is where I started to find it got much harder. 
For folks with some RE experience, I'd expect this to not be very challenging.  For me, with extremely limited RE experience, I didn't even know some of these techniques existed.  It was a great learning experience.  I eventually completed it, but needed some hints along the way.

FireEye provided another binary for this challenge, named 'binstall.exe' and some clues such as 'Run this on a machine that someone uses FireFox on' or something.  That's telling that this may be getting into the malware reversing aspects.  So naturally, the first thing I did was run it.  It launches cmd, does who knows what and closes.  Suspicious.

So the second thing I did was try to attach a debugger.  No go, it's a .NET binary again.  Thankfully I now knew what to do with those and was able to open it with dnSpy.  Only, this time it looked a little... different than I expected:



It looks obfuscated.  Those are clearly functions and variables and classes, but it all appears to be unicode values.  Now the interesting bit was that these aren't just reversible unicode characters.  Looking at them, it's easy to tell that many of the hex codes fall outside of the normal ASCII printable character set, so it's unlikely I'd be able to just interpret it as unicode and get anything meaningful out.

Having no idea how this obfuscation technique worked, I started looking around the application.  My knowledge of obfuscation is towards lower-level, where the file is encoded or encrypted then decoded / unencrypted at runtime by a stub.  This would make the entire binary, or just the shellcode or hijacked section garbage to static analysis.  This is a little different, the application can be debugged and analyzed statically.  It's just that it makes little sense.

I noticed that there is an interesting class name, visible in the bottom left of the above screenshot 'ConfusedByAttribute'.  Looking at that class, I saw the below:



A quick google shows that this is indeed the culprit.  An obfuscator for .NET called 'ConfuserEx' was used to mangle anything useful in here.  Immediately there's a starting point to figuring out what's happening here.  Looking around, I stumbled upon multiple articles about defeating this application's obfuscation techniques.  While a stellar article by Talos helped me understand it, these manual techniques just weren't necessary.

This is classic for me, I spent a lot of time expecting this would be some difficult manual deobfuscation and the automated tools wouldn't work, but as it turns out there's a tool called NoFuserEx.  It does pretty much what the name implies and thankfully it works for this application.  Running NoFuserEx isn't as simple as just running a binary, as the source is available on github but it needs to be compiled and has some dependencies.  Once compiled, you'll see the following when it runs:



It's a pretty slick little app.  Here's what it looks like when it's run:



After doing what it said, I was left with a 'NoFuserEx_Output' folder with a copy of binstall.exe within.  Opening it within dnSpy wasn't as immediately rewarding as I thought it might be.  Although it certainly did deobfuscate, the variable, class and function names were still garbage.  One thing that was immediately visible to me though were the string values and such.  Take a peek at the below screenshot.  This is effectively the same position as the first screenshot of the binary in dnSpy, as it's the module entry point:



While the names may not be deobfuscated, the string definitely was:


string text = Environment.ExpandEnvironmentVariables("%APPDATA%\\Microsoft\\Internet Explorer\\browserassist.dll");

Well, that's not suspicious at all!  Seems like progress is being made.  A little bit of research and I came across another tool: de4dot  de4dot is another deobfuscator, but works with all sorts of obfuscation techniques and tools.  Running it shows a plethora of options, but it's relatively simple once you get it working.  Obligatory screenshot:



I think the '-p cr' switch on the end is superfluous at this point as NoFuserEx was already run, but alas it worked with it anyway.  Looking at the resulting binary in dnSpy now shows a major difference:



Although mostly generic names were applied, this is wayyyyy easier to work with.  I was thrilled when I saw this.  Now it really became time to figure out what was happening here.

I know that a DLL is being accessed or created, but I have no idea what it is.  Looking around the code, there's a huge Base64 encoded string, right after 'if (!File.Exists(A_0))'.  This b64 string is converted from b64 and added to a byte array, which means it's probably a file being written.  Debugging the code while it's running shows that the check to see if the file exists is checking for the DLL:



At the end of this function is 'File.WriteAllBytes(string_0, array);', so if the file doesn't exist, it's created.

I learned a lesson here regarding paying more attention to what is happening in the code.  I initially took the b64 string and simply decoded it, passing the raw bytes into a file.  I ran file against it to see what it might be and was a little surprised at the results:


/root/Desktop/b64_unknown.bin: DIY-Thermocam raw data (Lepton 2.x), scale 56542-56996, spot sensor temperature -223653429732442112.000000, color scheme 222, calibration: offset -8029395317822586880.000000, slope 337726.437500


Thermowhat?  Turns out this is a thermal camera image format.  So, I download some thermal reader and open the file:



After lots of fiddling, I'm still at a loss.  I spent a bit more time looking at the code and realized I missed a fairly important portion...

for (int i = 0; i < array.Length; i++)
{
 if (array[i] != 0 && array[i] != 33)
 {
  byte[] array2 = array;
  int num = i;
  array2[num] ^= 33;
 }
}

Well, once I discovered that it made a bit more sense.  The carrot indicates this is an exclusive OR, so that's why linux thought it was thermal imaging data-- the file headers just happened to be that format.  The next question was trying to figure out what the DLL was doing.  Googling for the file name 'browserassist.dll' didn't yield any telling information, except that people had uploaded this file to online AV scanners, etc.  Looks like it's just a garbage name.

This is where I started needing some hints, I didn't have familiarity debugging DLLs outside of tools like Immunity, so I wasn't sure how to invoke DLL functionality (especially not knowing what exists within it).  A lot of places mentioned using IDA-- while I did get it working, I ran into a few issues.

One, the newest IDA Pro (free) has had the debugger removed.  Two, I couldn't do much with the raw instructions of the DLL, I just didn't know what to even begin looking for.  Three, I learned a bit about FLIRT signatures, but couldn't find any applicable ones.  It was a little out of my league.  So, I asked around and got my hand on a copy of IDA Pro 5 (free edition) which still had debugger functionality.  Albeit not quite as smart as newer versions of IDA, it did the job.  It didn't understand a lot of the functions inherently, like IDA 7 did, but I could correlate the two and figure out what functions were what on the older version by their offsets.

I knew, from FireEye's hints, that I should be looking at Firefox for this challenge.  Likely, this meant that when I launched FF, the browserassist.dll file would called.  So, I launched FF.  Again and again and again.  Nothing happened.  I launched it in Immunity and tried to debug the dll, but I didn't have any luck.  Without the static analysis experience, I was dead in the water.

At this point I did a little searching around.  I found a few discussions regarding challenge four for FLARE and found people had experienced a very similar problem.  I try to avoid walkthroughs while I'm trying a challenge, but a hint can sometimes be appreciated.  As it turns out, this DLL will only work with certain 32-bit versions of Firefox, specifically <55.  So I installed an earlier version of FF and got to work.

I set IDA up to launch the newly installed Firefox and to debug the DLL, setting a breakpoint on it's entry point.  The DLL starts off by calling 'GetModuleFileNameA' which takes three arguments: hModule, lpFilename and nSize.




A backslash, 5C, is then pushed to the stack.  At 0x67062640 a strrchr is called, locating the last position of a backslash to obtain an index of where the process name begins.  This is then converted to lower case.

Once lowercased, the process name is hashed and compared to a hardcoded hash value in the application.  If it matches, it's Firefox.  I was unaware of this, but I guess it's a common malware technique to avoid revealing the actual process name.



If the comparison is true, at 0x67062869 a version check is initiated, calling 'GetFileVersionA' and 'VerQueryValueA'



Shortly after 'VerQueryValueA', a comparison is done to verify if the located version number is less than 55.

0x670627B3 cmp  edx, 37h            ; 37h = 55 decimal
0x670627B6 jnb  short loc_670627BF  ; jump if not below
0x670627B8 mov  [ebp+var_1C], 1     ; mov 1 (true) into ebp+var_1C



After the checks have passed, the DLL runs a CreateThread:



at 0x67069270, or RVA +9270, is the thread that will be created.  This thread contains two functions, let's look at the first.



So, at 0x67069273 a call is made to the first function, offset +6E10  At first I was at a loss-- no idea what I was looking at.  After looking through I noticed some variables being pointed to (thanks IDA): lpszObjectName and lpszServerName

Debugging until an unknown call, right after lpszServerName gets pushed to the stack:



Looks like some good progress.  More base64.  Decoding it appears to just be garbage again.  I pulled the function used in creating the DLL and replaced the base64 with the pastebin content, just to see if it was xor'd by 33.  If this is an encrypted string, there must be a built in decryption routine.  I noticed another function after the download that had two arguments, 'pcbBinary' and 'pszString'.  A quick google for the first argument returns 'CryptStringToBinaryA' as the first result...

At offset +2430 a call is made which first performs 'GetModuleFileNameA', then 'CryptStringToBinaryA' if it returns true.  After this, multiple other functions run until it eventually arrives at 'CryptDecrypt'




I followed through the crypto routines and ended up placing a breakpoint right after that function.  Here's the result:



Hell yeah. Following this variable, which I renamed to 'var_PastebinConf':



It's a config file of some kind, full of javascript.  I pulled it and saved it to a file.  Now, I understand what the first function does of the initial new thread created by the DLL after verifying it's the right version of Firefox.

The second function does a loooot of stuff.  Stuff that's beyond me, except that eventually Firefox launches.  Since this is right after the config file, it's safe to assume that the second function handles hooks into the web browser.  I suspected this because I set breakpoints right after the function returns, and they were never hit.

There are still questions, though.  I was stumped at this point, so I had to take another hint.  I wish I hadn't though, as it was a forehead-slapping moment of 'duh'.

Looking back through the javascript it's pretty clear that any injects that will happen are going to be against 'flare-on.com'. Specifically, a few files are named and are likely candidates:
  • /js/controller.js
  • /js/model.js
  • /js/view.js
Browsing to flare-on.com, I first looked at controller.js  It seemed like typical javascript.  I then went to the same site in my compromised browser and again, looked at controller.js  What I saw made a lot of sense:



The function at line 43 in the controller.js file has been hijacked.  On the left side we see the typical JS that's loaded, where on the right side in our compromised machine we see a new function thrown into the mix.  Looking at this function, it's clear that it's associated with a password.  What is the password used for?

Well, there's also more purposes for these injects, looking through the config that was downloaded I noticed a specific line,

Within the JS there's also a line that adds some functionality to the flare-on.com 'bash' prompt, specifically it adds 'su' functionality:

"else if(val.substr(0, 2) === ',27h,'su',27h,') view.askPassword();

Once that inject is active, typing 'su' into the prompt on flare-on.com results in a password prompt.  Otherwise, you get a normal command not found error.

So what's the password?  Let's look back at that JS function that was inserted.

function cp(p) {
    if (model.passwordEntered = !1, 10 === p.length && 123 == (16 ^ p.charCodeAt(0)) && p.charCodeAt(1) << 2 == 228 && p.charCodeAt(2) + 44 === 142 && p.charCodeAt(3) >> 3 == 14 && p.charCodeAt(4) === parseInt(function() {
            var h = Array.prototype.slice.call(arguments),
                k = h.shift();
            return h.reverse().map(function(m, W) {
                return String.fromCharCode(m - k - 24 - W)
            }).join("")
...
...
...
}

I redacted a lot of it, as the beginning shows the jist of what's happening.  Basically, it first checks if a password was entered, and that its length is 10 characters.  The next part is where things get fun:

123 == (16 ^ p.charCodeAt(0))

This snippet checks to see if the the value of the first character of the entered password is equal to 123 once xor'd by 16.  The really nice thing about xoring things is that it's reversible and we know the cipher.  If we simply xor 123 with 16, we'll get the value:

> (123 ^ 16);
< 107
> String.fromCharCode(107)
< "k"

The next character is a bit different:

p.charCodeAt(1) << 2 == 228

The above snippet performs another bitwise operation against the character at position one (two, because arrays), specifically a left shift, or shl.  This is another function that can be reversed to have it give us the character:

> String.fromCharCode(228 >> 2)
< "9"

Neat, huh?  The third character, a breeze:

> String.fromCharCode(142 - 44)
< "b"

The snippet for discovering the char at position 3 (4) does not yield enough information to easily check at this time:

p.charCodeAt(3) >> 3 == 14

So, moving onto the fourth position (char 5) is where things get a little more involved:

p.charCodeAt(4) === parseInt(function() {
    var h = Array.prototype.slice.call(arguments),
        k = h.shift();
    return h.reverse().map(function(m, W) {
        return String.fromCharCode(m - k - 24 - W)
    }).join("")
}(50, 124) + 4..toString(36).toLowerCase(), 31)

This one looks really, really complicated.  It's not, just eval it:


Easy peasy.  Eventually it was concluded that the password was: k9btBW7k2y

Once logged in, it seemed I was root!


Buuut, unfortunately it wasn't over.  This is where I needed yet another hint.  Two variables are set when the password is entered successfully, 'model.root' and 'model.password'.  'model.root' is set to '1' and 'model.password' is set to the password value.  The javascript has many cryptic looking functions, one that piqued interest if named 'de'.  'de' references 'model.password', not much to go on, but following it lead to a nasty bit of JS.  Using a similar technique as prior, it's possible to just eval this code in out javascript console and see the final value we need:



So, change directory to Key and ls:



There we have it.

Although some hints were necessary, I'm confident that if I put enough time in and didn't crumble to the allure of just completing the task, I would've figured it out in time.  I am proud of how far I got without needing any though, as this was all new to me.

Takeaways


For starters, I'm really looking forward to FLAREON 6.  Although I only got through 3.5 of the challenges by myself, even getting a few hints resulted in me being exposed to so many more techniques and I can only imagine the difficulty increase for challenge five!

I can easily imagine FireEye could add arbitrary difficulty to this, for one thing, the JS we downloaded was only mildly obfuscated.  Imagining a minified version of that script is ugly, and would add a lot of difficulty.  Not necessarily more difficult overall, but significantly more time consuming-- that's essentially equivalent, right?

I learned a lot about IDA from all this, simply by using it, and found a lot of new tools like dnSpy.  I'll probably spend some time doing some of the HackTheBox reversing challenges next, as I'm finding it's useful in understanding how applications work, as well as building my lower level foundation.

I got all my hints from forum posts and eventually this document in particular.  Thanks Tyler Dean!

Thanks for the good stuff, FireEye, looking forward to next year.


Comments

Popular posts from this blog

06 - How to maybe not be so bad at fuzzing, Part 2

07 - Just Another OSCE Review

05 - How to maybe not be so bad at fuzzing, Part 1