00:00:00.100-->00:00:05.339 >>Alright everybody. Thanks for coming here. Um... 10 AM is early. I could barely convince 00:00:05.339-->00:00:10.577 myself to wake up for this talk so I don't know how you're doing it. Uh.. I'm Lee Holmes. I'm 00:00:10.577-->00:00:15.949 the, uh, lead security architect for Azure Management and Microsoft. I'm the, uh, author 00:00:15.949-->00:00:21.021 of the Windows Powershell Cookbook and also I've been a developer of Powershell since 00:00:21.021-->00:00:27.961 it's early versions, so you may know me from such parameters as ExcecutionPolicy Bypass or, uh, 00:00:27.961-->00:00:32.966 other such things. So let's, uh, do some context right here. So you're an engagement, you, uh, 00:00:36.737-->00:00:43.610 fish somebody. You get inside the network. You find some creds and you got some code execution. 00:00:43.610-->00:00:48.615 So you do a little bit of recon. You find out that the some-somebody has been 00:00:53.120-->00:00:58.825 connecting up to a machine. That machine has one port open. Recognize that one: five nine 00:00:58.825-->00:01:03.764 eight five. There you go. That's WSMan Powershell. So you got Powershell, you got creds, this 00:01:06.867-->00:01:12.205 is going to be a good day, right? You connect up using Powershell to that machine 00:01:12.205-->00:01:18.245 they're connecting to. I don't know, fifteen, twenty commands, that seems a little bit off... 00:01:18.245-->00:01:24.518 but you plow on, you plow on. Who am I? You run that thing, you see that you're the DNS 00:01:24.518-->00:01:29.122 Administrator. DNS is almost always co-located with active directory, so you're good. You 00:01:29.122-->00:01:34.127 got creds. You got Powershell. You run a DC as the DNS Admin. Decide to burrow yourself a 00:01:36.430-->00:01:41.802 little bit deeper. You add in another account here but you get- you get an error. That's 00:01:41.802-->00:01:45.639 kind of weird. So you move on. You try to run kind of one of those traditional download 00:01:45.639-->00:01:51.645 cradles. It doesn't work either. You get an error that, that you're in constrained language 00:01:51.645-->00:01:55.882 mode and that you're not allowed to run that kind of stuff. You're like, "This is weird." So 00:01:55.882-->00:02:01.955 I, I'm a DNS Admin. Let me see if I can restart the DNS Server. That works just fine, but then 00:02:01.955-->00:02:07.427 you try to restart WinRM and that doesn't work. So what's going on here? Your against, 00:02:07.427-->00:02:11.865 obviously, a target of a completely different category than you've run into before. 00:02:14.935-->00:02:21.208 You, my friends, have run into "just enough administration." Just enough administration is 00:02:21.208-->00:02:27.114 the idea that admins are your attack surface and you cannot treat them as your buddies 00:02:27.114-->00:02:32.486 anymore. So you may say, "Lee, are you saying that we get rids of Admins?" No, no no no. Of 00:02:32.486-->00:02:37.491 course we need admins, but here's the issue. People make mistakes. Sometimes they're just 00:02:40.227-->00:02:46.633 bad actors, but the big thing to realize is that everything they can do, an attacker can do as 00:02:46.633-->00:02:52.539 well. All the stuff you see about Powershell is just unauthorized administration, so 00:02:52.539-->00:02:55.409 if you're worried about Powershell attacks, you gotta be worried about your 00:02:55.409-->00:03:01.548 administrators. So wouldn't it be great if people didn't need these huge Admin privs to do 00:03:01.548-->00:03:07.320 their jobs? What if you popped a machine, and you didn't get all these high value creds that 00:03:07.320-->00:03:14.061 people are leaking everywhere? And what if you didn't need to give them full domain admin to 00:03:14.061-->00:03:18.065 just manage DNS? What if you could just give them the stuff they needed to do their actual 00:03:18.065-->00:03:23.070 jobs? And, of course, logging. What if everything that happened on a machine got logged and when 00:03:25.238-->00:03:31.912 a bad thing happened you could always go back and take a look. So the answer there is just 00:03:31.912-->00:03:36.983 enough administration. This is based on the security features that have been in Powershell 00:03:36.983-->00:03:42.422 since 2008, so exchange online uses this really, really heavily. They are putting just 00:03:42.422-->00:03:47.427 enough administration based principles to the entire internet. So it's based on three 00:03:50.063-->00:03:56.136 simple concepts. One is role capabilities. So this is making sure and defining what people 00:03:56.136-->00:04:00.574 are allowed to do when they connect. Then there's the endpoint which is the 00:04:00.574-->00:04:05.879 configuration that happens when they connect to the machine itself. And then you've got the 00:04:05.879-->00:04:10.684 identity. So this is, when they connect, what is the identity that runs the commands that this 00:04:10.684-->00:04:16.623 person is running. So these three things together are what let's you lock down a machine 00:04:16.623-->00:04:23.463 like crazy. And a good way to think about this, you know your security exposure on a given 00:04:23.463-->00:04:28.268 machine is kind of two things. It's- it's the time that somebody has access to the 00:04:28.268-->00:04:34.341 machine, but it's also the capability of what they can do. So we've done a pretty good job 00:04:34.341-->00:04:39.513 as an industry of starting to lock down the time. We've got all sorts- sorts of PAM modules 00:04:39.513-->00:04:45.051 and all that kind of stuff that will say, "hey, your - this is just in time admin. You're only 00:04:45.051-->00:04:50.490 allowed to do, uh, a certain task once you ask for privilege and then your only allowed to do 00:04:50.490-->00:04:54.628 it say between nine and five..." Something like that. So we're starting to see some good stuff 00:04:54.628-->00:04:59.633 to do this kinda lockdown. Just enough administration is all about the capability. So yeah, 00:05:01.701-->00:05:06.072 sure, if somebody's got access for a given amount of time, but why do you need to give them 00:05:06.072-->00:05:11.077 full access to the domain controller once a service ticket is open? So combining the two - 00:05:14.014-->00:05:20.020 this is what your attack surface reduction can look like. So it seems like it's kind of a cool 00:05:20.020-->00:05:24.858 thing, being able to reduce your attack surface by this much. I even, I was talking to this guy 00:05:24.858-->00:05:29.362 on Twitter about it. Um, it was kind of going very slow and it made me realize that some 00:05:29.362-->00:05:34.367 people... are very slow learners. At risk noting. You think you might realize the 00:05:39.539-->00:05:44.878 danger of, uh, admin running amok but obviously not. This thread didn't make it much 00:05:44.878-->00:05:51.318 further. Now how does somebody actually go through and do one of these "just enough 00:05:51.318-->00:05:57.991 administration lockdowns." The first part is defining role capabilities. So this is what 00:05:57.991-->00:06:04.731 can a person do? And it really is this simple. You can say that when they connect you import the 00:06:04.731-->00:06:09.736 DNS server module and here are the five cmdlets that they're allowed to run. But what you 00:06:13.073-->00:06:18.845 could also do is declare your own function so if a built in cmdlet isn't good enough you can 00:06:18.845-->00:06:23.750 define a function, let's say "who am I," and this is the example you saw before. So this 00:06:23.750-->00:06:28.755 is a custom written function that you want to expose. Then you've got the endpoint itself. 00:06:31.992-->00:06:38.832 So this is, defining what happens when somebody connects. The key point with just enough 00:06:38.832-->00:06:44.237 administration is that you say the session type is "restricted remote server." Of course you 00:06:44.237-->00:06:48.275 want to log all the things so you set up a transcript directory. Everything that you 00:06:48.275-->00:06:54.347 do comes and goes through this transcript directory. So you can audit this at all times. I'll 00:06:54.347-->00:06:59.152 get into the "run as virtual" account in a second. That's your definition of what identity you 00:06:59.152-->00:07:03.490 want these things to run as. So the run as virtual account is the best one you want to use 00:07:03.490-->00:07:08.495 here. And then you configure the role definitions. So this is me saying that when the DNS admin 00:07:11.698-->00:07:17.103 connects, this can be either a principle or a group. When they connect they get the role 00:07:17.103-->00:07:22.275 capability of, let's say, DNS maintenance. And that's really just a reference to what you saw 00:07:22.275-->00:07:27.280 in that last slide. Identity is the big one here. So by default when you connect up with 00:07:29.983-->00:07:35.989 Powershell, the- the identity that you use is the connecting user. So when Lee connects up to 00:07:35.989-->00:07:40.694 a machine, well then that process runs as "Lee whatever." You can do a named identity. 00:07:40.694-->00:07:44.998 That's being able to configure a- a very specific domain account. You might need this for 00:07:44.998-->00:07:51.771 some specific reasons. A danger with a named identity, if that endpoint gets popped, then you 00:07:51.771-->00:07:56.009 do have an issue of, well then somebody can steal the creds of that named identity and do stuff 00:07:56.009-->00:08:01.281 with it so you want to be very, very careful with that named identity. A virtual account, 00:08:01.281-->00:08:07.320 that's kind of what happens in IIS. When you can have a temporary local administrator. 00:08:07.320-->00:08:11.391 Windows handles all the password maintenance. You don't need to worry about rotating these, 00:08:11.391-->00:08:18.031 nothing else. When DNS Admin connects up, this now runs as a local administrator but just 00:08:18.031-->00:08:22.802 without a password that you have to worry about rotating. And then you have group managed 00:08:22.802-->00:08:28.241 service accounts. These are the same thing as a named identity, but this is the kind of identity 00:08:28.241-->00:08:32.679 that active directory will automatically rotate and manage passwords for you so this a 00:08:32.679-->00:08:36.349 very, very strong option if you don't want to have to deal with that kind of password 00:08:36.349-->00:08:41.354 maintenance. And what makes things super cool is you can even get really really deep in 00:08:44.057-->00:08:49.696 defined grained proxy control. So you saw there before somebody was able to run and they were 00:08:49.696-->00:08:56.269 able to restart the DNS service but they weren't able to restart the WinRM service. So here's an 00:08:56.269-->00:09:02.008 example of somebody restricting restart service and using some parameters that says, "The named 00:09:02.008-->00:09:08.548 parameter, I'm going to lock this down to only the DNS or the DNS cache services." So you can 00:09:08.548-->00:09:13.553 get super, super hardcore on this. So you might say here, "This is Fort Knox. This is 00:09:18.191-->00:09:23.129 like, what am I gonna do." This is the impenetrable wall. Somebody's actually got their 00:09:23.129-->00:09:27.066 stuff together. They're not a whale. They're not just, deploying a server with no 00:09:27.066-->00:09:31.971 patience. What can they actually do to start getting some, what can you do if your on an 00:09:31.971-->00:09:36.976 engagement trying to attack one of these things. Turns out there's a couple things and, uh, 00:09:40.079-->00:09:45.819 I guarantee you everything I- I'm gonna show you next I could be pulling up CallE. I could 00:09:45.819-->00:09:50.023 show you some shells writing, but we don't have the time for the applause in twenty minutes 00:09:50.023-->00:09:56.763 so I'm gonna skip on that. The first thing is porous, right? I showed you this wonderful world 00:09:56.763-->00:10:00.967 of connecting where machine all you've done is exposed Powershell remoting. But 00:10:00.967-->00:10:04.337 realistically you're going to run into systems where somebody's still doing things 00:10:04.337-->00:10:09.676 like exposing RDP or Tellnet or something like that. So if you've got creds and there's 00:10:09.676-->00:10:14.814 also letting you use those creds for RDP, well then yeah you just RDP in and whatever. So just 00:10:14.814-->00:10:20.353 enough administration, all it can do is lock down Powershell access. So there's the first 00:10:20.353-->00:10:26.059 kind of misconfiguration you're gonna see. But here's another example. I showed you locking 00:10:26.059-->00:10:31.664 down, you know, a handful of cmdlets. But some cmdlets, they're not immediately obvious 00:10:31.664-->00:10:38.171 what's going on. So here's an example from, uh, "Matt Weeks Script Junkie" where if you 00:10:38.171-->00:10:44.711 expose something like hey I'm gonna give an endpoint where I can join systems into the 00:10:44.711-->00:10:49.349 domain, well hey, if you're an attacker and you got access to that cmdlet, you don't have to 00:10:49.349-->00:10:55.822 pick it. You don't have to add it to the domain that- that the enterprise wants. You could also 00:10:55.822-->00:11:00.493 add it to your own domain and start pu-pushing out malicious group policies or something like 00:11:00.493-->00:11:05.498 that. So the commands are not always obvious. Language mode is another huge one. I showed you 00:11:08.001-->00:11:13.606 back there a- trying to run any script and that didn't work. Well, here's an example of 00:11:13.606-->00:11:17.710 somebody trying to make it a bit more friendly and using a version of Powershell called 00:11:17.710-->00:11:22.348 Constrained Language Mode. So constrained language mode at first blush looks like it's 00:11:22.348-->00:11:27.687 pretty cool. I can't do arbitrary dot net scripting or anything like that. But the big 00:11:27.687-->00:11:33.026 thing with JEA. This is why constrained language is a bad idea. If you take a look on the 00:11:33.026-->00:11:39.132 right hand side, the way that JEA controls the access, is it places a very big distinction 00:11:39.132-->00:11:44.604 between the functions that you expose to the user and then the capability that you're allowed 00:11:44.604-->00:11:50.376 to run as part of one of those functions. So if you do constrained language, in 00:11:50.376-->00:11:54.781 constrained language you're allowed to create new functions, and so all you gotta do is 00:11:54.781-->00:12:00.453 create a new function and that function now can run anything you want. That i-is no longer 00:12:00.453-->00:12:04.891 restricted and so you create a new one and then you've completely bypassed anything 00:12:04.891-->00:12:11.598 somebody's put in when it comes to- to a georestriction on commands. So "no language mode," 00:12:11.598-->00:12:16.903 if you ever see anything but that - this - they have a complete RCE and, uh, that's 00:12:16.903-->00:12:21.908 your way in. But, by far, by far, your biggest danger is vulnerable functions. Code, 00:12:25.445-->00:12:30.450 y'know, i-if you saw the- the- the session before and all that, the theory of vulnerabilities is 00:12:33.720-->00:12:39.459 all about the code, right? When you're letting people expose any sort of functions to untrusted 00:12:39.459-->00:12:44.297 input, they're gonna make mistakes. Command injection is nothing new about command 00:12:44.297-->00:12:49.168 injection in our industry. It turns out if someone is writing a Powershell function, they can 00:12:49.168-->00:12:54.240 just as easily write a command injection vulnerability when it comes to their functions. So 00:12:54.240-->00:12:58.878 here's an example of somebody taking user input and then for some reason deciding to call 00:12:58.878-->00:13:03.516 Powershell directly and injecting that user input. Like anything you're used to in the 00:13:03.516-->00:13:08.521 past, you can put a semi-colon in there and run arbitrary commands. And it's not just 00:13:10.523-->00:13:16.029 Powershell of course. If they're calling CMD for some reason you can also input that user input 00:13:16.029-->00:13:21.034 there and get arbitrary CMD injection. By far the biggest thing that you're going to see 00:13:23.636-->00:13:28.875 though are vulnerable functions. So this is someone doing invoke-expression. And 00:13:28.875-->00:13:34.514 invoke-expression is almost never needed in Powershell itself. Here's an example of 00:13:34.514-->00:13:40.320 somebody thinking they want to take some user input and then, for example, a process name, and 00:13:40.320-->00:13:44.857 call getprocess with that name and what you'll normally see is that- that they're used to doing 00:13:44.857-->00:13:50.797 things from Python r whatever where you do an execcall or a systemcall. So this is somebody 00:13:50.797-->00:13:55.334 trying to do that with getprocess. That's a bad idea. Invoke-expression says 00:13:55.334-->00:14:01.674 "Powershell I'm gonna give you a chunk of script. Run whatever I give you." User input can 00:14:01.674-->00:14:08.214 contain semicolons or any sort of download cradle and you're done there. There's a bunch of 00:14:08.214-->00:14:12.685 obscure forms of script injection coming here from InvokeScript, 00:14:12.685-->00:14:17.690 CreateNestedPipeline, add script, so make sure to keep your eyes on those. But here's 00:14:20.426-->00:14:24.597 an example of it being done correctly. It turns out you almost never need 00:14:24.597-->00:14:30.970 invoke-expression in Powershell. Of course in- in Powershell I can do arbitrary user input and 00:14:30.970-->00:14:35.808 just provide that as a variable to a parameter name. If I don't know the parameter name 00:14:35.808-->00:14:41.414 beforehand I can also dump things into a hash table, the parameters and their values, and 00:14:41.414-->00:14:46.252 do something then called "splatting" that on a command like, that's the a- at- at sign 00:14:46.252-->00:14:51.758 parameters there. So this is a way to invoke a cmdlet with no parameters that you don't know 00:14:51.758-->00:14:56.129 beforehand, still without any sort of injection vulnerabilities. And even if you 00:14:56.129-->00:15:00.233 don't know the cmdlet beforehand you can use the invocation operator and still get the same 00:15:00.233-->00:15:05.238 thing. So this is the- the best approach. This is what people should be moving forward to. But 00:15:07.573-->00:15:11.577 sometimes you'll see somebody who thinks that there's aware of the invoke-expression 00:15:11.577-->00:15:17.984 vulnerability and what they decide to do here is, well I'm gonna do giveprocess dash name. 00:15:17.984-->00:15:22.088 I'm gonna escape the user input with single quotes, 'cause single quotes are kind of the 00:15:22.088-->00:15:26.659 safest kind of thing in Powershell, and the line before that I'm going to escape out 00:15:26.659-->00:15:31.097 single quotes with other single quotes. You see this, people trying to prevent sql injection 00:15:31.097-->00:15:37.770 a lot of times. But here's the issue. Powershell was raised in a world of Outlook and Exchange 00:15:37.770-->00:15:42.608 and Wordpress where there is all kinds of quotes. There's curly quotes, there's back quotes, 00:15:42.608-->00:15:47.246 there's hanging quotes, there's like six kinds of quotes in Powershell. The version you see 00:15:47.246-->00:15:52.051 here only escapes one of them. Use any other form of quote and you're right through this. 00:15:55.655-->00:16:00.660 Sometimes you'll see script block injection. So script block injection is when somebody wants 00:16:00.660-->00:16:05.598 to take user input and then take that input and run it via Powershell Remoting on another 00:16:05.598-->00:16:12.438 system. So here is somebody who takes that script block at a- based on user input, but they 00:16:12.438-->00:16:17.443 need to create a script block. They do this with a string. That can be injected as well. There's 00:16:19.679-->00:16:23.816 an obscure form of this as well you see that new script block with the string, that can be 00:16:23.816-->00:16:27.119 injected, so there's a bunch of ways to make sure you're not doing that and that's the 00:16:27.119-->00:16:34.026 approach you want to take. String expansion is another thing that you'll sometimes see 00:16:34.026-->00:16:38.598 where somebody wants to take a variable and figure out the value of that variable so they 00:16:38.598-->00:16:44.003 use the expandstring API. There's a couple versions the- of that exposed through 00:16:44.003-->00:16:49.008 execution context as well. That can go through some string expansion vulnerabilities itself 00:16:52.044-->00:16:56.449 and so you don't have- you don't want to be using expandstring on user input. The best thing you 00:16:56.449-->00:17:00.553 want to do is use it- the getvariable cmdlet to figure out the current value of a variable. 00:17:02.955-->00:17:07.026 There's some obscure things here. Method injection if you're taking user input and using it 00:17:07.026-->00:17:12.198 in the, ForEach cmdlet. You can start running arbitrary methods, arbitrary functions there. Don't 00:17:12.198-->00:17:16.969 want to make that happen. You've got vulnerable functions too. Here's an example that can 00:17:16.969-->00:17:22.475 really burn a lot of people. If you want to take user input and send it into the addtype cmdlet, 00:17:22.475-->00:17:27.480 well the addtype cmdlet can run arbitrary C Sharp code, so if you ever see anybody taking user 00:17:30.516-->00:17:35.221 input and sending it into the addtype cmdlet, well now you have arbitrary C Sharp, 00:17:35.221-->00:17:40.226 arbitrary access to Win32 APIs and Bob's your uncle and it's over. Now that's a lot to keep 00:17:42.662-->00:17:47.166 in mind. What can you do if you're actually trying to audit this, or defend against any of 00:17:47.166-->00:17:52.171 these things? Turns out that Powershell is powerful and it's not just powerful when it comes 00:17:56.108-->00:18:01.747 to all the stuff it lets you do, but every scripting language pretty much goes through two 00:18:01.747-->00:18:06.319 phases when it understands a script. The first thing is tokenizing. So this is an 00:18:06.319-->00:18:12.258 example of tokenizing here, where you give it a command and Powershell actually has an API 00:18:12.258-->00:18:16.362 that let's you see okay there's a generic token, there's a parameter, some left parens, 00:18:16.362-->00:18:22.535 there's an operator. You could start to do some really smart analysis against the token level 00:18:22.535-->00:18:27.540 of Powershell itself. But on top of every token stream there's the parse stream which generates 00:18:30.409-->00:18:36.082 an AST. So this is what Powershell has as an AST, which is kind of a tree-like structure 00:18:36.082-->00:18:42.688 of what it interprets the command to be. So you can even do crazy things here like, the 00:18:42.688-->00:18:48.194 value of a parameter, was that a constant expression or was that the result of doing some binary 00:18:48.194-->00:18:53.199 operators on some things. Here's an example of the powershell AST. There's a great module on 00:18:56.769-->00:19:02.008 the Powershell gallery that you can just run installmodule on and start browsing through the 00:19:02.008-->00:19:07.013 AST for a different command. So the AST is cool but how is this actually going to get- help you 00:19:09.148-->00:19:14.987 either find issues or prevent issues? And the answer here is there's a lot of wicked hunting 00:19:14.987-->00:19:20.126 things you can do with the AST. So you see at the bottom here kind of read this one from the 00:19:20.126-->00:19:26.198 bottom to the top. That last line. There's a method on the AST that let's you find all 00:19:26.198-->00:19:32.371 things that match a given predicate. So this is doing a tree, a navigation of the tree, 00:19:32.371-->00:19:36.275 looking for anything that matches the predicate that you ask. >>If you want people to 00:19:36.275-->00:19:41.280 read it, we're going to have to bump the signal for a second because it's popping up. >>Okay. 00:19:44.383-->00:19:48.621 So hunting with the AST is a really big thing, so that bottom line, just keep it in your- in 00:19:48.621-->00:19:54.126 your mind's eye. So the bottom line was going through and- the bottom line you can do an AST 00:19:54.126-->00:19:59.131 searcher based on anything that- any predicate that you give. And so the top line... [pause] And 00:20:07.306-->00:20:13.412 so the top line was an example of a predicate that you can do and start to run against all the 00:20:13.412-->00:20:18.551 AST. So this is an example of finding an invokemember AST where the thing being invoked 00:20:18.551-->00:20:24.590 was not a constant expression. Kind of one of those examples. But doing this at scale is a 00:20:24.590-->00:20:30.730 really really hard problem. So that's why Powershell has the script analyzer. This already is 00:20:30.730-->00:20:36.235 used in things like Visual Studio code and stuff. Everytime you, for example, do an alias or 00:20:36.235-->00:20:42.208 whatever, it can find things like aliases that you could remap to something else. So I 00:20:42.208-->00:20:47.546 can start to take those predicates and write AST rules against arbitrary Powershell 00:20:47.546-->00:20:52.551 scripts but this time based on known script injection vulnerabilities. So, obviously, 00:20:59.625-->00:21:03.796 that's a great thing to do,so we did it. What we're releasing now is the Powershell injection 00:21:03.796-->00:21:10.035 hunter that takes every single thing I talked about today and does it for you automatically. 00:21:10.035-->00:21:15.074 So you run Powershell script, uh, script analyzer against these injection rules and then 00:21:15.074-->00:21:21.814 you're done. This will flag everything that you might be worried about. But even better 00:21:21.814-->00:21:26.452 you've got integration with Visual Studio code. You enable this in Visual Studio code and 00:21:26.452-->00:21:30.456 you can see these things while you're developing your Powershell scripts, anything 00:21:30.456-->00:21:35.461 that might cause an issue when exposed to untrusted user input. [applause] So here's the thing. 00:21:46.605-->00:21:51.277 If you're- if you're on an engagement, make sure you're testing for administrative 00:21:51.277-->00:21:57.616 complexity, testing for script injection vulnerabilities. But if you've got things to defend 00:21:57.616-->00:22:03.556 man up. Use- woman up. Use JEA, use things like that and use script injection hunter to find 00:22:03.556-->00:22:08.561 vulnerabilities. Thanks for your time. Glad you could make it so early. [applause]