Vulnerabilities > CVE-2017-12083 - Information Exposure vulnerability in Meetcircle Circle With Disney Firmware 2.0.1
Attack vector
NETWORK Attack complexity
LOW Privileges required
NONE Confidentiality impact
LOW Integrity impact
NONE Availability impact
NONE Summary
An exploitable information disclosure vulnerability exists in the apid daemon of the Circle with Disney running firmware 2.0.1. A specially crafted set of packets can make the Disney Circle dump strings from an internal database into an HTTP response. An attacker needs network connectivity to the Internet to trigger this vulnerability.
Vulnerable Configurations
Part | Description | Count |
---|---|---|
OS | 1 | |
Hardware | 1 |
Common Weakness Enumeration (CWE)
Common Attack Pattern Enumeration and Classification (CAPEC)
- Subverting Environment Variable Values The attacker directly or indirectly modifies environment variables used by or controlling the target software. The attacker's goal is to cause the target software to deviate from its expected operation in a manner that benefits the attacker.
- Footprinting An attacker engages in probing and exploration activity to identify constituents and properties of the target. Footprinting is a general term to describe a variety of information gathering techniques, often used by attackers in preparation for some attack. It consists of using tools to learn as much as possible about the composition, configuration, and security mechanisms of the targeted application, system or network. Information that might be collected during a footprinting effort could include open ports, applications and their versions, network topology, and similar information. While footprinting is not intended to be damaging (although certain activities, such as network scans, can sometimes cause disruptions to vulnerable applications inadvertently) it may often pave the way for more damaging attacks.
- Exploiting Trust in Client (aka Make the Client Invisible) An attack of this type exploits a programs' vulnerabilities in client/server communication channel authentication and data integrity. It leverages the implicit trust a server places in the client, or more importantly, that which the server believes is the client. An attacker executes this type of attack by placing themselves in the communication channel between client and server such that communication directly to the server is possible where the server believes it is communicating only with a valid client. There are numerous variations of this type of attack.
- Browser Fingerprinting An attacker carefully crafts small snippets of Java Script to efficiently detect the type of browser the potential victim is using. Many web-based attacks need prior knowledge of the web browser including the version of browser to ensure successful exploitation of a vulnerability. Having this knowledge allows an attacker to target the victim with attacks that specifically exploit known or zero day weaknesses in the type and version of the browser used by the victim. Automating this process via Java Script as a part of the same delivery system used to exploit the browser is considered more efficient as the attacker can supply a browser fingerprinting method and integrate it with exploit code, all contained in Java Script and in response to the same web page request by the browser.
- Session Credential Falsification through Prediction This attack targets predictable session ID in order to gain privileges. The attacker can predict the session ID used during a transaction to perform spoofing and session hijacking.
Seebug
bulletinFamily | exploit |
description | ### Summary An exploitable information disclosure vulnerability exists in the apid daemon of the Circle with Disney running firmware 2.0.1. A specially crafted set of packets can make the Disney Circle dump strings from an internal database into an HTTP response. An attacker needs network connectivity to the Internet to trigger this vulnerability. ### Tested Versions Circle with Disney 2.0.1 ### Product URLs https://meetcircle.com/ ### CVSSv3 Score 5.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N ### CWE CWE-416: Use After Free ### Details Circle with Disney is a network device used to monitor and restrict internet use of children on a given network. When connected to a given network and configured, it immediately begins arp poisoning all filtered devices on the network, such that it can validate and restrict all traffic as is seen fit by the parent/administrator of the device. The apid binary is a web server listening on the Disney Circle, that serves as the main API for user functionality, it is forked from https://acme.com/software/mini_httpd/, a rather robust web server that's optimized for embedded devices. Through the apid server, all configurations and queries are made from the 'Circle Home' application from the administrator's phone. For all the under-the-hood and low level processing of HTTP headers, the majority of the code is from minihttpd, with a few modifications by Circle (once it gets into the Api parsing, however, the code is naturally all written specifically for the Circle). Interestingly, minihttpd has no support for an HTTP request with any Body parameters, it sort of just stops reading when it finds the end of the HTTP headers (“\r\n\r\n”). Because the Circle needs Body params for certain api calls (/api/CONFIG/restore and /api/UPLOADFIRMWARE), it became necessary for Circle to add this functionality, and they did this using the memory management functions already included in minihttpd. Both within mini_httpd and apid, the following code is used to parse the HTTP headers: ``` //handle_request( void ){ start_request(); for (;;){ char buf[10000]; int rr = my_read( buf, sizeof(buf) - 1 ); if ( rr < 0 && ( errno == EINTR || errno == EAGAIN ) ) continue; if ( rr <= 0 ) break; (void) alarm( READ_TIMEOUT ); // alarm in loop, lol add_to_request( buf, rr ); if ( strstr( request, "\r\n\r\n" ) != (char*) 0 || strstr( request, "\n\n" ) != (char*) 0 ) break; } ``` The code will loop and continuously read in 0x2710 bytes until it finds either “\r\n\r\n” or “\n\n”, denoting the end of the HTTP headers, and for each section that it reads in, it'll add it to the request variable via `add_to_request`, which is listed below: ``` static void add_to_request( char* str, size_t len ){ add_data( &request, &request_size, &request_len, str, len ); } ``` Which then gets to the heart of the matter: ``` static void add_data( char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len ){ if ( *bufsizeP == 0 ) { // [1] *bufsizeP = len + 500; *buflenP = 0; *bufP = (char*) e_malloc( *bufsizeP ); } else if ( *buflenP + len >= *bufsizeP ) { *bufsizeP = *buflenP + len + 500; *bufP = (char*) e_realloc( (void*) *bufP, *bufsizeP ); //[3] } if ( len > 0 ) //[2] { (void) memmove( &((*bufP)[*buflenP]), str, len ); *buflenP += len; } (*bufP)[*buflenP] = '\0'; } ``` At [1] we check to see if there's already an existing buffer with the user's request. Assuming this is the first iteration of the read loop in `handle_request(void)`, the function will malloc a buffer of sizeof(newdata) + 500. After this buffer has been malloc'ed, it just copies the user's data into this new heap chunk [2]. When we already have user data inside the buffer though (and this will happen for any size request that is bigger than 10000 bytes), instead of mallocing another buffer, the program just reallocs the old buffer to a new size of (oldsize + sizeof(new_data) + 500) [3]. An issue exists with how mini_httpd was extended to handle Body parameters, due to how Circle reused the `add_to_request` function to handle the rest of the HTTP request. This, by itself, would not normally be an issue, however, between the first and second calls to `add_to_request`, a set of variables is assigned to HTTP headers for further use. Within a loop, the server looks for a given HTTP header, and then assigns a pointer to the address of that header's value within the original request. For example: ``` loc_406040: # la $a1, aOrigin # 'Origin:' jal strncasecmp # [1] li $a2, 7 # n bnez $v0, loc_4059D0 # [2] addiu $v1, $fp, 0x27C0+addr_of_request [...] move $a0, $v1 # s la $a1, asc_42D810 # " \t" jal strspn # skip spaces/tabs sw $v1, 0x27C0+query_addr_tmp($sp) # addr of 'http://....' lw $v1, 0x27C0+query_addr_tmp($sp) lui $a0, 0x45 addu $v0, $v1, $v0 j loc_4059D0 sw $v0, Origin # [3] ``` It will look for 'Origin:' in every line of the request[1], and then assign the value [3] if it's found [2]. This is done for 'Origin', 'Cookie', 'Host','Authorization', 'User Agent', and 'Content-Length', among other less important values. After these pointers have been assigned, if it's an HTTP POST request, Apid then reads in bytes equal to the given 'Content-Length', using the add_to_request function just like before, acting on the same exact buffer as before, and this is where the problem lies. As per man malloc: ``` void *realloc(void *ptr, size_t size); [...] The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any built-in type and may be different from ptr, or NULL if the request fails. ``` Since the heap implementation is a uClibc version of dlmalloc https://github.com/kraj/uClibc/blob/master/libc/stdlib/malloc-standard/malloc.c, the conditions for when a realloc will return a different pointer than the one provided are not too complicated. Since the user controls the size of the HTTP request, the user can control the size of call to realloc to some degree. Going back to the realloc_loop disassembly (since this is Circle code now): ``` loc_4056A4: # start of loop lw $v0, -0x58E8($s1) # s1 == content_length addiu $a0, $sp, 0x438+memcpy_src subu $a1, $v0, $s0 # s0==len_of_Body_data sltu $v0, $s0, $v0 # v0==Content-Length beqz $v0, loc_4056FC # was set to 0... sltiu $v1, $a1, 0x400 [...] li $a1, 0x3FF [...] loc_4056C8: # a0=dstBuff jal read_bytes # a1=num_bytes nop bgez $v0, loc_405684 nop [...] sw $v0, 0x438+size_of_malloc($sp) # don't_get_here move $a1, $s3 # curr_malloc_buff_size addiu $a2, $s2, -0x58B4 # malloc_read_offset addiu $a3, $sp, 0x438+memcpy_src jal malloc_and_copy # add_data() add $so, $v0 j loc_4056A4 # go back to top of loop ``` Just like before, the HTTP server will keep reading in bytes from the SSL socket until it hits the end of the request, and then for every 0x3FF bytes, we will hit the adddata function from before. Since the user controls the size of the Body parameters (via Content-Length), the user can cause realloc to be hit as many times as seen fit, in ever increasing chunks of size (oldsize + 0x3FF + 0x1F4 ). After enough of these reallocations, the heap pointer returned from realloc will actually change: ``` [^_^] Malloc/realloc (ret_ptr:0x44a754, malloc_size:0x44a750, curr_size:0x52bf4, lolidk:*0x7fd98e00) memcpy src: 'QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ' ----------- [^_^] 0x4041a4 | Realloc(0xc3d208,0x531e7) = 0xc3d208 [1] [...] [^_^] Malloc/realloc (ret_ptr:0x44a754, malloc_size:0x44a750, curr_size:0x8c25f, lolidk:*0x7fd98e00) memcpy src: 'QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ' ----------- [^_^] 0x4041a4 | Realloc(0x76e7e008,0x8c76c) = 0x76e7e008 [2] ``` As shown above, after reaching a big enough size, we can actually make the reallocation result in an mmap'ed allocation instead of a normal heap allocation, and we can continuously repeat this. Which brings us back to those HTTP headers we mentioned before. Out of all the HTTP headers that are parsed and assigned to variables, five of them result in dangling pointers if we shift the underlying memory location with increasing reallocs: 'Host','Cookie','Origin',”User-Agent', and 'Authorization'. Only 'Origin' and 'User Agent' are ever referenced in the code after their initial assignment, as a lot of features were stripped from mini_httpd when being forked into Apid. User-Agent gets read into a buffer with snprintf and then parsed and checked for a regex of “MSIE”, which doesn't really do much, since we already have control of 'User-Agent'. Unfortunately, the only real opportunity is that the value of 'Origin' is returned in the HTTP response to the user (assuming the value of 'Origin' is initially valid). An example of this would be: ``` [^_^] gotta response! HTTP/1.1 200 Ok Server: apid 1.0 Date: Mon, 28 Aug 2017 18:19:31 GMT Vary: Accept-Encoding, Origin Access-Control-Allow-Origin: http://localhost Content-Type: application/json Content-Length: 54 Connection: close ``` Where `Access-Control-Allow-Origin: http://localhost` corresponds to the line in my initial HTTP request `Origin: http://localhost`. The value is written into the request response with `snprintf(response_buffer,”Access-Control-Allow-Origin: %s”, Origin)`, which is taken straight from the dangling pointer from before. While this Use-After-Free is very limited in it's actual use, there's still the potential of being able to read something out of the heap remotely, assuming that we can get something to allocate into the freed slot that “Origin” is pointing to. The obvious thought of information to leak would be passwords or tokens or PINs, etc. Unfortunately for the purposes of this exploit, not much is actually done on the heap in this daemon. Aside from the two instances of our realloc loop, there's a call to strdup in between, another realloc_loop on another buffer (the response), and then anything that occurs inside of the API parsing. After looking through all the unauthenticated API calls, it was found that the '/api/USERINFO' api call will repeatedly use `new[] void *` and `delete[] *` to allocate space for reading `/mnt/shares/usr/bin/configure.xml`, which contains a decent amount of sensitive data. A sample of my configure.xml should suffice to explain: ``` <device uid="ab:cd:ef:12:34:56"> <ip>192.168.1.5</ip> <hostname>purgatory</hostname> <displayName>purgatory</displayName> <manufacturer>Unknown</manufacturer> <mode>None</mode> <isGo>false</isGo> </device> </devices> <contact> <phone>+12223334444</phone> <countryCode/> <email>[email protected]</email> <name>Lilith Wyatt</name> </contact> ``` Regardless of what information it is, we still need a way to force the database's buffer to be read into where “Origin” is pointing. Coming full circle, as mentioned before, this is uClibc's version of dlmalloc, so it's pretty standard. If we can get our initial request to be within the same heap bin as the database read, then it should be smooth sailing after our realloc effectively frees our buffer. We don't know the size of the database, but it can be quickly brute forced, as the minimum size of the database is 2380 bytes (with the version in our test being about 4000), and also because of the binsizes of dlmalloc: ``` /* Indexing Bins for sizes < 512 bytes contain chunks of all the same size, spaced 8 bytes apart. Larger bins are approximately logarithmically spaced: 64 bins of size 8 32 bins of size 64 16 bins of size 512 8 bins of size 4096 4 bins of size 32768 2 bins of size 262144 1 bin of size what's left The bins top out around 1MB because we expect to service large requests via mmap. */ ``` So we only really need to brute force in 512 byte intervals after 2048, and then every 4096 bytes after that. If we had control of a malloc() and free() instead of realloc(), things would be simpler, as the size of our buffer would remain constant, and it would remain in the same bin, from start to finish. With realloc() however, this is not the case. If a realloced buffer does not shift its underlying memory (i.e. input ptr != output ptr), it's still possible for it to shift bins. Put another way, even if a realloc crosses the boundary of a bin, the input pointer could still be equivalent to the output pointer. And this is a problem, since we need the realloc to shift memory in order for the UAF to work. So, if we increase the size of our buffer to a point where we know that the memory location will shift after a realloc, it won't be in the same bin as the database, and we won't get anything useful from the UAF. But if we keep the HTTP request buffer at a size comparable to the database's buffer, we don't trigger the effective “free(req_ptr)” in the first place. The only option left is to try and force realloc to shift without having to increase it's size past a bin boundary, and this requires another allocation, which we can achieve using `strdup`. The most surefire way to force realloc() to shift memory is to have another chunk of memory allocated immediately after it. Given the context of this code flow, the only available allocation between the two realloc() loops was a call to strdup (starting from immediately after the first realloc loop on the HTTP headers): ``` jal malloc_and_copy # realloc loop addiu $a3, $sp, 0x27C0+ssl_buffer lw exp, -0x58AC($s0) # expanding+request addiu $a1, $s1, -0x2830 # needle (\r\n\r\n) jal strstr move $a0, exp # haystack bnez $v0, loc_4058F8 move $a0, exp # haystack loc_4058F8: # looks for end of jal Extract_http_line # first line (\r\n) and then # null terminates nop beqz $v0, loc_405D2C # v0 == 'POST /api/.... HTTP/1.1\r\n' lui $a1, 0x43 jal strdup move $a0, $v0 ``` The above code will look for the first instance of '\r\n', and then null terminate it, resulting in a cstring of the HTTP method, path, query string and version. This new cstring is then taken and strdup'ed, after which the new duplication is parsed further. Thankfully for our purposes, strdup() will allocate size for a copy of the argument string. Since apid does no validation of the 'HTTP/1.1' portion of the request, we can pad it such that the length of the first line is within the same heap bin as the database. ``` req="POST /api/USERINFO?api=1.0 AAAAAAAAAAAA[....]\r\n" ``` Also, since the Apid server doesn't need many HTTP heaers, we can minimize the rest of the request such that the strdup() and the HTTP header realloc() both land within the same bin, such that the heap looks something like this: ``` [0xc2c560] [0xc2c560+len(req)] [POST /api/USERINFO...\r\n\r\n][strdup(“POST /api/USER...\r\n”)] ^ || (Origin*) ``` And then, when the second realloc loop occurs, any slight increase of the HTTP request's size will cause it to move past the strdup, without changing which heap bin it is in: ``` [0xc2c560] [0xc2c560+len(req)] [….] [Free Buffer][strdup(“POST /api/USER...\r\n”)][POST /api/USERINFO...\r\n\r\n] ^ || (Origin*) And after the database is read in, the heap looks like such: [0xc2c560] [0xc2c560+len(req)] [….] [Configure.xml][strdup(“POST /api/USER...\r\n”)][POST /api/USERINFO...\r\n\r\n] ^ || (Origin*) ``` Allowing for us to disclose information from the database. It should be noted though, that due to the constraint of having the “Origin:” header below the gigantic “POST /api.....\r\n” line in the request means that we have a limited space in configure.xml to read from, but the most valuable information (phone number/name/email) occurs at the bottom of the file. ``` # python get_bodied.py [O_O] GOGOGO (Connected to circle...) [~_~] S-s-s-sendding!!!?! len: 0x105d [o_o] gotta response! [O_O] gotta another response! 4:51 GMT Vary: Accept-Encoding, Origin Access-Control-Allow-Origin: [email protected]</email Content-Type: application/json Content-Length: 1030 Connection: close [^_^] Thanks for hangin out!<3 ``` While it's a limited information disclosure, it should be noted that, because of the constraints placed upon this bug (remote/unauthenticated), this exploit can be used in conjunction with TALOS-2017-0437, and can target any Circle in the world that has internet connectivity. Couple this with the fact that anyone can make a Circle send it's owner an SMS with the device's PIN inside (via /api/PASSCODE/sms), that can be used for further escalation, it would be plausible for the owner's phone number to be a valuable link in an exploit chain involving social engineering. ### Timeline * 2017-09-12 - Vendor Disclosure * 2017-10-31 - Public Release |
id | SSV:96823 |
last seen | 2017-11-19 |
modified | 2017-11-09 |
published | 2017-11-09 |
reporter | Root |
title | Circle with Disney Apid Use-Between-Reallocs Information Disclosure Vulnerability(CVE-2017-12083) |
Talos
id | TALOS-2017-0435 |
last seen | 2019-05-29 |
published | 2017-10-31 |
reporter | Talos Intelligence |
source | http://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0435 |
title | Circle with Disney Apid Use-Between-Reallocs Information Disclosure Vulnerability |