
Welcome everyone to our talk. Um there have been a few other DevOps exploitation style um talks throughout the day. So I think you guys will be pretty familiar with um the topic at this point. Uh we're going to be attacking GitHub action workflow files from open source repositories. The names on screen. Me David Rohan and Andre is not here but also one of the guys that helped build this tool that we are going to talk about. So first, who are we? Um, we're from Worldly Labs, Cape Town based consultancy, security consultancy company, but also um we develop custom code analysis solutions. So what is that? Any kind of product or tool that helps helps you explore or hack a
program. Usually um our in-house consultants need a tool for a job that doesn't exist and we'll create it. Uh last time we were here we spoke about creating a tool for mining um Java derization vulnerabilities from Maven. You know large scale just pulling these guys analyzing the code when a warning comes up look at it create a PC and then we showed an exploit which was quite cool. So um this time it's not Java it is a bit of JavaScript actually. Um, so how do we or what is a GitHub action expression injection and how do we automate the large scale mining of this? Well, if you don't know what GitHub actions is, um, it's a CI/CD
service. What is a CI/CD service? Um, basically automating anything that comes as a reaction of an event on your repository. Um, so you make an issue, a pull request, you can write some code that actually reacts and does something based on that. Um, specifically into GitHub. Uh, that's the topic, the the GitHub one. And um, a lot of the logic of this is defined in work workflow files. It's specifically quite generous on a free tier. Once you've lock your code behind a private wall, it, you know, asks you to get your credit card out before you get some computation. So yeah, you get quite a lot for your free open source repositories. Um, an example of
something you can do uh is on a pull request it goes through a workflow workflow file and maybe you can look at you know an issue title or something and then push notification on based on maybe a slack team that should responding. Um there's a lot of logic you can do but uh what's important is that you can look at the branch name, the issue details, pull request details, a commit hash. Um and yeah, there's a lot of automation you can do. But a lot of these fields are actually attacker controlled. Um you can put code in an issue title and if you don't handle it properly, you get uh code execution on the actions runner. So
basically the computer that executes the code for that job. It's called an expression injection. Um, familiar faces have covered this um, not too long ago. So, um, what are we doing? Well, we're diving deep into this topic and again automating it. Uh, there is related work that does try to scan for these things, but a lot of them are llinters. Um, basically a little bit surface level. uh and what are the shortcomings of that and um how do we deploy um bit more of an advanced scanner um on a large scale. So let's show an example of a of an expression injection. Um the run command is basically where your shell interpreter starts. So you can write a
shell script there. And in red on the right you will see a event pull request title. So that's a title of a pull request. And that dollar sign with the fancy brackets, that's interpolation. So it means that anything come out of that will not only be accessed but also be interpreted possibly as co as code. So in this example, you could um semicolon and then write some arbitrary bashcript, send some tokens tokens to attacker.com and um that's pretty bad. Your secrets compromised. Yeah, that's pretty bad. Uh this also explores the JavaScript world. So you can have an external action and um what that looks like is you have the little on the top right uh users
noob/trustme bro at v1 that references some kind of um other git repository that has a index.js JS or something and that looks like that where you have um some function that uses the actions core to get an input and it can go anywhere in JavaScript code in this case um exec so that's yeah code execution but now not bashcript it is JavaScript uh this could also be docker files etc um but it's not sometimes you'll actually come across code that looks like this and you can't just trust anyone's action um so what is the impact of an expression injection ction. Well, you can push code to protected branches. Um, the GitHub token can have write
permissions that can allow you to modify code uh during release pipelines. Um, if you steal secrets, you can, you know, have privileges on systems that those secrets are for. Um, so pretty bad, pretty bad I think is the summary of that. And really what the inspiration for us was that um a cousin project of ours uh that we open source tool that we maintain that uses that tool. They got exploited one day. Um they woke up to a random account called white hat hacker pushing to main directly with a commit title that says fixes action injection. So that's pretty wild. Um they were running around like headless chickens. Um and we were having a little bit of a
giggle. It could have been a lot worse. So they are actually a security oriented company and for that to happen is pretty embarrassing and the whitehead hacker, you know, that title isn't really an account. It's just they use the exploit to make some madeup account. Um sort of the commit author and stuff can just be specified. Um but that was really cool and we wanted to know like how can we get in on that action. So how difficult is it to statically verify um action workflow files that that we can say that this injection exists and is exploitable and scrape this from the whole of GitHub. You know open source repositories are just there. So surely we can just download them and
automate them and churn them and maybe see what comes up. Um and as is the case with these kinds of tools um they are false positives. Sometimes we'll say there something and it's not really. So how do we minimize uh the human in the loop? Um and this is another candidate for the zero day machine. So this is some wordy labs law. Um you know one day we'll have enough tools to just mine all the zero days as they come out. So keep that in mind. So let's look at some related work. So you get um action lint. So it's some uh YAML passing matching some patterns and says if something looks pretty sus. Octoan extends that um
adds a bit more security rules instead of just the sort of best practice rules of action lint. um and your friends Sam Grap and CodeQL the usual suspects have rules for um these kinds of things. Um they're really fast and easy to use to write uh rules for and they're mostly designed for the obvious cases the inline injections like the earlier examples I showed where pretty much interpolation right at the point where um it's bad. But when you look at cases where attacker control data goes into an external action, it just says, I don't know, this looks pretty bad. You should check it out, but I don't know what to do anymore. So, what we decided to do
was to add a way to pull those actions and analyze them on demand and put them back into the analysis. So, the theme of going beyond the boundaries, that's where it is. So, let's revisit this JavaScript example. Um, yeah, sometimes I call it a plugin, sometimes I call it external action, but related tools will just say, "Yeah, um, you've put something, the GitHub event head commit message in an input to some external action. You probably shouldn't do that. You should probably sanitize that and call it a day." But there's a lot of room to improve on that. So, we created a tool called action attack. Um, basically, yeah, it'll scrape repositories that have a workflow files,
b have interpolated attacker control variables in those workflow files, and it will then pull that repo from the GitHub search API, determine if the expression injection actually occurs or can it occur via some mutation or transformation in an external action, um, or if that external action allows users, attackers to define the input to some kind of vulnerable or dangerous sync. um like a file write or something like that. So here's the big workflow. Uh we will revisit this image a few times and we'll go through it step by step but top left is stage one scrap and Q candidate repositories for scanning. So that long list on the right over there that is pretty much
everything you have to watch out for. Um this we are the interpolated values that are tacker controlled and the GitHub API. It's pretty limited if you're using it for free, but you have to kind of give your token and there's rate limits and all that kind of stuff. So, um, you can't even limit the size of the repository that hits. So, it'll give you the first thousand results of when this occurs and you just have to live with it. So to maxim out the diversity of results, we'll just give one of those syncs with the specification that they have to be workflow files. It has to be a code in a workflow file and the
language of the code is to be YAML. And so we just run that in the background. Boom. Boom. Just in a database. Here are the repositories that we should scan. Um our vulnerability database now has all a bunch of unscanned repositories. Now let us analyze them. So we have a YAML passer. U one logic we have to check for is not all uh actions are vulnerable. So specifically they have to have certain triggers. So if someone opens a pull request, if someone um opens an issue, someone makes maybe pushes then that is a candidate case for this to be exploitable. And the next one will to be determine if the either a with command or a run command has interpolated um
attack control variable in it. For example, it doesn't have to be interpolated. It could be assigned to an environment variable and then it won't be interpreted as code. But more on that later. So initially we tried octascan to minimize the effort um of creating a zero day machine. But a lot of false positives. Um again as a llinter there's not that much if checks and logic and especially on big repositories you get millions of results and they're nearly exploitable but there are a lot of reasons why it isn't actually exploitable in practice. So more on the strategy is that we then also had to determine if there is an external action. So if there's no direct
injection but they are external actions referenced then let's register them put them into vulnerability database as more repositories to scan and push that repo aside for now and first scan its dependence which are these these these workflow files uh these actions that could be for example JavaScript [Music] um so that I described was queuing an action for scanning as we've just yeah found action we ceue it for scanning. Um and now let us analyze any cued actions. So yeah pretty much is there something to scan that is an action um then continue but we only handle JavaScript for now. There are other um languages like you can do it in Docker or another workflow file or bashcript.
Um now I'm going to describe the data flow analysis. So that's just a term for tracking data. Does data at one point reach uh another point? And when it comes to the JavaScript, you have the way to to take an input is um the get input argument and then the kind of syncs that are dangerous. We're looking for like exec, read file, write file etc. Um, and we just need to know it's some string value that goes through get input. So that would define the input and um, does it define the data that goes to any of those guys because that's pretty bad. Um, it's a may analysis. So like data may define that sync but um, rather be
safer than sorry. So there's a little bit of imprecision, but it does it is hopefully already minimized by all the validation we do on the YAML file. Um so here's an example revisited. So you can see the require file at the top is the assigned to call which is called get input and the source value is user commit and it define some value that goes into exec and as we can see over here we have the user commit take the interpolated um use attacker control data. So they could define JavaScript to then execute on that. Um, which isn't immediately obvious unless you've actually audited the action. Um, another way that the action could propagate attacker control data is
by sending it back to the action. So actions can define outputs and another data flow analysis that we perform is to check in case now is the sync defining an output. So the output would look something like steps and then the ID of the step outputs and then the output name. And if that is interpolated that is a another attacker controlled value um which is what a lot of the llinters say anything the llinters would say that anything that looks like that always going to be bad but we want to sort of minimize that false positive rate. So after this data flow analysis we get a whole new set of syncs that says yeah if attacker goes into that input
attacker comes out again. So here's another example. We can see again source value user commit and then um under go some kind of processing or whatever. Uh but then it goes to the output using core set output. And we can see on the left that if in this step my action it takes that attacker controlled action. We can then assume that this is also attacker controlled and if it goes into a sync you they also have rce there or expression injection there. So, you know, that is some of the novelty um and the sort of pushing the edge of um you know, what other tools don't do. Uh sneak come up came up as one of the the actions that actually
have this vulnerability of um doing bad stuff. So, specifically on the inputs, the sneak version and the input OS are just interpolated values that the tager controls. So it's not very regular that for an input you would let the attacker or like you would give a pull request title to define a sneak version. Um but it is an interesting way that um to think that there were ways to mitigate this and uh assign this to envir environment variable but they decided not to. Um, and it's surprising that this kind of um I don't want to necessarily careless, but it is unlikely that someone's going to misuse this, but yes, it is not best practices. And
um yeah, that did come up in analysis. So you could probably trust this action, but if you gave bashcript to the sneak version, it does execute. Um, which is pretty bad. though more likely the case is that the only thing that's going to be inter interpolated into that is going to be something coming from like the job matrix. Uh but yeah and that's kind of a theme we'll see is that uh while there are a lot of ways to exploit these things in practice um yeah attack control variables don't always go into these inputs. So, as we are open sourcing this tool, we thought we might as well make it at least look good. And um Andre made
a nice little terminal gooey and um you can see all the repositories on the left, all the findings, a bit of code. So, um it's be a nice fun tool for you guys to check out. Hopefully that motivates you. Um but yeah, that's enough of the technical design of the tool. Um the results are going to be the the sweet uh the sweet little thing at the end. But for now, let me hand over to Rowan, who is my um security rubber ducky.
Thank you. Thank you everyone for coming uh to our talk. Um as David said uh this is affecting uh CI/CD pipelines and uh if anyone here uses GitHub um please raise your hand. That's that's quite a bit of you. So we can see GitHub is quite a popular tool. Some people might not know about it but um it's quite useful. And does anyone here use GitHub actions? Yeah. Uh personally I haven't used it until now. I uh I'm glad I didn't. Um so um I'm just going to try and go to the next slide. Okay, cool. U yeah so um this sounds pretty bad. Uh remote code execution even though um we've seen it a few times and
we've gotten kind of desensitized to it especially from all the you know good research coming out of Wally Labs. uh um we we should still be quite careful about this because being able to execute code on a machine um even though it's just uh a runner in this case um the runners don't have to run on GitHub machines uh you can have a runner running on your own personal machine and this can be good and bad um for some uh in in certain cases if you're running um this code and you um code execution on a GitHub runner, a GitHub hosted runner. You uh most likely wouldn't have uh any of your personal stuff on that machine. Though um you do
have your GitHub token, you also uh wouldn't be able to see the output of the program if you have the the code running on your own personal machine. So for GitHub, the runner shows the log um or the output, the output of the runner is shown on the GitHub uh interface. But if you have the code running on your own machine or the runner running on your own machine, um nobody would really be able to see it unless you configure it in that way. So why hasn't why haven't you seen a ton of stuff about everybody's GitHub repos being owned or uh about that white hacker that fixed our um sister company's uh repo. Well, by
default, at least from now, GitHub has a few configuration um security configurations in place. For example, any uh organization made after or in 2022, uh their repos, newly created repos, um have or their tokens are set to read only by default. So, you would have to purposefully go into the token settings and say, I want this GitHub token um to be able to edit the I want the GitHub token to be able to edit uh files in the repo. Otherwise, it would just have read access. So, it' be able to see uh the titles or the um the pull requests coming in, but it wouldn't actually be able to to make any modifications. Um and also,
uh GitHub Security Lab does their rounds. They go around uh checking some um known vulnerabilities. They probably have their own scanners uh as well. Uh so, yeah. Um, uh, you have your your permissions. Uh, you can, um, sorry, uh, you can set what you would like your actions to be able to do. Uh, and what you'd like your your tokens to be able to affect. As you can see, uh, they have some some options. Uh, the is that laser? Okay, cool. Um the uh disable actions shows that even if somebody accidentally in the repo makes a push and says that hey I'm going to create an action um whoever owns the repo can say no no you
can't uh um and workflow permissions um as I said just now by default now uh it the the GitHub token that's created um when the workflow when the workflow runs uh only has read permissions, but before 2022, it used to be the default for the the default used to be read and write permissions. Um which which sounds extremely safe. Uh and also this is quite a dangerous one here. Um if you have this checkbox checked, you should probably go into your GitHub repo and change it right now during this talk. Uh so this basically means that if somebody forks your repo um if your repo is public uh any changes um sorry any any actions uh running on the fork repos can
affect the base repo. Uh so it doesn't even matter if there are some configuration settings in the in your in the base repo that prevent this from running from from working because it will affect the the base repo no matter what. Um yeah and uh now comes the the most boring part actually fixing the vulnerability. Um please don't do this. Uh setting um setting your permissions uh per job is is recommended. So um under your your actions file you have this permissions uh keyword that you can use and you can set permissions for a few different um sections uh or uh facets in the uh in the workflow actions. Uh and the main ones are contents oh
sorry and the main ones are uh contents and um uh pull requests. So the content permission if it's set to write allows a action um or rather the GitHub token that's created for that action to modify the repo uh which means it can create pull requests, it can uh create files um it it has right access to the contents. Um, and uh, the pull requests permission, funny enough, um, makes perfect sense, doesn't actually, uh, allow you to create pull requests. So, if you want to stop somebody from making pull requests, setting pull requests to deny doesn't actually stop anyone from making pull requests. You have to set the contents um, permission to uh, to read or or none. Um, the pull requests
permission actually only lets you um control whether someone can label or comment or sorry um if the action can label or comment on the um on an issue. Uh oh, sorry on a on a pull request. So um sorry my mouth is is getting really dry. So these are the global permissions usually set for all of the workflows in a repository. And uh this would be per um workflow. So workflows are usually split into um into different workflow file uh workflow files that uh contain related jobs. So in a workflow file you have a job um or you have multiple jobs and those jobs have steps and um you can set permissions per job um and that that is
uh the recommended way to do it. Um so uh this is an example from uh the Are we Are we allowed to are we going to say what? No, I'm guessing can you took it out? Yeah, it's not on the screen. Okay. Oh. Oh, it's on the screen. Yeah. So, this is a real world example from the Graphana repo. It's still live if anyone wants to go and um do anything to it. Uh so uh they we did send them emails to um to fix this but uh they didn't really acknowledge that. So uh yeah uh you you can can take a look at the actions file and it contains a a run keyword. Um this
is this is quite a a nice little find. Uh it contains a run keyword with a reference to the the name of um the pull requests. So this action runs um when a pull request is made and specifically when the pull request name starts with release uh and it is uh and it gets merged. So um if you make the the pull request name um something uh that is a command you can get that to run in the runner. Um yeah that's sorry I'm just looking at that because it's just so um yeah uh it is a little bit more difficult to to exploit since it is the title of um sorry the header is actually
uh not the title of the uh of the pull request headerf is the title of the branch that you create that you that you're going to be merging. So it's a little bit more difficult to exploit in this specific uh situation because branch names have character restrictions. You can't have for example a um backslash in the name or a uh a quote which means it's a little bit more difficult to escape from this context. Um but there is there are ways. Um so the recommendations for when you for when writing uh GitHub actions uh don't use run um wherever you can not use run uh because that is just uh asking for trouble and somebody later is
going to come and make a modification to the run file which makes it vulnerable. But uh there are other ways to do this. Um, for example, if you want a action that labels um a pull request or uh an issue, instead of having a a uh run keyword in your action, um you can use something like a pre-made uh action, a JavaScript uh action that somebody has created, not because somebody else is a better programmer than you, because we all know that we are the best programmers. Um uh but rather because if you have your um uh if you if you use the run keyword and you are uh interpolating any any name that a user
can can control um then they can essentially inject anything into that unless you use environment variables and that's the next thing uh use environment variables for user controlled uh string strings. So if if you're going to interpolate the name of a pull request or an issue or anything that a user can control, um put it into an environment variable. And what this does is uh it stops GitHub from treat from treating that as a command uh and it just treats that value as a string. Um so um yeah uh you should set your your settings to require approval for outside collaborators. One of the settings is require approval for firsttime um collaborators, but uh that is still
dangerous since someone can make a change that's actually uh useful and then that gets uh that pull request gets gets accepted and then after that they're like okay cool now we can mess with the repo. Uh yeah um thank you. Uh so something that you that you should definitely do um is disable workflows for forks. Um because you don't want people to be able to control your uh your runners from from their base from from their forks. Your base repo is yours. Let's keep it that way. Uh now the difference between pull request and pull request trigger is a great example of GitHub's um brilliance documentation for their uh actions. So, if you haven't used actions before, um, one of the the
main ways, um, sorry, one of the, uh, the things that you always have to set up is the action that triggers your action. Um, the thing that triggers your action. Uh, and that is set by the on keyword. So, uh, you can choose different events that would trigger your action. For example, if someone creates an issue, an action would run. And in this case, if you want a an action to run after a pull request is made, you have two options. And now, if you Google how do I um how do I get an action to run in a pull request, you might come across uh a tutorial that tells you to use pull request trigger. And uh if you did that
uh anyone that forks your repository and runs an action uh will have uh run access on your repository. So this this um event means that that action that runs on uh someone else's repo will have access to your repo secrets and a bunch of other nice fun little um party tricks. Uh so um permissions should be set to none for as as far as possible uh as the slide says. So um yeah as you can see I prepared very well for this for this talk. So so um uh you want your your permissions to be set as low as possible like in any situation. Um and uh I think um if you if you are setting some
um some permissions on actions or uh globally, you should yeah uh you should make sure that any actions that do have the run keyword also uh are configured specifically um or configured individually because those uh would be the most um targeted. Okay. Uh yeah, back to Dave. Thank [Applause] you. Okay. So, I know it's been pretty academic, pretty nerdy, and pretty lectury, but um let's actually see what this imaginary tool we wrote um found. Uh so we showed some close findings which you know were like yes it exists but there's very limited scope into how we could exploit it but there are more legit findings. Um so 7 hours scan found 111 findings out of 17,500 repositories
which included scanning the external actions 73% of them were true positives i.e. they are exploitable if they didn't follow all the mod um mitigations that Rowan spoke about. So there are some things that we can check like the permissions but a lot of the stuff that our user settings uh are sort of bit more tricky to actually validate uh covertly like we would have to make a pull request for example and then it would say you're blocked until someone approves you and then you're like sitting there with um some huge binary script injection in your issue title and you're like okay time to close that quickly. Um so yeah depends on the mitigations but from this about 65%
precision a lot of the false positives came from JavaScript plugins. Um so we did correctly track a uh a input to an output but there was some kind of sanitation that we didn't validate at the time of the um analysis. So like stringify uh will pretty much transform your your any kind of script or injection to something pretty unusable. But that can be accounted for. So in the latest version on master, we have accounted for some of the sanitizers. So that improves our precision to closer to 80%. What is left over is stuff that is like um maybe they'll actually check your variable for certain um certain commands codes in the bash script that is a little bit nondeterministic, quite
difficult to actually validate without running the program. Um but that's pretty good I think um for all you bounty hunters out there. Um yeah, give it a run. Um here's one that looked pretty good. Um one of the biggest projects that came up with the findings, 38,000 stars. Um again, a head riff. So pretty tricky, but um you have control to you have you have access to that. And on the right is our tools um report. So that gives you the line number uh what uh where something's been used. actually might be the wrong screenshot. But in any case, the vulnerabilities on the left, on the right, I think is the same repo but a
different vulnerability um that actually use a pull request body. Um so yeah, that is probably the more impressive legit finding we found. And some future work is to prioritize active projects. A lot of those findings were smaller projects, dormant projects, maybe something that we don't really care to exploit. Um lots of things that you could just make like an issue and you can you know hello world their action. Um maybe support some external types and uh refine filtering try to covertly detect some of these permissions these mitigations as far as possible. Even during create the creation of this tool a few more things were added that um we had to account for that made certain things uh no longer
exploitable. Um so before the demo we do have time for a demo. Yes. Um, here's the tool. Go ahead, uh, run it. Uh, all you need is a GitHub API with pretty much no permissions. It just needs to, um, be able to use a search API and see what you can find cuz obviously I know the hacker community is um, respects big corporations and not showing zero days for them. So, I've left that in your hands. Um, and with that, let me open the IDE. So, this is what the project looks like. Well, the the read me there thereof. Um, some instructions. And AutoGPT is one of those classic examples that um you'll find a lot of blogs. So,
let us run. So, this is a once- off scan. This is not the monitor that scrapes. That's a different mode. Um, you give the repo owner, the name, and the commit you want to say, and then the name of your database file. It's a SQLite file. Um, we'll be scanning and cloning. So, what's happening now is basically it'll fetch from GitHub, clone the repo. It's a big repo, takes a bit of a while, uh, checks out to the commit, and then runs the scan. The scan is pretty fast if you don't need to pull any actions because then that each action is another repo to scan. Um and then it will store the findings in the database as unvalidated
uh or unvalidated by a user. Um so there it's done. So then we go to review mode. So review mode will give us this little guy. So we have if we have multiple repositories, you'll see them on the left. You can use the um arrow keys to go between these screens. The body is uh you need intent for this. Uh the body of the code is actually pulled from GitHub and it'll say you know what's wrong. In this case there is a pull request head ref request things in a interpolated string and then you can press yes or no to then say it's valid. So I'll say yes. You can say yes again and then we've validated all the
findings that we've mined and then a little report. Let's generate that. So this will generate a little HTML report um of everything that you found to be valid. So I believe it's this guy and um yeah the stuff that uh you validated will come here and then you can show in GitHub and it'll also highlight the line uh for you to see. So pretty fun, pretty cool. Um yeah, here's another hit I guess another yet another repository we've pointed out on um to an audience of cyber security professionals. But um this one is an interesting example because this is an example where they do put it through an environment variable but then they again put it in a um
interpolated string. So then it would have been safe if it was just a dollar sign and accessed like an environment variable but it's been and this is actually mentioned in the GitHub documentation. If you put the interpolation around it, it becomes interpreted as code again. So then it's exploitable. So this we call an alias injection. Um so that's another kind of injection that the tool detects. So yeah, that is our talk. Any questions? Yeah. [Applause]
So everything is in the SQLite database. Um which I think would be the best. Yeah. Yeah. So then yeah just SQLite you know findings is valid validated and then you can take all the properties. It also includes the details of the actions that that it uses. Um there is like a foreign key connection and then you can say it is vulnerable because of these actions that have this vulnerability etc. Yeah. And then generate your own report. I would say the HTML is more of just a proof of concept kind of here's a nice little report. Yeah. Sure.
So the scan command will basically run the monitor as if it just has one repository to scan. So in a way that is the functionality it does. Um the way the code if you want to do something like that you can also insert the repos into the database um and then the background thread will just pick it up. If you say repo commit commit unscanned equals well is is scanned equals false then the process will pick it up scan the job and then the findings will land in your database again. Yeah. Um I'll probably go on holiday. uh it is December but in Jan if you open up some issues um I'd be happy to take a look
and um get them get them sorted maybe even if you know address some of the future work I think um yeah this is our first big uh open source project as the company so it' be great to see some uh some tooling for you guys to use cool thanks [Applause] Across the attack surface, scattered products and siloed views create blind spots that feel unstoppable. The deadliest risks are in these gaps where attackers move in. It's time to unify fragmented snapshots into one allseeing view of risk and unleash a platform born with one intention. Isolate and eradicate your priority exposures from IT infrastructure to cloud environments to critical infrastructure and everywhere in between. This is tenable.
Your exposure ends here.