← All talks

Finding A Vulnerability In Half Life by Ryan Saridar

BSides Basingstoke29:3685 viewsPublished 2024-03Watch on YouTube ↗
Speakers
Tags
StyleTalk
Show transcript [en]

but how are you all doing everyone good high energy from the break yeah yeah I can see a question for you before I all start before I start how many of you have played the game Half-Life before oh yeah quite a few so could you keep your hands up for me if you have Half-Life installed on one of your devices somewhere Okay so we've all come prepared for the talk then not your devices anymore sorry of course joking but um so my name is Ryan sayedar as was mentioned I'm a cyber security consultant with jump Shack and vulnerability research is very much a hobby for me so my hope with this talk is basically to give you guys a little bit of insight

onto the journey that I took in going through and finding and exploiting this vulnerability but also hopefully to give those of you who don't have as much of a background in vulnerability research and memory corruption vulnerabilities a little bit of an understanding to do that I'm just going to build up a little bit of background first and some basic Theory so quickly just on on the point of memory within a process memory space as you can see there's quite a lot going on there are three things that are important for this first of all being the program code which is just shown in the image there if you can see it clear enough is program image and when I say

code in this context I'm not talking C or C plus plus or any high level language so machine code that's that's embedded in the executable that's used and can be loaded and executed by the CPU immediately the same goes for any of the library code that's loaded and the stack if you're not familiar with it is is just a basic data structure that's that's used to store program information for some specific purposes but I do want to go in a little bit more on that just to see where the vulnerability part comes in so this is just a picture of a stack and how it might be used by by an operating system and particularly one thing that you'll

notice there is the stack grows downwards so it's a stack just in the sense of a physical stack that you might know when you stack up your plates the concept is basically that you put on play on the top the next plate you remove isn't going to be from the middle of the bottom you have to take it off the top as well and with the stack here it's the exact same thing and if you think about a function in code then starting uh starting starting from the top there if you're running an existing function and you're going to call another function then that function might have certain parameters and so the function will put those parameters onto the stack

before it gets into into the next function now that's that's valuable because that function can then refer back to them and grab those values but before it transitions over and moves into the next function you've also got this return address value which is really important to know about and the return address basically is is the code leaving its Mark of where it started so if you're sitting in a function and you've called you know function a equals function B then once function B is done running you want to return back to function a and so this return address is the location in memory where the instructions are for that function a very blessed and then this in a

different color is is called the stack frame for function B and that's basically its own context of the stuff that it's putting on the stack uh including stuff like local variables now what's important about this is if you think about your local variables maybe within my function B I'm going to have some input and I'm going to allow users to to type their own data in and when they press enter it's all going to be copied into this variable but if I have let's say a 10 byte variable assigned there and I take in input without safely checking that the input is of the right size to fit in that box then it can overflow and that's where you have your

stack based buffer overflow occur and what can happen if you go sufficiently large in the Overflow is you can actually overwrite this return address what that means is when this function is done running it's going to look to that value that you put in there as to where it's going to go back that means that we can leverage this to control program execution so to start with my my inspiration around exploring the vulnerability that I have is actually from a few years ago and my journey developing this and and finding the vulnerability starts about two and a half years ago this is something that that popped up three years ago somebody had found a vulnerability in Half-Life but another

buffer overflow based one particularly one that targeted a specific command line parameter the dash game parameter for the game and if you provided too much data to that parameter then the Overflow would occur so based off of this I was I was very interested to pursue that concept and I thought well if that Dash game parameter might have that vulnerability then perhaps other parameters that the game supports might do and so I went into a bit of exploration around that now you can see here uh page from Val their official page which has gold sources the game engine that was used to implement Half-Life it has a bunch of command line parameters here that are possible and there's

there's many more below that but screenshots not big enough uh so my my initial idea was all right well just going to be dead easy let me grab the whole list of command line parameters that are there I'm gonna put them all together in one big string I'm gonna write a script that will take 1 000 characters let's say the letter a and put them after each of those parameters I'll throw that at Half-Life and if it crashes then I know probably something's there and I did and it did and so from there I went through a little bit of a a process of trying to identify all right which parameter is it that I found and I I did a binary search

type approach I split the payload in half so let's try the first half it crashed great let's try the second half it crashed so I thought well I've got two vulnerabilities on my hands that's excellent and so I continued the binary search approach let's see let's identify which ones are they and I halved each of them nothing all right half the next ones nothing and I wasn't splitting the the Thousand characters in half I was just separating out what parameters I was actually testing the vulnerabilities for so after a little bit of thinking and checking I realized that the Overflow wasn't occurring on any of them it was occurring on the input as a whole

and so that's where basically I go into my my exploration of this vulnerability now this is where I need all of your help I need a little bit of luck wished upon me because I had the brilliant idea for my presentation that I'm going to show and not tell which means demo and that's what it will be for the rest of this it's the goods yeah so I'm going to break out of this I'm going to actually show you so that you guys can find the vulnerability with me and maybe we can see some sort of exploit for it so to start with then I've got Half-Life ready and just sort of like uh the magician

showing you his empty hat I'm just going to show you I have no command line parameter set if I click play here Half-Life will launch so we load a game yeah cool so I'm actually going to to show you that initial buffer overflow that I was talking about too much data just being passed into the program and you get a crash and I wrote a quick script and I can I can show you the script it's not particularly complicated just for your interest it's just going to take five thousand characters and copy them to my clipboard that I can then use yes the frustrated screaming of a vulnerability researcher and then if I click play you'll see

nothing pops up at all and then it just stops and that's because it's crashing before I even get to rendering anything so in order to dig into this and tear it apart a little bit what I'm going to do is I'm going to use a debugger and figure out what's going on so to start with and you use this thing called windy bug and I'm going to launch Half-Life as you can see from my previous testing paste in those A's that we put in if I go into a debugger session here you'll just see this ldrp do debugger break it's just breaking for me right at the start before I actually start execution of the

game just to make my life easier it allows me to enter any break points I want or anything and I'm just gonna go ahead and so if you have a look at what we've got here you can see access violation and and so it seems promising on the first sight but when I saw this initially I was actually concerned because what you'll see here in the instruction is a move instruction that's that's is crashing up now when I was explaining to you how the the stack based buffer overflow might work is the concept is is that you're putting in so much data you overflow that return address and so when the function returns you're going to run into an issue and

and particularly I would understand if this was the returning but that's a move instruction not a return instruction so I want to understand what's actually going on there and see if there's anything more to this to do that I'm going to pull up a piece of software that's already open called ejac ija is just a reverse engineering tool but if I go over here and and look at the crash you can see on the left there that's your highlight this address one four zero one one five three and I can take that and put that into idrat and ask it to tell me a few of them ask it to tell me what's at that

location

and we can see the instruction that we found in the debugger as well now and everything else that's around it I could do this with the debugger as well but it's just nice and clear for you to see here and you can see that on the right hand side it's tried to decompile it and it's giving you its interpretation of what the C code merger would look like obviously imperfectly is is it's not able to reverse it for me but we can get a little bit of insight as to what's happening here and if you have a look at this function we can see that red instruction that I've just highlighted is not too far at all from the move

instruction that was stuck at so if we can pass this and potentially we can hit that reps let's understand then what's actually going on here now if we look at this move instruction we're moving something from the eax which is a register in the processor into this ebx plus 4 location and those square brackets basically mean that it's doing the math on ebx plus 4 and then it's treating that value as an address so whatever ebx Plus 4 is should Point somewhere that it can move data two and if we have a look at ebx we can see here what its value is four one or one or one or one two some of you you might know

what that is already but to those who aren't familiar that's the hex character for uppercase a which I absolutely blasted at the program so what we're seeing is that the ebx value has actually been overwritten by my payload so clearly when the program runs usually without me interfering there is some legitimate value in there that it can move something to so in order to determine what that is I'm going to run it again without my overflow without my spam of characters and what I'll do is I'll put a breakpoint the next time it runs at this address that I've got so I can actually see if without without any additional data from me what that value would

naturally be on its own and performance so now I'll put a break point that when I hit go you can see we're on that instruction this time no access violation and we've got ourselves a value here so whatever that is plus before that's gonna be a memory location that it can write data write data two so I can actually take this Incorporated in my payload and then at the exact location where the data is that's overriding that ebx value I can place that and then it should work skip the step now for the sake of time I'm not going to go through all of that and I'm just going to cheat a little bit and go back to my original

case let me just actually copy this value

so if I copy the overflow payload please then when I go we find ourselves at this location again and I can set that ebx register manually to here but realistically in the process of developing the exploit you'd incorporate that in the data that you're sending over and something you can do to identify where in that string of long A's this actually corresponds to is you can there are pattern generating scripts that generate unique patterns and then based on the value that you identify in the ebx register you can then find the offset and incorporate this address value there so now that I've set that and I click R just to check the registers again you can see that that's

now correctly at the right position and if I step over we can see here go

cool so we can see here access violation except this time the instruction that it's trying to execute is that four one four one four one four one and that is because that's the value of our EIP register that's the instruction pointer register that tells the CPU where the next instruction that it needs to execute is located so that means that the return successfully grab that value off the stack and put it in the eeib also so from here where do we go well now we have the ability to determine what's in the EIP register we can tell it to execute any section of memory that we like so how do we actually leverage this well we could point it to some code

within the application that we know is already bad but they haven't unfortunately included any malicious code inside the half-life executable but we can leverage our ability to provide input to the program and if we look we've got the stack pointer that's going to point to to our data that we put in so you can see there all the A's that means ultimately that if I can find something that can jump to that stack location then then I can essentially include Shell Code in the payload that I send as part of the command line arguments and it will jump straight to it now a couple ways of doing that first of all I can do a search on the entire

memory space and just to show you something I'll type LM which is this modules and you can see here all of the different modules that are loaded in the application and their address ranges so this is what I was referring to around the program code and this is these are all the libraries the library code that I was mentioning effectively so if I go and do a search I'm going to search memory starting at zero length till the end I'm going to look for this jump ESP instruction jump to the stack pointer and just to show you what that looks like I'm going to use this online assembler we're give it the jump ESP instruction

and you're going to see that it corresponds to the bytes FF E4 so I'll put ffe4 there we can see a whole bunch of different options to do that so that's great it's easy and straightforward now the hard part of this exploit and now I'm going to introduce you to something called aslr and we're talking about mitigations against these kind of attacks now aslr stands for address space layout randomization and if I go back to those that list of modules that you saw here effectively if aslr is enabled then certain compatible modules will randomize the location that they start at each time which means those those instructions that I just found the addresses of I can't rely on them consistently

because if I use it now and I restart my computer it's going to be a different address for the same instruction so how do I get around this well there are a couple of ways that people usually do it but I'm going to show you if I open up another program called immunity debugger and have a look at half-life I'm going to use a tool here called Mona which is going to actually check which of the modules that are included have aslr enabled and you can maybe hardly make it out but the top one that I've got highlighted there is hl.exe that's the half-life executable the rest of them all have it enabled that's no

surprise really because these are all modern Windows binaries whereas half-life.exe 98 I think the game came out so yeah they haven't done much for that but it also means that if I can find that jump ESP instruction within that memory space then I have my ticket and I have an exploit that can reliably work time and time again now if I look here and I put in in my search command the starting address right up here for the half-life module then I'll just set the length accordingly nothing so there is no such instruction within that space and that complicates things a little bit because I need to find some equivalent way of using that that memory space

to to trigger that jump to ESP style Behavior this basically is where things get a little bit more complicated um I did a bunch of additional searching I used techniques like checking for for effectively spaces between certain instructions and return instructions that's effectively forming if you're familiar with the term a rock chain to get around this problem unfortunately I won't be covering it here it's me on to to the next Point around this vulnerability which is this is unpatched I found that about two and a half years ago initially I reported it to Val they did nothing I reported it again to valve they did nothing and then a couple years later I found an

evolution to the exploit and still nothing nothing is done so about two and a half years after after it's found and the last time that I was able to see from from the steam databases Half-Life was updated was three years ago so they've abandoned this this ship and and that's why I'm not going to go into all the details around crafting that that rock chain and how that works but I won't leave you hanging I'm going to show you it functioning so I've got the script here so Mona has functionality to do that uh it wouldn't find something that advanced it was a lot of manual effort to get the rock chain together because it uses a

lot of unconventional techniques to get things working um I did try Mona first but unfortunately no no dice so one thing with the command line argument point is that it's it's an overflow of the entire command line argument string which also includes the part to the executable so what your payload actually looks like is going to depend on what that is and so I'm using the default I'm just going to accept that and also a point here is that if you launch the game through Steam it appends automatically a dash theme parameter so we have to account for that because if I don't subtract the additional the additional length of the payload that's required to accommodate for that

parameter then it's not going to work so I'm going to say yes to launch via Steam and now I've got something copied to my clipboard we're going to stop the debugging session here and this one we're going to go back to steam

we get a calculator so

you know like like the magician with his hat I showed you it was empty so um just going back in the timeline a little bit to when I first found this about two and a half years ago as I said I initially submitted this as a bug Bounty at the time it wasn't accepted because this was all it was and whilst there was a vulnerability uh there wasn't an easy way of executing it for a typical person you'd have to social engineer someone into putting all of that from online data in themselves so since then I've done a lot of exploration around this or a bit of exploration every once in a while I go

back and pick it up and think is there something more I could do here and uh I found a way of leveraging it that uses a local privilege escalation and the way that that works which I'll show you quickly is if you go into

the Steam folder uh not Half-Life sorry we want the user data folder and then I can go to a this this is an ID that relates team accounts logged on in the system if I go to the config here you can see this local config.pdf now when you log in on Steam with a user account there's no natural link between the user account you've logged in with versus the user account that you're using on that Windows system because you could theoretically log in from any of them as such this local config file is available for any user on the system to modify and what that means is since it stores the command line parameters and you can see

here there's my payload I could go and edit this if I was another user on the system and then the next time you run Half-Life there's my there's my callback uh so this is this is the way you can leverage it for local code execution I did resubmit this with them and uh at the time I was told they couldn't accept it because somebody else had already found this vulnerability before so I said to them was that me and I said no someone has found remote code execution with this so to be wary um I do have a couple of ideas around how how that might have gone about a couple of things I explored but where

there's potential room to do more I'm not going to go into it here but um if if you're interested you can catch me after off the recording I I'm happy to discuss it cool

now just a point on on mitigation before I wrap up with this um what can you do about this well they haven't patched it they have no intention of patching it so the number one thing you can do is uninstall it now that's that's no fun um so what you can do and I haven't tested this and seen if there's any impact with with steam in other capacities but if you go to that that local config file um then you can actually edit the permissions on there restrict it to a particular user account if you know that that user is the only one that's going to be using it that will cover the local code execution piece as for anything

else that may arise out of this I think uninstalling is the only option all right thank you guys

[Applause] any questions

I I haven't tried those ones actually um yeah I I can if you're on oh no I can't actually they're not installed I don't know I don't think we have the bandwidth for that um I did try other games though so so there's a piece of this you know unfortunately with the time limitation I I can't show everything that I went through but I did go and actually do a little bit of a more reverse engineering on the guidra side and if you have a look at this function um you'll see here there's a string that's included in it and using that basically I managed to find from from Source information online the particular function that's vulnerable and what was

going on under the hood and I I noticed that that was present in and that function would still be present in in modern games so I did try I tried portal I tried uh Team Fortress 2 all the all the modern Source engine games and they have actually patched it on those ones they just neglected it on this one so great yes

well uh in in reality when I do debugging unfortunately unfortunately it will just suspend the process entirely so I wonder I wonder if it's accumulating at all however how much of that time was calculator [Laughter] possibly half yes now that a lot of their source code I I may have allegedly uh yeah so I've done a lot of exploration on stuff like that going through uh Source SDK and so on no comment about any any leaks um and just doing some searches for for like memory corruption possibilities and so on there there are some insecure input handling functions that are used in places usually ones that are out of scope because they're odd tools here and

there that might have the highest impact they have also made a lot more of the functions memory safe and they've included rappers in in that code just for safe memory handling so I've explored it a little bit and I'm sure there's there's much more to explore but I've had no luck there yet anyone else oh yeah I'm not good enough unfortunately I stick to my talents awesome thank you guys