 everybody, thanks for coming here. 10 a.m. is early. I could barely convince myself to wake up for this talk, so I don't know how you're doing it. I'm Lee Holmes. I'm the lead security architect for Azure management and Microsoft. I'm the author of the Windows PowerShell cookbook. And also I've been a developer of PowerShell since its early versions. So you may know me from such parameters as execution policy bypass or other such things. So let's do some context right here. So you're in an engagement. You fish somebody. You get inside the network. You find some creds and you got some code execution. So you do a little bit of recon. You find out that somebody has been connecting up to a machine. That machine has one part open. Recognize that one 5985. There you go. That's WS man. PowerShell. So you got PowerShell. You got creds. This is going to be a good day, right? You connect up using PowerShell to that machine they're connecting to. I don't know, 15, 20 commands. That seems a little bit off. But you plow on. You plow on. Who am I? You're on that thing. You see that you're the DNS administrator. DNS is almost always co-located with Active Directory. So you're good. You got creds. You got PowerShell. You're on a DC as the DNS admin. You decide to burrow yourself a little bit deeper. You add in another account here. But you get an error. That's kind of weird. So you move on. You try to run kind of one of those traditional download cradles. It doesn't work either. You get an error that you're in constrained language mode and you're allowed to run that kind of stuff. You're like this is weird. So I'm a DNS admin. Let me see if I can restart the DNS server. That works just fine. But then you try to restart WinRAM and that doesn't work. So what's going on here? You're against obviously a target of a completely different category than you've run into before. You, my friends, have run into just enough administration. Just enough administration is the idea that admins are your attack surface and you cannot treat them as your buddies anymore. So you might say, Lee, are you saying that we get rid of admins? No, no, no. Of course we need admins. But here's the issue. People make mistakes. Sometimes they're just bad actors. But the big thing to realize is that everything they can do, an attacker can do as well. All the stuff you see about PowerShell is just unauthorized administration. So if you're worried about PowerShell attacks, you gotta be worried about your administrators. So wouldn't it be great if people didn't need these huge admin privs to do their jobs? What if you popped a machine and you didn't get all these high value creds that people are leaking everywhere? And what if you didn't need to give them full domain admin to just manage DNS? What if you could just give them the stuff they needed to do their actual jobs? And of course logging. What if everything that happened on a machine got logged and when a bad thing happened you could always go back and take a look? So the answer there is just enough administration. This is based on the security features that have been in PowerShell since 2008. So Exchange Online uses this really, really heavily. They're putting just enough administration based principles to the entire internet. So it's based on three simple concepts. One is role capabilities. So this is making sure and defining what people are allowed to do when they connect. Then there's the endpoint, which is the configuration that happens when they connect to the machine itself. And then you've got the identity. So this is when they connect, what is the identity that runs the commands that this person is running? So these three things together are what lets you lock down a machine like crazy. And a good way to think about this, you know you're a security exposure on a given machine. It's kind of two things. It's the time that somebody has access to the machine, but it's also the capability of what they can do. So we've done a pretty good job as an industry of starting to lock down the time. We've got all sorts of PAM modules and all that kind of stuff that will say hey, this is just in time admin. You're only allowed to do a certain task. Once you ask for privilege and then you're only allowed to do it, let's say between nine and five, something like that. So we're starting to see some good stuff to do this kind of lockdown. Just enough administration is all about the capability. So yeah, sure somebody's got access for a given amount of time, but why do you need to give them full access to the domain controller once the service ticket is open? So combining the two, this is what your attack surface reduction can look like. So it seems like it's kind of a cool thing being able to reduce your attack surface by this much. I even I was talking with this guy on Twitter about it. It was kind of going very slow and it made me realize that some people are very slow learners. Edward Snowden, you think he might realize a danger of admin running amok, but obviously not. This threat didn't make it much further. Now how does somebody actually go through and do one of these just enough administration lockdowns? The first part is defining role capabilities. So this is what can a person do? And it really is this simple. You can say that when they connect, you import the DNS server module. And here are the five commandlets that they're allowed to run. But what you can also do is declare your own functions. So if a built-in commandlet isn't good enough, you can define a function, let's say who am I, and this is the example that you saw before. So this is a custom written function that you want to expose. Then you've got the endpoint itself. So this is defining what happens when somebody connects. The key point with just enough administration is that you say the session type is restricted remote server. Of course you want to log all the things. So you set up a transcript directory. Everything that you do comes and goes through this transcript directory. So you can audit this at all times. I'll get into the run as virtual account in a second. That's your definition of what identity you want these things to run as. So the run as virtual account is the best one you want to use here. And then you can figure the role definitions. So this is me saying that when the DNS admin connects, this can be either a principal or a group. When they connect, they get the role capabilities of, let's say, DNS maintenance. And that's really just a reference to what you saw in that last slide. Identity is the big one here. So by default when you connect up with PowerShell, the identity that you use is the connecting user. So when Lee connects up to a machine, well, then that process runs as Lee, whatever. You can do a named identity that's being able to configure a very specific domain account. You might need this for some specific reasons. A danger with a named identity, if that endpoint gets popped, then you do have an issue of, well, then somebody can steal the creds of that named identity and do stuff with it. So you want to be very, very careful with that named identity. A virtual account, that's kind of what happens in IIS when you can have a temporary local administrator. Windows handles all the password maintenance. You don't need to worry about rotating these, nothing else. When DNS admin connects up, this now runs as a local administrator, but just without a password that you have to worry about rotating. And then you have group managed service accounts. These are the same thing as a named identity, but this is the kind of identity that Active Directory will automatically rotate and manage passwords for you. So it's a very, very strong option if you don't want to have to deal with that kind of password maintenance. And what makes things super cool is you can even get really, really deep into fine grained proxy control. So you saw there before somebody was able to run and they were able to restart the DNS service, but they weren't able to restart the WinRM service. So here's an example of somebody restricting restart service and using some parameters that says the name parameter, I'm going to lock this down to only the DNS or the DNS cache services. So you can get super, super hard core on this. So you might say here, this is Fort Knox, this is like what am I going to do? This is the impenetrable wall. Somebody's actually got their stuff together. They're not a whale. They're not just deploying a server with no patients. What can they actually do to start getting some, what can you do if you're on an engagement trying to attack one of these things? Turns out there is a couple things. And I guarantee you everything I'm going to show you next. I could be pulling up Kali. I could show you some shells raining, but we don't have the time for the applause in 20 minutes, so I'm going to skip on that. The first thing is ports, right? I showed you this wonderful world of connecting where a machine all you've done is exposed, power shell remoting, but realistically you're going to run into systems where somebody is still doing things like exposing RDP or Telnet or something like that. So if you've got creds and they're also letting you use those creds for RDP, well then yeah, you just RDP in and whatever. So just enough administration, all it can do is lock down power shell access. So there's the first kind of misconfiguration you're going to see. But here's another example. I showed you locking down a handful of commandlets, but some commandlets they're not immediately obvious what's going on. So here's an example from Matt Week's ScriptJunkie where if you expose something like hey, I'm going to give an endpoint where I can join systems into the domain. Well hey, if you're an attacker and you get access to that commandlet you don't have to pick it, you don't have to add it to the domain that the enterprise wants. You can also add it to your own domain and start pushing out malicious group policies or something like that. So the commands are not always obvious. Language mode is another huge one. I showed you back there trying to run any script and that didn't work. Well here's an example of somebody trying to make it a bit more friendly and using a version of power shell called constrain language mode. So constrain language mode at first blush looks like it's pretty cool. I can't do arbitrary dot net script or anything like that. But the big thing with GIA, this is why constrain language is a bad idea. If you take a look on the right hand side, the way that GIA controls the access is it places a very big distinction between the functions that you expose to the user and then the capability that you're allowed to run as part of one of those functions. So if you do constrain language and constrain language you're allowed to create new functions and so all you got to do is create a new function and that function now can run anything you want. That is no longer restricted and so you create a new one and then you've completely bypassed anything somebody's put in when it comes to GIA restriction on commands. So no language mode. If you ever see anything but that, they have a complete RCE and that's your way in. But by far, by far your biggest danger is vulnerable functions. Code, you know, if you saw the session before on all the ethereum vulnerabilities, it's all about the code, right? When you're letting people expose any sort of functions to untrusted input, they're going to make mistakes. Command injection, there's nothing new about command injection in our industry. It turns out if somebody is writing a PowerShell function, they can just as easily write a command injection vulnerability when it comes to their functions. So here's an example of somebody taking user input and then for some reason deciding to call PowerShell directly and injecting that user input. Like anything you're used to in the past, you can put a semicolon in there and run arbitrary commands. And it's not just PowerShell of course, if they're calling CMD for some reason, you can also input that user input there and get arbitrary CMD injection. By far the biggest thing that you're going to see though are vulnerable functions. So this is somebody doing invoke expression and invoke expression is almost never needed in PowerShell itself. Here's an example of somebody thinking they want to take some user input and then for example a process name and call get process with that name and which you'll normally see that they're used to doing things from Python or whatever where you do an exec call or a system call. So this is somebody trying to do that with get process. That's a bad idea. Invoke expression says PowerShell, I'm going to give you a chunk of script, run whatever I give you. User input can contain semicolons or any sort of download cradle and you're done there. There's a bunch of obscure forms of script injection coming here from invoke script, create nested pipeline, add script. So make sure to keep your eyes on those. But here's an example of it being done correctly. It turns out you almost never need invoke expression in PowerShell. Of course in PowerShell I can do arbitrary user input and just provide that as a variable to a parameter name. If I don't know the parameter name beforehand I can also dump things into a hash table, the parameters and their values and do something called splatting that on a command let. That's the at sign parameters there. So this is a way to invoke a command let with no parameters that you don't know beforehand still without any sort of injection vulnerabilities. And even if you don't know the command let beforehand you can use the invocation operator and still get the same thing. So this is the best approach. This is what people should be moving forward to. But sometimes you'll see somebody who thinks that they're aware of the invoke expression vulnerability and what they decide to do here is well I'm going to do get process dash name. I'm going to escape the user input with single quotes because single quotes are kind of the safest kind of thing in PowerShell. And the line before that I'm going to escape out single quotes with other single quotes. You see there's people trying to prevent SQL injection a lot of times. But here's the issue. PowerShell was raised in a world of outlook and exchange and word press where there's all kinds of quotes. There's curly quotes, there's back quotes, there's hanging quotes. There's like six kinds of quotes in PowerShell. The version you see here only escapes one of them. Use any other form of quote and you're right through this. Sometimes you'll see script block injection. So script block injection is when somebody wants to take user input and then take that input and run it via PowerShell remoting on another system. So here is somebody who takes that script block based on user input but they need to create a script block. They do this with a string. That can be injected as well. There's an obscure form of this as well. You see the new script block with a string that can be injected. So there's a bunch of ways to make sure you're not doing that. And that's the approach you want to take. String expansion is another thing that you'll sometimes see where somebody wants to take a variable and figure out the value of that variable. So they use the expand string API. There's a couple versions of that exposed through execution context as well. That can go through some string expansion vulnerabilities itself. And so you don't want to be using expand string on user input. The best thing you want to do is use the get variable commandlet to figure out the current value of a variable. There's some obscure things here. Method injection if you're taking user input and using it in the for each commandlet. You can start to run arbitrary methods, arbitrary functions there. Don't want to make that happen. You've got vulnerable functions too. Here's an example that can really burn a lot of people. If you want to take user input and send it into the add type commandlet, well the add type commandlet can run arbitrary C sharp code. So if you ever see anybody taking user input and sending it into the add type commandlet, well now you have arbitrary C sharp, arbitrary access to win 32 APIs and Bob's your uncle is over. Now that's a lot to keep in mind. What can you do if you're actually trying to audit this or defend against any of these things? It turns out that PowerShell is powerful and it's not just powerful when it comes to all the stuff it lets you do, but every scripting language pretty much goes through two phases when it understands a script. The first thing is tokenizing. So this is an example of tokenizing here where you give it a command and PowerShell actually has an API that lets you see, okay there's a generic token, there's a parameter, some left parens, there's an operator. You can start to do some really smart analysis against the token level of PowerShell itself, but on top of every token stream there's the parse stream which generates an AST. So this is what PowerShell has as an AST which is kind of a tree like structure of what it interprets the command to be. So you can even do crazy things here like the value of a parameter, was that a constant expression or is that the result of doing some binary operators on some things? Here's an example of the PowerShell AST, there's a great module on the PowerShell gallery that you can just run install module on and start browsing through the AST for a different command. So the AST is cool, but how is this actually going to help you either find issues or prevent issues? And the answer here is there's a lot of wicked hunting things you can do with the AST. So you see at the bottom here kind of read this one from the bottom to the top. That last line there's a method on the AST that lets you find all things that match a given predicate. So this is doing a tree, a navigation of the tree, looking for anything that matches the predicate that you ask. So hunting with the AST is a really big thing, so that bottom line just keep it in your mind's eye. So the bottom eye was going through and the bottom line you can do an AST searcher based on anything that any predicate that you give. And so the top line, and so the top line was an example of a predicate that you can do and start to run against all the AST. So this is an example of finding an invoked member AST where the thing being invoked was not a constant expression, kind of one of those examples. But doing this at scale is a really, really hard problem. So that's why PowerShell has the script analyzer. This already is used in things like visual studio code and stuff. Every time you, for example, do an alias or whatever, it can find things like aliases that you could remap to something else. So I can start to take those predicates and write AST rules against arbitrary PowerShell scripts, but this time based on known script injection vulnerabilities. So obviously that's a great thing to do, so we did it. What we're releasing now is the PowerShell injection hunter that takes every single thing I talked about today and does it for you automatically. So you run PowerShell script analyzer against these injection rules and then you're done. This will flag everything that you might be worried about, but even better, you've got integration with visual studio code. You enable this in visual studio code and you can see these things while you're developing your PowerShell scripts, anything that might cause an issue when exposed to untrusted user input. So here's the thing, if you're on an engagement, make sure you're testing for administrative complexity, testing for script injection vulnerabilities, but if you've got things to defend, man up, use woman up, use GIA, use things like that and use script injection hunter to find vulnerabilities. Thanks for your time. Glad you could make us early.