Overwriting argv[]

Malware can overwrite argv[] to mask its process name. This is done to hide from analysts.

Example

Here is a simple program which sleeps for two minutes before exiting. Notice that ps shows this process’s name is argv.

wildcat# cat argv.c
#include <stdio.h>
#include <unistd.h>


int main(int argc, char *argv[])
{
	sleep(120);
	return 0;
}
wildcat# gcc -o argv argv.c
wildcat# ./argv &
[1] 79927                      <--- pid of argv
wildcat# ps      
    PID TTY          TIME CMD
  33595 pts/0    00:00:00 sudo
  33597 pts/0    00:00:01 zsh
  79927 pts/0    00:00:00 argv <---
  79928 pts/0    00:00:00 ps

The following modification of argv.c overwrites argv[0] with “ps”, which may help it blend in with processes on the system:

wildcat# cat argv.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
	memset(argv[0], '\0', strlen(argv[0]));
	strcpy(argv[0], "ps");
	sleep(120);
	return 0;
}
wildcat# gcc -o argv argv.c
wildcat# ./argv &
[1] 80316                                  <--- pid of argv
wildcat# ps -80316
    PID TTY      STAT   TIME COMMAND
  80316 pts/0    SN     0:00 ps            <--- not "argv"

Hunting

On systems with procfs, you may be able to detect this behavior. In this example, you can see that /proc/PID/cmdline differs from /proc/PID/comm and the “Name” line from /proc/PID/status. Comparing these values to each other against all running processes may unearth some of these processes.

wildcat# ./argv &          
[1] 80400
wildcat# cat /proc/80400/cmdline
ps#                                                                            wildcat# cat /proc/80400/status      
Name:	argv
...snip...
wildcat# cat /proc/80400/comm
argv

The following script can identify processes exhibiting this behavior:

#!/bin/sh

# Find processes that are potentially lying about their argv[] in an
# attempt to hide in 'ps' output

for process in $(find /proc/[0-9]*/ -maxdepth 0 -type d); do
    pid=$(echo $process | cut -d / -f 3)

    # skip if cmdline is missing
    if [ ! -e ${process}cmdline ]; then
	continue
    fi
    cmdline=$(tr '\0' '|' < ${process}cmdline)

    # skip processes without command lines.
    if [ "$cmdline" = "" ]; then
	continue
    fi

    cmd=$(echo $cmdline |cut -d '|' -f 1)
    status=$(grep -E "^Name:" ${process}status)

    echo $cmdline | grep $(echo $status |awk '{print $2}') >/dev/null
    if [ $? = 1 ]; then
	echo "[+] This process is sketchy:"
	echo "  PID:             $pid"
        echo "  command:         $cmd"
	echo "  /proc/X/cmdline: $cmdline"
	echo "  /proc/X/status:  $status"
	echo
    fi
done

Here is an example of this hunting script in action. It correctly identifies the argv program above. Notice the false positive detection for /sbin/init:

wildcat# ./argv &
[1] 80613
wildcat# chmod +x find-argv.sh.
wildcat# ./find-argv.sh 
[+] This process is sketchy:
  PID:             1
  command:         /sbin/init
  /proc/X/cmdline: /sbin/init|splash|
  /proc/X/status:  Name:	systemd

[+] This process is sketchy:
  PID:             80613
  command:         ps
  /proc/X/cmdline: ps|||||
  /proc/X/status:  Name:	argv

Real-world Examples

Here are some examples of malware that leverage this technique.

MalwareLink
icmp-backdoorhttps://github.com/droberson/icmp-backdoor/blob/master/icmp-backdoor.c
DDoS Perl IrcBothttps://gist.github.com/tlongren/afe81698cafaafcbe386
PRISMhttps://github.com/andreafabrizi/prism/blob/master/prism.c