Software Assurance            Software Hardening            Autonomic Computing

Thwarting Insider Attacks with Advanced Static Analysis

AdobeStock_115471071-2.jpeg

INTRODUCTION:

The security threat posed by insiders is often underestimated. According to an IBM study, 32% of attackers are insiders and 24% are “inadvertent actors” (e.g. people making mistakes that lead to a system breach or incorrect behavior.) One such class of insider attack is malicious code added during development that allows for future exploitation. Advanced static analysis tools can detect these within source and binary code before they get shipped to customers. In addition to existing detection for security vulnerabilities, this post also talks about specific security vulnerability checks to detect certain insider attacks.

Related:


What are insider attacks?

Insiders are people working inside the secure perimeter either as users, developers or other trusted personnel. The big difference from regular cyber-attacks is the insider is often on a trusted network or has physical access to the device or system. The attack surface for insiders is larger than for outsiders. According to the SEI, 21% of electronic crime was perpetrated by insiders and 43% of respondents to their survey had experienced at least one insider attack.

Insider attacks might be due to unintentional mistakes or intentional malice by disgruntled employees. Attacks can be perpetrated when a product is in the field by intentional misuse or via pre-programmed vulnerabilities. Attacks programmed into the product ahead of time are of interest in this post, and in the same SEI survey, 37% of insider attack were caused by “virus, worms or other malicious code.”

Using Advanced Static Analysis to Detect Possible Inside Attacks

Advanced static analysis tools can detect different classes of security vulnerabilities such as buffer, numeric and stack overflows, command injection, and use of insecure library functions. The tools can also trace data flow from outside sources to the vulnerable code (tainted data analysis), which leads to a verifiable attack vector. These types of vulnerabilities can be left intentionally and undetected during development for later exploitation. A priori knowledge of these vulnerabilities would be a special case of a zero day exploit known only to the insider. Therefore, it’s important to detect and remove these types of vulnerabilities through proper security measures throughout the software development life cycle, something discussed in a previous post.

Binary analysis for finding attacks

CodeSonar's binary code analysis technology is capable of analyzing stripped optimized executables; roughly speaking, it finds the same class of defects that can be found in the source code. The tool’s new integrated analysis is capable of analyzing source and binaries simultaneously. This is useful in cases where you have source code for most of the project, but only binary libraries for some components.

GrammaTech CodeSonar provides additional malicious code detection for detecting dangerous process creation, use of CHROOT, and possible time bombs. The following examples illustrate the types of exploits CodeSonar checks for.

Examples

The following examples are specific malicious code checkers available in CodeSonar for detecting certain types of insider threats.

Untrusted Process Creation

Process creation is always potentially dangerous, especially if it’s possible to manipulate the process name or parameters (e.g. command injection.) CodeSonar detects untrusted process creation by checking the arguments to functions that create processes against a blacklist. The blacklist is configurable but includes well known, potentially dangerous commands. The example below produces a warning with the sh command but not with “myprocess” which isn’t in the blacklist.

#include <stdlib.h>
#include <stdio.h>
 
void ut_proc(const char *command) {
 
    FILE *pipe_file;
 
    if (pipe_file = (FILE*)popen("/usr/bin/myprocess","r")) {
      /* not blacklisted */
      pclose(pipe_file);
    }
 
    if (pipe_file = (FILE*)popen("/usr/bin/sh","r")) {
      /*'Untrusted Process Creation' warning issued here */
      pclose(pipe_file);
    }
}

Untrusted Library Load

An insider might try to load an untrusted dynamic library at runtime that contains malicious code. However, detecting every library load would cause too many false positive warnings so a blacklist of unwanted libraries is recommended (regular expressions are supported.)

In CodeSonar the project configuration file would have the following, for example:

UNTRUSTED_LIB_BLACKLIST += ^.*hack.*$

CodeSonar will issue a “Untrusted Library Load” warning in the following code:

#include <lfcn.h>

void * io_ut_lib_bad(void) {
  
return dlopen("./myhackylibrary.so", RTLD_LAZY);
  /* 'Untrusted Library Load' warning issued here */
}

void * io_ut_lib_ok(void) {

  return dlopen(""./myproperlibrary.so", RTLD_LAZY);
  /* ok: does not include blacklisted substring "hack */
}
 

CHROOT Without CHDIR

Issuing the chroot() (change process root directory) Unix/Linux command is potentially dangerous, and malicious code can exploit the situation to access files in other parts of the system. A best practice is to issue the chdir() (change current process directory) command right before or after chroot(). In the example below, a warning is raised because chdir() isn’t always called right after the chroot() command due to the check on the variable fname.

#include <unistd.h>
#include <stdio.h>
int chroot_no_chdir(const char *fname, char *buf) {
 
    FILE *localfile;
    int bytesread=0;
 
    
if (chroot("/downloaddir") == -1) {
      /* chroot without chdir' warning issued here */
      return 0;
    }
    if (fname) {
        if (localfile = fopen(fname, "r")) {
            bytesread = fread(buf, 1, sizeof(buf), localfile);
            fclose(localfile);
        }
 
        if (chdir("/")==-1) {
          /* chdir() is only called if fname!=NULL */
          return 0-bytesread;
        }
     }
     return bytesread;
}
 

Potential Timebomb

Time bombs are possible in code that uses and checks for time values from the system clock. Reasonable checks for time are acceptable, but code that uses a value not derived from the current time, such as a hardcoded constant, may be “waiting” for a specific time and date to execute malicious code. In the example below, the second “if” statement compares the time value against a hardcoded constant.

#include <time.h>
 
void misc_timebomb(void) {
 
    time_t deadline = 1893456000;
    time_t now = time(NULL);
 
    if (now > time(NULL)) {
      /* ok: time value compared against another time value */
      /* ... */
    }
    if (now < deadline) {
      /* 'Potential Timebomb' warning issued: time value compared */
      /* against non-time value */
      return;
    }
    /* An inside attacker could put malicious code here:
     * it would only be executed once the deadline was past. */
}

 

Untrusted Network Addresses or Ports

An insider could allow external access to an application or device via a network connection or through an unauthorized network port. Intended network connections may be indistinguishable from illegitimate ones under casual inspection, more so if addresses and port numbers are purposely obfuscated. Static analysis tools can easily detect network connection functions. However, to prevent false positives, specifying a list of addresses and port numbers to exclude is recommended. For example, in CodeSonar the project configuration file would contain the following:

NETWORK_HOST_BLACKLIST += allow ^0:0:0:0:0:0:0:1$
NETWORK_HOST_BLACKLIST += .+\.[a-zA-Z]{2,6}($|\s+|\\|/|:)
 

For network ports:

NETWORK_PORT_WHITELIST += ^80$
 

CodeSonar will issue a “Unstrusted Network Host” warning in the following code.

#include <stdlib.h>
int ut_host() {
  int status;
  struct addrinfo *res;
 
  status = getaddrinfo("0:0:0:0:0:0:0:1", "80", NULL, &res);
  /* explicit blacklist exception */
  if (status != 0) {
    if (res)
      free(res);
    return status;
  }
   
  status = getaddrinfo("2001:DB8:1:2:3:4:5:6", "80", NULL, &res);
  /* 'Untrusted Network Host' warning issued here. */
   
  if (status != 0) {
    if (res)
      free(res); 
    return status;
  }
  /* ... */
}

 

CodeSonar will issue a “Untrusted Network Port” warning in the following code:

 #include <stdlib.h>
 
void ut_port( const char *myhost, char *myport ) {
  struct addrinfo *res;
  int rv;
  
  rv = getaddrinfo( myhost, "1234", NULL, &res );
  /* 'Untrusted Network Port' warning issued here */
 
  rv = getaddrinfo( myhost, "80", NULL, &res );
  /* whitelisted */
  /* ... */
}

Weak Cryptographic Functions

The use of weak cryptographic functions is poor security. However, an insider may intentionally use these functions to overcome the encryption at a later date. For example, application files with MD5 or DES encryption might seem adequate, however these are known weak encryption algorithms. Sensitive data could be decrypted by attackers with access to the stored files.

CodeSonar issues a “Weak Cryptography” warning with the following code:

#include <openssl/md2.h>
 
void weakcrypto(MD2_CTX *ctx) {
  
if (MD2_Init(ctx)) {
     /* 'Weak Cryptography' warning issued here */
     /* ... */
  }
  /* ... */
}

CONCLUSION:

Insider threats in the form of malicious code written by insiders are a significant, but often overlooked, source of cyberattacks. Advanced static analysis tools can detect intentional malicious code using security vulnerability analysis. GrammaTech CodeSonar detects different types of security vulnerabilities in addition to tainted data flow analysis. CodeSonar also includes checkers for specific malicious code types that help prevent exploitable code from reaching the finished product.