A QR code that sends you to a different destination – lenticular and adversarial
81 comments
·January 23, 2025Normal_gaussian
t_mann
All of those could be done much more stealthily server-side, though, I don't get what the QR code modification would add here? Also, neither use case makes use of the hack described in the OP. Where I could see an attack based on that hack would be where an attacker plasters their code over a legitimate one. It would be kind of random which code gets read, so they could send some %-age of users to the original destination, hence possibly delaying detection. But it doesn't seem a given that this would compensate for the reduced traffic to their link.
post-it
Some sort of MITM attack by someone who owns the display but not the server, maybe. Like a malicious ad company.
t_mann
Ok, but then I'd still prefer a method that sends users to a unique URL. OP's method may help with obfuscating the changing of the code, but I'm sure there are ways to better achieve that without having to introduce this quasi-randomness. The simplest would probably be to just to regularly hide/show the code (which would happen anyway on a typical digital ad display that cycles through a number of ads).
michaelmior
But if you own the display, you can send the user to whatever server you want.
dspillett
> All of those could be done much more stealthily server-side, though, I don't get what the QR code modification would add here?
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.
michaelt
There are actually attacks based on changing public QR codes already!
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.
Terr_
Hmmm, there might be some criminal utility in capturing <100% of visitors, so that the true owner doesn't easily realize that activity for that location has ceased. In other words, giving up a certain number of victims in order to keep the attack going for longer.
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.
alphan0n
I almost got taken in by a fake parking ticket scam, perfect ticket/envelope, the url printed on the ticket led to a 404 on the legitimate city website, the QR code led to a very convincing website/url, especially on mobile.
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”.
nroets
I guess similarly you can carefully craft a poster with a QR code and put it on a wall in a room with two LED bulbs. The bulbs will have different color temperature and the decoding will be dependent on which bulb is lit.
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.
chii
> banking app displaying the name of the beneficiary
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).
fxtentacle
Most likely, they have <1s bank transfers, too, like pretty much all of Asia. That way, if you pay someone with a QR code, they'll immediately get the notification that they received your payment. And if they don't, you immediately know that something went wrong.
heeen2
you could use glossy/matte or metallic finish, then the reflected light could appear brighter than the white parts
hnlmorg
> alters the QR code based on information it has about the current user, without appearing to change significantly.
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.
eieio
I appreciate you laying out malicious use-cases instead of just having the setup section; I would have struggled to think of those!
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
alan
Static a/b testing?
tbrownaw
> pretend to offer some form of probabilistic chance to win a prize, but bias winning to some identifiable characteristic. e.g. race, age, "beauty"
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.
Normal_gaussian
Identifying this should be relatively easy in the core libraries; finding alternate valid QR codes using "less optimal" grids.
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.
jdoe1337halo
Hey guys I made a website so yall can try this out yourself! I don't have the exact methodology that Christian uses, so here is how I did it:
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.
HenryBemis
It didn't work for me. I generated two links (BBC, CNN) and tried on my Android, with the app "QR & Barcode Scanner" v2.2.47. I also tried with the stock camera of an iPhone 13. In both they couldn't 'read' them.
Aachen
Doesn't scan from a screenshot in https://f-droid.org/packages/com.atharok.barcodescanner/ either
jdoe1337halo
Strange, I am using an iPhone 12 pro max and it works for me. I am going to keep playing around with different pixel painting techniques and see if I can get a more reliable, 50/50 split on both links. Thanks for trying!
nixpulvis
The most interesting thing about this to me is that on iOS a long press on the image claimed it's going to github.com, while the preview itself was for mastadon. This indicates that it's parsing the QR code twice and getting different results? I could see this being used to mislead some people, though I'm not sire how many people look at the long press dropdown URL.
codetrotter
Tangential but once in a blue moon I come by some situation where I’m on my phone and I’m looking at something that has a QR code showing on the screen of the phone itself.
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.
kccqzy
Long pressing still doesn't work for me. Perhaps because I turned off some features related to image intelligence.
poglet
On iOS I believe the option is in Settings > General > Language & Region > Live Text. This was introduced in iOS 15.
davchana
Yes, I usually share it with Google App, and Lens tab.
russellbeattie
I can totally see two parts of the OS both using their own QR parsing code - SmartText using one, and the imaging system another. Apparently each one has their own slightly different error correction implementation.
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.
the_arun
QRCode should also show the target url in text, so the user knows where it is taking - something like explicit consent.
efreak
Google lens and other apps do this.
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.
jimjimwii
Yep, like how browsers show users urls in their location bars.
_august
When I long-press on iOS, it shows me the mastadon link as the main "Open" link, as well as "Open in Github" (app link) in the context menu.
layer8
This is likely the typical case of related code calling the same function or getter method twice in a context where it is imperative for both calls to return the same result.
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)
pas
Usually it's called a TOCTOU bug/vuln
layer8
I often see it in multiple-time-of-use scenarios as well that need to be consistent, i.e. no check vs. use involved.
efreak
Shouldn't the compiler optimize this into a single call if they're called so close together?
re
(Scroll up from the starting position to see the lenticular one)
dang
Should we change the top link to https://mstdn.social/@isziaui/113874436953157913?
winternewt
Yes please
dang
Weirdly, it already is! I don't understand what's going on there. Are we getting sent to a different spot on the page depending on what angle we click from?
ShakataGaNai
That is gnarly. My iPhone tended to lock into one or the other, rotating the phone seemed to help it go one way or the other. But a couple times it did flash back and forth between the Mastadon and GitHub links.
andrewla
If you want to try it out, here's some quick and dirty code.
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).dwheeler
Shouldn't one of the URLs implement a Rick Roll :-) ?
65
This would be cool to use in a scavenger hunt.
johnea
QR codes, like shortened URLs, are just begging to exploit.
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...
danvoell
This is such a cool concept! If you're focusing on the end goal, another approach could be using a "switch" at the URL destination—something that redirects users to a different page based on a randomizer, user data, or other criteria. For anyone exploring this kind of functionality and interested in testing physical stickers for their projects, I work with a lot of SaaS companies on variable labels and would be happy to share insights, print some samples or collaborate.
mkl
My phone (Samsung Galaxy Note 20) seems to reject almost all of these, and not recognise them as QR codes. I got one to work for one URL by moving way away from the screen.
trebligdivad
Someone needs to define a human-readable attachment to QR that can be checked by the QR reader; e.g. the root of the URL printed above the QR code at a specific position offset or with a specific mark; so then the QR decoder could OCR it and verify it matched the URL encoded. Only the root of the URL would be included so the QR could include a specific to that location complex path. Now, we just need to backronym SPQR to fit...
I guess an interesting attack would be a screen in a public setting that alters the QR code based on information it has about the current user, without appearing to change significantly.
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.