
we have an amazing keynote uh really privileged to have Valentina speak for us today and she will be talking about the exploit development life cycle from concept to compromise uh Valentina palotti or as she's known on the Internet is trumpy uh specializes in lowlevel vulnerabilities exploit development and offensive security she is really an amazingly talented uh woman who has done Android Linux windows and a bunch of other things as well so let's welcome her to the
[Applause] stage uh thank you so much Sylvio for that amazing introduction um and thank you Kylie as well uh for inviting me to speak today um and all of you for being here um uh I was told that this is the biggest technical conference in Australia so I hope to not disappoint um but my talk today is going to be on the ex exploit development life cycle from concept to compromise oops oh no already okay so um thank you siio for introducing me you may know me by my uh handle Chompy uh I'm a reverse engineer a vulnerability researcher an exploit developer a post exploitation developer and I'm interested in all things offensive security um I've been in
security for about 7 years it's going to be 7 years this November um time flies um I'm the head of vulnerability research and Export development in IBM exforce um where I oversee the use of uh using zero day exploits and endday exploits on red team engagements um I'm chronically online professional poster and as of this year I'm a pony Award winner um so a little bit of background on my motivations for this talk um I wanted to talk about what I know best which is exploit development um I find exploit development is somewhat misunderstood because while vulnerability research is really awesome uh OD days and bugs seem to grab headlines and get all the glitz and
glamour but all of the work uh behind building a production level exploit kind of is overlooked or Taken has a given after a bug is found um and in reality there's significant preparation that's required um to get an exploit um to production like value ready to go one click and um hit I'm sorry I'm just going to this is bothering me so I'm just going to put it back on full screen I am really sorry about that guys um let's start over I'm Valentina it's great to be here with all of you today let me tell you a little bit about why I'm doing this talk um I find exploit development is typically misunderstood um VR gets all the glitz
and glamour but um I chose this topic not only because I am passionate about exploits but also because of my role um at IBM xforce gives me a unique perspective on the whole development um life cycle of exploits which is not really common um in those that participate in the life cycle of exploits like it's not really usual for the exploit developer or the bugfinder to be the one to actually get to deploy the exploit on a real life environment which is um a process that I get to oversee and I'm fortunate enough to participate in all of those steps so I am going to share those insights um with you today my gosh okay so uh what is an
exploit an exploit is a method or a piece of code that leverages vulnerability in software networks operating systems Hardware um or anything else to trigger behavior that was not intended by the designer or the developer um now one of my biggest pet peeves is uh using the word find with in combination with exploits um have you ever heard that before maybe you're guilty of it um why is that my pet peeve um it I promise you it's not because I'm a pedantic nerd that can't help but correct people um there's really a reason for it and the reason is because the distinction is important um vulnerabilities are found and exploits are built um and so the reason why I don't
like the term is because using the word find kind of discounts all of the hard work that goes into building the exploit after a vulnerability is found um and one of the points that often gets overlooked is that exploits are software like anything else and so a lot of the steps to building an exploit have analogous steps uh have an analogous step to like building software mature commercial software in the software development life cycle which is kind of why I pick the title um okay so what does the timeline for building an exploit actually look like how long does it take um and the V the answer varies on a lot of factors um but in the most complex scenarios it can
take months and months of work to actually complete the whole thing um and each of these stages could easily fill a talk in itself um but what I'll talk about with you all today is going to be more of an overview my goal is to like provide um some like high level context to each of these steps and provide um some insights on what kind of goes on on each of them okay okay so um not all exploits are equal so if you've ever tried to run something that um you got off of GitHub or exploit DB and it failed you kind of realize this right um there's a difference between having a PO that
might trigger uh Dem demonstrably trigger a bug and maybe cause a crash um or maybe a PC that does demonstrate arbitrary code execution um but it's only works on specific versions or maybe not that stable and only works some of the time to a fully fledged capability that takes one click and done um and so there's different uh there's different stages and different forms of quality for exploits so the first step in the exploitation timeline um is evaluating exploit uh exploitability of a vulnerability so deciding how realistic is it that you can get arbitrary code execution from a bug or a group of bugs that you may have um and here's where the concept of
good and bad bugs come in um software vulnerabilities will produce an exploit primitive and an exploit primitive is a generic building block for an exploit um vulnerability should be evaluated in terms of exploitation by the exploit primitive that they provide so a bad bug is one that doesn't give a very strong primitive or any primitive at all um doesn't trigger any type of behavior that could lead to arbitrary code execution or can't be built on top of
that um and vulnerabilities of course are non-uniform um there's many different types of vulnerabilities in vulnerability classes um and the are very many variables to determine how they're useful um and those variables depend on the type of vulnerability uh but ultimately in general it really does boil down to what can I influence where and how um for example a bit flip at a static offset is going to be less useful than a controlled right at a controlled offset um and so now for weaker Primitives they can be turned into stronger ones uh via more State transitions which we'll talk about in a second um and you might also want to consider the repeatability of a vulnerability so
can this uh primitive be safely triggered more than once um and that makes for a a more flexible um flexible primitive um and of course you need to talk uh think about exploit mitigations uh mitigations don't necessarily prevent bugs themselves from happening um they're really intended to neuter Primitives exploit Primitives uh think like pack that protects uh somewhat protects against overriding function pointers or CFI that prevents uh against roing to individual instructions or arbitrary instructions or making certain uh areas of memory read only um so your bug might be really strong but if it can only target one of these areas that's neutered um by exploit Primitives uh exploit mitigations then um the Primitive becomes weaker and maybe
sometimes ineffective um and ultimately at this stage evaluating exploitability is a cost risk assessment so you're betting time against a reward research time um it's probably one of the most difficult steps in the cycle due to the fact that it's extremely challenging to make definitive claims about exploitability in one way or another um meaning it can be really hard to determine uh whether a vulnerability that's exploitable in theory um can't actually be exploitable in practice um I know that exploit developers that I know are typically very cautious when uh saying something can or cannot be realistically exploited because um if there's some Road bu or some seemingly insurmountable challenge we typically think that there's just
some clever clever check that exists that we can get around it or um we could probably figure it out given enough research time however you can't can't spend an infinite amount of time trying to figure it out there has to be a limit um and that is also comes into play when evaluating exploitability is kind of drawing a Line in the Sand of like okay how much time do I want to give myself to work on this and how much time do I want to risk uh against the the reward of a a fully-fledged
exploit um so now we're getting into the actual exploit construction the fun part um of which the first stage is primitive construction building on top of that initial um exploit primitive that you got from the vulnerability um so I can't talk about exploits without mentioning weird machines and a weird machine is essentially a computational artifact where execution out occurs outside of the original specifications of the program um and triggering a bug causes a system transition from normal machine to a weird machine uh halver flake has an excellent presentation called limiting weird machines I recommend you all check it out he builds a very intuitive model on how to think about weird machines which I'm briefly going to borrow for this
talk uh to talk about exploit Primitives construction uh a program can be modeled as an intended finite State machine um running on an underlying CPU with say n bit n Bits of memory and so each bit of the CPU can be either zero or one um which means means that there's two to the N possible um two to the nend nodes in this state space um and so the execution of a program traces out a path in the state space for every combination of state and CPU instruction there's an edge leading to the next State uh here the colored nodes represent the samean state or the intended states of the program and of course there could be
different combinations of Stan States depending on control flow
uh so when a buger is triggered execution gets knocked off the same state path and enters this weird State um as we can see here jumping from the colored same state node to an undefined State node um so now the instruction sequence is applied to a weird State each instruction will jump to a new unknown State outside of the same path when constructing an exploit the idea is to be able to create a path from the initial weird state to any other state um from a concrete perspective we want to think about what types of atomic operations can we perform using the undefined application behavior and how can we link them together to create a
turning complete weird machine um and so this process looks different for different vulnerabilities um say you have an outof bounds read vulnerability you might research how to turn that into an outof bouts right vulnerability which is a more powerful primitive and from the outof balance right vulnerability uh primitive then you might take that out of bounds right and turn it into an arbitrary rate primitive and finally turn that arbitrary right where you can control any contents to any address um into arbitrary code execution um so you don't have just have the bug to trigger um to control the state space or the memory State you can also leverage the normal program Behavior to influence the memory State
and this example that I've uh depicted in this slide um this uses Heap grooming to control adjacent memory and turn an out of- bounds read to an out of bounds right but even after constructing most of the exploit Primitives that you need your job might still not be done after that um which lead us to this next stage of the exploit construction uh which is refactoring or reconstructing your exploit primitive uh one of the reasons why you might need to do this are exploit mitigations essentially exploit mitigations serve to reduce the number of reachable states um from a weird state for example address authentication mitigations like pack or mte uh will bend many edges to the seg
vating state um so you can't do all those State transitions to all the intermediate States um in the space it just takes you right to seful um so mitigations require a contortion of Primitives additional steps or state transitions to achieve stable code execution uh oftentimes requiring additional vulnerabilities um such as an information leak to supplement your original Primitives too so getting around those like address authentication mitigations for example you'd use an infol leak um so now finally after all of that we have arbitrary code execution um so we've chained together all of the exploit Primitives all of the bugs in our chain to achieve arbitrary code execution and now we have a turning complete weird machine um and this is
this is where we're riding high this is the fun part uh so just to keep quick recap on what we have so far we have a bug identified a crash we've assessed its exploitability we've built a primitive chain rebuilt them the Primitive chain as needed we can execute a weird machine and finally we have arbitrary code execution now most public Pro of Concepts kind of stop here um or before this even um but because we want to create a a fully fledged mature exploit um that can be used in a real environment our work is not done uh which takes us to the next part where we're riding High we're really proud of ourselves because we did the
cool thing uh but now we have to think about uh chain stability um which is really important process in this step um and the reason why is because exploits are not deterministic meaning the behavior when you of when you run an exploit could change across exploitation attempts um so before I competed at pontoon this year um and if you don't know what ponton is it's basically like a live exploit uh code demonstration I was trying to explain what it is to friends and family um and after explaining to them that no people are not going there and finding OD days and building exploits right on the spot um and trying to explain that if that was
possible we'd be in really big trouble um they were just like wait so what's the point like why are you nervous about it you're just going to go run your thing and that's it um why like who who cares you're done uh just test it on your machine it's fine um and I needed to explain that actually a lot of things things can go wrong and they do go wrong um if you ever watch the competition um and again this is a common theme of my talk it depends uh reliability does sometime depend on the nature of vulnerability or exploitation technique um not all vulnerabilities produce unstable Behavior that's part of the reason why you might hear some
discourse um around which is better memory corruption or logic bugs because logic bugs are kind of considered to be more stable than memory corruption bugs um and the behavior might be easier to predict um so um some things that might impact stability are um the vulnerability requires getting the system to a specific state to be able to trigger it um it might require winning a race condition which is not always reliable uh mitigations can also impact stability um a lot of mitigations include a factor of Randomness um and introduce conditions that thwart individual exploitation attempts um but the goal is to increase exploit reliability to 100% such that one trigger means successful
poning so now how do we get from po quality code to where it works most of the time and on most systems maybe um to uh a mature exploit that works no matter what every single time um you will want to refine your Primitives some examples of that are tightening race condition windows or uh widening racing condition Windows where you uh make it so your exploit wins the race condition every single time that might include adding like a uh triggering a slow path for what you're racing um making your memory sprays more precise and ensuring that the layout of your uh memory layout is exactly as how you need it to be uh you might swap gadgets or objects to more
reliable ones and overall just refining the exploit up a little bit um to make sure that the reliability is 100% this might also require developing New Primitives like we said before um like Oracle Prim Primitives to bypass some Randomness mitigations and essentially you need to analyze all of the program's control flows um and refactor your exploit code so that it's able to deal with all of the possible scenarios um that might happen during execution um I'll also mention cleanup at this stage this will come into uh play a little bit later but it also comes into play for stability so you want to release all of your used resources destroy all your objects for all your
memory close all your handles um and make sure that post exploitation the system behaves exactly as it was before um and preferably if you're exploiting an individual program that that continues executing like nothing happened um the goal is to minimize exploitation side effects and restore everything um to its original function um so I've developed uh I've presented the exploitation timeline as linear but in reality it might look a little bit like this right one roadblock might land you back to square one or conversely you might have had some of these steps sorted out from a previous bug so you don't have to do some of these things that I talked about before um but in general starting from stretch
you'll have to do all of this uh so now we've moved on from the actual exploit construction to real world deployment considerations uh the first of which is versioning so what versions of the target will be supported by the exploit software um in general the more specifically Target the exploit the better um and that's because the broader um broader product targeting means that you have to deal with more variants different versions of a product have differences that can impact exploitation uh some examples are types of objects that are available and object sizes implemented programming Fe program features offsets signatures available uh gadgets drastic differences in programming logic um and implemented or supported exploit mitigations um I have
a funny story about this when I was working on my Ponto own exploit um I had it working perfectly like 2 months before the competition and all I needed to do was hope that my BG didn't get patched um and it was working perfectly on Windows 10 uh really stable and beautiful and then I went to test it on Windows 11 um and it crashed and so you might think well okay Windows 11 has all those you know fancy exploit mitigations maybe it was something like that that thed the um the exploit but no that's not why so a few years ago Microsoft uh deprecated one of their uh memory allocation apis in the kernel uh I think
it's called ex allocate uh pool with tag uh and only on Windows 11 they replaced all of the calls to this with the new one in in the OS ENT kernel um so there's only one difference between the deprecated one and the the new one um Can anyone guess what it is no uh the only difference is that uh the memory is zero zero initialized so before returning the pointer all of that memory is zero initialized the reason this is done is to prevent information leaks so if that buffer is returned to user space and it's not zero initialized um you might get some uh information leak from the previous allocation in the kernel um and that's bad but this also
had the secondary side effect of ruining my use after free race condition because it was a really tight uh race window and so if that memory was re uh zero initialized then that call caused a null pointer D reference causing the whole machine to crash so now I needed to find a whole new object to spray using either the old deprecated API but I couldn't use anything in the kernel or using the new API that specifically uh passed in an uninitialized flag telling the kernel to not zero initialize that memory which severely limited the types of objects that I could use I did eventually find one but that really limited the flexibility because the original object
that I used to spray uh was elastic in size and content and the new object that I chose was neither elastic in size and did not have any controllable content that I could control um so that was added weeks of extra work just so I could Target Windows 11 for the competition um so just like in the example I mentioned the story I just told different versions of the exploit might need to run um differently depending on the target um and at this stage targeting and fr fingerprinting is about determining how the exploit will know which of those versions to run uh if it's a remote exploit is there a way to do remote fingerprinting if
it's local fingerprinting will the exploit self modify once it has the required information or what information will be gathered before deploying the exploit so there's only one version that could possibly run um and there are various ways this can be done depending on what kind of exploit it is local or remote um some ways include like inmemory pattern matching API using an API call to return system version information reading system files um analyzing like service response metadata like a banner um from a network response um um or just you can also just uh gather that information before and only deploy one version of the exploit um and now this is one of my favorite uh topics to talk about but uh
environmental considerations so exploits in the real world have um a lot of difficulties and that's because real life systems are hard to emulate like with real life traffic and real life users the more details you have about the target the better so you can more closely emulate uh what's going on there um and you can try to approximate real systems by creating inorganic system noise uh one way to do this is using like performance testing tools or like stress testing that's available in various development kits to try to simulate noisy environment um and so some things you might want to consider uh system load can affect reliability so like Network traffic and running programs can use
resources that interfere with the exploit so like think about um a memory corruption exploit or like a use after free um some program might request that memory uh before your CH your exploit has a chance to reallocate the vulnerable object or something like that um and there also the concept of uh low resource versus high resource environment really can affect uh exploit execution for example exploiting a race race condition um requires a minimum core count of two because you have to have two simultaneous threads running to be able to win a race condition um so uh I have a funny story about this which is um we were deploying an exploit on uh for a client and the client ran
crowd strike uh so I was uh testing my exploit against crowd strike and it didn't work and I was freaking out thinking like oh they detected some malicious behavior or something um but no what actually happened was the API that I was using to spray which I called thousands of times to allocate thousands of objects was hooked and each hooked by crowd strike and each of those calls was slowed down by an order of magnitude so my exploit just never finished running it it wasn't that it wasn't successful it just took like it was would have taken like hundreds of hours to run um so things running on the machine not necessarily related to security um Can
impact how your exploit runs versus just testing on a plane development machine that has nothing going on on it um so this takes us to the next stage um which is in the real World deployment considerations which is stealth um so there's a concept of being burned that you might have heard about um and so this is just this just means that your exploit has been recovered or it's no longer uh the it's no longer in just your possession somebody else has caught it um and so this is only relevant for things that need to stay private so not anything like uh that's going to go for into public disclosure to be released publicly uh last year you
guys had uh Maddie Stone as your keynote um she did some wonderful work in in the wild uh detection exploitation for Google tag and as she said as she told you in the wild exploits have been uh increasing um so we can only infer that detection technology has matured and being on the offensive side um you kind of need to be able to blindly anticipate at how those um inth wild exploits are being detected so that you can avoid your exploit from being detected and being on one of those year-end charts that Google releases um so knowing what you're up against is crucial um uh having an EDR lab is um is important but even that can
be kind of tricky because offline labs for EDR um are tricky because the EDR might not respond the same way um if it's connected calling back to home then it's then disconnected offline and also specific EDR configurations can impact uh detection Behavior so you might not necessarily have the information of how um a target's EDR is configured um and so depending on that things can alert or not um and also the most valuable targets may be monitored by Solutions not available to the public so this is always tricky um to determine what you're up against and so having the most amount of information is the best and so other steps of the uh exploit development cycle can come into
play here so stability does the exploit crash because if it crashes then a crash dump might be produced and that kind of gives away your whole bug right um clean up does the does the expit release all of this resources or does it leak a bunch of memory to make it really noticeable um that something's gone on after exploitation um does it does the system continue functioning as normal or does the can the user um kind of figure out that something wonky is going on um exploitation Primitives are they known are they public um can they be signatured so bug triggers are kind of difficult to Signature because um you know you you can't anticipate when a bug
is going to occur and if you could then the bug wouldn't exist right but exploitation side effects um are easier to Signature because they have um a tangible uh a tangible outcome so something like credential overwrites token swapping function pointer overwrites changing values in important memory that like that you can hash the before and after those are things that are kind of easy to figure out if they've occurred um so exploit Primitives are more likely to be signatured than bug triggers and of course environmental in considerations what's running on the target EDR or even other adversaries um and some other considerations that seemed minor but might have a big impact so like size um is your exploit artifact are you
transferring like gigs and gigs um is it a small native binary or is it like some weird script written in a weird scripting language um that executes from like a shady folder or something um methods and preporation So within your exploit are you calling weird apis that only exploits call um or are you you know doing something sthier like maybe doing using direct CIS calls or something like that and last but definitely not least um which might seem funny but strings um so if there are any malware analysts in the room uh they can verify that a lot of exploits do not strip their strings um and that's like a major easy way to reverse engineer an
exploit is just by reading its strings um I have a funny story here like one of my first projects when I was studying in boot camp was to write a key logger and the goal was to write the stealthiest key logger you can and then the other team was going to hunt you on the machine and so spent a week staying up all night uh developing a novel key logging technique uh that had never been uh that was novel at the time and I was so proud of myself deployed the whole thing and I got caught on the first day because I didn't strip the pdb string from my binary um so the bottom line is uh you
want to minimize the unavoidable side effects of exploitation and make it like you were never there uh so now this takes us to obac attribution um and this might seem like it's uh kind of like stealth but it's distinctly different so stealth is about hiding as best as possible OBC is kind of about minimizing the damage control if you're caught already um so designs decisions at the stage largely depend on the actor and their goals and can be kind of subjective so not all the Sol there's no best solution for this um and the stakes can be high depending on the actor um so binary some things to consider are like binary artifacts like I just talked about um can be in
inadvertently left behind by compilers or other things so there's a decision to be made there do you strip it do you manipulate it to shift attribution um that's the decision you have to make um applying anti- reversing uh anti-reverse engineering or obfuscation to your exploit so if it is recovered and making it really hard to analyze reproduce or understand so maybe your bug might not get patched right away or you can conversely try to make it look innocuous like a normal system thing um techniques and coding Styles so specific Primitives and techniques might be associated with specific threat actors so if you've ever read like a Google tag report about an in the wild exploit
they often mention uh exploit Primitives being associated with certain actors so like certain actors prefer to use like specific exploit Primitives or objects or have a similar exploitation styles that kind of become known um with with the group so if you want to minimize what can be burned if your exploit is caught maximizing novelty isn't always the best strategy it's kind of a cost risk assessment right you might want to use things that have already been used publicly for one to not call attention to to the exploit and make it seem like run-of-the-mill something you that's already been seen before um shift attribution um and then minimize your your new novel strategy and kind of keep
that for a higher Stakes operation or something like that um and again you also want to separate your post exploitation tooling to minimize uh that getting burned as well so only burning as as few things as possible um and finally we've gotten to the testing and maintenance uh part of the timeline um in other exploit devops considerations um this is me when the offsets change on the exploit um at the end of the day exploits are just software and they need to be tested and maintained just like real software so doing things such as proper documentation so operators know how to use it um testing against all targeted versions and environments um unit testing exploit Primitives um you
might want to set up a um automated cicd pipeline to run your tests um automatically when you push new changes to the exploit so you don't have to do all of that stuff manually and of course the fixing up the tool chain when uh unexpected situations evily pop up so dealing with bug reports for your exploit that triggers a bug um and keeping up with new OS software builds so that's where the maintenance part comes in so whenever a new version comes out testing the exploit and making sure that everything still works as uh as intended and when it inevitably breaks fixing it for the new version um so some conclusions exploit development is hard and there's a lot of
grunt work that goes outside of the initial Rush of getting um arbitrary codex execution um so it's its own art separate from vulnerability research although the two go hand in hand um many people do both but it is its own art um and a lot goes into weaponizing a bug um the complexity of this timeline and this life cycle makes it so that actors with more researchers with more resources and researchers have a big Advantage um sophisticated exploit development is highly skilled work um it's very Niche and very few people are doing it for free um and the bar is high which is why we don't really see Mass exploitation of the critical vulnerabilities found in
valuable high value targets um it seems like every other day we read reports about fear mongering about the latest critical vulnerability and that you need to patch immediately or risk total ruin by hackers and you you should definitely patch as soon as possible but due to the time and skill needed to uh develop a oneclick and go exploit that could be used by like script kitties and the masses um for public consumption by your run-of-the-mill cyber crime groups um it's really unlikely that that is going to happen overnight it takes a lot of work and a lot of resources um of course there has been some exceptions but those have been in exceptional circumstances right uh so we
did see a you know a few years ago the Eternal blue being used in massive ransomware campaigns but that was an exceptional circumstance where it was the tooling was leaked from um an actor with a lot of resources that had already productized the exploit it wasn't uh Community Source exploit or anything like that um and that's it thank you so much to for to these people for helping me out uh giving me feedback on my presentation and I don't know if there's any time but um if there is I'll take questions