Hello, DEF CON! Uh, we're very excited to have you here. Uh, today we're going to talk about a very long research we've conducted in the past few years. Exploiting OPC UA in every possible way. So, let's start. My name is Jaron Brezinov. I'm a vulnerability researcher at Cloud AT Media 2. I also have a DEF CON black badge. Woo! Uh, with me is Noah Moshe, uh, is also our senior researcher. And, uh, we would like to say thank you very much for the other researchers who worked on this project, Uri Katz and Vera Menz. So, thank you very much. Today, we're going to talk about a very long research we've conducted on OPC UA security. So, basically, we researched a lot of, uh, actually dozens of OPC UA protocol stacks and different products. And we found some core issues in their implementations. So, it's not like we found a one-off vulnerability in one product. Rather, we found and were able to develop multiple attack vectors that exploited multiple products that support OPC UA. So, all in all, we found around 50 CVs and, uh, we developed 12 unique generic attacks that we gathered into one huge framework. And, uh, we're going to release it open source. Uh, we also released, uh, a phaser, OPC UA phaser, uh, that, uh, actually many vendors are currently using as part of, uh, secure, secure development. And they also found a couple of bugs with it. So, uh, I think it was very helpful. Um, and finally, big thanks to ZDI. Um, some of this research, at least, uh, were very incentivized because of Pwn2Own. So, ZDI has their Pwn2Own competitions, uh, and also they have the specific ICS category, which they emphasized on OPC UA. So, it gave us, uh, the cash prize, obviously. Uh, gave us, uh, a good incentive to research OPC UA, find bugs, and get 200K. So, today we're going to talk about, uh, what is OPC UA, uh, cover some protocol stack implementations, go over bits and bytes, uh, which is a bit boring. Uh, we're going to, to cover the research methodology. So, how we approach OPC UA. Uh, we're going to, uh, approach this project, how we research different protocol stacks, uh, et cetera. Uh, and afterwards, we're going to show you, uh, some cool vulnerabilities and exploits, uh, we're able to find and finally release our OPC UA exploit, uh, framework. So, let's start with what is the problem. So, why OPC UA was created from the fir- first place. So, in the past, uh, let's say we had, uh, physical process. In this case, uh, water tank, uh, that we want to keep track of the water level. So, we have a PLC, uh, with some kind of a logic that keeps track, uh, using sensors on the water level. And these are configured as variables. So, the sensor is reading where the water level, uh, is. And we have a variable that is changing in the process. Now, if we want to monitor this, uh, procedure, uh, for example, from an HMI or a SCADA server, uh, we had to use the specific proprietary ICS protocol. So, we had to use the protocol, um, in order to communicate with the PLC to read and write, uh, these tags values or variables. Um, and obviously it's not very convenient because if you want different products to communicate with it, uh, we're very limited. So, OPC UA was introduced, uh, in order to have kind of a unified way to communicate between different devices and products within a SCADA network. So, now, we could all communicate OPC UA and we do not- do not have to be, uh, limited by the specific ICS protocol. Uh, and this is why many vendors joined in and today most of the vendors are supporting OPC UA, uh, in various ways, uh, from servers to clients to support in PLCs, SCADA servers. So, it became, uh, kind of, uh, the new standard, uh, and it's very popular today. Um, and if- in essence, the- the protocol was created for kind of a unified way for data extraction, uh, and, uh, it's very popular today. Um, and if- in essence, the- the protocol was created for kind of a unified way for data exchange between industrial devices. So, we have the server, which stores the variables, for example, the water level variable, and we have clients that read or write to these variables to, uh, monitor the process or also alter and modify the process. Now, it's very used, uh, it's very widely used, uh, by almost any vendor. So, let's go over a bit of the history of OPC UA. Now, OPC UA was created by OPC family. Uh, OPC UA was, uh, the foundation, uh, and the first specs goes back to 2006, and it was created based on lesson learned from the OPC Classic, OPC DA. So, in the past, there was a different protocol with similar purpose, uh, but it wasn't good enough. Uh, it lacked, uh, independent- platform independent, so it was very tied to Windows, uh, and it was not scalable, it was not secure, so, OPC foundation created OPC UA. also created a very thorough and uh deep specifications to make sure everybody are using it correctly and also to cover all the topics that anyone needs to know about OPC UA. So for example uh how uh objects are created in OPC UA is found in a specification. How security works is also found in specification. So if everyone anyone wants to implement OPC UA uh obviously they need to read the specification and they're pretty good so it's uh very kind of uh uh easy to read and follow. Now to accelerate the use of OPC UA, OPC foundation created uh three main protocol stacks. They created the Java protocol stack, .net protocol stack and NCC protocol stack. Basically what they tried to do is they tried to create some kind of SDKs or core libraries that other protocols don't have. So basically what they tried to do is they tried to create some kind of SDKs that other products could easily implement in their software to add support to OPC UA. So in essence to expedite popularity OPC foundation created the first OPC UA protocol stacks. And today there are hundreds uh maybe thousands of products using OPC UA from servers to clients to protocol gateways and they can all be found in the OPC UA uh OPC foundation website. And when we started this research we also went back to this website and we looked at the different products and it's very convenient uh to see what products are supporting OPC UA. Now the problem is that most products are actually heavily relying on the base protocol stacks that OPC foundation created. So if we look at uh some of the top products today that implement OPC UA we can see that most of them are using OPC foundation core libraries. So for example the OPC foundation dot net is actually the the actual first OPC UA dot net uh protocol stack. So the the vendors created products that use the core libraries and added a little bit uh of their own touch or their own code to modify it but in essence they're still using the same protocol stack as the core libraries so that's where the problem is. So this is a kind of a So we wanted to find vulnerabilities in the base protocol stocks so we will not exploit or find vulnerabilities in just one product rather we will exploit all of them or at least a big portion of them. And to do this we created a very long list of different OPC UA protocol stocks and products and we tried to divide them into different categories. So we picked different protocol stocks from C to C++, Python, Java, basically any modern programming language and we divided all of these products to different categories which in each category is based on a very similar protocol stock. In this way if we found a bug or a vulnerability in one of the core libraries of a specific category we could exploit all of the product that used the same core. So before we dive in into the vulnerabilities themselves let's go over quickly how OPC UA is implemented. So in OPC UA we have this concept that is called nodes. So in OPC UA everything is a node. For example our water level variable is a node of type variable and it has its own subtype float. So basically it's a variable that has its own subtype float. So it's a variable that has a value of type float. And in OPC UA also we have the concept of name spaces so name spaces are kind of containers for nodes and we can have different name spaces for different purposes. So for example we can have a name space with all the base nodes and we can create our own name space that extends different objects for our purposes. In name namespaces we have the nodes so therefore nodes are identified by namespace ID and also node ID. So if we want to refer to a specific node in the entire address space we'll need to specify the namespace and the identifier. Now since the specifications are very very thorough and uh detailed uh they also tell us how to encode these nodes. So for example in the specification they tell us that uh we can encode uh namespace with a single byte and identify with two bytes and we actually looked at the specification in order to understand how to implement it ourselves so it was very uh beneficial for us to read the specification and understand this. In OPC UI we also have this concept of services so uh services is our interaction with the server and by activating some kind of a services and we can activate different services on different nodes. So for example if we want to read a variable or a tag we can use the read service to read its value. If we want to write to it we'll use the OPC UI write service. So services are very uh is our way to interact with the server and actually to implement OPC UI you need to implement a lot of these services. So if we sum up uh this uh crash course uh so we have uh for in our example for example uh uh a uh a uh a uh a uh a uh a uh a uh a uh a tag water tank in our water tank we want to monitor this process um so the water level in our OPC UI model will be a variable of type float and if we want to read it we will use the read service from our HMI and our HMI will continuously read this variable using OPC UI the read service. Sorry for the boring stuff let's go uh to some more exciting stuff. Uh so we have uh uh research we researched OPC UI for a very long time. And uh when we came up to start this research we needed to come up with a plan. So we started with buying two Intel NUCs uh which are very powerful computers somewhat powerful. Uh and we installed uh VMware E6I on those uh NUCs. Uh we did this because we wanted to install many many products from uh different categories. So for example to install OPC UI servers, OPC UI clients, OPC UI uh protocol gateways uh and also different protocol stocks and SDKs. So we needed a lot of different virtual machines in order to install all of these products so we could research their binaries or review the code. And to do this we started to build our own client. So we wanted a way to interact with these products and we wanted a way for us to uh kind of poke these servers to reach some uh code path. And we decided from very early on the research to build our own uh protocol stack. So we wanted to build our client that we could easily modify or change and play with the different packets. We also wanted hands on experience with OPC UI. So we wanted to understand better how objects are created. We wanted to understand better how uh services are being used. And so we used we created our own client to make sure we really understand how OPC UI is being used. After building our client uh the next step for us was to have some kind of fuzzers running passively in the background. Uh so we wanted uh something to fuzz all of the products that we installed and we had dozens of products installed in our in our uh environment and we created our network fuzzer. Uh so the network fuzzer uh was based on bufuz framework. Uh it's uh very convenient to easily write uh network fuzzers based on this framework. Uh and we implemented six different services including grid, browse, write, etc. Uh and we fuzzed them all on all the product. So we had dozens uh or even more than dozens of uh uh products installed. Uh so we had uh a number of products installed. Uh fuzzers trying to exploit or at least find crashes in all the products that we installed. And it was actually very beneficial because it helped us to find a couple of bugs and we also released it as open source so now vendors are using this as part of their security development cycle. We also created coverage based fuzzers so at one point we found NCC OPC UA stock so it was a source code of the implementation of a protocol stock and we took it, we created some harnesses with lib fuzzer and AFL and we also ran this for a couple of weeks. Unfortunately we did not find any bugs mostly because Kaspersky did the same thing a few years ago but it was still very helpful for us because we created a lot of corpuses so we created a way to reach a certain code and we were able to do that. So we were able to do that. So we were able to do that. So this gives us the source code path within these products. Finally we needed a way to control all the fuzzers, all the running fuzzers. So we built umm a slack bot that monitors all the different fuzzers and whenever we reached the crash we received a notification and also a screenshot so it was very helpful for us to monitor these hundreds of fuzzers running in our infrastructure. And it also it also helped us to understand what's going on and keep, uh, and getting the status every single time. Next, while we have all of our fuzzers running in the background, we wanted to move on and do some manual research. So we turned out to the specification and started to understand and look for esoteric and complex features and mechanisms. So basically we asked ourselves, what will developers overlook when implementing the specification? And just to give you an idea what it means, let's go over this example. So when we read the message header specification, so in OPC UA we have a very strict way to send OPC UA messages and part of these messages we have the header. And inside the header we have a flag saying whether the message we're sending is complete or whether it is chunked. So we can send a very, very, very big, long message and we can send and we can divide it into different chunks and send each chunk every time. So in the header we have a way of saying, of notifying the server whether this specific packet is a chunk or it's the final message and now it needs to process all the different chunks we sent. So we asked ourselves, what happens if we're sending the server a chunk and another chunk and another chunk? And the answer is no. So we're sending a chunk without ever sending the final chunk. What will happen? The server will terminate the session, the server will crash. So these types of features we tried to research and explore. So let's move on to the cool stuff. The vulnerabilities and exploits. And we'll start with the denial of service scenarios. Obviously denial of service is a big deal when we're talking about ICS or Scudam. Because, you know, it's a big deal. It's a big deal. It's a big deal. Shutting down a server might mean shutting down an entire factory. So obviously we wanted to research and explore denial of service scenarios. For example, what happens if we're able to crush the OPC UA server? And to do this we did not just want to think about, okay, a single crash or something that our father found. We actually approached it from a very thorough way, I would say. Because we created ourselves categories. For example, uncontrolled memory management. And we followed these categories to find specific attacks against these categories. So we actually, when we reversed engineered the code or when we did the code review, we tried to think what will cause uncontrolled memory management. Or what will cause a thread deadlock. And we followed different code paths that we believed will get us to a deadlock. And we followed different code paths that we believed will get us to this point. So if we take example, the chunk floating, as I started to describe earlier, then as I mentioned, we have in the OPC UA header, we have the chunk type. This is the flag I was referring to. And it has two main values. C or F. C means this specific packet is a chunk. And it's part of a longer chain of chunks. And F means this is the final chunk. And now you need to process all the previous chunks. If we look at different servers, for example, this is the OPC UA .NET stack. We will see an if statement checking for is final meaning is the flag F. And if so, stop processing the message. Stop to receive more chunks and start to process the entire message. So what we did was simple. We sent a lot of chunks. And we just needed to process the whole never sent the final chunk. And finally the server crashed. So we used this vulnerability not once. So it was not an off one vulnerability. We actually used this vulnerability against different protocol stocks because apparently many developers did not think what will happen if they will receive a lot of different chunks. And we're able to exploit and actually crash a lot of different OPC UA servers. Another example from our use after free category is method calling from a dead session. Now it turns out that in OPC UA we have a way to activate methods remotely. So we can configure in the OPC UA server a method. For example here we have a method to multiply two nodes and these nodes are obviously from type integer or float. And we have a way to activate this message remotely by sending a method activation or invoke. Now we looked at the specification and we noticed something very interesting. It actually says in the specification what if the if the method calling is created from a session and we actually send a lot of different methods and then terminate the session. The specification say that the method calling is created from a session and then terminate the session. The specification say that the server should not return answer to the client because the session is terminated. Obviously we thought ourselves OK. What happens if the developers did not implement this correctly? So what will happen if we will send a very long list of methods for example 255 methods in an array for the OPC UA server to start processing and we terminate our session in between. So how it looks like? We have a very long list of methods. So we have a very long list of methods. We have a very we're preparing a lot of methods to send to the server, we're sending all these methods to the server, the server starts analyzing all the methods and in between we're terminating our session. Now the server continues to process all the methods and finally it needs to send back the result to the client. However the session is gone and if developers are not implementing this correctly they will try to dereference non-existing session which will result with an access violation. So again this was, this scenario was based on something we read in the specification and we thought ourselves will be interesting to implement. And by the way all the attack vectors are implemented in our uh framework, exploit framework uh which you can access uh through GitHub and we'll show you later how to access it. Okay, so the null of service is okay uh I mean we can cross over into a scan networks, but that's not a big deal enough. We wanted to have a way to remote code execution. We wanted a way to control the OPC UA server and maybe modify these tags. So for example not just crushing the opposite service, but actually change the water level from zero to one hundred and change how factories and change how the physical process looks like. This news come back totally episode. It makes is much more interesting. So, uh, we decided to research, uh, the PTC capware. Um, this is a very popular software. Uh, it's one of the industry leading, uh, OPC UA servers used in the biggest manufacturing lines including oil rigs, wind farms, uh, etc. Uh, it's Windows based, uh, 32 bits implemented as a server, as a service. And we, we, we searched this, uh, product for, uh, quite some time. By the way, uh, we all, we also let our fuzzer to fuzz this, uh, process, uh, and this, uh, product for quite some time. And, uh, one day, uh, at night, uh, we received a notification from our slack bot, uh, that there is a crash. Uh, and at the beginning we were very spectac-spectacled that, uh, it's not a real crash. Um, but our researchers, uh, Uri and Vera, started to look at it and they discovered it has something to do with, uh, string manipulation. So, they tried to get even deeper into this, what happened exactly in the process of converting strings and they discovered something very interesting that we'll cover now. In OPC UA, uh, we needed to encode our strings in some way and transfer it over the network line. So, uh, if we, for example, have, uh, a tonic ID in our example, we have a tonic ID, uh, or a tonic location name, uh, then we need to encode the string, as you can see here, in some way, uh, and send it over the line to the OPC UA server. Now, in OPC UA, all the strings are UTF-8 encoded. But, some of the servers are using UTF-16. For example, Kepler is using UTF-16 as a way to encode the strings. So, whenever Kepler read the strings from the packet, it actually tried to convert from UTF-8 to UTF-16 and there we noticed the crash. So, there was something in the conversion between UTF-8 to UTF-16. So, before I explain what happened, let's go over what is UTF-8 and UTF-16 encoding. UTF-8, uh, uh, is a very good encoding that we can uh represent symbols for example uh the letter A with some bits uh for example A is uh 41 hexa. But if we have other symbols that are bigger than uh 7 f hex we need uh we need another byte to represent this symbol. So for example uh this funny looking A is represented as a C380. So some bytes or sorry some symbols are represented as a single byte and some symbols are represented as 2 bytes or 3 bytes or even 4 bytes. And capware whenever it received a message OPCA message with a string it tried to understand how many symbols are within this string. Why? Because it needed to understand how to allocate memory for the UTF-8 uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh uh UTF-16 conversion. So for example if we're sending uh this string capware will try to understand how many symbols are inside. So it knows that 41 means one symbol and C3 probably means 2 symbols because this is above 7 f. And so it tries to calculate how many symbols we have. For example here we have one symbol represent represented with 1 byte. Another symbol represented with 1 byte. Another one with another byte. And we have a symbol this funny looking A with 2 bytes until it reaches a null terminator. And whenever it reaches a null terminator it stops and calculating exactly how many symbols it has so it could convert it to UTF-16. But what happens if we're sending just the C3 at the end? Capware will probably think it's symbol, one symbol represented with two bytes and will jump two bytes. So let's see how it happens. So we have the 41 which is one symbol, one byte. 41, one symbol, one byte. Another one. And then we have C3. C3 in Kepler logic means to jump two bytes. So it will jump above the null terminator and we're starting to jump on the heap. And this means we will jump on the heap until we're reaching another null terminator and we could leak data. So we actually used this vulnerability in order to leak a lot of data from the heap. But we also were able to leverage this further. So first of all, leaking data from the heap, we used the read tag. So for example, if we want to read variables or tags from the OPCA server, we'll use the read service and we could just specify the node ID. Uh so node ID and namespace ID. And we could read the tag. Tag information. But if we're sending uh if the the node is encoding with a C3 at the end, whenever Kepler will try to convert, we'll start to read data from the heap. So we used it to leak a lot of data from the heap. And this way we were able to leak pointers and defeat ASLR. We also did kind of the opposite. So we used the write service in order to write memory into the heap. And we used the write functionality. So for example, we wrote a tag that ends with C3. And whenever Kepler tried to convert it from UTF-8 to UTF-16, we started to write, overwrite the heap. So now we had uh uh out of bound read to leak pointers and defeat ASLR. And we also had out of bound write to construct our rope chain and eventually get remote code execution. So uh our researcher Uri uh we're able to really convert it into a full chain. And first of all he used uh the leak uh primitive to leak a lot of data from the heap. Then he overwrote uh he calculated some addresses uh that are needed in order to create our rope chain. Overwrote some uh information on the heap. And finally triggered a block. So we were able to convert it into a full chain. And we cleared the bug and got full remote code execution. Great. So we covered the denial of service scenarios. Also remote code execution scenarios in OPCA servers. Let's see how to do this with remote code execution in clients. Okay. Thank you very much Sharon. Uh like Sharon said at this point we exploited many different products. It started with OPCA uh uh servers. Also some OPCA gateway protocols. But that's when we thought to ourselves let's try and look at clients as well. Now what is the usual suspect and like the immediate thing that you think about whenever you're talking about exploiting OPCA clients? Well it might look like this. We have an OPCA client connecting to a rogue or malicious OPCA server. And whenever he does it, he tries to read, write or interact with different tags. And somehow by returning malicious data uh the server is able to exploit and run code and execute code on the OPCA clients. Now when we realized this is the attack scenario we thought to ourselves yeah we looked at pretty much logical bugs and memory corruptions and these are very very tough. Meaning they take a lot of time to research but when you do these things, they go to the right direction. So you just don't want them to be active. So we did our own way to get fully exploit and it's not that easy. I mean, just on the memory corruption Sharon showed you just now, to actually fully exploit it on Windows 10 machine, it takes months and months and many of our researchers like Uri worked on it for a very long time. However, we thought to ourselves, maybe there's an easier way to exploit OPC UA clients that are less relevant to OPC UA servers. And that's when we looked at two very, very popular OPC UA clients, being Inductive Automation Ignition and Softing Data Feed Edge Aggregators. Now, this is kind of the big names in OPC UA clients. These are SCADA and data servers that also have the functionality to connect to an OPC UA server and read and write tags to it. However, one more thing they have in common is that they are both web based. Meaning they are used from a browser by a client and connect to the Ignition, Inductive Automation Ignition server using web browser. And that's when we thought to ourselves, maybe this is the right way to exploit OPC UA. So, yeah, web browsers are a little bit easier. What can we do in them? Well, of course, the main functionality in OPC UA clients is reading, writing or subscribing to tags. Meaning I want to read a variable, so I'll read or write to it. That's when we thought to ourselves, yeah, let's say this scenario will happen. We have our OPC UA client connecting to our malicious server and trying to read a tag. Like in Sharon's example, trying to read the water level. Well, this is a very, very simple scenario. So, whenever it tries to read a tag, our server simply says, yeah, sure, here you go, read a tag. However, instead of returning actual value, like a float number or a string, we return a simple XSS script tag, which then the client takes and inserts it into its DOM. Now, whenever it inserts into its DOM, it simply executes it and we have the ability to execute code in the context of the client's browser. And as it turns out, both of these servers are actually have an XSS vulnerability in the reading and writing tag functionality. Meaning we were able to, as you can see here, achieve alert in softening and, of course, in ignition as well. Now, XSS is pretty cool, but I guess all you can do with it is maybe re-crawl the user. I mean, it's not remote code execution per se. That's when we thought to ourselves, how can we take it further and actually leverage our XSS vulnerability into achieving a full-on remote code execution on the ignition, the inductive automation ignition and softening edge aggregator servers? Well, as it turns out, we actually chained multiple vulnerabilities in both servers in order to achieve the remote code execution vulnerability. So, let's take a look at both of these exploitation chains and see how easy it was in comparison to the full-on pledged memory corruption vulnerability we showcased to you before. So, in the case of ignition, one of the main functionalities the server offers is that it's able to execute a full-on memory corruption ability to import and basically upload a new project. Now, in this project, we can set up something called gateway events. What's gateway event, you might ask? Well, basically, it's a callback script that we can add that will be executed whenever a certain thing happens. Like in this case, we're talking about a script being executed every few seconds on a scheduled event. Now, inductive automation ignition chose to allow users to add Python code to their gateway events. Meaning, by simply having our, by simply uploading our callback script, we are able to execute arbitrary Python code and achieve remote code execution on the ignition server. Now, this was a whole chain starting from the XSS in the OPC UA client read. However, we also wanted to look at softening and try and exploit a few functionality there. Well, in softening, we don't have a project upload. However, we have software that is able to do that. So, in softening, we don't have a project upload. However, we have something that could be similar. Procedure restore. Meaning, we want to restore our old configuration of the server and go back in time and basically have the old settings. Well, whenever we do it, perform a procedure restore procedure, we actually upload a zip file to the server. And the server basically unpacks it and loads an XML and configuration files from this zip. Well, we looked at the softening server and found a few vulnerabilities involving zip slip and Pratt traversal. Meaning, we had the ability to basically write files anywhere in the system whenever we invoke the procedure restore. And by uploading a shared object that will execute code whenever it is loaded, we are able to execute code on the server, allowing us to basically control it and having remote code execution on both servers. Now, these exploitations started from the OPC UA vector. Meaning, we had the ability to execute code in the context of the browser. However, by chaining it with a few different vulnerabilities, we are able to achieve full on remote code execution on the client, allowing us basically to exploit it as well and achieve fully off pledged remote code execution. Now, everything we showed you is pretty cool and pretty extensive. However, during the development of this entire process and research, we developed our own exploitation framework that we're going to share with you today. And like Sharon showed you, we researched dozens and dozens of different protocols and we actually discovered over 50 unique CVAs. I think we are getting closer to 60 at the moment. And developed a lot of the, what we call attack concept. Meaning, one vulnerability, one kind of logic flaw that could affect multiple different servers and multiple different products. That's why we created our own OPC UA client and our own actually OPC UA exploitation framework that you can feel free to clone it and scan it and do it for your own. And this framework is pretty extensive and contains the entire knowledge base that we researched in the last few years. And we hope it will be useful. And basically it is released as an open source project. So feel free to download it from our GitHub account. And in this framework you'll find the full on POC script and exploitation techniques of all of the OPC UA projects. So, we're going to show you some of the different attacking scenarios we developed. Like the chunk flooding that Sharon showed you. Like the capware vulnerability in the UTF-8 beta location and bed string concatenation. And of course dozens and dozens of other vulnerabilities we uncovered over the last three years. So feel free to use it and feel free to share it and use it in your own servers, environments, etc. So, let's summarize everything up and see the entire. Process we've done. So, like Sharon told you, during the last three years we researched Pwn2Own heavily. We researched OPC UA heavily. Mainly as the Pwn2Own contest. However, also on our own. Looking at different products, different OPC UA implementation, protocol stacks, code bases, etc. We found and discovered over 50 vulnerabilities, exospecies, protocol stacks that affects dozens and dozens and actually probably thousands of different OT environments. And work closely with the vendors to fix every vulnerability and disclose it to them and make sure that the environments are more safe and more secure. We actually gave them early access to our OPC UA exploitation framework and we can basically giving the OPC UA vendors and developers the ability to test their products. Again, the different attack techniques we developed and look for bugs in their own code base. And we are happy to say that a lot of them used it and actually found new vulnerabilities using our own OPC UA exploitation framework. So, please do use it as well in your OT environments, in your products, etc. Use it. Add to it. It is open source. And so of course you can contribute to it as well. And thank you very much for our talk, for attending our talk on OPC UA. Thank you very much.