Stopping symlink attacks with ASL
This isnt a new attack, but we’re seeing it used more often so we wanted to highlight a feature we’ve had in ASL for over a year to stop this. Attackers discovered a way to get access to files on a shared web server in other users accounts. This lets them get access to things like database usernames and passwords in other accounts, that they dont have access to, giving them the ability to compromise all the accounts on a system from just one single account. They do this by one or more symlinks to other parts of the system, and you can use ASL to stop this attack.
A symlink is a pseudo file or directory, it basically allows a user to create a file or directory that points to another file or directory. Its create a “link” to this file or directory. Its a great tool, and you can do some cool things with this capability in Linux. Following symlinks to files you dont own will only work if you have permission to read those files. So in practice it seems like this attack wouldnt work. If a user doesnt have permission to a read file, they cant gain access to this. And on most web servers this is the case, nevertheless this attacks works. But how?
This works because apache runs as a special user (e.g. apache, nobody, www). And apache has to be setup to be allowed to read any web content on the system, across all the users on the system. Even though those users can not read each others web content, the apache user can. Again, this doesnt byitself seem to be an issue, because the apache user can only access whatever that user has made available for that users account or virtual host. If you go to www.example.com you dont get the content for www.victim.com.
But, if these two domains are on the same server, a clever attacker can gain access to all the files in every domain or users accounts on the system.
How the attack works
An attacker only has to be able to get access to one domain or user account on the system, lets call that the compromised user and lets say they have the domain www.example.com. From there, they create a symlink to files and directories in other users accounts or domains on the same system, lets say they create a symlink from the www.example.com web content directories to the www.victim.com directory:
ln -s /home/webusers/victim.com/public_html /home/webusers/example.com/public_html/hack_victim
As the compromised user they cant follow that symlink and access the files in /home/webusers/victim.com/public_html (this is assuming the targeted user has set up permissions on those files to prevent other users from accessing them, which is normally the case). However, the apache user can, as it must be able to read them.
The attacker then just accesses their own website, www.example.com, and simply requests the URL hack_victim. Apache then follows the symlink and the attack can now have access to anything in that directory. In practice, most attacks just create a symlink to the root directory on the entire server so they can get access to anything apache can.
Again, this works because apache has to be setup to be allowed to read every users web content (otherwise, it cant serve it up). Users on the system are not (normally) setup to be allowed to read other users files. Attackers gain access to the system and create a symlink to another users files, they cant read them because they dont have permission to read them. However, apache does, so they simply setup these symlinks in the compromised web directories on the same server, and then access those files through apache.
How ASL stops them
The ASL kernel can stop this. It does this because symlinks are just like files. They have owners. A symlink is owned by the user that creates it. ASL will check a symlink to see if it points to a file owned by the same user. If its pointing to a file thats not owned by the user that created the symlink, it will not allow it to be followed. This completely stops the attack.
There have been other attempts to address this vulnerability, such as Apache’s SymlinksIfOwnerMatch option. Unfortunatelty, this option has an inherent race condition that prevents it from stopping actual attacks. The problem is that apache only verifies the ownership of the symlink by performing a stat() against the target of the symlink before it is followed. This sounds like it will work, however all an attacker has to do is setup a symlink to point to a same-owned file, then replace the symlink with one that targets another user’s file just after Apache “validates” the symlink. In security yerms, this is refererred to as a “Time of check to time of use” race condition. This is a classic bug caused by changes in the system between the times when the condition is checked, but assuming that a change hasnt occured. To use an analogy, this is a bit like driving up to an intersection, and only checking the light once. The drive from a distance sees the light is green and never checks again, simply proceeding through the intersection with assumption the remains green. If the light turns red, you assume its green because you already checked it once. And since attackers can do things quickly, they just need to automate this, they can out race apache. And of course, thats exactly what they do because this is a well known bug.
Unlike this bug in Apache, the ASL kernel includes a race-free replacement for Apache’s “SymlinksIfOwnerMatch” for users in the “symlink” group. To use the analogy again, this time around our driver doesnt assume the light is always green, the driver continuously checks it. This makes it possible to stop this type of attack, whereas the apache option can not.
How to set it up
The ASL kernel creates a special user group called “symlink”. Any user put into this group will be unable to create a symlink to a file they dont own. Just add any users you want to be restricted to this group. You can run this command, as root, to add a user to that group:
useradd -G symlink username
Just change username to whatever username you want to be added to this group and that user will no longer be able to use this attack.