Offensive Security Exploit Developer
I purchased the EXP-301 Windows User Mode Exploit Development course near the end of December 2021, to prepare for the Offensive Security Exploit Developer (OSED) certification exam. This certification was the final one of the three required (OSWE, OSEP and OSED) to achieve the next-gen Offensive Security Certified Expert (OSCE3).
My amazing journey started 3 years ago with the OSWE certification. Countless hours of study and three 48-hour exams later, I am proud to have completed all three required certifications (OSWE, OSEP and OSED) to earn this title!
In this course review, I will cover course contents and tips and tricks from my personal course experience.


The main focus area of OSED is binary exploitation, with topics like:
  • Dynamic analysis with WinDbg;
  • Buffer Overflow;
  • SEH Overflow;
  • Egghunting;
  • Reverse engineering/static analysis with IDA FREE;
  • Memory protection bypasses, such as ASLR and DEP;
    • DEP bypass via ROP Chaining;
    • ASLR bypass via memory leak/bruteforcing/creative function abuse...
  • Writing Custom Shellcode;
  • Format String Attacks;
You can find the table of contents at the following link:
The course covers the entire binary exploitation process, from protocol analysis and vulnerability discovery via reverse engineering with IDA Free and WinDbg to developing an exploit with custom shellcode and encoding. There is a strong focus on assembly and various tricks to overcome certain problems.
OSED was by far the exam I dreaded the most, as my binary exploitation skills were limited to OSCP-style buffer overflows and public proof of concepts at that time. This course was definitely going to push me to my limits.


I've got to be honest here. Even though I officially purchased the course at the end of December, it took me until after the holiday period to finally gather up the courage to begin. This may or may not have been because I promised my girlfriend to ditch my computer during the holidays after an incredibly busy end of 2021.
One of the first things I did, was to book the exam date for the 2nd of May. I did this because of two reasons:
  1. 1.
    Exam spots fill up pretty quickly. Either plan ahead if you would like to take the exam on a certain hour of day, or be flexible and regularly check if someone canceled. I would recommend the former;
  2. 2.
    Planning the exam ahead gives you an end date, which in turn gives you something to look forward to. This also puts some pressure, which forces you to commit your hours;
In the first month, I managed to cover everything up to chapter 6 (egghunters). I spent my days working and my evenings and weekends studying. I progressed slowly but surely and took my time with the extra miles. These exercises challenge you to build on top of the course knowledge and solve issues independently. In case of Offensive Security, it feels like they kick of the training wheels and push you down the hill, #TryHarder style.
However, the next two months proved incredibly busy at work and I barely advanced. With one month to go until the exam date, I realized I had to either reschedule the exam or turn on the turbo. There was no way I was going to pass the exam without putting in sufficient hours to completely understand the topics. I decided to halt all other activities, request some free time at work and spend every single free hour of April on this course.
On average, I would say that it took me a total of 2 months of disciplined study, spending at least 6 hours/workday and two entire days every weekend. The most important part here is to make a realistic planning for yourself and keep in mind that life happens. I would recommend to book the exam at least 4 months ahead to give yourself enough time to practice.

My Course Experience

I received quite a few questions about the course prerequisites. The truth is, people of all sorts of backgrounds and experiences start this course and some may find it harder than others. Though, one trend I noticed on Discord is that even guys who did the old OSCE sweat on the reverse engineering part of the course. Here is an overview of my relevant experience, before I started the course:
  • I did the old OSCP, which included basic stack buffer overflows. This is also where the EXP-301 course begins;
  • I learned some assembly at school 6-7 years prior to starting the course, though this was a very limited instruction set. It definitely helped me understand the basics, but the course goes way beyond this;
  • I did two junior reverse engineering CTF challenges in the past, though they were more about bypassing something (like a license check) instead of code execution. I used OllyDbg for both;
  • I had some Windows API knowledge from the OSEP certification and Red Team tool/implant development. This included basic C, C++ and C# experience. I was in no case a low-level programming wizard;
  • Custom shellcode was like dark magic to me;
  • Apart from OSCP (Immunity Debugger) and two OllyDbg uses, I had no low-level code debugging experience;
In every chapter, most of the concepts were new to me, but they are explained thoroughly. In my opinion, the course knowledge and exercises are sufficient to pass the exam. I did not use any additional platforms/exercises to practice, nor did I even complete all of the extra miles. I simply did not have the time. However, it is still recommended to do these.
Where the course was perhaps lacking a bit, was the assembly knowledge. Nonetheless, Connor McGarr (@33y0re) produced a huge collection of high-quality binary exploitation writeups on his blog, containing lots of tips and tricks on chaining gadgets with lesser-known assembly instructions and required compensation gadgets to bypass DEP and ASLR. Definitely check out his articles and try to get yourself familiar with as many "strange" assembly instructions as possible!
Upon completion, my key takeaways from this course were:
  • Learn how to write, manipulate and encode custom shellcode on assembly-level. I guess I can now expand on the knowledge from the course book to replace the function resolver with Hell's gate, Halo's Gate or Tartarus Gate techniques to bypass EDR hooks;
  • Learn how to write your own shellcode generator and disassembler. Quite sure this will come in handy on the next red team engagement as an initial stager!
  • Learn how to Reverse engineer protocols and hunt for vulnerabilities in lower-level programming languages. I will likely apply this knowledge later on Red Team engagements and in bug bounty. The learning curve was steep, but I've started to enjoy WinDbg and IDA;
  • Master how to bypass DEP and ASLR. The knowledge from the course immediately helped our team write a custom Cobalt Strike stager, chain it with a ROP chain to bypass DEP and load a beacon directly in memory of the target process from a buffer overflow exploit. This actually evades some EDRs if you avoid RWX memory!
  • Expand assembly knowledge and develop a methodological approach when confronted with unknown instructions;
  • Improve Python binary exploit scripting skills to chain multiple vulnerabilities and achieve remote code execution;
From a red team perspective, I think this course can be a real added value and I would very much recommend it!


You are not alone in this certification. There is an awesome Discord community with all sorts of people going through the same pain and sharing tips & tricks to nudge you in the right direction, without giving you the final solution. Definitely check it out!
A few people also share scripts. I found epi's GitHub repository to be the most useful. There are some great examples of things you would like to automate. I extensively used the attach-process.ps1 and took some of the other scripts as base to build my own tools. Don't forget to give this guy a follow and the repo some stars. Thanks, epi052! Fair warning: do not use these scripts blindly, but rather try to understand them and implement your own variation!
Additionally, I would absolutely recommend a custom WinDbg dark theme, like the one from nextco's GitHub repository. This one is much more readable and less tiring for the eyes. Trust me, in a 48-hour exam this makes a huge difference.
On Discord, there is also a user named b0ats, who developed an awesome framework to search for high-quality ROP gadgets based on rp++ output. This script suite is not open source at the time of writing, but I learned a ton about gadget filtering and assembly variations to achieve certain tasks from his input. @b0ats, if you read this, thanks!
Special shoutout also goes to ApexPredator, JustinSteven and PopPopRet. They gave me some #TryHarder-style nudges in the right direction when I was stuck, and took the time to explain some concepts. Thanks, guys!

Custom Tools

Automate all the things. Really, you should. I found myself quite a few times in a situation where I was doing tiring, error-prone repetitive work. Do not waste precious exam-minutes on this. I listed a few things I automated.

ROP gadget identification and categorization

The course recommends rp++ to build a list of ROP gadgets to bypass DEP. However, this results in an unfiltered list, taking the target module's preferred base address as starting address. There are other tools available, but they often require you to download the binary file to your kali machine, which is by default not permitted as per course policy.
You could also use regexes to filter for interesting gadgets, like the one below that would highlight mov instructions from any register to eax:
^0x[0-9a-fA-F]{8}: mov eax, [a-Z]{3} ; ret ;
This still requires a lot of time, and you can easily miss good gadgets or variations that achieve the same (e.g. xchg eax, [other register]). I wrote a long and dirty script to filter the rp++ output txt for high-quality (short & with limited side effects) gadgets, removing addresses with bad characters and taking ASLR into account if specified.
When you add the ASLR argument, the script also replaces part of the address automatically with "dllBase+" to make it easy to copy and dynamically recalculate the base address in your exploit. However, this requires the image base address rp++ used for its output, which can be passed with --image-base. Try not to waste time on this on your exam as this is heavily prone to human error.
You can find my script here.

Rp++ image base discovery

Rp++ does not output the image base for the resulting rop gadgets. Therefore, I wrote a simple script that you can drop on the dev machine and run on the same module as rp++ to find out which dll base address rp++ used in its output file. The output of this script can be used as --image-base for Run this on Windows to not violate exp-301 course policies.
You can find my script here.

Ad-hoc gadget filters

Similar to the large script described above, I created live lookup scripts as well. These were simple regex wrappers with a length filter, searching rp++ output and returning a specified number of gadgets, sorted from shortest to longer. The following example allows you to search for add instructions (or adc) between 2 specified registers and returns the shortest gadgets first:
def add(gadget_list, src_register, dst_register):
gadgets = []
r = re.compile(r'^0x[0-9a-fA-F]{8}:.*(add|adc) ' + dst_register + r', ' + src_register + r'.*ret')
long_gadgets = list(filter(r.match, gadget_list))
gadgets += sorted(long_gadgets, key=len)
return gadgets

WindDbg dark theme

I edited the dark-green-x64.wew from nextco's repository to be more compatible with the course, you can find my edited version here. The main differences are:
  • Symbols are only resolved locally, as this otherwise hangs the UI for a while when starting WinDbg and attaching to a target process;
  • Removed some unused windows. The Workspace now displays the assembly code & command windows like in the course and additionally also an overview of the registers and stack on the right. This greatly improved my work speed and visibility.
In my opinion, this should be the default workspace in the course. Credits to nextco for creating this!
My tools may not be as good as some of the brilliant stuff some people created, but they got the job done. They definitely helped me get a better understanding of the course content and where my scripts lacked, I was still able to quickly get what I needed with regex searches.

Other Tips

  • During the course, pick a challenge and write a report. There are some specific reporting requirements, so familiarize yourself with the reporting template beforehand!
  • Write your report as you progress through the exam;
  • When reverse engineering, note down your payload restrictions for every code path. You will forget about certain conditions as you progress.
  • Take regular breaks on the exam. I took a break ever 2 hours and after every small or big victory. Everyone is saying this and you are no exception. You also need breaks!
  • Comment everything, both in IDA and in your scripts. Something may look straightforward to you now, but your future sleep-deprived self might spend expensive exam-minutes or even hours to reverse engineer past thoughts!
  • You are not always allowed to download binaries to analyze with IDA. Make sure you know how to do some stuff in WinDbg as well, like how to find IAT addresses. If I recall correctly, there was someone in the Discord chat who failed the exam because he downloaded a binary to resolve IAT entries in IDA.
  • If you read between the lines, the course teaches you lots of assembly tricks. Make sure you can work with available ROP gadget instructions. E.g.: How to subtract when only add is available and there is a bad character involved? Can xchg or push pop be used as alternative for the mov instruction? How to compensate for side effect X of a certain assembly instruction?
  • In IDA, use color codes e.g.
    • Red for potentially vulnerable memory corruption functions (memcpy, strcpy, memmov...);
    • Yellow for potential memory leaks (fopen, ... , format string functions ....);
    • Green if any of the above functions are deemed not vulnerable;
    • Blue for code paths leading to a vulnerable function;
  • In IDA, rename variables and functions as you discover their purpose. Sometimes I even used things like callsmemcpy1 for a function that has a code path to a vulnerable memcpy function. A function calling this function would become calls_calls_memcpy1 etc. This was just to help me identify code paths to a vulnerability when the function's purpose was not immediately clear.


As with all the Offensive Security exams for the OSCE3 certification path, you are required to complete several practical challenges during a 48-hour exam. At the end, you have 24 hours to finish and send in your report.
I always spend the final days before an exam like this preparing a playbook, as I do not want to spend any brainpower or precious exam minutes on or debugging typing mistakes in commands. The playbook usually consists of 3 parts and I go quite far in this. I included an overview below, to give you an idea:
  • Pre-exam preparation:
    • Contact details in case of proctoring/exam issues. No need to waste time on this when already stressing about technical or other issues;
    • Folder prep: 3 assignment folders with the necessary subdirectories, helper scrips, notes markdown file and exploit templates already present. The goal is to just cd into and work in this directory after picking an assignment, with everything in the exact place you expect it to be;
    • Steps to go over before starting the exam, including proctoring login, applying shared folders in your VM, opening the course syllabus and my course notes for easy reference. I also note down what to put on which screen as to not mess with the workflow that I practiced during the course;
    • Commands to run to unpack and launch the vpn, rdp into a machine while exposing an SMB share, PowerShell commands to import a custom WinDbg workspace and import a script to easily attach WinDbg to processes...
  • Main playbook:
    • Commands I used during the course for easy copy-paste;
    • WinDbg commands to achieve specific tasks;
    • Rop chain & ASLR bypass tips
    • Procedures to follow when reverse engineering in IDA;
    • Things to try when stuck;
  • Post-exam tasks
    • Procedure to zip & upload the required files;
According to the OSED Exam guide, you must complete at minimum two out of three challenges to acquire a passing score. The final proof of concept exploits should be written in Python3 and included in your final report zip.
I started my exam on the 2nd of May at noon and it was brutal. I spent around 12 hours on the first challenge At 1am, I managed to wrap it up and to sleep. I recommend anyone to sleep at least the first night on every 48-hour OffSec exam. You do have plenty of time to solve the challenges, but you need your full brain capability. The same goes for taking breaks.
The second day, I made sure to finish reporting on the first challenge and started the next one at around 10am. It took until 8pm to finalize that one and wrap up the report. I spent the rest of my exam time on the final challenge.
My final report was >180 pages and I am very proud on some of the stuff I did. As usual, Offensive Security managed to put together a great exam, which challenges you to go above and beyond!


Offensive Security put together an amazing course on binary exploitation. The course content explains the complex concepts in a way that is quite easy to understand, but don't get me wrong, the pain is real. This certification has been a rollercoaster of emotions. The following message from my study buddy PopPopRet sums it up the journey quite well:
The Exp-301 experience in a nutshell
I cannot begin to describe the feeling when I got word that I passed the Offensive Security Exploit Developer (OSED) exam. After 3 years of dedication, I am now officially an Offensive Security Certified Expert (OSCE3). It was all worth it and left me with a great sense of achievement!

Other Reviews