Advanced Linux Security Part 1: AppArmor Intro

In light of recent “OS X needs Antivirus” controversy, I’ve been trying to get across the point that there’s more effective measures than AV software for reducing attack vectors to your system. After a bit of arguing back and forth on various discussion boards about this, it seemed apparent that a lot of people who care about security aren’t aware of such technologies, so I am going to write a 4-part tutorial series on various ways you can secure Linux against attacks.

This first part is an introduction to the big-picture idea of what we are trying to do. I’ll explain in simple terms the problem with traditional privilege models used in most OS’es today, along with a solution. I will then close off with a simple real-world example to show how simple this can be in practice.

The Problem

Exactly what threat are we up against? Well, allow me to describe a recent real-world example that has happened too many times in the past:

VLC media player contains an unchecked buffer which allowed an attacker to craft a malicious video file. An attacker sends the victim a video file. The user opens it up and the video file exploits this hole in VLC to read the user’s SSH private key and send it to the attacker. With this in hand, the attacker is able to log into many of the victim’s SSH machines.

What is wrong with this picture?

The astute reader should be asking in outrage: What the HELL is VLC media player doing reading private SSH keys? How could the operating system allow this to happen? Well, let’s use UNIX 101 to see why. This is what VLC looks like running on my system:

jdong     7603  3.0  0.5 378660 23040 pts/1    Sl+  09:24   0:00 vlc

The first column says that I, jdong, own the VLC process. What do the permissions on my SSH key say?

-rw——- 1 jdong jdong 1264 2007-10-01 12:50 /home/jdong/.ssh/id_dsa

Again, I own my SSH key. Note the tight  -rw——- permissions that are required and default on a SSH key: only the owner is allowed to have any access to it. Let’s read that again:  only the owner is allowed to have any access to it. But, VLC’s process is owned by me, jdong, the same person as the SSH key. The OS puts 2 and 2 together and says that VLC has access to my SSH key.

What this example shows is that the UNIX permissions scheme is not fine-grained enough to allow me to specify the minimum amount of access VLC has to my system. Ideally, I’d like to tell VLC it can read my Videos directory, but NOT my Documents/Tax Forms directory

The Solution: AntiVirus edition

What is the AV solution to this problem? Write an AV signature to recognize the malicious video file that caused this exploit. While that sounds good in theory, it should be clear to see why this idea is ridiculous. Do we stop terrorists by compiling a big list of pictures of known terrorists, distribute the list to the TSA and if a person at the security checkpoint doesn’t look like anyone in the list, he’s not a terrorist? Ok fine, we do, but how well is THAT working out?

While this approach can be useful for blacklisting individual types of exploits, what if tomorrow a different hole is found in VLC? You’d have no protection against that exploit until your AV vendor figures out.

The Solution: AppArmor Edition:

The solution I’d like to propose is to be able to define finer grained permissions that I hinted to earlier. I want to tell Linux: VLC Media Player can access ~/Videos and its own data. It can write to its preferences directory. It CANNOT touch anything else, launch any other processes, su to root, etc. A rule like this would universally stop a wide class of exploits against VLC.

Quick Intro to AppArmor:

I’ve used the name of the product but I haven’t really said what it is. AppArmor is known as a Mandatory Access Control mechanism for Linux. What that means is basically it allows you to specify finer grained permissions on stuff running on your system than the traditional ownership-permissions model. It is Open Source software, and installed by default on Ubuntu and OpenSUSE. It’s known for being relatively simple to configure for restricting a select set of at-risk services and applications.

AppArmor works inside the kernel based on configuration files known as AppArmor Profiles stored in /etc/apparmor.d. The tricky part of configuring AppArmor is writing a profile that gives the right level of access that allows your program to do its work, but not pose a security threat to the rest of your system.

What about SELinux?

RedHat/Fedora users are probably thinking at this point: Wait a minute, isn’t that what SELinux is? Yes, you’re right! SELinux is another framework that’s in the vanilla kernel and default in RedHat Enterprise Linux and Fedora which accomplishes similar goals to AppArmor. It’s much more powerful and allows even more fine-grained permissions control, and even offers other neat features like Role-Based Access Control which allows you to have a different set of permissions depending on what you’re doing at the moment. I plan to cover SELinux in detail at a later time, too.

Our Example: myscript

Ok, now that we know what AppArmor is, and why we’d want to use it, I figure it’s better to explain the rest in practice instead of all this hypothetical talk. Let’s try a simple example to illustrate how AppArmor can work to save our rear. Suppose I have a simple Python script called myscript

#!/usr/bin/python
import os
print "Welcome to MyScript!"
foo=raw_input("Please give me the name of a file to md5sum:")
os.system("md5sum "+foo)

Ok, so it’s supposed to do something useful (calculate md5sums) but is poorly written. Perfect recipe for disaster! Let’s see what it does when it works:


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:12)
Welcome to MyScript!
Please give me the name of a file to md5sum:myscript
9149480416db480c99f4f95b79fda227 myscript

Cool! It works! Or does it work TOO well:


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:16)
Welcome to MyScript!
Please give me the name of a file to md5sum: /dev/null;bash
d41d8cd98f00b204e9800998ecf8427e /dev/null
jdong@droptop:~/code/myscript$ whoami
jdong

Ahh, when foo; bar gets passed to os.system, bar is executed as if it were a separate command. Yikes, we spawned an arbitrary shell!

The Solution: AV Style

Luckily for me, I am the author of award-winning jdong Antivirus ™, so I incorporate my advanced AV engine in myscript:


#!/usr/bin/python
import os

print “Welcome to MyScript!”
foo=raw_input(”Please give me the name of a file to md5sum:”)
if “;” in foo:
print “BZZ! JDONG ANTIVIRUS FOUND MyScript.ShellEscapeExploit!”
else:
os.system(”md5sum “+foo)
Does that work? Let’s see:


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:22)
Welcome to MyScript!
Please give me the name of a file to md5sum:/dev/null; bash
BZZ! JDONG ANTIVIRUS FOUND MyScript.ShellEscapeExploit!


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:27)
Welcome to MyScript!
Please give me the name of a file to md5sum:/dev/null && bash
d41d8cd98f00b204e9800998ecf8427e /dev/null
fjdong@droptop:~/code/myscript$ id
uid=1000(jdong) gid=1000(jdong) groups=4(adm),20(dialout),24(cdrom),44(video),46(plugdev),108(lpadmin),116(pulse-access),117(pulse-rt),123(admin),124(sambashare),127(libvirtd),1000(jdong)

Well, the typical problem with AV signature based approaches — I slightly changed my attack and the signature no longer matches. Note that myscript is doing the same thing wrong, executing bash when it shouldn’t have the ability to, but a different way. Again, an AV doesn’t care what is being done, just the exact way how it is doing it. Even worse,it gets REAL hair when I start talking about files with ; or & in the filename — at some point the AV will have to trade off between false-positives and completeness of detection.

The solution, AppArmor style

Since this is a motivational introduction to Apparmor, I’ll just present the solution without gory details on how I did it. I’ll link to some resources for those who want to know NOW but next week’s installment will go through step-by-step a more useful example.

So, instead of blocking bad input, I will use an AppArmor profile to rigorously define what myscript should do. We make the following observations:

  1. MyScript uses Python. So, it probably needs access to the Python interpreter and the Python libraries.
  2. MyScript executes md5sum. So, it probably needs access to /usr/bin/md5sum and perhaps /bin/sh because of the way os.system works (not intuitively obvious). It shouldn’t have to execute anything else.
  3. MyScript NEVER has to write anything but to the screen.
  4. Suppose MyScript is for checksumming my media collection and itself.  Hence, it should only be able to read ~/Videos and ~/code/myscript.

With these rules, we can concoct an AppArmor profile, like the one I prepared. This is /etc/apparmor.d/home.jdong.code.myscript.myscript after about 10 minutes of work:


# Last Modified: Fri Dec  5 10:41:50 2008
#include <tunables/global>


/home/jdong/code/myscript/myscript {
#include <abstractions/base>
/usr/bin/python2.5 ixmr,
/home/jdong/code/myscript/* mr,
/home/jdong/Videos/*/ mr,
/home/jdong/Videos/** mr,
/usr/share/pyshared/** mr,
/usr/local/lib/python2.5/** mr,
/usr/local/lib/python2.5/*/ mr,
/usr/lib/python2.5/** mr,
/usr/lib/python2.5/*/ mr,
/var/lib/python-support/*/ mr,
/var/lib/python-support/** mr,
/bin/dash ixmr,
/lib/* mr,
/usr/bin/md5sum ixmr,
/etc/python2.5/** mr,
}

Now, what does it do? Let’s try our normal workflow:


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:50)
Welcome to MyScript!
Please give me the name of a file to md5sum:myscript
9149480416db480c99f4f95b79fda227 myscript

Well, that still works. Shall we try an exploit?


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:53)
Welcome to MyScript!
Please give me the name of a file to md5sum:myscript; bash
9149480416db480c99f4f95b79fda227 myscript
sh: bash: Permission denied

What the heck? Permission denied? /bin/bash is chmodded 755, everyone can access it, right? Not according to AppArmor and dmesg:


[ 7487.802336] type=1503 audit(1228492429.848:201): operation="inode_permission" requested_mask="::x" denied_mask="::x" fsuid=1000 name="/bin/bash" pid=9671 profile="/home/jdong/code/myscript/myscript"

So, AppArmor is telling us that myscript has no access to /bin/bash. Attack averted. The observant reader will ask: But you allowed access to /bin/dash, can’t we still exploit that? Good question, let’s try!


[jdong@droptop:code/myscript]$ ./myscript (12-05 10:54)
Welcome to MyScript!
Please give me the name of a file to md5sum:myscript;/bin/dash
9149480416db480c99f4f95b79fda227 myscript


ls
^C
^C^C^C^C^C^C^C^Z^Z^Z^Z^C^C

Well… that did something different. What exactly happened? Let’s ask dmesg again:


[ 7651.571036] type=1503 audit(1228492593.617:202): operation="inode_permission" requested_mask="::rw" denied_mask="::rw" fsuid=1000 name="/dev/tty" pid=9712 profile="/home/jdong/code/myscript/myscript"
[ 7655.969245] type=1503 audit(1228492598.017:203): operation="inode_permission" requested_mask="::x" denied_mask="::x" fsuid=1000 name="/bin/ls" pid=9714 profile="/home/jdong/code/myscript/myscript"

In English:

  • dash was started, no error generated of course.
  • However, apparmor denied its right to open the terminal at /dev/tty to output to the screen, and accept special terminal input such as CTRL-C.
  • It did hear me say ls,  but AppArmor said it has no access to ls.

Well that’s effective, but no fun! For amusement’s sake, I’ve allowed access to /dev/tty so it’s easier to see what an attacker of myscript has the access to do:


[jdong@droptop:code/myscript]$ ./myscript (12-05 11:19)
Welcome to MyScript!
Please give me the name of a file to md5sum:/dev/null;/bin/dash
d41d8cd98f00b204e9800998ecf8427e /dev/null
[%{%}%n%{%}@%{%}%U%m%u%{%}:%{%}%2c%{%}]%(!.#.$) export PS1="#"
#ls
/bin/dash: ls: Permission denied
#echo foo
foo
#echo "foo" > myscript
/bin/dash: cannot create myscript: Permission denied

Well, now that we can see the shell’s output, we can see that this is really an anticlimactic end to an exploit. From this shell, we can only run dash again or execute shell built-ins like echo or redirections. Since we don’t have write access anywhere, we can’t even overwrite myscript with redirection. Now we’re having fun, aren’t we? Let’s ramp it up: I’m going to add rmix permissions /bin/* and /usr/bin/*, which will allow us to execute the standard UNIX commands , as an ultimate demonstration of AppArmor’s power:


[jdong@droptop:code/myscript]$ ./myscript (12-05 11:24)
Welcome to MyScript!
Please give me the name of a file to md5sum:/dev/null;/bin/bash
d41d8cd98f00b204e9800998ecf8427e /dev/null
bash: /etc/bash.bashrc: Permission denied
bash: /home/jdong/.bashrc: Permission denied
<2m%}%U%m%u%{%}:%{%}%2c%{%}]%(!.#.$) export PS1="$"
$ $ls -al
ls: cannot open directory .: Permission denied
$ls -al myscript
-rwxr-xr-x 1 1000 1000 235 2008-12-05 10:50 myscript
$rm myscript
rm: remove write-protected regular file `myscript'? y
rm: cannot remove `myscript': Permission denied
$echo "foo" > myscript
bash: myscript: Permission denied
$ls /
ls: cannot open directory /: Permission denied
$find /
/
find: `/': Permission denied
$ping google.com
ping: unknown host google.com
$wget http://localhost/
wget: Cannot read /etc/wgetrc (Permission denied).
--2008-12-05 11:26:27-- http://localhost/
Resolving localhost... failed: Name or service not known.
wget: unable to resolve host address `localhost'
$wget http://127.0.0.1/
wget: Cannot read /etc/wgetrc (Permission denied).
--2008-12-05 11:26:35-- http://127.0.0.1/
Connecting to 127.0.0.1:80... failed: Permission denied.
Retrying.
...
$sudo
sudo: uid 1000 does not exist in the passwd file!
$su root
$id
uid=1000 gid=1000 groups=4,20,24,44,46,108,116,117,123,124,127,1000

As you can see, even with the ability to execute every command on the system, you STILL can’t do anything useful. Note that wget and ping don’t work and su seems to fail silently. dmesg is interesting for those cases:


[ 9438.556736] type=1503 audit(1228494380.604:256): operation="socket_create" family="inet" sock_type="dgram" protocol=0 pid=10298 profile="/home/jdong/code/myscript/myscript"

[ 9453.934059] type=1503 audit(1228494395.980:263): operation="socket_create" family="inet" sock_type="stream" protocol=0 pid=10304 profile="/home/jdong/code/myscript/myscript"

[ 9512.316953] type=1503 audit(1228494454.364:291): operation="capable" name="setuid" pid=10317 profile="/home/jdong/code/myscript/myscript"
[ 9512.316958] type=1503 audit(1228494454.364:292): operation="capable" name="setgid" pid=10317 profile="/home/jdong/code/myscript/myscript"

In English these 4 errors are showing:

  1. socket_create is a POSIX capability to create network sockets. I never granted this privilege in AppArmor to myscript, so MyScript is actually incapable of any network access period.
  2. setuid and setgid are also POSIX capabilties that MyScript doesn’t have. Therefore, it can’t even use a freebie su-to-root to elevate its access.

Ready to see something even cooler?


[jdong@droptop:code/myscript]$ sudo ./myscript (12-05 11:28)
Welcome to MyScript!
Please give me the name of a file to md5sum:/dev/null;/bin/bash
d41d8cd98f00b204e9800998ecf8427e /dev/null
bash: /etc/bash.bashrc: Permission denied
bash: /home/jdong/.bashrc: Permission denied
<2m%}%U%m%u%{%}:%{%}%2c%{%}]%(!.#.$) export PS1="# "
# ls
ls: cannot open directory .: Permission denied
# whoami
whoami: cannot find name for user ID 0
# id
uid=0 gid=0 groups=0
# ls -al myscript
-rwxr-xr-x 1 1000 1000 235 2008-12-05 10:50 myscript
# rm myscript
rm: cannot remove `myscript': Permission denied
# su
# su jdong
# id
uid=0 gid=0 groups=0
# ping google.com
ping: unknown host google.com

Yes, you are reading that correctly: even with free root access, myscript still can’t do anything. Not even root is immune to AppArmor. And without write access to /sys, root can’t even disable AppArmor.

In conclusion, and coming soon…

I hope this introduction has shown why technologies such as AppArmor and SELinux can provide a robust level of protection against a wide range of threats, better than an AntiVirus program can. I hope it also shows that the general concept and process of doing so is simple. Next time, I will provide a step-by-step walkthrough of a lengthier, more useful example: Bodhi-Zazen recently posted a HOWTO on using rbash to restrict shell users to a safe set of commands. I will show how I broke free of the jail in 10 seconds and was snooping around the rest of the system, and how AppArmor can be used to robustly provide users restricted SSH access.

In the meantime, if you really want to get started writing some AppArmor profiles, OpenSuse and Ubuntu both have great step-by-step guides.

What do I protect with AppArmor? I have rigorous profiles defined for:

  • Skype
  • Firefox
  • my fetchmail script
  • my script-rich irssi client
  • Apache
  • dnsmasq

These are the services I’ve identified to be at the greatest risk on my system for this class of exploits.

your ads here (468x60) - after 1st post.

Chumby Is Very Cool, But It’s Not Perfect

I received my Chumby yesterday and it is definitely a neat little device with a fair price tag. So when I say it’s not perfect, I mean exactly that.

According to a post on the Chumby forums by a Chumby employee, “the device has a 266MHz Freescale ARM9 with 133MHz bus, 32MB SDRAM and 64MB of NAND Flash.” It also has two external powered USB ports and a headphone jack on the rear of the unit, along with the power connector and a power button (be careful grabbing it by the back when moving it; I’ve turned it off accidentally once). Another interesting thing to note is that the unit’s wifi is actually a USB 802.11g dongle on the inside of the device, which means you can replace it if need be. There is also a second powered USB port on the inside which is not in use. Lots of room for expansion on this thing.

The other great thing about the Chumby, before we go into my personal experience with it, is that 100% of the software running the thing is either GPLv2 or LGPLv2.

Inside the shipping carton was a plastic sleeve sealed with a Chumby logo sticker, and inside that was the burlap or canvas bag that contained the Chumby and all its accessories. The entire package has a “recycled materials” feel to it, from the coarse fabric bags that contain the product to the recycled paper manual. If you’re the sort of person who uses the phrase “carbon footprint” in normal conversation, you will likely be pleased at the packaging in which the Chumby arrives.

chumby.jpg

Being the type of person I am, I immediately discovered a velcro-sealed pouch in the bottom of the unit that contained a connector for a 9V battery, one of which I happened to have laying on my desk. The Chumby powered up right away and I set about watching the introductory video that plays on first boot. Shortly thereafter the problems set in.

Now to be fair, I should say up front that I wasn’t thinking about some things. Like how a puny 9V battery isn’t going to power something with an LCD display for very long…and it didn’t. However, going back to my bedroom and plugging it in near the night stand that is now its permanent home, I was having a lot of trouble getting a wireless signal.

My wireless router is probably 30′ in a straight line from the Chumby and the signal has to go through one or two internal walls to reach the Chumby if my door is closed, which it usually is (roommates). It grabbed a signal and an IP address early on, but then would fail to reboot properly. Upon trying to regain connectivity, it would fail — over and over and over. I took the unit out to my work area and plugged it in so it was within a couple feet of the WAP, where it proceeded to function flawlessly (except when I had it on rapidly-draining 9V power only, when it would make awful stuttering static noises and shut down).

Eventually I got it to grab a signal from my bedroom and it hasn’t lost it again (about 25 hours now). So just be aware that you may experience some issues when you first get it out of the box. Not sure if it just needs burn-in time or what, but it’s run beautifully since.

After you activate the Chumby, a process which consists of tapping the screen to replicate a pattern shown on the Chumby site, you can begin to have fun with it. There are several hundred widgets available already, including 67 clocks, and you can have multiple “channels” containing unique widgets. I currently have a “Clocks” channel with about 10 of my favorite clock designs (a bunch of BBC TV clocks from the 80s, a Nixie tube clock and some other retro stuff), a “Productivity” channel with a Google calendar applet and a mail checker, and the stock “Default” channel where I literally dump anything and everything that looks remotely interesting.

“Neat, it’s a $179 clock. Big deal,” I hear you saying. It’s a bit more than that. Admittedly, I don’t use the news and RSS widgets as much as most people probably will. Why not? Well I usually have my MacBook sitting right here next to me, so I prefer to read news on the Mac’s larger screen. But I’ve been able to offload 100% of my internet radio needs to the Chumby. It has a built-in SHOUTcast browser, Mediafly podcasts, “Radio Free Chumby” which has a load of FM stations that broadcast via web and you can even plug your iPod directly into one of its two USB ports and play songs from it.

All of the widgets are Flash 7 movies, so it’s possible to author a widget with anything that can produce Flash, not just Macromedia’s proprietary (and usually Linux-hating) tools. This is good news, and I can’t congratulate the Chumby team enough for making such good choices when it came to licensing and implementation of widgets.

The unit can be converted to use wired ethernet with very little work and at least two particular models of 10/100 dongles, a Linksys and a Trendnet, are known to work with it. You can ssh into the unit easily enough, by activating what amounts to an easter egg, as well as perform all sorts of other hacks. These are detailed on Chumby’s own wiki. It runs its own webserver which can be accessed by entering its IP into a web browser, but the only content is a summary of wifi information.

Now, to delve into some of my complaints with Chumby:

The touch screen is a bit “meh.” It works, and it works well enough, but sometimes I find myself having to press a button five or six times before it finally registers, and this occurs whether I use my thumb or my forefinger, pad or fingernail. This seems to be exacerbated when I’m laying down (start using one, you’ll see what I mean) but sometimes even if I’m sitting up and holding it directly in front of me it still gets iffy. It works for the most part and you can recalibrate the touchscreen as and when necessary.

Centralized widget control is the other issue. All widgets for general consumption have to be approved by Chumby and placed on the Chumby Network. This isn’t so bad, but it smacks of Apple’s “all your iPhone applications are belong to us” nonsense they tried to pull (looks like they can’t really enforce this, however, which is good news). I like quality control, but I like choice, too.

“Seeing extra widgets on your chumby that aren’t shown in your channel above? These are added by Chumby Industries and content partners. Sharing these promotional widgets with you is how the Chumby Network stays FREE.” That was a message that gave me some cause for concern. I understand that bandwidth isn’t free, but I did pay $180 for this thing. Having seen some of these nefarious widgets, I have to say that they’re really not so bad. The ad content disappears in under 30 seconds and you’re not forced to watch the advertisment videos…this is a good compromise and I’m certainly not begrudging Chumby Industries their meal ticket.

As you can see, my complaints with the device are minor. I was pleasantly surprised by how little about the thing irked me at all. Instead of having to grumble about ads, centralized control and a flaky touchscreen, I’ve just been enjoying the hell out of it instead. I think you will, too, if you get one. The only way to really experience it is to use one. I’m glad I bought mine. (4.5/5 rating)

You can find a bunch of videos of Chumby unboxings and other things that are probably way better than the one I tried to shoot on, where else, YouTube.

xclip - Use the Clipboard From the Command Line

Yesterday morning I had a bit of a strange question (so I thought), although it turned out to be a good one. “Is there any way to pipe or redirect output from the console to the clipboard?” I asked my good pal jdong if there was indeed a way to do this. He quickly turned me onto this Debian page where I discovered a neat little application called xclip.

You can read the nuts and bolts of its usage on the Debian page, but it’s quite easy. I’ll use an example based on what I originally wanted it for (being able to paste file listings of various directories into a pastebin or document):

ls -al /media/storage2/Comedy/* | xclip

You can retrieve what went into the “clipboard” by running xclip -o, which will paste back into the terminal whatever you copied using the example above.

Unfortunately what it did not do was save the output to the regular “X” clipboard (where items normally go when you highlight and do a Ctrl+v or Ctrl+x in the GUI or Ctrl+Shift+c/x in the console), which is what I was expecting, but this is easily remedied. In order to do that, we must run the following:

ls -al /media/storage2/Comedy/* | xclip -selection c.
(To paste from the X clipboard it’s simply a matter of running xclip -selection o, but you can also just use Ctrl+Shift+v)

xclip supports a lot of extended features such as split clipboards (which I’m still not quite sure what those are), but I mainly wanted to use it for grabbing file listings. So all I did was make an alias by running:

alias xclip='xclip -selection c'

Now when I pipe output to xclip I can quickly switch to my web browser or document, hit Ctrl+v and my console output appears in a pastebin or whatever else i’m working on. It’s quite handy for both writing technical documents and for troubleshooting. Pastebin is the darling of IRC idlers, and this just made it easier.

I didn’t bother with aliasing “xclip -selection o” simply because I can just use Ctrl+Shift+v and paste back into the console from the X clipboard as mentioned above.

Hopefully that wasn’t too confusing. Use it a few times and it becomes a lot more intuitive. I certainly like it, especially for large outputs that would exceed the scrollback buffer (I use the Awn Terminal Applet constantly, which has a small buffer).