Forging SWIFT MT Payment Messages for fun and pr... research!
-
Oliver Simonnet
- 7 Feb 2020
TLDR: With a bit of research and support we were able to demonstrate a proof of concept for introducing a fraudulent payment message to move £0.5M from one account to another, by manually forging a raw SWIFT MT103 message, and leveraging specific system trust relationships to do the hard work for us!
Before we begin: This research is based on work we performed in close-collaboration with one of our clients; however, the systems, architecture, and payment-related details have been generalized / redacted / modified as to not disclose information specific to their environment.
With that said… clears throat
The typical Tactics, Techniques and Procedures (TTPs) against SWIFT systems we see in reports and the media are - for the most part - the following:
This attack-path requires the compromise of multiple users, multiple systems, an understanding of how to use the target application, bypass of 2FA, attempts to hide access logs, avoid alerting the legitimate operators, attempts to disrupt physical evidence, bespoke malware, etc. – so, quite involved and difficult. Now that’s all good and fine, but having reviewed a few different payment system architectures over the years, I can’t help but wonder:
“Can’t an attacker just target the system at a lower level? Why not target the Message Queues directly? Can it be done?”
Well, let’s find out! My mission begins!
So, first things first! I needed to fully understand the specific “section” of the target institution’s payment landscape I was going to focus on for this research. In this narrative, there will be a system called “Payment System” (SYS). This system is part of the institution’s back-office payment landscape, receiving data in a custom format and output’s an initial payment instructions in ISO 15022 / RJE / SWIFT MT format. The reason I sought this scenario was specifically because I wanted to focus on attempting to forge an MT103 payment message - that is:
Message type aside, what does this payment flow look like at a high level? Well I’ve only gone and made a fancy diagram for this!
Overall this is a very typical and generic architecture design. However, let me roughly break down what this does:
OK, so now I have a general understanding of what I’m up against. But if I wanted to exploit the relationships between these systems to introduce a fraudulent payment without targeting any payment operators, I was going to need to dig deeper and understand the fundamental technologies in use!
So how are these messages actually “passed” between each system? I need to know exactly what this looks like and how its done!
More often than not, Message Queues (MQ) are heavily used to pass messages between components in a large payment system. However, there are also various “Adapter” that may be used between systems communicating directly with the SAG (Such as SAA or other bespoke/3rd party systems). These are typically the:
Having identified that MQ was in use, my initial assumption was that there was most likely a dedicated Queue Manager (QM) server somewhere hosting various queues that systems push and pull messages from? However, due to SWIFT CSP requirements, this would most likely - at a minimum - take the form of two Queue Managers. One which manages the queues within the SWIFT Secure Zone, and another that manages queues for the general corporate network and back office systems.
Let’s update that diagram to track / represent this understanding:
Now I could research how this “messaging” worked!
There are multiple ways to configure Message Queues architectures, in this case there were various dedicated input and output queues for each system, and the message flow looks something like this:
Full disclosure, turns out it’s hard to draw an accurate - yet simple - MQ flow diagram (that one was basically my 4th attempt). So it’s… accurate “enough” for what we needed to remember!
Now I had a good understanding of how it all worked, it is time to define my goal: “Place a payment message directly on to a queue, and have it successfully processed by all downstream systems”.
This sounds simple, just write a message to a queue, right? But there are a few complications!
But OK, there’s no point dwelling on all of that right now, I’ll just clearly define what I want to do! The goal:
What I was not interested in doing for this research - yet needed to understand nevertheless for a full attack chain was:
What I wanted to 100% avoid at all costs:
Now I had an idea of what to do, I needed to make sure I could write a raw MT103 payment instruction! Typically, even when operators write payment messages using a messaging interface application like Alliance Access, they only really write the message “body” via a nice GUI. As raw data this could look something like:
:20:TRANSACTIONRF103 :23B:CRED :32A:200102GBP500000,00 :33B:GBP500000,00 :50K:/GB22EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59K:/FR20FBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, PARIS, FR :70:12345-67890 :71A:SHA
I’ll break this down in the following table:
Name | Field | Value |
---|---|---|
Transaction Reference | 20 | TRANSACTIONRF103 |
Bank Operation Code | 23B | CRED (Message is to “credit” some beneficiary) |
Value Date / Currency / Amount | 32A | 200102 (02/01/2020) GBP 500,000.00 |
Currency / Original Credit Amount | 33B | GBP 500000,00 (£500,000.00) |
Ordering Customer | 50K | GB22EBNK88227712345678 (IBAN) JOHN DOE (Name) JOHN’S BUSINESS LTD (Line 1) 21 JOHN STREET, LONDON, GB (Line 2) |
Beneficiary | 59K | FR20FBNK88332287654321 (IBAN) ALICE SMITH (Name) ALICE’S COMPANY (Line 1) 10 ALICE STREET, PARIS, FR (Line 2) |
Remittance Information | 70 | 12345-67890 (essentially a payment reference) |
Details of Charge | 71A | SHA (Shared charge between sender and receiver) |
Now as this is a valid message body, if I were targeting a payment operator on SWIFT Alliance Access, I could - for the “most” part - simply paste the message into SAA’s raw message creation interface and I’d be pretty much done. With the exception of adding the sender / recipient BIC codes and most likely selecting a business unit. However, these values are not stored in the message body. Not stored in the message body you say? Well that complicates things! Where are they stored exactly?
The message “body” is referred to as “block 4” (aka the “Text Block”) within the SWIFT MT standard. As suggested by the name, there is probably also a block 1-3. This is correct; and these blocks are typically generated by the payment processing applications - such as SWIFT Alliance Access - and not necessarily input by the operators. A “complete” MT103 message consists of 6 blocks:
So it looked like I was going to need to learn how to craft these various “blocks” from scratch.
Reading through some documentation, I crafted the following “Basic header” block:
{1:F01EBNKGB20AXXX0000999999}
A breakdown of what this translates too is as follows:
Name | Value | Context |
---|---|---|
Basic Header Flag | 1 | Block 1 (Not 2, 3, 4, or 5) |
Application Type | F | FIN Application |
Message Type | 01 | 01 = FIN (I.e not ACK/NACK) |
Sender BIC | EBNKGB20 | EBNK (Bank Code) GB (Country Code) 20 (Location Code) |
Sender Logical Terminal | A | Typically A, unless they are a significantly large institution and require multiple terminals |
Sender Branch | XXX | All X if no branch needed |
Session Number | 0000 | The session number for the message |
Sequence Number | 999999 | The sequence number of the message |
Taking a step back, I already identified two potential problems: the “session” and “sequence” numbers! These are described as follows:
Hmmm, at this point I was not sure how I could predetermine a valid session and/or sequence number - considering they seemed to be application and “traffic” specific? But there was nothing I could do at the time, so I noted it down in a list of “issues/blockers” to come back to later.
A bit more dry reading later, I managed to also throw together an application header:
{2:I103FBNKFR20XXXXN}
Again, I’ve broken this down so it makes sense (if it didn’t already; I’m not one to assume):
Name | Value | Context |
---|---|---|
Application Header Flag | 2 | Block 2 |
I/O Identifier | I | Input Message (a message being sent) |
Message Type | 103 | 103 = Single Customer Credit Transaction |
Recipient BIC | FBNKFR20 | FBNK (Bank Code) FR (Country Code) 20 (Location Code) |
Recipient Logical Terminal | X | All General Purpose Application Messages must use “X” |
Recipient Branch | XXX | All General Purpose Application Messages must use “XXX” |
Message Priority | N | Normal (Not Urgent) |
Awesome! No issues crafting this header!
Note: At this point I should probably mention that these BIC codes are not “real”, however are accurate in terms of in format and length.
The third block is called the “User Header” block, which can be used to define some “special” processing rules. By leverage this header, I could specify that the message should be processed using “Straight Through Processing” (STP) rules which essentially attempts to ensure that the message is processed end-to-end without human intervention. This could be specified as follows:
{3:{119:STP}}
However, this was not yet a valid header! As of November 2018 the user header requires a mandatory “Unique end-to-end transaction reference” (UETR) value, which was introduced as part of SWIFT’s Global Payments Innovation initiative (gpi)! This is a Globally Unique Identifier (GUID) compliant with the 4th version of the generation algorithm used by the IETF standard “RFC4122”. This consists of 32 hexadecimal characters, divided into 5 parts by hyphens as follows:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
where:
This value can be generated using Python as seen below:
$ python -c 'import uuid; id = uuid.uuid4(); print "Value:", id; print "Version:", id.version, id.variant'
Value: 8b1b42b5-669f-46ff-b2f2-c21f99788834
Version: 4 specified in RFC 4122
With an acceptable UETR generated, this is how the third block looked:
{3:{119:STP}{121:8b1b42b5-669f-46ff-b2f2-c21f99788834}}
And as before, a breakdown can be found below:
Name | Value | Context |
---|---|---|
User Header Flag | 3 | Block 3 |
Validation Flag | 119 | Indicates whether FIN must perform any type of special validation |
Validation Field | STP | Requests the FIN system to validate the message according to the straight through processing principles |
UETR Field | 121 | Indicates the Unique end-to-end transaction reference value |
UETR Value | 8b1b42b5-669f-46ff-b2f2-c21f99788834 | Unique end-to-end transaction reference used to track payment instruction |
I’ve already discussed “block 4” (the message body), so to wrap this section up, I’ll be looking at the final 2 blocks: Block 5, aka the “Trailer”; and block S, aka the “System” block.
Before going forward, let me take a moment to explain the pointlessly complicated concept of input and output messages:
OK, moving swiftly (aaaahhhhh!) on.
For Input messages, these blocks were not too much of a problem. The headers only really seemed to be used to flag whether the message was for training / testing or to flag if it was a possible duplicate, which syntactically took the following form:
{5:{TNG:}}{S:{SPD:}}
Where “TNG” indicated “training” and “SPD” indicated “possible duplicate”.
However, with Output messages, it got considerably more complicated. An example of what the trailer and system block could look like on an Output message is the following:
{5:{MAC:13461AEF}{CHK:4A3367FD3D76}{TNG:}}{S:{SPD:}{SAC:}{COP:P} {MDG:5E87F8F390E5FB886E8311E4D7C994371FA9AF3119B2C314DAE458738AFF08AC}}
A breakdown of these various values is:
Trailer ({5:) MAC – Message Authentication Code calculated based on the entire contents of the message using a key that has been exchanged with the destination bank and a secret algorithm; CHK – This is a PKI checksum of the message body, used to ensure the message has not been corrupted in transit; TNG – A flag to indicate that the message is a Testing and Training Message.
System ({S:) SPD – Possible Duplicate Flag SAC – Successfully Authenticated and Authorized Flag. This is only present if:
COP – Flag indicating that this is the primary message copy; MDG – The HMAC256 of the message using LAU keys.
However, these seemed to only be values I would need to consider if I was to try and forge an “incoming” message from SWIFTNet or an “outbound” message on the output of the SAG.
So… I’ll stick with crafting an “input” message trailer:
{5:{TNG:}}{S:{SPD:}}
Now, having said all that, it turned out the trailer block did seem to sometimes hold a MAC code and a message checksum (sigh), meaning I actually needed to construct something like:
{5:{MAC:XXXXXXXX}{CHK:XXXXXXXXXXXX}{TNG:}}{S:{SPD:}}
So that was +2 to my “issues/blockers” list. However, issues aside, I now understood the complete message format, and could put it all together and save the following as a draft / template MT103 message:
{1:F01EBNKGB20AXXX8888999999} {2:I103FBNKFR20XXXXN} {3:{119:STP}{121:8b1b42b5-669f-46ff-b2f2-c21f99788834}} {4: :20:TRANSACTIONRF103 :23B:CRED :32A:200102GBP500000,00 :33B:GBP500000,00 :50K:/GB22EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59:/FR20FBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, PARIS, FR :70:12345-67890 :71A:SHA -} {5:{MAC:XXXXXXXX}{CHK:YYYYYYYYYYYY}{TNG:}}{S:{SPD:}}
Highlighted in bold above are the areas of the message I was - at this point - unable to pre-determine. Nevertheless, a summary of what that the message describes is:
To wrap up this section, i wanted to take a moment to explain some logic behind the target of 500,000 GBP, as it is also important.
Aside from the many reasons it would be better to transfer [even] smaller amounts (which is an increasingly common tactic deployed by modern threat actors), why not go higher? This is where it’s important to understand the system and environment you are targeting.
In this instance, let’s assume that by doing recon for a while I gathered the understanding that:
This was because a transaction over £500k was determined to be “abnormal” for SYS. As such, if my transaction was greater, the message would not propagate through all systems automatically.
OK, so now that I understood:
And with that, it was time to take a break from MT standards and establish an understanding of how I would even get into a position to put this into practice!
To place a message on a queue, I was going to need two things:
Depending on the environment and organisation, access to queue managers could be quite different and complex. However a bare-bones setup may take the following form:
So, in this scenario, to gain access to the message queues I - as an attacker - would need to compromise the MQ admin’s AD account and workstations, then use this to gain access to the jump host, from where I could then access the message queues given I knew the correct channel name and was configured with authorization to access it… and maybe throw some MFA in there…
That is understandably a significant requirement! However, when discussion sophisticated attacks against Financial Market Infrastructure (FMI), it is more than reasonable to accept that an Advanced Persistent Threat (APT) would see this as a feasible objective - We don’t need to dig into the history of how sophisticated attacks targeting SWIFT systems can be.
Next, it was time to finally identify a feasible attack vector for message forgery.
Now with an idea of how to gain the right access, as well as an understanding of the various technologies and security controls in place; I update my diagram:
You may have noticed I’ve added something called “LAU” around the SAA-to-SAG adapter, and another “LAU” to the MID-to-SAA MQ channels, which I have yet to explain. “Local Authentication” (LAU) is a security control implemented by SWIFT to authenticate messages using a pair of shared keys between two systems. These keys are combined and used to generate a SHA256 HMAC of the message and append it to the S block. This can then be validated by the recipient system. Effectively, this validates the origin and authenticity of a message. As such, even if an attacker was in position to introduce a fraudulent payment, they’d first need to compromise both the left and the right LAU signing keys, generate the correct HMAC, and append it to the message in order to have it accepted / processed successfully.
But LAU aside, I now just needed to figure out which queue to target! There were a lot of queues to work with as each system essentially has multiple “input” and “output” queues. With that in mind, it was important to note that: an incoming message would require being in the format expected by the target system (from a specific upstream system) and an outgoing message would need to be in the format “produced” by one target system and “expected / ingested / processed” by its respective downstream system. So to figure this out, I worked backwards from the Gateway.
This was the least feasible attack vector!
Next!
So what if I wanted to drop a message on the “outbound” channel of SAA?
LAU and the SWIFT adapter aside, remember those session and sequence numbers? Well, messages which leave SAA are in the near-final stages of their outbound life-cycle, and as far as I understood would need to have valid session and sequence values. Given I didn’t know how to generate these values without gaining access to SAA or how they worked in general (and lets not forget the LAU signing) this didn’t currently seem feasible.
Next!
This solution didn’t actually transport messages back and forth; it just reads messages off the queues and performed checks on their details. Not much I could wanted to leverage here.
To target MID, I could try and inject a message onto SAA’s “input” queue, or the “output” queue of MID. This would only need to match the format of messages produced by the Middleware solution (MID). Following this, in theory, the [mistial] message session and sequence number would be added by SAA, along with the UETR. This was promising!
However, MID was a SWIFT “message partner”, which are typically solutions developed using the Alliance Access Development Kit that allows vendors to develop SWIFTNet compatible software, and consequentially, implement LAU. So again, in-order to forge a message here, I’d need to compromise the left and right LAU signing keys used between SAA and MID, manually HMAC the message (correctly!), and then place it on the correct queue… This also no longer looked promising…
OK, how about the input of the next system down - the “Payment System”?
As described previously, the inbound data was a custom “application specific” payment instruction from the institutions back office systems, and not a SWIFT MT message. This would be an entirely new core concept I’d need to reverse - not ideal for this project.
But how about the output queue?
Although SYS received custom format data, I found that it output what seemed to be an initial SWIFT MT messages. This was perfect! Additionally, SYS did not have LAU between itself and MID because (unlike MID) SYS was not a SWIFT message partner, and was just one of many-many systems within the institution that formed their overall payment landscape.
Additionally, because SYS was esentially just one small piece of a much larger back office architecture, it was not part of the SWIFT Secure Zone (after all you cant have your entire estate in the Secure Zone - that defeats the purpose) and as such, made use of the Queue Manager within a more accessible section of the general corporate environment (QM1).
With this in mind, and having - in theory - compromised the MQ admin, I could leverage their access to access on the corporate network to authenticate to QM1. I could - in theory - then write a fraudulent payment message to the SYS “output” queue, which we will call “SYS_PAY_OUT_Q” from here on.
OK! It seems like I finally had an idea of what to do! But before I could put it into practice, I of course needed to create a diagram of the attack:
I think it’s important to take a minute to refer back to the concept of “trust” which is what lead to this attack diagram. My theory behind why this may work is because the MID application, implicitly trusts whatever it receives from its respective upstream systems. This is intentional, as by design the security model of the payment landscape ensures that: at any point a message can be created, a 4 (or 6) eye check is performed. If there was a system whose purpose it was to ensure the validity of a payment message at any point upstream, the downstream systems should have no real issue processing that message (with some exceptions). After all, It would be next to-impossible to maintain a high-throughput payment system without this design.
And with that said, the plan was now clear:
It was finally time to try to demonstrate a Proof-of-Concept attack!
So at this point I believe I had everything I needed in order to execute the attack:
I needed to begin by creating a valid payment message using valid details from the target institution. So before moving on I was provided with the following (Note: as with many things in this post, these details have been faked):
Some of you may have notice that the sending and receiving BIC’s are the same. This was because, for the sake of the research, I wanted to send the message back to the target institution via SWIFTNet so that I could analyse its full end-to-end message history. Furthermore, you may have noticed we are using “test & training” BIC code (where the 8th character is a 0) - this was to make sure, you know, that I kept my job.
But yes, with access to these “valid” account details and the knowledge gained during the research so far, I could now forge a complete Input MT103 messages:
{1:F01EBNKGB20AXXX0000000000} {2:I103EBNKGB20XXXXN} {3:{119:STP}{121:eb02c40e-e060-400a-ac74-47f076dd26e3}} {4: :20:TRANSACTIONREF103 :23B:CRED :32A:200103GBP500000 :33B:GBP500000 :50K:/GB21EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59:/GB22EBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, MANCHESTER, GB :70:FORGED-PAYMENT-TEST :71A:SHA -}{5:{TNG:}}{S:{SPD:}}
Note: Field 33B is actually an optional field, however, the MT standard stated that “If the country codes of both the Sender’s and the Receiver’s BIC belong to the country code list, then field 33B is mandatory”. As such, if 33B was not present in the message, it would fail network validation rules and SWIFTNet would return a NAK with the error code: D49.
Optional / Mandatory fields aside, it was not quite that simple! There were a few minor changes I needed to make based on the specific point in the message’s its life-cycle I was planning to introduce it!
As I list these changes, remember that the objective is to introduce the message to the output queue of SYS (Which exists before MID, SAA and SAG)
And the final message was as follows:
{1:F01EBNKGB20AXXX8888999999}{2:I103EBNKGB20XXXXN}{3:{119:STP}}{4: :20:TRANSACTIONRF103 :23B:CRED :32A:200103GBP500000,00 :33B:GBP500000,00 :50K:/GB21EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59:/GB22EBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, MANCHESTER, GB :70:FORGED-PAYMENT-TEST :71A:SHA -}
Note that the location in which I introduce the message has resolved all of the “issues / blockers” I’d tracked whilst researching the message structure! It would seem the further upstream you go, the easier the attack becomes - given MQ is still used as a transport medium.
Now I had my raw MT103 message, I just need to save it to a file (“Message.txt” - sure why not) and place onto the “SYS_PAY_OUT_Q” queue using one of the admin’s tools:
And it was off!
Loggin in to Alliance Access and opening the message history tab, we sat awaiting for an update. Waiting, waiting, waiting… waiting… and…
ACK! It worked!
That’s a joke; did we hell receive an ACK!
See, this last section is written slightly more “linear” than what actually happened. Remember those “tweaks” used to fix the message in the previous section? I hadn’t quite figured that out yet…
So roughly seven NACKs later - each time troubleshooting and then fixing a different issues - we did indeed, see an ACK! The message was successfully processed by all systems, passed target system validation rules, passed sanctions and AML screening, passed SWIFTNet validation rules, and SWIFT’s regional processor had received the message and sent an “Acknowledgement of receipt” response to the sending institution!
For the sake of completeness, I’ve included the ACK below:
{1:F21EBNKGB20AXXX1947392344}{4:{177:2001031102}{451:0}}
And of course a breakdown of what it all means:
Name | Value | Context |
---|---|---|
Basic Header Flag | 1 | Block 1 |
Application Type | F | F = FIN Application |
Message Type | 21 | 21 = ACK |
Institution Code | EBNKGB20AXXX | EBNKGB20 (BIC) A (Logical Terminal) XXX (Branch) |
Sequence and Session No. | 1947392344 | 1947 (Sequence No.) 392344 (Session No.) |
Date Tag | 177 | 200103 (Date) 1102 (Time) |
Accept / Reject Tag | 451 | 0 = Accepted by SWIFTNet |
Excellent! WooHoo! It worked! … That took a lot of time and effort!
But the ACK wasn’t enough, I wanted to make sure I understood what had happened to the message throughout its life-cycle. From the message I placed on the initial queue, to being processed by SWIFTNet.
Thankfully, as we sent the message back to the target institution we could see its entire message history. I already knew what the raw message placed on the queue looked like, so I wanted to focus on what became of the message once it had been processed by SAA:
{1:F01EBNKGB20AXXX8888999999}{2:I103EBNKGB20XXXXN}{3:{119:STP}{121:b42857ce-3931-49bf-ba34-16dd7a0c929f}}{4: :20:TRANSACTIONRF103 :23B:CRED :32A:200103GBP500000,00 :33B:GBP500000,00 :50K:/GB21EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59:/GB22EBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, MANCHESTER, GB :70:FORGED-PAYMENT-TEST :71A:SHA -} {5:{TNG:}}{S:{SPD:}}
OK, so that was SAA. Now let’s see how it looked it once it passed through the Gateway and regional processor:
{1:F01EBNKGB20AXXX1947392344}{2:O1031102200103EBNKGB20AXXX19473923442001031104N}{3:{119:STP}{121:b42857ce-3931-49bf-ba34-16dd7a0c929f}}{4: :20:TRANSACTIONRF103 :23B:CRED :32A:200103GBP500000 :33B:GBP500000 :50K:/GB21EBNK88227712345678 JOHN DOE JOHN’S BUSINESS LTD 21 JOHN STREET, LONDON, GB :59:/GB22EBNK88332287654321 ALICE SMITH ALICE’S COMPANY 10 ALICE STREET, MANCHESTER, GB :70:FORGED-PAYMENT-TEST :71A:SHA -} {5:{MAC:13579112}{CHK:D8E8FCA2DC0F}{TNG:}}{S:{SPD:}{SAC:}{COP:P}}
OK, we can see a few changes now.
I also took a look at the entire outbound message history, just to see all the “Success” and “No violation” statements to make it feel even more awesome!
So that’s that really…
With a bit of research and support I was able to demonstrate a PoC for introducing a fraudulent payment message to move funds from one account to another, by manually forging a raw SWIFT MT103 single customer credit transfer message, and leveraging various system trust relationships to do a lot of the hard work for me!
As mentioned briefly in the introduction, this is not something I have really seen or heard of happening in practice or in the “wild”. Perhaps because it clearly takes a lot of work… and there is a huge margin for error. However, if an adversary has spent enough time inside your network and has had access to the right documentation and resources, this may be a viable attack vector. It definitely has its benefits:
All an attacker may need to do is compromise one specific user on the corporate network: a Message Queue administrator.
The industry is spending a lot of time and effort focused on securing their payment systems, applications, processes, and users to keep - among other things - payment operators safe, Messaging Interfaces locked down, and SWIFT systems isolated. But the reality is,; the most valuable and most powerful individual in the entire model, might just be a single administrator!
As always, a security model is only as strong as its weakest link. If you’re not applying the same level of security to your wider institution, there may very well be many weak links within the wider network which chain together and lead to the comrpomise of systems which feed into your various payment environment.
I think the main thing to remember when reflecting on this research is that it did not abuse any vulnerabilities within the target institution’s systems, or even vulnerabilities or weaknesses within the design of their architecture. It simply leverages the legitimate user access of the Message Queue administrators and the trust relationships that exist by design within these types of large-scale payment processing systems.
So the harsh reality is, there is no particular list of recommendations for preventing this type of attack in itself. However, the main point to drive home is that you must ensure the security of your users - and overall organisation - is of a high enough standard to protect your highest privileged users from being compromised. Things such as:
However, in the context of Message Queues, there is one particular control which I think is extremely valuable: The implementation of channel specific message signing! This, as demonstrated by SWIFT’s LAU control, is a good way in which to ensure the authenticity of a message.
As discussed, LAU is - as far as I know at the time of writing - a SWIFT product / message partner specific control. However it’s concept is universal and could be implemented in many forms, two of which are:
This is a complex requirement as it requires considerable effort on the client’s behalf to implement either approach. However, SWIFT provides guidance within their Alliance Access Developers guide on how to implement LAU in Java, Objective C, Scala and Swift;
An example of how this may work in the more flexible middleware solution proposed is where the original service is no longer exposed to the network, and is altered to only communicate directly with the custom “LAU-eqsue” service on its local host. This service would then sign and route the message to its respective queue.
When received, the core of the recipient payment service would seek to retrieve its messages from the queues via the “LAU-esque” signing middleware, which would retrieve the message and subsequently verify its origin and authenticity by re-calculating the signature using their shared (secret) keys. Key-pairs could further be unique per message flow. This design could allow for the signing to be used as a way to validate the origin of a message even if it had passed through multiple [local] intermediary systems.
As a final bit of creative effort, I made yet another diagram to represent what this could perhaps look like - if life was as easy as a diagram:
If you made it this far thanks for reading all… ~6k words!? I hope you found some of them interesting and maybe learned a thing or two!
I’d like express our gratitude to the institution who facilitated this research, as well as specifically to the various SMEs within that institution who gave their valuable time to support it throughout.
Fineksus - SWIFT Standard Changes 2019
https://fineksus.com/swift-mt-standard-changes-2019/
Paiementor - SWIFT MT Message Structure Blocks 1 to 5
https://www.paiementor.com/swift-mt-message-structure-blocks-1-to-5/
SEPA for corporates - The Difference between a SWIFT ACK and SWIFT NACK
https://www.sepaforcorporates.com/swift-for-corporates/quick-guide-swift-mt101-format/
SEPA for corporates - Explained: SWIFT gpi UETR – Unique End-to-End Transaction Reference
M DIBA - LAU for SWIFT Message Partners
https://www.linkedin.com/pulse/lau-swift-message-partners-mohammad-diba-1/
Prowide - About SWIFT
https://www.prowidesoftware.com/about-SWIFT.jsp
Microsoft - SWIFT Schemas
https://docs.microsoft.com/en-us/biztalk/adapters-and-accelerators/accelerator-swift/swift-schemas
SWIFT FIN Guru - SWIFT message block structure
http://www.swiftfinguru.com/2017/02/swift-message-block-structure.html