← All talks

Bsides Berlin 2022: Nils Ole Timm - False-Positive to 0-day

BSides Berlin41:07148 viewsPublished 2023-02Watch on YouTube ↗
About this talk
Building an exploit chain is a process of combining multiple exploits to compromise a target system. In these attacks, it's impossible to use a single exploit to compromise a target but instead combining a series of exploits. About the speaker: Firzen enjoy breaking stuff, sometimes even on purpose. I mainly do reverse engineering and exploit dev and am part of the ALLES! CTF team. When I’m not hacking for fun I’m doing it for work. https://firzen.de/
Show transcript [en]

okay hey everybody this talk says that it's about uh local privilege escalation in the Lenovo IM controller but that's just to draw you in it's like a bit of a lie so what this talk is really about is it's about the process of building a full exploit chain that is more or less complicated I think for a lot of people who are new to this this is sort of intimidating because what we always get to see is the finished product and that can be very very complicated but you don't see all the steps you don't see all the dead ends they ran into and I feel like at least for me when I was starting out

that was something I was afraid to even get into because it felt like I didn't know what I was supposed to do and when I asked a bunch of people they were like just do that like just start making stuff happens and so what we're going to do is we'll start from a false positive so you know start with the failure I think who was it yeah learn from errors right um and a bunch of roadblocks but the the main goal is I want to make it less intimidating I want you to all look at the last slide where I show what the full chain is all the different parts of it and I want you to go in inwards and

think if this if this presentation had started with that slide I would have zoned out I would have been like this is not for me and moved on and maybe at the end a few of you will feel like this is something I can do like maybe it's a lot of work but it's not something that's impossible or like Out Of Reach and that's my real goal so the details aren't important but the journey is and the the sort of more abstract approach and if you want to hear more details ask me afterwards because oh wow questions is cut off great um so this all started because I made a thing um I had to look through a bunch of

donut assemblies I was doing some research in game City and unity does some annoying stuff like having a I am oh

okay um so Unity has a modified.net runtime oh well this is okay um and that makes a lot of things a little complicated so I made a tool to automatically scan for Dangerous patterns so I could scan my whole steam Library no problem um and so I can mask again I can Define custom patterns and it can obtained analysis which actually turned out to be pretty useless in practice because if you get to that point you can probably just use a well-known like chain that already exists you don't need to find your own um unfortunately it's proprietary so I hate to disappoint all of you I wish it wasn't but that's not up to me

unfortunately um so a colleague of mine was using it and said hey YouTube flag the Microsoft thread modeling tooling I was like that sounds pretty interesting let's take a look at that um so this is the output tool and what you see is a lot of false positives wrong on all of these didn't work out so that was a little disappointing so you know all false positive not great but then I spotted something else um I I hope you can see it it says Lenovo IM controller I hope um and that looked interesting so I I decided does my tool find anything else Lenovo related and I did find a bunch of little related stuff all of these are

like in the Lenovo tooling um and that was great so where where do I stop huge code base

also

be bad enough okay fingers crossed oh this is some real life stuff uh I'll put it in okay okay I hope this is better um so where do you start looking it's a huge code base so stuff that's really interesting because you get a lot of bang for your buck libraries stuff with high prips that gives you local privilege escalation typically and things that do network communications because if you want remote code execution you kind of have to talk to the thing and then.net that's a bunch of quick ones and security serialization is everywhere still um if you find a binary formatter you've probably got code execution so congrats a lot of command injection vulnerabilities and you can do a

surprising amount by just messing with paths if you can trick Microsoft into thinking something's an SMB it'll happily send you credentials so still great great source of of hacking into stuff um so this is what the actual output looks like the details aren't as important but this ticks all the boxes it's a utilities dll so that's probably a library that's used in a bunch of places doesn't have high privileges well it has a function called launch system process and user session so that's kind of promising does it do network communications well it's a system update plugin so it probably gets that update from somewhere so that takes all the boxes so this is where I started looking

and throughout the talk I'll have this sort of status chart where I keep track of a more abstract view of things because that's a lot to keep track of and I hope this will be the thing if nothing else that you can remember about the talk so we're at the start and we want to get to profit and how do we do that well we look at stuff so at the very beginning we open all the things and when we open all the things we start looking at the thing we're interested in which is the launch system process in user session function and the nspi which is a nice tool which is sadly deprecated but still

very useful um has an analyze feature which tells you where everything is used or something you're looking for so it tells me this is used nowhere which is disappointing so this is a false positive because maybe this is a dangerous function but it's not used anyway so that's not helpful okay but it's it's in an interface like it's an implementation of an interface so maybe that interface is used in places so let's Analyze That also not used anywhere so again very disappointing so at this point you kind of have to dig a Little Deeper I hope that you can see this it's a little hard to see the the takeaway is this is the the actual thing that starts the process

that I'm interested in and like all the surrounding stuff takes care of putting it in the user session and so on but I I kind of really only care that I can run a system process in the end and so if I analyze that finally I find some stuff that actually uses it and the interesting bit here is that this is sort of it looks like this is in in a service support Library and if we look at where this is used this is a contract agent I don't know how many of you are familiar with like the concept of contracts and programming the basically it defines like an interface for RPC in the widest sense

um so this is something you can send a request to and it will do something maybe we don't know that much about what's happening yet um so looking at this this is the interesting bit and it takes in this info argument and has a switch statement which has the launch processor system function depending on what's in that and that is just passed in as an argument so the the idea is we we kind of want to keep track of this info that's passed in here is what actually determines if we run a system in the end and then at the end it does some cleanup it deletes some files and that's all this method does

and what I care about is can I get to this can I somehow manipulate this info that is passed into that so that I can execute stuff as system so this is only used by two other methods the first is called download and install application and it's in the contract agent so I'm thinking maybe I can send a request to install stuff and looking at this what I care about is at the bottom here is where that info comes from and where where does it originate well there's a method up here called get app information and at the top there's a request so the argument that's passed in here is like a really long class name request

and that request determines all the rest that happens here so what I'm keeping track of is there's an install request that maybe I can send maybe I can send I don't know that yet but if I can control that maybe I can do stuff and so the other thing I look at is install only or install application only I think it's called it's a little small on my screen so this is similar at the bottom is this install call and at the very top here is that get app information call and if we compare these two that's a sort of weird thing you might notice so this one on the right is pretty short it does like get app information then it

downloads it and then it installs it and this one on the left is install only so by name you would expect the one on the right this one to do more stuff it's supposed to download and install and the one on the left is install only but it's way longer so why so stuff like that is what I typically look for like things where I go huh that's interesting because that's where somebody put in some extra effort or made some extra steps to mitigate something or to try and circumvent something and they didn't do it in another spot so that's like some discrepancy that I care about and so if we look at the difference

between these two get app information calls the only real difference is that the stud argument is true in one case and false in the other and just keep that in mind for now so it's almost an identical call and if we look at all the extra stuff that is done on the left side I'll enhance um first if you decompile stuff it will have generic variable names so let's name them something a little bit better so let's just call this a root path for you know reasons because time really is the reason so this is the root path and then this is the installer path because we're still in this context of like triggering the installation of some application

and if you look at this line with pool flag 2 what it really does is it gets the full path of the installer and then checks that it starts with that root path so effectively it's just checking that we're in a path that's like below the root path so we're not in some arbitrary directory but we have to be starting with that root path that's the thing that it's checking so let's add that to our chart that's oh if we control the app info if then the next thing that happens is there's some path check so in my this is how I think about this this is my mental chart of stuff I really care about I don't care about a

specific line of code I care about like an abstract sense of what it's doing where does data come from where does it go cut Nigel and um I'm sorry okay so that's the first thing the fact the the second thing that it does is it called some validation tool gets trust status verify checksum a lot of stuff and the debug output says the is not trusted so in my head I'm I'm not analyzing this further at this point I'm just thinking there's some kind of trust check maybe a checks a checksum probably because it says verify checksum maybe it checks signatures as well don't know uh doesn't matter at this point but in my mental chart I'm like there's a trusted

check and after that is profit because that's when install application is called and if I get there and I control the app info I probably won because I can make it execute an installer as system well we can't yet I guess so not thumbs up um so let's recap a little bit it's almost an identical call so what does this third argument actually do and why does the program not seem to trust the input there are like these extra checks in one case but not in the other one and the only real difference is the start argument so what is that third argument I don't think you can see it on the projector but I'll enhance uh

so the third argument is use cache file so if it uses a cached file from the file system somewhere it doesn't trust it so why and this is a really long method that you definitely can't read on the projector right now but we can do a maybe the people in the first row but we'll do a short mental exercise so a lot of code is error checking in other conditions that don't really matter if you're buck hunting because I only care about controlling this app info I don't care about all the other stuff if that's an error and like it acts like it errors out I can't control the admin for like the process is done

no problems no worse so I I will mentally just think about some of it so let's highlight these I cannot return result kind of important I don't care about this catch like if there's an error I probably don't control anything and then this block it's like XML pausing and so on but I only care about where the data comes from like can I control that so I only kind of really only care about this line that assigns the result and then there's a small error handling don't have this bit at the top I do care about and then we do some magic with paint and this is what it looks like and hopefully it is readable in this

shortened form and what we see is like this is kind of XML data and I'm just naming variables to make it easier to follow um and this XML text reader in the second to last line is also that like that's just sort of a wrapper around the XML data to make parsing easier but that's the bit where the data comes from and then the other thing is a path because path combined kind of indicates that it's probably supposed to be a path and like it looks like a path um and this path depends on the request there's a request app ID and so if we control the request we can probably do stuff with this path

and then if used cache file is true so that's one of the two cases the one where it doesn't trust the input and the file exists with the path that it's just constructed it'll go into the statement and what it does is it does some validation we don't know what that does we haven't looked at it yet but if that passes it reads all text from that file and pauses it at the end and that's the result so if we can go through this check we can control the app info and now we'll we'll do a little audience participation um oh no wait first we'll add that to the mental chart so if Get Trust status

is is going wrong we fail and that's it but if we succeed we control the app info because it's just read from that file and then we're good to go and so this is a path reversal you might have heard of it if you're from the security space which I assume a lot of you are um but if you're new to this what is the path reversal basically you can mess with the path you can make it go to The Parent Directory or the root directory or something else where you're not supposed to actually access by the intent of the programmer um and we control the request well well assuming we control the request the app ID

what can we do audience participation no you're supposed to like yelling ideas or something oh we can also do a party well okay what do you prefer do we do a party like or do you want to suggest something what would you put as the app ID like if if nobody says anything I will nominate someone okay go yep

yeah yeah you're exactly right so that is one of two uh main options you can do here so we'll do a little exercise called default Windows permissions so what's a directory that's or like where where do we control stuff well there's some locations on Windows where you can just create directories even as a guest and that's for example C your your Root Drive everybody can just create directories because uh reasons I guess also program data everybody can create directories that'll come in handy later and the common source of problems is that these permissions are inherited by default so if you just create a directory everybody can still create directories in there and guess who did that Lenovo did that

uh up here is C program data Lenovo and it inherits uh write permissions for everybody yay all right maybe not yay um so we can probably control control an XML file that is red because we have this past reversal and we can just create a directory and see for example and put dot dot slash dot dot slash dot dot slash my directory and like dot XML I don't know uh and it'll read that request so we probably get to that trust status check but that's the second vulnerability here so in this box does anybody want to take a guess well you have to because this is audience participation and any any hands up this is this is a

little more difficult to spot I think I nominate others you win it is a time of check time of use vulnerability because we pass on the path twice so this file gets open twice and if in between we maliciously swap it out it'll do the check on one file and then read another file so this is like a typical race condition but going back to the vulnerability decks uh talk to is the the short name for it time of check time of use so like if you think of race condition this is probably the kind of thing you're thinking of and in this case we can just swap the file out in between and you can do this in really clever

ways like you can do file system watches and see like oh this file was accessed in about 10 nanoseconds I have to switch this file out but you really don't need to because this check takes quite a while you can just swap the file back and forth and that's good enough like you get maybe you're at maximum minute wait time or something and then you just hit the race condition so no need to be fancy about it um so that's a plan and this brings us back to if we control this request we can do that well first things first if we fail we can just try again it's a race condition so we raise

so we we just Swap this file back and forth and send requests and at some point we'll just get lucky and swap the file in between no need to be fancy or extra smart about it and we'll do a little time warp because I only have 45 minutes and you also only have limited attention span and we'll skip a lot of tedious reverse engineering which isn't actually that interesting you kind of just Trace back where this data comes from it's it's really just sort of busy work in most cases there's no extra sanitization this request is fully user controlled and we also fully control the app ID no extra checks on it in the cache case

that's also the reason the download case doesn't matter because for the download case it'll just fail because it can't download it if it's like a past reversal app ID it's like that's not in the catalog so we get this arrow for free as well so now where are we stuck at the path check so what do we do about that path check wait first we should check some assumptions because we've we've looked at a lot of stuff and we haven't actually done anything yet like we haven't interacted with this program at all so let's check that it actually works and Lenovo gives you this broker request agent that lets you just send arbitrary requests to this endpoint and has it

handled and so if you send a request it does respond back in the way that you would expect it says requested application not contained in catalog so we probably got to roughly the point where we expected to get to so thumbs up to look further it's important to check your assumptions all the time I've skipped over a lot of parts where I already did this when I was actually doing the research but it's easy to get stuck if you have something in your head that you think is true but isn't actually and so I would really recommend just while you work on stuff always just think what are my assumptions what do I think is true because if if I got to here and

it's like now that some checked like seven seven calls to XD that prevents this I probably might not have caught it with static analysis which brings us back to the path check so the installer path has to be a subpath of this long path which doesn't actually matter what it is the the thing that matters is we don't have right permissions here Lenovo actually in the subdirectory for IM controller prevents us from writing so they sort of worked it out I guess um and the check for the subpath is really hard to bypass like even with Sim link trickery or like messing with uh the the case sensitivity features that are relatively new in Windows like it's you

what maybe you can but I haven't found a way to get around it so for now let's think what can we do without bypassing it and any ideas yeah uh this is even more difficult than the other one um so looking back at the install at the end it deletes stuff it does some cleanup and so maybe we can do something with that and the first thing it cleans up is this path up there which is exactly the path that this path check is for so this one is constrained in some way like this has to be like something that passes that path check and we can't really mess with it that much but what about the second one well it

depends dll to it okay um and it depends on the app ID but both of these depend only on the app ID not on the installer name and the first one does contain the installer name and paths combine in.net has a nice Quirk which also exists in Python for example if you put in an absolute path it just starts over from there so if we put the installer name as an absolute path whatever we put as the installer name is just the result of this path combine call and the same thing is true for that app ID where we append dll and so the real constraint for this app ID is really that it only has to end

with DOT dll and so what that means is if we just put absolute paths for the app ID and the installer name we can just put the installer name as the expected installer and it will naturally pass that path check because it's where it's supposed to be and The Trusted check because it's what it's supposed to be and so we get all the way to the install for free essentially because we put in all the expected stuff but that leaves the app ID fully free to be controlled by us and it'll delete something.dll with that app ID and so if we just use the expected installer we can delete an arbitrary dll nice and so is that all we can do like

deleting a dll is pretty bad I think a lot of you like have this this mental image of Windows exploits five years ago or something where you just dropped the dll that was unused and got code execution that way um and so what if we just Create a Sim link like mysimling.dll points to I don't know the Sam file and the system is pricked the only problem with that is you need you need elevated privileges to create some links on Windows um so that's not not viable or is it because uh James Forshaw did some awesome Research into this um I don't have the time to go into it it's worth looking into I put some

references on the slide so you can check them out later basically low prep users can create some links on Windows you just need some trickery with like dos backwards compatibility features um so what does that mean our arbitrary dll delete is now an arbitrary file delete so we can delete any file in the system as a system user nice so coming back to the path check once again I didn't find a way to bypass it but if we can just put something in the right path then we don't need to bypass the path check we can just execute what we want if we can copy it there so I just looked at file accesses in this library that

are in the elevated Parts especially move or copy because that means I can control like a whole directory and that would be nice and it's a lot to look through so I'm skipping a little bit but what you find is I hope this is readable it's called history data transfer and yes this is in the class Constructor I don't know um but the the relevant bit is this so it calls directory exists twice and so if a specific directory exists and another one doesn't exist it moves the one that exists over to where the one that doesn't exist should be so this is probably some migration code path that's supposed to run like you've just updated the version and it still

has all the data but it's in the wrong directory so that it moves it over that's what I think it is I didn't ask her to know people um but the first path is Lenovo system update plugin and the second one is like the root path that is used for checking the path condition so if we can do this we can put any data we want in the correct path and then we don't need to bypass the check we just pass it naturally and to recap like don't mind the the paths the the shot is if we can delete that directory or it just doesn't exist we're good to go we can just have it

move it over and that's a big if though so if we can do this we're good to go um so what does it mean history data transfer lets us plant our own installer and that passes the path check because it's in the right path and what do we need probably an arbitrary directory delete to get the and so oh that's supposed to show up at the end just ignore it for now um so we can only delete a file right right wrong because NTFS has this neat little feature called streams and they actually Implement directories as streams on like a directory file and you can just delete that file stream for the index of the directory and that

deletes the directory so even if you only have a file delete you can also delete directories so that's a little too much to go into in detail but nice right so I'll try to file delete that we have might let us delete this directory awesome well I I should maybe Pace myself with the slides so anyway we can delete the directory and check out the history transfer right wrong because in order to delete the directory it actually has to be empty um and because this is the data directory for the system update plugin that this issue is in it will always create like new temp files or whatever so it's never empty and you can't delete

it it's like the one directory you really want to delete and you can't every other directory you can get rid of with this issue but not the one you actually care about so yeah so that goes away big sad so let's take one step back and check that if we can do this we actually get what we want so if we can delete that directory and it doesn't exist or something like that then the thing you want to happen happens so we'll pre like we'll create the directory structure and everything to set up like the conditions we want and plan to CMD exe and just see what happens and then if you look in procmon

what you see is this is the first check if the directory exists and that exists great and then this one doesn't exist that's the one for the path check because I deleted it manually just to check that my assumptions are right and then it moves it over so great the if we can do this it copies our files over but unfortunately it's not just one step back but two steps back because if you look a little later in the trace it also immediately deletes the CMD EXE um because there are more checks that are happening and I'm obviously only showing you a tiny fraction of that code but what it does is it checks that

everything has expected permissions and if it doesn't it just deletes it and so the CMD exe we planted goes away and so my brilliant plan is foiled so this is actually this files are deleted again great completely disconnected island of my beautiful paint flowchart and so what actually happened well procmon actually captures stack traces so it's easy to identify which method actually deleted stuff and if you look at it what you see is this block up here and that's the important bit the bottom bit just sets the expected permissions on the new directory it creates and what it does is if the directory exists and it's not owned by System it deletes it recursively that's what

that true means so we get a recursive delete of directories so I'm just adding this whoa I added this Arrow to show um that that check deletes our stuff and so what do we do now well the check that the lead stuff is in a chat Library like I'm still in the utilities thing so that's probably not just used by the system update plugin by by other stuff too and if that's a plugin that checks the directory that we can control we can probably mess with it it deletes recursively so that's useful and we're looking for a way to delete directory so we'll do the timewap again and I don't have pictures here because Lenovo patched it before I could take

pictures um but there's a system Hardware scan plugin which works exactly the same way which executes ldx which is some Lenovo diagnostic tool and that's the users the old path location in the Lenovo directory where we can create our own stuff so oh and I said that so audience participation what do we do we we can create this directory or like something in place of this directory any ideas yeah Sim link except on Windows it's called like a junction but yeah a junction is something you can do as a low profuser oh wait I'm supposed to not notice so a junction is something you can do as a low profuser it's essentially a Sim link between directories so if we create

a junction to the directory we want to delete that won't help us but if we create it to the Parent Directory that just wipes Everything clean because it deletes it recursively and so now we can actually fulfill the conditions for the history transfer by sending a Diagnostics request that starts ldx which checks the directory permissions and then deletes the directory nice so we're we're getting pretty close at this point this chart is getting relatively full and that's like two more obstacles so that's our first step forward in a bit and the last remaining problem is CMD exe is being deleted almost immediately so what do we do about that well we could try and like do a race

condition because it's not immediately deleted we could try and like send two requests at the same time and time and everything really carefully but we don't actually have to because a really dumb way works Windows ACLS also let you deny permissions so you can literally just deny system the permission to delete the file and it will fail no really here's the problem Trace access denied yeah good luck so there's also no error handling code like if the delete fails it doesn't even notice so we're good to go on that front files are deleted now prevent the lesion and so we can plan our own installer that that means now we get all the way to the trusted check

and what about the trusted check well not a problem because cmdexe is signed by Microsoft so we're also got to go on that front in the end it all resolves pretty nicely um yeah so cmdxd assigned The Trusted check passes like they do a signature check and then they check the checksums so even if it wasn't signed we could Forge the checksum in like the app info and that would also pass um but you don't even have to because it's signed by Microsoft and at this point we have all the puzzle pieces so here's the full chain what we like it's it's a lot right it's not a really simple exploit we did like some path traversal to plant on XML

we're racing to like pass through some trust check we're doing some setup to migrate files over to plant them in the correct directory to get an around another check and so if I had started with this if I had just shown you the exploit or explained it I think for a lot of you it would have been maybe a bit overwhelming and like intimidating and I hope that through like walking through this process with me maybe some of you feel like this is something that I can do and this is something that's like maybe tedious but not like super difficult or like has a high barrier to entry because all the individual steps I think

are not that far-fetched and so it's time for a demo which I'm sure will work uh how do I move this off yeah because like any self-respecting hacker this is on YouTube well or not here we go no that's my mouth that's a lot of delay okay so let's start that over so we can not create a new folder here this is like the Lenovo directory we do not have write permissions I did that a little fast um but this is the system update plugin directory and what we're doing is we're going to run the exploit so moving over and running and so you can see it plans the CMD exe and it creates that Junction to delete

the directory to be able to do the history transfer conditions and everything and now it's downloading an application from the Lenovo store because I need one valid XML right in order to pass a check I need a valid one so I'm just downloading something doesn't matter what it is and now what you can see hopefully is that ldi exists now oh wow crap uh this is this is not PG anymore okay so ldiac exists now because the ldx tool ran and deleted all the directories so we met those conditions and then in the IM controller plug-in data in this really long path now there's a private directory which doesn't seem like it should be there

and it contains the CMD exe so nice and what you can see here is this is the response from like the service when I'm losing the race Condition it's like that doesn't look like it's a valid app ID which it kind of doesn't right because it isn't um but after a little while because I'm literally just swapping the file back and forth we got a nice little system shell should be right about now yep and so Huawei system and that's the demo for this so like this full chain works and it's relatively complicated I think and so

there are some people I want to thank uh lucasratz who gracefully landed me a little over laptop to like prepare this presentation after I lost access to my original laptop I did this research on um the folks at Alice and at Luna sorcery for being my test audience and no thanks for helping me with the Lenovo this closure process because they had my mail in the junk and I had to go through a back channel to get them to fish it out and Grace Tamlin for proofreading and just being a good friend and if you have any questions you can't ask me now because there's no q a but ask me in the break thank you

[Applause]

[ feedback ]