KhiZaRix Posted March 31, 2015 Report Posted March 31, 2015 Affected software: GoAhead Web ServerAffected versions: 3.0.0 - 3.4.1 (3.x.x series before 3.4.2)CVE ID: CVE-2014-9707Description: The server incorrectly normalizes HTTP request URIs thatcontain path segments that start with a "." but are not entirely equalto "." or ".." (eg. ".x"). By sending a request with a URI thatcontains these incorrectly handled segments, it is possible for remoteattackers to cause a heap overflow with attacker-controlled content orperform a directory traversal attack.Fixed version: 3.4.2Bug entry: https://github.com/embedthis/goahead/issues/106Fix: https://github.com/embedthis/goahead/commit/eed4a7d177bf94a54c7b06ccce88507fbd76fb77Reported by: Matthew DaleyDetail:The vulnerability lies in the websNormalizeUriPath function. Thisfunction correctly handles the normalization of URIs consisting ofnormal segments as well as "." and ".." segments, but fails to handleother segments that start with a '.' character.A quick runthrough of the important parts of this function:The function starts by splitting up the URI into segments (at forwardslashes) into an array. At the same time, it calculates the totallength of these segments.The function then iterates through the resulting array in order toperform an in-place normalization (both the input and output pointerspoint to the same array):* If a given segment does not start with a '.', it is simply copied from the current input pointer to the current output pointer. The for loop's increment code will then advance both the input and output pointers.* Otherwise, if the segment is "." or "..", the input and output pointers are adjusted appropriately (taking into account the for loop's increment code) but (correctly) no segment is copied.* Otherwise the segment starts with a '.' but is not "." nor ".."; in this case the function incorrectly does nothing and both the input and output pointers are simply advanced by the for loop's increment code. This effectively skips over a segment in the segment array without any modification by the function.After this iteration has completed, a string buffer for the finaloutput is allocated. The size used for this allocation comes from thepreviously-calculated total segment length, with the addition of spacefor forward slashes to join the segments back together again and anull terminator. The segments in the array up to the final outputpointer are joined together in this buffer with forward slashesseparating them.There are two ways to exploit this incorrect handling of certain segments:1) Heap overflowThe heap overflow exploitation lies in the possibility to create adisconnect between the lengths of the segments left in the segmentarray after the iteration has completed and the previously-calculatedtotal segment length. The previously-calculated length should, intheory, be the worst-case (longest) final output string buffer sizerequired (when all segments are left and none are removed by thenormalization iteration). However, since we can force the iteration toskip over certain segments in the array, it is possible to effectivelyduplicate segments in the resulting array; this is done by having thesegment copied from one location to another but then also having theoriginal copy skipped over, making it appear in the resulting arraytwice. When this is done, the previously-calculated length is nolonger long enough for the final output's string buffer, and a heapoverflow occurs while joining together the final result.As an example, take the following URI as input to the function:"/./AAAAAAAA/.x".The URI is first split into the segments "", ".", "AAAAAAAA" and ".",with the total segment length calculated as 0 + 1 + 8 + 2 = 11 bytes.The normalization iteration proceeds as follows:* The "" segment is simply copied from input to output, and hence remains unchanged. Both the input and output pointers are then advanced.* The "." segment causes the output pointer to stay in place while the input pointer advances forward.* The "AAAAAAAA" segment is simply copied from input to output, and hence overwrites the previous "." segment. Both the input and output pointers are then advanced.* Finally, the ".x" segment is incorrectly handled: no modification of segments is performed but both the input and output pointers are still advanced, moving the output pointer over the original "AAAAAAAA" segment.Hence, the resulting segments in the array that are left up to thefinal output pointer are "", "AAAAAAAA" and "AAAAAAAA". Note that the"AAAAAAAA" segment has been duplicated. These segments, includingspace for forward slashes to join them together with and a nullterminator, have a total length of 0 + 8 + 8 + 2 + 1 = 19 bytes.A string buffer is then allocated for the final output, which uses thepreviously-calculated total segment length of 11 bytes plus 3 bytesfor forward slashes and 1 byte for a null terminator, giving a totalsize of 11 + 3 + 1 = 15 bytes.The resulting segments are finally joined together into this finaloutput string buffer. In doing so in this case, however, the buffer isoverflowed by 19 - 15 = 4 bytes.So, a remote attacker can make (ie.) a simple HTTP GET request for theURI in question and cause a heap overflow. ASAN gives the followingoutput in this case, which shows the exact moment that the heapoverflow occurs:===================================================================2613==ERROR: AddressSanitizer: heap-buffer-overflow on address0x60200000d47f at pc 0x7ffff6f34020 bp 0x7fffffffd410 sp0x7fffffffcbd0WRITE of size 9 at 0x60200000d47f thread T0 #0 0x7ffff6f3401f in __interceptor_strcpy(/usr/lib/x86_64-linux-gnu/libasan.so.1+0x2f01f) #1 0x7ffff63a7d6d in websNormalizeUriPath src/http.c:3320 #2 0x7ffff639b4de in parseFirstLine src/http.c:969 #3 0x7ffff639a905 in parseIncoming src/http.c:880 #4 0x7ffff639a4c9 in websPump src/http.c:829 #5 0x7ffff639a19c in readEvent src/http.c:802 #6 0x7ffff6399de7 in socketEvent src/http.c:740 #7 0x7ffff6399cbc in websAccept src/http.c:719 #8 0x7ffff63ac8ed in socketAccept src/socket.c:327 #9 0x7ffff63ade95 in socketDoEvent src/socket.c:638 #10 0x7ffff63add5f in socketProcess src/socket.c:622 #11 0x7ffff639daf8 in websServiceEvents src/http.c:1307 #12 0x401b5c in main src/goahead.c:153 #13 0x7ffff597ab44 in __libc_start_main(/lib/x86_64-linux-gnu/libc.so.6+0x21b44) #14 0x4011d8(/home/matthew/goahead-3.4.1/build/linux-x64-debug/bin/goahead+0x4011d8)0x60200000d47f is located 0 bytes to the right of 15-byte region[0x60200000d470,0x60200000d47f)allocated by thread T0 here: #0 0x7ffff6f5973f in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x5473f) #1 0x7ffff63a7d04 in websNormalizeUriPath src/http.c:3318 #2 0x7ffff639b4de in parseFirstLine src/http.c:969 #3 0x7ffff639a905 in parseIncoming src/http.c:880 #4 0x7ffff639a4c9 in websPump src/http.c:829 #5 0x7ffff639a19c in readEvent src/http.c:802 #6 0x7ffff6399de7 in socketEvent src/http.c:740 #7 0x7ffff6399cbc in websAccept src/http.c:719 #8 0x7ffff63ac8ed in socketAccept src/socket.c:327 #9 0x7ffff63ade95 in socketDoEvent src/socket.c:638 #10 0x7ffff63add5f in socketProcess src/socket.c:622 #11 0x7ffff639daf8 in websServiceEvents src/http.c:1307 #12 0x401b5c in main src/goahead.c:153 #13 0x7ffff597ab44 in __libc_start_main(/lib/x86_64-linux-gnu/libc.so.6+0x21b44)(... snip ...)As with all heap overflows, it's likely that this can then go on to beexploited in order to gain full remote code execution, especially inembedded systems which are less likely to have heap allocators withmodern hardening techniques.2) Directory traversalThe directory traversal exploitation lies in the fact that we canforce the normalization iteration to skip over certain segments in thearray; namely, we can force it to skip over a ".." segment. The ".."segment will pass through unchanged into the final output stringbuffer, where it is treated by the rest of the server as an actualparent-directory relative segment.As an example, take the following URI as input to the function:"/../../../../../.x/.x/.x/.x/.x/.x/etc/passwd".The URI is first split into the segments "", "..", "..", "..", "..","..", ".x", ".x", ".x", ".x", ".x", ".x", "etc", and "passwd". (Thetotal segment length that is calculated during this operation isirrelevant for this mode of exploitation.)When the normalization iteration reaches the ".x" segments, thecontents of the segment array are still untouched (as all the previoussegments are either empty or are "..") and the output pointer is stillpointing back at the "" segment. The incorrect handling of the ".x"segments only causes the output (and input) pointers to be advancedforward over the "" and ".." segments.When the iteration reaches the "etc" segment, all the "" and ".."segments have been skipped over; the output pointer is now pointing atthe first ".x" segment. The "etc" is copied over the first ".x"segment, and the "passwd" segment is copied over the second ".x"segment.Hence, the resulting segments in the array that are left up to thefinal output pointer are "", "..", "..", "..", "..", "..", "etc" and"passwd"; note that the ".." segments are still present.The final output string buffer is created and the resulting segmentsare joined together to give a string of "/../../../../../etc/passwd".The rest of the server is expecting that the result from the functionis normalized and that it contains no relative segments. Hence, the".." segments go unnoticed when opening the content file whilehandling the HTTP request. The end result is that the local filesystemis traversed up from the administrator-configured web root untilreaching the filesystem's root directory and back down again into the"/etc/passwd" file. Hence, the file "/etc/passwd" is given in responseto the HTTP request, regardless of the configured web root.So, a remote attacker can make (ie.) a simple HTTP GET request for theURI in question and get the contents of the "/etc/passwd" file:$ echo -ne 'GET /../../../../../.x/.x/.x/.x/.x/.x/etc/passwdHTTP/1.0\r\n\r\n' | nc localhost 4700HTTP/1.0 200 OKServer: GoAhead-httpDate: Sun Nov 16 17:21:01 2014Content-Length: 1346Connection: closeLast-Modified: Sat Oct 25 17:07:25 2014root: x: 0: 0:root:/root:/bin/bashdaemon: x:1:1: daemon:/usr/sbin:/usr/sbin/nologinbin: x : 2 : 2 : bin:/bin:/usr/sbin/nologinsys: x : 3 : 3 :sys:/dev:/usr/sbin/nologinsync: x:4:65534:sync:/bin:/bin/syncgames: x:5:60:games:/usr/games:/usr/sbin/nologinman: x:6:12:man:/var/cache/man:/usr/sbin/nologinlp: x:7:7:lp:/var/spool/lpd:/usr/sbin/nologinmail: x:8:8:mail:/var/mail:/usr/sbin/nologin(... snip ...)Of course, 5 ".." segments may not be enough to reach the filesystem'sroot directory in all cases and so the crafted URI may have to beextended with more ".." and ".x" segments.- Matthew DaleySource: http://dl.packetstormsecurity.net/1503-exploits/goahead341-overflowtraversal.txt Quote