(23 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
{{cve title|CVE-2017-5753 (Spectre, Variant 1)}} | {{cve title|CVE-2017-5753 (Spectre, Variant 1)}} | ||
− | '''CVE-2017-5753''' ('''Spectre''', '''Variant 1''', '''Bounds Check Bypass''') is a [[microprocessor]] vulnerability that allows an attacker to cause | + | [[File:spectre-text.svg|200px|right]] |
+ | '''[[cve id::CVE-2017-5753]]''' ('''Spectre''', '''Variant 1''', '''Bounds Check Bypass''') is a [[microprocessor]] vulnerability that allows an attacker to cause otherwise correctly executing code to expose information to the attacker that wouldn't normally be exposed due to [[bounds checks]] being temporarily bypassed, changing the cache states of the [[microarchitecture]], thereby leaking information through [[side-channel analysis|side-channel timing analysis]]. For this attack to work, only [[speculatively execution]] is needed; the processor can still be [[in-order]]. | ||
+ | |||
+ | This attack can be use on top of {{cve|CVE-2017-5715}} (Spectre, Variant 2) in order to cause a correct program to lead to this (Variant 1) vulnerability by making the microprocessor take the wrong [[branch target]]. | ||
== Overview == | == Overview == | ||
Line 19: | Line 22: | ||
This method can then be used repeatedly to read a larger part of memory. | This method can then be used repeatedly to read a larger part of memory. | ||
− | == | + | == Example == |
− | {{ | + | Consider the following code: |
+ | |||
+ | <source lang=C> | ||
+ | #include <stdlib.h> | ||
+ | |||
+ | /* a data structure */ | ||
+ | struct array { | ||
+ | size_t length; /* length of data */ | ||
+ | unsigned char val[]; /* value of data */ | ||
+ | }; | ||
+ | |||
+ | struct array data = { 10, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} }; | ||
+ | struct array data2 = /* bigger array */; | ||
+ | |||
+ | struct array *secrete_data = /* e.g., AES private key */; | ||
+ | </source> | ||
+ | |||
+ | In the code above there are three buffers. The first two buffers contain a series of values and a third buffer which contains secrete data for its own purpose. Now consider the following function. | ||
+ | |||
+ | <source lang=C> | ||
+ | void victim_function(size_t untrusted_caller_input) { | ||
+ | if (untrusted_caller_input < data.length) { | ||
+ | unsigned char temp = data2.val[data.val[untrusted_caller_input] * 256]; | ||
+ | /* some more code that does something useful with `temp'. */ | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Under normal execution, if the caller calls <code>untrusted_caller_input</code> with a value [[greater than]] <code>data.length</code>, the if statement will result in <code>false</code> and the code inside the {{c|if}} block will not execute. However, consider what happens if an attacker trains the branch predictor that the if statement is going to be true. This can be done by simply calling <code>victim_function()</code> with valid inputs repeatedly. Now consider what happens if the value of <code>data.length</code> is uncached. This can also be done by reading a sufficiently large array of values. | ||
+ | |||
+ | When the [[branch predictor]] is trained and <code>data.length</code> is uncached and a shared array <code>data2.val</code> is also uncached, an attacker can call <code>victim_function</code> with a more malicious input such as the value of <code>secrete_data - data</code>. When this is done, the microprocessor will issue a [[cache miss]] and request to get the value of <code>data.length</code> from memory. Meanwhile the processor will mispredict the path since we trained it to assume the if statement is most likely going to be true. Meanwhile there will be a substantial delay until the value as a result of the [[cache miss]] is brought back from [[DRAM]]. During this time, the processor will [[speculative execution|speculatively]] execute <code>data2.val[data.val[untrusted_caller_input] * 256]</code>. | ||
+ | |||
+ | Since the attacker called <code>untrusted_caller_input</code> with the value of <code>secrete_data - data</code> which is out of bound, the speculative execution logic will then use <code>secrete_data[0]</code> to compute the address of <code>data2.val[secrete_data[0] * 256]</code>. Since <code>data2.val</code> is uncached, the microprocessor will issue another cache miss for that address and go and grab that value from memory. | ||
+ | |||
+ | When this happens, the value of <code>data.length</code> will come back and the processor will realize that it has [[branch misprediction|mispredicted]]. The processor will then rewind the state of the machine back and start executing at the correct path (where the if statement is false). | ||
+ | |||
+ | Although the architectural state of the machine has been restored, some microarchitectural states have changed. In particular, the value of <code>data2.val[secrete_data[0] * 256]</code> which resulted in a [[cache miss]] will come back. Since the rest of the values of <code>data2.val</code> are not cached, the attacker can now use [[side-channel analysis]] to infer the values of <code>secrete_data</code>. | ||
+ | |||
+ | For example, suppose <code>secrete_data</code> was initialized as followed: | ||
+ | |||
+ | <source lang=C> | ||
+ | struct array *secrete_data = { 8, { 'S', 'e', 'c', 'r', 'e', 't', 'e', '\0'}; | ||
+ | </source> | ||
+ | |||
+ | If the attacker is trying to infer the value of the first byte, we will feed <code>untrusted_caller_input</code> the correct offset which will result in <code>data2.val[secrete_data[0] * 256]</code> being executed which in term will cause <code>data2.val['S' * 256]</code> or <code>data2.val[21248]</code> to be loaded into the cache from [[main memory]]. Using a [[side-channel analysis]] timing attack, the attacker will attempt to read from <code>data2.val[X * 256]</code> various values. Since only <code>data2.val[secrete_data[0] * 256]</code> is cached, reading any other value will result in a cache miss which will take some time for the processor to bring the value back from memory. By analysing which index was instant (i.e. did not result in a cache miss), the attacker can infer the value of <code>secrete_data[0]</code>. | ||
+ | |||
+ | The attacker can then continue to do this whole procedure repeatedly to get the value of <code>secrete_data[1]</code>, then <code>secrete_data[2]</code>, etc until the entire content of the secrete data is indirectly leaked to the attacker. | ||
+ | |||
+ | === Breaking sandbox isolation === | ||
+ | Since the attack makes use of the very fundamental behavior of a modern microprocessor, it can be taken advantage in a more restrictive environment such as a [[sandbox]] (e.g., a [[browser]]). A specially constructed piece of [[JavaScript]] can running on a browser that has a [[JIT]] compiler can be used to leak the entire memory of its parent process (i.e. the browser, other tabs, and any other stored shared memory) using this method. This aspect of the vulnerability is particularly dangerous since [[JavaScript]] can be sent to an innocent web client via something as simple as an ad. | ||
+ | |||
+ | ==== JavaScript ==== | ||
+ | The paper describing the attack (see [[#References]]) listed the following JavaScript snippet. | ||
+ | |||
+ | <source lang=JavaScript> | ||
+ | if (index < simpleByteArray.length) { | ||
+ | index = simpleByteArray[index | 0]; | ||
+ | index = (((index * TABLE1_STRIDE)|0) & (TABLE1_BYTES-1))|0; | ||
+ | localJunk ^= probeTable[index|0]|0; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | The cache status of <code>probeTable[byte * TABLE1_STRIDE]</code> can then be used by an attacker to [[side-channel analysis|infer the value]] of a given memory location. | ||
+ | |||
+ | == Affected Processors == | ||
+ | Below is a list of known affected processors, alphabetized. This is '''NOT''' en exhaustive list but rather the once we were able to verify! | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! colspan="3" | List of Processors affected by Spectre, Variant 1 | ||
+ | |- | ||
+ | ! Designer !! Processor/Architecture !! Related Notes | ||
+ | |- | ||
+ | | rowspan="6" | [[Apple]] || {{apple|Swift|l=arch}} ({{apple|A6}}/{{apple|A6X}}) || rowspan="6" | [https://support.apple.com/en-us/HT201222 Post]<br>[https://support.apple.com/en-us/HT208331 Post] | ||
+ | |- | ||
+ | | {{apple|Cyclone|l=arch}} ({{apple|A7}}) | ||
+ | |- | ||
+ | | {{apple|Typhoon|l=arch}} ({{apple|A8}}/{{apple|A8X}}) | ||
+ | |- | ||
+ | | {{apple|Twister|l=arch}} ({{apple|A9}}/{{apple|A9X}}) | ||
+ | |- | ||
+ | | {{apple|Hurricane|l=arch}} ({{apple|A10}}/{{apple|A10X}}) | ||
+ | |- | ||
+ | | {{apple|Monsoon|l=arch}} ({{apple|A11}}/{{apple|A11X}}) | ||
+ | |- | ||
+ | | rowspan="5"| [[AMD]] || {{amd|Bulldozer|l=arch}} || rowspan="5" | [https://www.amd.com/en/corporate/speculative-execution Post] | ||
+ | |- | ||
+ | | {{amd|Piledriver|l=arch}} | ||
+ | |- | ||
+ | | {{amd|Steamroller|l=arch}} | ||
+ | |- | ||
+ | | {{amd|Excavator|l=arch}} | ||
+ | |- | ||
+ | | {{amd|Zen|l=arch}} | ||
+ | |- | ||
+ | | rowspan="10" | [[ARM Holdings|ARM]] || {{armh|Cortex-R7|l=arch}} || rowspan="10" | [https://developer.arm.com/support/security-update Post] | ||
+ | |- | ||
+ | | {{armh|Cortex-R8|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A8|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A9|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A15|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A17|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A57|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A72|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A73|l=arch}} | ||
+ | |- | ||
+ | | {{armh|Cortex-A75|l=arch}} | ||
+ | |- | ||
+ | | rowspan="3" | [[Fujitsu]] || [[SPARC64 X+]] || rowspan="3" | [http://support.ts.fujitsu.com/content/SideChannelAnalysisMethod.asp Post] | ||
+ | |- | ||
+ | | [[SPARC64 XIfx]] | ||
+ | |- | ||
+ | | [[SPARC64 XII]] | ||
+ | |- | ||
+ | | rowspan="10" | [[IBM]] || {{ibm|PowerPC 970}} | ||
+ | |- | ||
+ | | {{ibm|POWER6|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|POWER7|l=arch}} || rowspan="5" | [https://www.ibm.com/blogs/psirt/potential-impact-processors-power-family/ Post]<br>[http://www-01.ibm.com/support/docview.wss?uid=isg3T1026811 Security Bulletin] | ||
+ | |- | ||
+ | | {{ibm|POWER7+|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|POWER8|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|POWER8+|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|POWER9|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|z12|l=arch}} || rowspan="3" | | ||
+ | |- | ||
+ | | {{ibm|z13|l=arch}} | ||
+ | |- | ||
+ | | {{ibm|z14|l=arch}} | ||
+ | |- | ||
+ | | rowspan="13" | [[Intel]] || {{intel|Nehalem|l=arch}} || rowspan="2" | | ||
+ | |- | ||
+ | | {{intel|Westmere|l=arch}} | ||
+ | |- | ||
+ | |{{intel|Sandy Bridge|l=arch}} || rowspan="11" | [https://security-center.intel.com/advisory.aspx?intelid=INTEL-SA-00088&languageid=en-fr Post] | ||
+ | |- | ||
+ | | {{intel|Ivy Bridge|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Haswell|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Broadwell|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Skylake|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Kaby Lake|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Coffee Lake|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Silvermont|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Airmont|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Goldmont|l=arch}} | ||
+ | |- | ||
+ | | {{intel|Goldmont Plus|l=arch}} | ||
+ | |- | ||
+ | | rowspan="2" | {{mipstech|-|MIPS}} || {{mipstech|P5600}} || rowspan="2" | [https://www.mips.com/blog/mips-response-on-speculative-execution-and-side-channel-vulnerabilities/ Post] | ||
+ | |- | ||
+ | | {{mipstech|P6600}} | ||
+ | |- | ||
+ | | [[Motorola]] || {{motorola|PowerPC 74xx}} || rowspan="3" | [https://tenfourfox.blogspot.co.at/2018/01/actual-field-testing-of-spectre-on.html Post] | ||
+ | |} | ||
+ | {{expand list}} | ||
+ | |||
+ | == See also == | ||
+ | * {{cve|CVE-2017-5715}}, Spectre, Variant 2 | ||
+ | * {{cve|CVE-2017-5754}}, Meltdown, Variant 3 | ||
+ | |||
+ | == References == | ||
+ | * Kocher, Paul, et al. "[https://arxiv.org/abs/1801.01203 Spectre Attacks: Exploiting Speculative Execution]." arXiv preprint arXiv:1801.01203 (2018). | ||
+ | * "CVE-2017-5753", https://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-5753 | ||
+ | |||
+ | == Documents == | ||
+ | * [[:File:intel-ref-336983-001.pdf|White Paper: Intel Analysis of Speculative Execution Side Channels]] | ||
[[category:cve]] | [[category:cve]] |
Latest revision as of 09:01, 27 February 2019
CVE-2017-5753 (Spectre, Variant 1, Bounds Check Bypass) is a microprocessor vulnerability that allows an attacker to cause otherwise correctly executing code to expose information to the attacker that wouldn't normally be exposed due to bounds checks being temporarily bypassed, changing the cache states of the microarchitecture, thereby leaking information through side-channel timing analysis. For this attack to work, only speculatively execution is needed; the processor can still be in-order.
This attack can be use on top of CVE-2017-5715 (Spectre, Variant 2) in order to cause a correct program to lead to this (Variant 1) vulnerability by making the microprocessor take the wrong branch target.
Contents
Overview[edit]
Bounds Check Bypass leverages the speculative execution behavior of the microprocessor in order to cause some code to expose more information than intended. This methods requires an attacker to identify a possible confused deputy segment of code that's used to check whether an input is in bounds. For example:
if (input < array_size) { // make sure the input (unsigned int) is not out of boundsd
val = data[array[input]]; // read data
}
In the code above there is an input validation check that ensures that input
doesn't go out of bounds. Once such code segment is identified, the attacker can then train the processor's branch predictor that the bounds check will likely be true. This is done by repeatedly invoking the code with valid input
values.
Once set, the attacker can then invoke the same code with a value for input
that is outside of bounds and with the value of array_size
uncached. When this happens, the processor is going to guess that (just like before) the if statement will be true, speculatively executing val = data[array[input]];
using the malicious input
value. In other words, the processor is being tricked into executing the wrong code path using an invalid input. The processor will then load the value from data
into the cache at the address given by array[input]
which is controlled the attacker.
Since the value of array_size
was not cached, there is a delay from the time the processor starts executing val = data[array[input]];
until the value of array_size
comes back to indicate that the processor speculated incorrectly. When this happens, the processor throws away the wrongly speculated instructions (the vulnerability calls transient instructions), but the change in cache state is not not reverted which can be detected and used to find a byte of the victim's memory.
This method can then be used repeatedly to read a larger part of memory.
Example[edit]
Consider the following code:
#include <stdlib.h>
/* a data structure */
struct array {
size_t length; /* length of data */
unsigned char val[]; /* value of data */
};
struct array data = { 10, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} };
struct array data2 = /* bigger array */;
struct array *secrete_data = /* e.g., AES private key */;
In the code above there are three buffers. The first two buffers contain a series of values and a third buffer which contains secrete data for its own purpose. Now consider the following function.
void victim_function(size_t untrusted_caller_input) {
if (untrusted_caller_input < data.length) {
unsigned char temp = data2.val[data.val[untrusted_caller_input] * 256];
/* some more code that does something useful with `temp'. */
}
}
Under normal execution, if the caller calls untrusted_caller_input
with a value greater than data.length
, the if statement will result in false
and the code inside the if block will not execute. However, consider what happens if an attacker trains the branch predictor that the if statement is going to be true. This can be done by simply calling victim_function()
with valid inputs repeatedly. Now consider what happens if the value of data.length
is uncached. This can also be done by reading a sufficiently large array of values.
When the branch predictor is trained and data.length
is uncached and a shared array data2.val
is also uncached, an attacker can call victim_function
with a more malicious input such as the value of secrete_data - data
. When this is done, the microprocessor will issue a cache miss and request to get the value of data.length
from memory. Meanwhile the processor will mispredict the path since we trained it to assume the if statement is most likely going to be true. Meanwhile there will be a substantial delay until the value as a result of the cache miss is brought back from DRAM. During this time, the processor will speculatively execute data2.val[data.val[untrusted_caller_input] * 256]
.
Since the attacker called untrusted_caller_input
with the value of secrete_data - data
which is out of bound, the speculative execution logic will then use secrete_data[0]
to compute the address of data2.val[secrete_data[0] * 256]
. Since data2.val
is uncached, the microprocessor will issue another cache miss for that address and go and grab that value from memory.
When this happens, the value of data.length
will come back and the processor will realize that it has mispredicted. The processor will then rewind the state of the machine back and start executing at the correct path (where the if statement is false).
Although the architectural state of the machine has been restored, some microarchitectural states have changed. In particular, the value of data2.val[secrete_data[0] * 256]
which resulted in a cache miss will come back. Since the rest of the values of data2.val
are not cached, the attacker can now use side-channel analysis to infer the values of secrete_data
.
For example, suppose secrete_data
was initialized as followed:
struct array *secrete_data = { 8, { 'S', 'e', 'c', 'r', 'e', 't', 'e', '\0'};
If the attacker is trying to infer the value of the first byte, we will feed untrusted_caller_input
the correct offset which will result in data2.val[secrete_data[0] * 256]
being executed which in term will cause data2.val['S' * 256]
or data2.val[21248]
to be loaded into the cache from main memory. Using a side-channel analysis timing attack, the attacker will attempt to read from data2.val[X * 256]
various values. Since only data2.val[secrete_data[0] * 256]
is cached, reading any other value will result in a cache miss which will take some time for the processor to bring the value back from memory. By analysing which index was instant (i.e. did not result in a cache miss), the attacker can infer the value of secrete_data[0]
.
The attacker can then continue to do this whole procedure repeatedly to get the value of secrete_data[1]
, then secrete_data[2]
, etc until the entire content of the secrete data is indirectly leaked to the attacker.
Breaking sandbox isolation[edit]
Since the attack makes use of the very fundamental behavior of a modern microprocessor, it can be taken advantage in a more restrictive environment such as a sandbox (e.g., a browser). A specially constructed piece of JavaScript can running on a browser that has a JIT compiler can be used to leak the entire memory of its parent process (i.e. the browser, other tabs, and any other stored shared memory) using this method. This aspect of the vulnerability is particularly dangerous since JavaScript can be sent to an innocent web client via something as simple as an ad.
JavaScript[edit]
The paper describing the attack (see #References) listed the following JavaScript snippet.
if (index < simpleByteArray.length) {
index = simpleByteArray[index | 0];
index = (((index * TABLE1_STRIDE)|0) & (TABLE1_BYTES-1))|0;
localJunk ^= probeTable[index|0]|0;
}
The cache status of probeTable[byte * TABLE1_STRIDE]
can then be used by an attacker to infer the value of a given memory location.
Affected Processors[edit]
Below is a list of known affected processors, alphabetized. This is NOT en exhaustive list but rather the once we were able to verify!
List of Processors affected by Spectre, Variant 1 | ||
---|---|---|
Designer | Processor/Architecture | Related Notes |
Apple | Swift (A6/A6X) | Post Post |
Cyclone (A7) | ||
Typhoon (A8/A8X) | ||
Twister (A9/A9X) | ||
Hurricane (A10/A10X) | ||
Monsoon (A11/A11X) | ||
AMD | Bulldozer | Post |
Piledriver | ||
Steamroller | ||
Excavator | ||
Zen | ||
ARM | Cortex-R7 | Post |
Cortex-R8 | ||
Cortex-A8 | ||
Cortex-A9 | ||
Cortex-A15 | ||
Cortex-A17 | ||
Cortex-A57 | ||
Cortex-A72 | ||
Cortex-A73 | ||
Cortex-A75 | ||
Fujitsu | SPARC64 X+ | Post |
SPARC64 XIfx | ||
SPARC64 XII | ||
IBM | PowerPC 970 | |
POWER6 | ||
POWER7 | Post Security Bulletin | |
POWER7+ | ||
POWER8 | ||
POWER8+ | ||
POWER9 | ||
z12 | ||
z13 | ||
z14 | ||
Intel | Nehalem | |
Westmere | ||
Sandy Bridge | Post | |
Ivy Bridge | ||
Haswell | ||
Broadwell | ||
Skylake | ||
Kaby Lake | ||
Coffee Lake | ||
Silvermont | ||
Airmont | ||
Goldmont | ||
Goldmont Plus | ||
MIPS | P5600 | Post |
P6600 | ||
Motorola | PowerPC 74xx | Post |
This list is incomplete; you can help by expanding it.
See also[edit]
- CVE-2017-5715, Spectre, Variant 2
- CVE-2017-5754, Meltdown, Variant 3
References[edit]
- Kocher, Paul, et al. "Spectre Attacks: Exploiting Speculative Execution." arXiv preprint arXiv:1801.01203 (2018).
- "CVE-2017-5753", https://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2017-5753