ARMageddon: How Your Smartphone CPU Breaks Software-Level Security and Privacy

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Can someone do a tl;dw? I prefer reading than watching videos.

👍︎︎ 11 👤︎︎ u/didnt_check_source 📅︎︎ Dec 02 2016 🗫︎ replies

If you want to see just what they can do with this hack go to 22m47s. Basically they say it can beat sandboxing using collusion.

While this is technically original research it is far from new research. This kind of thing is talked about a lot lately in some papers. This continues to be the case because the industry by and large doesn't seem as concerned with this kind of sidechannel attack as they are with the kind of performance loss it would take to prevent it.

👍︎︎ 1 👤︎︎ u/happyscrappy 📅︎︎ Dec 03 2016 🗫︎ replies

does anybody have a translation into english?

👍︎︎ 1 👤︎︎ u/[deleted] 📅︎︎ Dec 04 2016 🗫︎ replies
Captions
hello and welcome to our talk Armageddon how your smartphone CPU breaks off the level security and privacy in the beginning I want to say that if you have a so safe software infrastructure meaning that your code has no back this does not mean that the execution will be safe because the hardware where the software runs on may leak information and today we want to focus on the CPU cache because maybe you've seen in the past that cache attacks can be used for covered communication and to attack rip two implementations however they have only been demonstrated on intellect 86 or now and as mobile devices like smartphones and tablets have become the most important personal computing device in the past and despite the fact that people thought that those that X cannot be applied on mobile devices we wanted to investigate why they don't work on arm and if we can do something about that but before that I want to introduce myself my name is Moe it's lip I'm from cut University of Technology and I'm a master student and I've just finished my master thesis if you want you can reach me on Twitter writing an email or just come and talk to me later on and my name is Clemens in movies I finished my PhD last year and I'm now post doc at class University of Technology you can also reach me on Twitter or by email or even later the rest of the research team is composed of Daniel who's half age type sir and stefan monk out and we are all from class University of Technology and before we delve into the details we are going to show you a quick demo so on left you should see the screen of this galaxy s6 and we have just started up the messenger app and on the right side we have a terminal and we will start our spy tool and now Clementine will type something into the smartphone and as you can see on the right whenever she presses and character our spy tool catches that and also if she presses the spacebar we see that on the right and at some point if she may decide that she wants to delete the word or a character we can also spy on that so because of that we can derive the words at length of the word she types and spy on the user and we will now show you how this is done and as we said this is no software pack it's just because of the hardware so for the rest of our talk we first give you some background information about CPU caches and about the CPU architecture on intellect 86 as well as on arm then we will discuss the challenges why those attacks are so hard to can be mounted alarm and then we will show you how you can solve those challenges then we will demonstrate you some attack scenarios what you can do with those attacks besides the demo and in the end we will give you all the tools that we've developed to mount such attacks and Clementine is going to tell you now about cache attacks in general yeah so as you may know we have a memory hierarchy that is composed of CPU registers different levels of sip of the CPU cache the main memory which is the DRAM and the disks or storage and we have different timings depending on where data reside on and this is precisely what we are going to exploit in cache attacks and in particular the difference of timing between data in the cache and in the main memory so if data resides in the cache and the access is going to be fast we call that a cache hit and if data is not in the cache then we have to retrieve it from the main memory and it's slow and we call that a cache miss now you can clearly see that we have we can see the difference between cache hits here and cache misses here and this is basically what we always talk to do when we not a cache attack we time the cache hit and the cache misses and then we can have some threshold because we know that we will never have any cache miss below as 700 CPU cycles and then just by measuring the time an access take we can say it's a cache hit or it's a cache miss so we also need for this cache attacks to know precisely how the CPU cache work for um and Inter CPU we have set associative caches so we have data that is loaded in a specific set and that depends on some bits of the address so this is a cache set here then we have several ways per set so this is one way here we have four ways and a cache line is loading in a specific way depending on the replacement policy of the CPU because usually when at the CPU loads a cache line into the cache it has to evict a previous line to make someone fog to make some room for the new cache line so the cache line is here for example and this is the smallest unit we are going to talk about so now on to cache attacks we have different types of Cascia text and the first one is called flush and we load and here what's really important is that we have some shared memory between the attacker and the victim so here that can be for example a shared library so the etiquette starts by mapping this shared library and data that is shared in the memory is going to be shared in the cache so if data if the cache Ram is in the cache it's in the cache for both the attacker and the victim so what the attacker now is going to do is the attacker is going to flush this shared cache line and so that it's neither in the cache for the attacker and the victim and then he's going to let the victim perform for example encryption that data come out to spy on and then the CPU will automatically reload some data now the attacker wants to know if this particular cache line was accessed by the victim so the attacker then we lost the data and measure the time it takes and by measuring the time it takes you can know if it's in the CPU cache which means that the victim did access this particular cache line and if it was not in the cache then the victim did not access so this attack is actually quite powerful because the attacker can know if the victim accessed one particular cache line which is usually around 64 bytes so this was the case in which we had some kind of shared memory if we don't have any kind of shall memory we can use Fram in probe so here the attacker is going to start by filling the cache and then again let's the victim perform for example encryption and again the victim is going and the CPU is going to automatically load data and this is going to evict some of the line of the attacker so here and the victim evicted 1/9 of the attacker then another line and now the attacker wants to know if the victim access this particular cache set and the attacker won't know which particular cache line but you can know if add the attacker exceed the victim access this kashyap so is going to prototype previously put in the cache so here on the first time that's a fast access because they died still in the cache and on the second line is a slow axis because the victim has a evicted one cache line of the article so the attacker now knows that ok the victim access some cache line that reside in this precise cache set and now what it is going to tell you about the differences between Intel x86 and arm so the first thing we need we need to be capable and find a way to remove an address out of the cache to the main memory and for this we need to invalidate the cache line and we have to cache maintenance instructions available which do that for us and on intellect 86 you may know the unprivileged CL flash instruction which you can use to perform this task on the other hand on army 7 while we have such cache maintenance instructions we can't use them because they are privileged and they need a kernel module to be unlocked for users based and this is not the case normally in addition on arm v8 we have also privileged instructions but four of them can be unlocked to user space entirely and the first big challenge there is that we have no flash instruction available on arm that we can use without any privileges so the easiest thing which comes to mind is we just fill the whole cache with different data then we are going to evict our target address and we are fine however this is way too slow so we can't rely on that we can improve on that by just filling a specific hash set and as you can see in this illustration the green block is the address you want to evict and we just start accessing as many addresses which map to the same cache set as we have ways so at one point we're going to evicted our gate address sounds easy it is easy but it's only the ideal case if we have a least recently used replacement policy on the cache active but in real life we don't have that because we have absurd a random replacement policy and when we do the same thing again just add accessing addresses it may happen that we just evict the address we have just loaded into the cache so in the end our target address will still remain in the cache and so this approach is also highly inefficient so the second challenge is that we have to take care of the pseudo-random replacement policy to find a nice way to evict the address still out of the cache in a fast way the second thing that we need to mount such cache attacks is we need to be able to measure the time it needs to take to load the address and on intellect 86 we have the RDS see instruction which is also unprivileged while we can access the cycle count on arm as well we can only do that in privileged mode so this is not easily done in a non rooted device and all the previous attacks that have been published require root access on the device so they are not very practical so the third challenge that we have is that we need to find unprivileged timing sources which are as accurate as we can as we need to distinguish between a cache it and the cache miss so beside cache maintenance and timing we also have the cache hierarchy that is quite different on our main on Intel CPUs so this is for Intel CPUs at the modern Intel CPUs so we have usually three levels of caches 11 1 and level 2 I private to each car and we have the last level cache that is level 3 and this one and it's also inclusive which means that everything that is on level 1 and level 2 will also be in this last level cache and in that also means that a shell memory that is shared in Shannon with is shared in the cache across all cost so we have no restriction on where and the Spy is with respect to the victim so the victim might pin cos 0 and the attacker can be on post ring it will not be a problem thanks to this inclusive property now on ARM Cortex a cpu we have two levels of caches now level 1 is still private and level 2 is shared but this time it is not inclusive and that means that shared memory that is not in level 2 is not shared in the cache and that people thought that it was basically the biggest obstacle to performing cross core cache attacks on arm so that's our first channel we have non included caches then we have the cache hierarchy on arm pick little and here we have not only one CPUs but two multiple CPUs and the input interconnected by this calling and we have the level 2 cache that is shared across core but only of one CPUs so we have CPUs that do not share a cache and once again we would like to have no restriction on where the attacker is with respect to the victim and this is our first challenge we have no shared cache between these CPUs so let's solve these challenges we have those five challenges so now flush instruction obscure random replacement policy no unprivileged timing non-inclusive caches and no shell cash so I'll start with the first one no flash instruction so the idea is quite simple we are going to replace this missing flash instruction with cash eviction that actually works well on a intel x86 because we have brahmin probe that i showed you at the beginning this one does not need any flash instruction and if we have some kind of shell memory but no flash instruction we can perform a variant of flash envelope that is called Ava can reload so challenge one done or is it well actually no it's not done yet because addiction can be slow and unreliable unless you know how to properly Big Data and this is kind of our thing in the group we're really good at editing data from the cache and that was in fact the central idea of all row homogeneous paper so if you recall rahama you access the theorem in some kind of patterns and then you're going to have a bit flip in one location that you never accessed so you need to reach the theorem and bypass the cache to do that and we also did that on JavaScript where you do not have any flash instructions so we're going to use the same kind of techniques that we did for this one which leads me to my to the second challenge this pseudo random replacement policy because evicting the cache would be really easy if it was not for this one because the cache on to be discarded is chosen to the randomly so what we did on kind of all Intel CPUs was accessing only ones and addresses in an N way cache sets such as mobis showed you earlier but because of the pillar random replacement policy this is really slow and unreliable so what we are going to do is we are going to access addresses that fills in to the same cache set not only once but several times and with different access patterns and the replacement policy is supposed to be pseudo-random so we should not be able to really influence anything but as we're going to see it actually works really well so this is the kind of numbers that we can have with the Alcatel OneTouch pop too so if we access 48 addresses that fit into the same cache set we access them one by one it takes six thousand cycles which is quite fast but the eviction rate is really bad the eviction rate is basically how the sorry if the line that we wanted to evict has actually been evicted so here it's actually been evicted in 70% of the cases which is really bad we want something better than that so if we access more addresses then the eviction rate goes up we have 96 percent eviction rates and it now is quite slow we have 33,000 cycles so we can access even more addresses let's say 800 ad eviction rate is no good 99 point 10 percents but it's really really way too slow 1 142,000 cycles so we are going to access these addresses multiple times and now is only 21 addresses that we access 96 times we have an eviction rate of ninety nine point ninety three percent and it only takes four thousand cycles we can even do a little bit better with 22 addresses even a bit more accesses with an eviction rate of 99.99 percent I takes five thousand cycles and with twenty three addresses then we have an addiction rate that is perfect we measure on 100% and it took six thousand cycles so this is really good and even the timing is better than the first one we had a very bad eviction rate so now I'm pretty sure that it sounded all voodoo we have different addresses we access them multiple times this is the kind of thing that we started to do manually with roham areas but it's really a pain to do so we fully automated this process the idea is that we are going to generate a lot of eviction strategies executive on the target device and then we have a lot of log files and we are going to build a database to be able to compare this eviction strategies so as the eviction strategies usually depend on the device because the cash characteristic are going to change from one device to the other we can find with these methods a fast an addiction efficient eviction strategies for any device and this is the kind of thing that we can now obtain with this eviction strategy so if you have some kind of shell memory but no flush instruction you can use evict and reload with these eviction strategies and as you can see we can really distinguish between the victim access and no victim access so we can have a threshold then this is with the kind of traffic you want to have now for priming probe if you do not have any kind of shell memory you have a bit more noise but you can still see this peak and if you put a threshold just after this peak you can say that roughly everything that is below this is no victim access everything that is after this is victim access so it looks a bit more noisy but as you will see later we can still mount a very powerful cache attack with that so the next challenge that we need to solve is that we don't have an unprivileged timing sauce and as I told you that the Foreman's counter can only be accessed in privileged mode so we need to find something different that we can use to distinguish between a cache hit in the cache miss and the first thing that we found is you can use the graphic interface with the probe event open Cisco which is unprivileged and we can also access the cycle counter through that however on new devices it's not available anymore on the one hand we just can't access the cipher counter or the kernel isn't built with this module so it doesn't work however we can also use the basic function clock at time we can pass the timer as an argument and we will have a nanosecond resolution if this wouldn't be available we can have a very simple approach by just implementing a thread counter which is a thread running on a different core just incrementing a global variable and this is enough for distinguishing cache hits and cache misses so we have now free unprivileged timing sources that we can use and they all give us a nanosecond resolution and this is enough to distinguish hits from misses as we can see on this plot so on the Left we have all the timing sources measuring a cache hit and then we have a huge gap and on the right we have the same only for the cache misses and it's easy to find the threshold in between we can use for our measurements in the end so the next thing that we have to take care of and this is probably one of the hardest challenges is that we have non-inclusive caches because for in this scenario the victim process is running on core zero and an address is loaded in its first level instruction cache and our attacking process is running on core one and this cache is for instance instruction inclusive and data non-inclusive and this means that the data from the instruction cache here has also be to be in the last level cache and what we can do now to evict the data from the first level cache of the other core which is private to the other cores by just filling our own local data cache and starting populating the last level cache and at some point we are going to evict the address out of the last level cache and because it's inclusive it will also get evicted from the first level cache and now we were able to evict an address from another core even if we don't have inclusive caches entirely so the second thing we need to do we need to find a way to measure if the other core has reloaded the address and the solution is that there are cache coherency protocols in place on the CPU which run all the time and they allow us to fetch data from a remote core and the reason behind this is that loading the data from another core is still faster if we need to have a memory access and as you can see on the plot while it takes roughly 100 hundred cycles more to load the address from another core it's still much faster than if it would have to make a memory access to the main memory and we can exploit this to measure if the address has been loaded by the other core so we can run cross-court decks as well the last thing we need to fix is that we have no shared cache on modern phones with which have multiple CPUs so that there is no shared cache between them and the answer is again cache coherency protocols because they allow us to fetch data not only from the remote core but from the remote CPU and this is also faster than if we would have to access the T RAM and why we see here that it's much slower than a local access it's still roughly 100 cycles faster than if we have to make an access to the DRAM and regarding that for performance things we need to we can exploit the cache coherency protocols which are running all the time and now we have found a solution for all those challenges and we want to present you some attack scenarios beside the demo that we've showed you in the beginning so the first thing you can do is you can build covered communication so for instance a user installs a malicious gallery application and he is not suspicious when he installs this app because the only permission it needs is to access the images so it doesn't need an internet access or read the contact data of the user in addition the user wants to see what the weather is outside and installs the malicious weather widget and he's also not suspicious because the only permission it requires is to have access to the Internet so what if both apps can build a cover channel to communicate with each other so that the gallery app can send the image to the weather app which uploads it to the Internet and so if two applications want to communicate with each other but they are not allowed to do so because they either don't have the permissions or there's just no mechanism they can use like shared memory they can use a cabbage channel and a cabbage channel enables to unprivileged applications to communicate with each other but it does not use any data transfer mechanism that is provided by the operating system and this evades the sandboxing concept and also the permission system and because you have two apps which need to work together this is called a collusion attack and what we did was we built a cover channel using the cache so we used addresses from shared memory from a shared library on executable and we transmitted bits through cash hits and cache misses so as an example if you want to transmit to zero you just to send the doesn't access the address and so the receiver would measure a cache miss if you want to transmit one the sender would access the address and the receiver will measure a cache hit and using those building blocks we can build the protocol based on packets so we use the sequence number and also a checksum to protect our payload so that we can check the integrity of the data and can recent data if we require to on the other hand the receiver responds with the sequence number he has already received and our cover channel works using flash and reload techniques evict and reload and also flash and flash which we haven't discussed but basically the flash instruction also leaks timing information depending of the if the address is cached or not so we can use it instead of the reload step so our cover channel works across core and also across CPU and is way faster than any of the state-of-the-art cover channel which has been published so for instance we had a cover terminal using the volume settings and it only got 150 bits per second or one using the type of intense with 4k bits per second so our cover channel in the best case using flash and reload had one megabit per second and as you can see when we use a victim reload it still it's much slower of course because there's much noise but it's even fat is still faster than any previously discovered cover channel and now Clementine will tell us how we did the demo and how you can use our attacks to spy on the user so on our second case study and for the rest and the attack scenario is a bit different than for the cover channel here we do not have two malicious application but just one malicious application that is going to spy on the other benign applications so problem is always which address do we want to spy on so locating these event dependent memory accesses so to do that we had cache Don Pettit acts on Intel CPUs and basically reported that one so the first step is we are going to map a shared library or executable then we are going to repeatedly trigger an events and in parallel of that we are going to use flash and we load on one address if we have a lot of cache hits then it means that the address the specific address is used by the library or the executive on and then we are going to repeat this second step for every address of the library if then you want to spy on more events then you just repeat step two and three so we obtain a catch template matrix which is basically how many cache hits we have for each pair event and address so if you have a dark cell it means we had many cache hits if you have a light cell no cash no cache its we can do that on shell memory and also add binaries so this example is on the android keyboard and what's quite interesting in here is to see that we have different numbers of cache sheets for different addresses depending on which type of input we had so here the user pressed an alphabet key the enter key the space key and the backspace key and if you see for example if we spy on this address in particular the only event that causes cache hits is the alphabet so if we spy on this one and we had some cash sheets it means that the user press an alphabet key now we can also combine different addresses and for example if we combine these two address since then we can differentiate between the backspace and the alphabet because both the backspace and the alphabets have cache hits on this address but only the alphabet Healthcare shapes on this one so basically if we have cache hits on this address but no cache sheets on this one it means that is the backspace and we can differentiate between alphabet and backspace like that and this is basically what we have seen in the demo so that was now to see from which address we are going to choose to monitor and now monitoring them using a victim reload and so on two different addresses on the Alcatel OneTouch pop two on this text file you can see that we can really differentiate keys from spaces so we have first we have very precise timing here and we have exactly one cache hit per of space that the users pressed so we can really see the in Turkey stroke timing here and we can modern that distinguish between the keys and the space because there is one address that reacts only two space has been pressed so we have a different fit between a key and spaces and really precise timing so this was an example on the keyboard but that is really endless possibilities just can alter libraries and as long as you have some secret dependent accesses then you will be able to automate these attacks and the really neat thing is that you do not need any source code for that just use the occasional method X so you can spy and learn about the users behavior other kind of attacks that we can foresee is like if the user turned on the GPS out the camera for example now we have our third case study and we're going to add a cryptographic algorithms using cache attacks so our use case is the AES t-tables and this one is a fast software implementation because it uses sub pre computed look-up tables now the bad thing is that since 2006 it is known to be vulnerable to cyber attacks so the attack has been done by all wicked an and it's a one-round known plaintext attack so you have Peter plaintext MK the secret key and at each round are you're going to compute an intermediate state and on the first round this intermediate state is AD the access table indices and it's just basically plaintext so secret key now this is a known plaintext attack so if we are able to recover this access table indices it means that we have recovered the key because it's just so so this has been shown in 2003 that's ten years ago so nobody uses that anymore right no that's actually the default implementation of bouncy castle it uses these two tables and we've seen that we can monitor a lot of things with cache attack so let's monitor these tea tables entries now the difference between this attack and the previous one is that for the previous one we had some kind of shell memory here the Java Virtual Machine creates a copy of the tea table when the app starts so it means that we do not have any kind of shell memory so we cannot use a victim reload of flushing reload but at the beginning I've shown you we have priming probe if you don't have any kind of shell memory so we just use that and here's the time the kind of figure that you can have if the attack is successful so here you have to plant expert values and here the at the addresses and the addresses are basically which tea table entry was accessed and this has been done using a key that is all zeros and as long as you have this really nice diagonal it means that the attack succeeded if the key is not zeros then you will just have a different access pattern here but you can in each case recover which T turbulently was accessed and therefore recover the key and now our last case study and in addition we want to show you that you can also use those attacks to monitor all the UM trust zone and the UM trust zone is a hardware based security technology which provides you with a secure execution environment and roots of trust and there are small applications running on it called trust let's and the run in the secure world in parallel to your untrusted operating system and they implement things like a credential store or a secure element for payments or maybe digital rights management and one wants to assume that information which is stored and processed in the trusted world should not be leaked to the non secure world however we find on one smartphone there is a trust let which you can use to create our essay signatures for arbitrary data and we measured some time difference depending on the key that is used and we had no knowledge about the implementation of the trusted operating system and also not about the trust let so we didn't have any source code and it's not necessarily required to do something without having a source code so what we can do is we can distinguish between valid and invalid keys that are processed in the trust zone and on this plot you see on the x-axis the cash set that is used and on the y-axis the mean squared error from two measurements in comparison with the mean over all valid keys and the black line is an invalid key and you see there is a huge difference on almost every care set if you use an invalid key in on the other hand if user valid key and over here you can see you can also distinguish between which key has been used on some cash sets and maybe you can build more sophisticated tags on that because the drafts on in the end leaks to the untrusted world and so you can monitor what's happening in the trust zone so last but not least we want to show you the tools that we have developed to mount such cash attacks and the first thing we did we build the library to build cross-platform cache attacks you can write your attic code once and can execute it on x86 mv7 and on v8 and it implements various attack techniques where some of them we've showed you previously and it's open source and you can down it download it online and all the describe examples that we have showed you today have been developed on top on this life of this library and we also include eviction strategies for the devices that we have used so you can go and try to code yourself if you have the same device and we provide you with a very sophisticated example code and good documentation how you can install and use the library and in addition this library also allows you to implement cross-platform roham attacks and this is the github all that we have and you can just go there download the code in addition if you have a device that we didn't have and you still want to mount those attacks you can use our eviction strategy evaluator and this is basically the tool which automatically tries several eviction strategies on your phone and provide you with a database that you can use to analyze and find an eviction strategy you want to use and it's also in our repository and last but not least we have also an implementation of the cache template the text so that you can also use them to find libraries and executables for vulnerable addresses so you can spy on the user like we did in the demo and we even give you a small tool which you can use to simulate input events so that you don't have to be on the phone and type everything manually and you find this also in our repository and now Clementine wants to talk about some countermeasures what we can do to prevent maybe some of these attacks so the first thing to know is that we had cache attacks on Intel CPUs for a good decade now and we still haven't found a good solution to prevent them because it uses this really crucial element for perform that is the CPU cache and we cannot just get rid of the cache doesn't make any sense so a first countermeasure that had been proposed for Intel CPUs was to have more coarse-grained timers because we really need to make it to find the difference between caches and cache misses and that is in the order of nanoseconds so the idea is if we have a more coarse grained timers that is on the resolution of let's say milliseconds we wouldn't be able to differentiate between these cache hits and these cache misses the thing is that as we've just shown you a thread timer is sufficient to recover an accuracy in the order of nanoseconds so as long as we had some really true parallelism for this file we will be able to have fine-grained timers so this is really not a good countermeasure and the second one is no shell memory so if we do not have any shell memory we cannot use a flush in Medora we can reload and this out easiest attacks we can do and also the most fine-grained so we can be very precise with this attacks the thing is that it that left us with priming probe and we can still come really nice attacks like we've shown you earlier so that was that was still raised the powerful attacker so that would still be a good thing to implement especially if we also have the system information that is restricted so for example no access to page map so in this case we wouldn't have any access to physical addresses and that makes the hotter the attacks much harder again for prevent probe how does again still possible it just raises the power for the attackers now I think a crucial thing is that on modern smart phones we have cryptographic instruction extensions so using like AES suitable implementation it should not be the case so yet this instruction extension are still not the default everywhere this is really bad also if you have a device that does not have these extensions we also know how to have crypto algorithm and software only that do not leak any information so that should be the case now it's not a system-wide a solution so it still wouldn't protect from a spy music behavior on the keyboard like we showed you in the demo so I think the message here is we know how to protect crypto so we should do that for the rest there is really no satisfying solution we didn't found solutions for Intel CPUs I'm not realistic that we find solution for arm CPU anytime soon now for the conclusion so what we've seen today is that all of the powerful cache attacks are also applicable to mobile devices and they do not require any permission or any privilege and you can use them to build fast copper channels to evade the sandboxing and privileged permission system and you can use them to spy on the user's activity with a very high accuracy as we have seen in the demo as well and you can also use it to derive cryptographic cheese and to monitor the um trust zone which should not be possible so we want to thank you for your attention and welcome you to try our tools yourself they are all online and if you have any questions feel free to ask them or talk to us later thank you
Info
Channel: Black Hat
Views: 32,428
Rating: 4.8726792 out of 5
Keywords: BlackHat, InfoSec, Black Hat, Information Security
Id: 9KsnFWejpQg
Channel Id: undefined
Length: 40min 16sec (2416 seconds)
Published: Thu Dec 01 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.