A QR code that sends you to a different destination – lenticular and adversarial
mstdn.social
mstdn.social
setup:
- make the QR code as a half/half code
- have a system to decide preference of target based on external input (e.g. camera based characteristic evaluation)
- make slight dynamic alterations to the colours of the code to bias the probability of it being picked up as the desired target. Desirable black/white can be made blacker/whiter, less desirable less so.
Where to use it maliciously:
- anywhere where people provide feedback - present alternate feedback forms to different demographics to engender the most positive (or negative) results.
- pretend to offer some form of probabilistic chance to win a prize, but bias winning to some identifiable characteristic. e.g. race, age, "beauty"
- target a specific person - have them join a different WiFi network, alter a payment page, etc.
In a static setting its less effective. I can't immediately think of a static attack that benefits from siphoning some reduced fraction of users.
I'm doubtful most people would notice a QR code dynamically changing, particularly in most public lighting.
1. the ad looks correct, and
2. the URL is the one they expect
So there may be a use case for a QR code that looks almost identical but goes somewhere else, allowing them to swap it out while someone is looking at it without them realizing.
A niche use case, to be sure, but being able to exploit a niche vulnerability is a skill.
Even if the adversary controls the server side as well, you need to tell the server the information needed to make a decision If you control everything that is easy enough too, but perhaps you want to keep the decision-making process local – for plausible deniability server-side, just to reduce server & bandwidth load, or because you are sending people to completely different destinations not just altering link parameters.
Replacing the QR code more statically sends everyone to the new address, not just the target(s), altering the QR code by a bit or two (and the relevant error correction bits too) in response to a pile of information available at the QR reading site (feeds from cameras, and such) would be how the server knows to react differently without having access to that collection of information itself. You might want to use some clever analysis to minimise the visual effect if you are altering the QR code while it is already displayed – the two examples here look very different, but the change could be much more subtle. If sending to very different destinations, so the codes for the URLs will look very different, then adding a link anonymiser between would keep the change minimal.
> Also, neither use case makes use of the hack described in the OP.
True.
They don't need anything as sophisticated as this dual QR code, though - the attackers just go to a car park with a "pay by phone" sign, slap their own QR code over the "scan to pay" code, and wait for the credit card details to start coming in.
That said, it'd probably be easier to implement that in software, where the phishing site redirects a certain portion of visits back to the legitimate one.
P.S.: There's also the physical stealth aspect, but I think a lenticular design would probably be easier for a human worker to notice, compared to a regular flat sticker which just happens to encode a typo-squatting URL.
The only reason I caught it was that I had gotten a legitimate ticket a month prior for parking too close to the fire hydrant and had marked the curb with chalk at the correct distance. So I tried to dispute the fine and discover that the ticket didn’t actually exist. And the city had no interest in the fake ticket whatsoever. They were just like “yeah, it happens all the time”.
Another idea would be a poster that's not quite flat: Each pixel that is different is slightly raised (pyramid shaped) and the sides of the pyramid is colored differently. So the QR code scans differently from different sides.
One of the dominant banks here in Tbilisi allows sharing of IBAN number as QR codes. In theory, the trick could be used to steal money, but in practice, there are many safeguards such as the banking app displaying the name of the beneficiary before completing the process.
and the scammer make an account with a name that look similar at a glance (e.g., swap the l with a 1, or something of the sort).
I doubt many people would notice if your average QR code was to change significantly. Most machine readable formats are just indistinguishable white noise to most people.
FWIW the place my brain went was some kind of magic trick, since having control of this could function kind of like a forcing a specific card or something
Do a facial recognition lookup against the RealID database (I'm sure someone must be selling a leaked or hacked copy by now) and make the prize depend on the first letter of the person's last name.
Of course the API confusion here becomes non-trivial, which hampers securing against it. And with existing libraries being widespread, its going to linger as an attack for a long time.
The attack is that a user looking at a QR code cannot determine that they are being served a different code to another person.
In a public setting a user would have no idea they are even capable of being targetted and treated differently.
This particular vector has the strength of being able to manipulate things other than URLs; QR codes support WiFi credentials, contact details, call, text (with content), email (with content), calendar events. Additionally, many app specific URIs never leave the device.
It has the significant downside of being plainly visible as a possibility to those in the know.
However, I suspect it may still be desirable to do this on the device rather than the server.
The other properties it varies are the obvious lack of reliance on a server. The improved "transparency" of showing the target URL for trust. The removal of the need for an internet connection.
But for an implementer I'd imagine the most beneficial upside is not needing to manage a timing-attack, and gaining additional targetting accuracy in the common case of a picture. There may also be organisational complexity reduction in pushing all the decisioning to the display/camera system.
Its fun to think about, with the significant weakness in both its visibility and its probabilistic nature I wouldn't expect widespread use.
The ambiguous QR code in this application works by combining two different QR codes into a single image using a diagonal split pattern. When two QR codes have different patterns at the same position, the cell is split diagonally - one half represents the first QR code and the other half represents the second QR code. When both QR codes have the same pattern at a position (both black or both white), the cell is filled with a solid color. Due to the high error correction capability of QR codes (using error correction level 'H'), QR code scanners can still read either URL depending on the scanning angle, though as noted in the UI, it tends to favor the second URL more frequently.
And so I do something silly like airdropping a screenshot of it to my laptop so I can scan it with my phone camera, or I get someone else (friends, family) to use their phone to scan the code from my phone screen with the camera app on their phone.
And all this time I was annoyed why I couldn’t just get the link directly from the image on my phone without involving another device, and without having to install yet another third-party app.
And today I learned that all I had to do was long press the QR code in the screenshot in my camera roll and it would actually parse it and make it so I could visit the link!
I think I must have tried long pressing QR code in an image in the camera roll years ago because it always seemed like something that would make sense to support via long press. Maybe they introduced this feature after I had tried to long press a QR code in an image in the past. Or maybe it was always possible and I didn’t actually ever try to long press it. Or maybe I long pressed the wrong part of the image that first one or two times I ever tried to do it in the past. Either way, very happy to have learned that this is actually possible.
I bet it'd be possible to create a standard QR Code with a deliberate error that does the same thing. You'd just have to figure out how they're correcting the error differently.
Seems like you discovered a bug-bounty bug just waiting for someone to claim.
I've seen apps that read QR codes that don't, however. Usually they're single-purpose and just don't recognize unexpected data (scan the magic code to get a character in a game, etc) but if the expected data is formatted as a URL, maybe it tries to fetch resources there (character information and image).
I can also see someone making a browser extension that allows scanning a QR code to immediately open it so you don't have to leave your current app.
It is reminds me of code like
if someCondition(getFoo())
then doSomethingWith(getFoo())
or even just doSomethingWith(getFoo())
doAnotherThingWith(getFoo())
which is always a code smell, as opposed to foo := getFoo()
if someCondition(foo)
then doSomethingWith(foo)
and foo := getFoo()
doSomethingWith(foo)
doAnotherThingWith(foo)Install the qrcode python package and run this code:
import qrcode
bar = qrcode.QRCode(border=0)
bar.add_data('bar')
bar.make()
bar_mat = bar.get_matrix()
foo = qrcode.QRCode(border=0)
foo.add_data('foo')
foo.make()
foo_mat = foo.get_matrix()
for l, r in zip(foo_mat, bar_mat):
line = ''
for lc, rc in zip(l, r):
line = line + (lc and '\u2588' or ' ')
line = line + (rc and '\u2588' or ' ')
print(line)
print(line)
What you get is indeed a dualing qrcode (which I can't quite paste here because no unicode on HN, and using "8" or "0" isn't enough to get my phone to recognize it).It's an inherently unreadable URL, you really have no idea where you will be sent, or how many times you will be redirected.
I don't use them...
[1] https://play.google.com/store/apps/details?id=com.scandit.de...
Did he subdivide the qr code cell into four sub pixels and make the left two one color and the right two another? That’s what I would have guessed for the “lenticular” effect at different angles. But the subpixels I see are more checkerboard ish
The image recognition that outputs the bit pattern of the QR code is inherently heuristic in nature, and only the checksum verification is what decides if the recognition worked successfully. The trick in TFA is to produce an image where two different results of the heuristics can both pass the checksum verification, so which one you get depends on circumstantial factors.
Looking up user IP (and thus country of origin), user agent, etc is enough to determine what content needs to be served.