[HN Gopher] A QR code that sends you to a different destination ...
___________________________________________________________________
A QR code that sends you to a different destination - lenticular
and adversarial
Author : zdw
Score : 517 points
Date : 2025-01-23 23:55 UTC (23 hours ago)
(HTM) web link (mstdn.social)
(TXT) w3m dump (mstdn.social)
| ShakataGaNai wrote:
| 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.
| re wrote:
| (Scroll up from the starting position to see the lenticular one)
| dang wrote:
| Should we change the top link to
| https://mstdn.social/@isziaui/113874436953157913?
| winternewt wrote:
| Yes please
| dang wrote:
| 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?
| 65 wrote:
| This would be cool to use in a scavenger hunt.
| TOMDM wrote:
| Maybe make it so that you need the results of both (all?) QR
| codes to get the final code/link/key.
| rhet0rica wrote:
| I summon QR Exodia!
| buildbot wrote:
| Interestingly, MacOS only sees the mastodon link when right
| clicking on the QR code.
| etrautmann wrote:
| That makes sense, I would imagine it would require some
| variability via a camera with different angles/lighting
| conditions in order to get both links at different times.
| tzs wrote:
| That's also what Mathematica's BarcodeRecognize[] sees.
| smashah wrote:
| Can MacOS natively scan QR codes?
| yoz-y wrote:
| Funnily though, iOS recognizes the GitHub part when long
| pressing on it.
| Normal_gaussian wrote:
| 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.
| pockmarked19 wrote:
| You don't need any of this if you control the app doing the
| scanning (or the website/app handling the result).
| Normal_gaussian wrote:
| You wouldn't control the app doing the scanning.
|
| 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.
| t_mann wrote:
| You don't even need to change the QR code to treat users
| differently, that's the point. You just send users to some
| fixed URL, which is by far the most common use for QR codes
| (hence completely unsuspicious), and you decide who gets
| served what there, based on whatever data you gathered
| about them. And the users that would care are already
| acutely aware that that's how a majority of the web works
| nowadays, QR codes or not.
| Normal_gaussian wrote:
| The value of an attack vector is not negated by there
| being other ways to achieve it.
|
| 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.
| csomar wrote:
| Static Qr-Code but serve different content? If you are
| targeting by the camera, you can try to link the person to
| the time the QrCode was scanned and have each QrCode print
| identified.
| alan wrote:
| Static a/b testing?
| Normal_gaussian wrote:
| 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.
| tbrownaw wrote:
| > _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.
| t_mann wrote:
| 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 wrote:
| Some sort of MITM attack by someone who owns the display but
| not the server, maybe. Like a malicious ad company.
| t_mann wrote:
| 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).
| post-it wrote:
| But hypothetically, the owner of the ad might pop in and
| make sure
|
| 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.
| michaelmior wrote:
| But if you own the display, you can send the user to
| whatever server you want.
| dspillett wrote:
| _> 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.
| eieio wrote:
| 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
| nroets wrote:
| 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 wrote:
| > 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 wrote:
| 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.
| echoangle wrote:
| But then it's too late, you have to find out before send
| the money.
| fxtentacle wrote:
| Most scammers will try to avoid situations where you can
| punch them in the face. => If scams usually get notified
| before the perpetrator can run away, that will discourage
| scamming. => In practice, it might be "good enough" if
| you know immediately after sending money.
| heeen2 wrote:
| you could use glossy/matte or metallic finish, then the
| reflected light could appear brighter than the white parts
| michaelt wrote:
| 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_ wrote:
| 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.
| hnlmorg wrote:
| > 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.
| nixpulvis wrote:
| 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.
| noitpmeder wrote:
| now THIS sounds like an exploit
| codetrotter wrote:
| 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.
| davchana wrote:
| Yes, I usually share it with Google App, and Lens tab.
| kccqzy wrote:
| Long pressing still doesn't work for me. Perhaps because I
| turned off some features related to image intelligence.
| poglet wrote:
| On iOS I believe the option is in Settings > General >
| Language & Region > Live Text. This was introduced in iOS
| 15.
| _august wrote:
| 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.
| the_arun wrote:
| QRCode should also show the target url in text, so the user
| knows where it is taking - something like explicit consent.
| jimjimwii wrote:
| Yep, like how browsers show users urls in their location
| bars.
| russellbeattie wrote:
| 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.
| layer8 wrote:
| 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 wrote:
| Usually it's called a TOCTOU bug/vuln
|
| https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
| layer8 wrote:
| I often see it in multiple-time-of-use scenarios as well
| that need to be consistent, i.e. no check vs. use involved.
| daft_pink wrote:
| I don't really understand the value of this compared to just
| putting another QR code right over the pre-existing code? Why
| bother getting a fraction of users, when you can get all of them.
| notRobot wrote:
| The value is that it's cool.
| hammock wrote:
| Can someone explain how it works?
| jkingsman wrote:
| A QR code is just a series of square pixels, and whether the
| pixel is black or white contributes to the data. This has two
| QR codes, essentially overlayed one on the other. Some pixels
| are half black/half white, so depending on the angle you hold
| your phone at, the software that decodes QR codes will detect a
| different color as being centered in the pixel it's examining.
| So, based on the angle you hold your phone (some people had
| better success with rotation), you get one QR code or the
| other.
| hammock wrote:
| What is half black/white though? I don't see any gray. Only
| black and white
|
| 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
| layer8 wrote:
| QR code decoders usually work by sampling the values from a
| dot grid overlayed on the camera image (or something along
| those lines). If a QR square isn't actually uniform, the
| sampled value for that square will change if you move the
| camera a bit. When you hold a QR code in front of the
| camera, the picture is continuously sampled, yielding
| different bit values all the time because you don't hold
| the camera a 100% still (in addition to sensor noise and
| the like). The QR decoder is however satisfied as soon as
| the result passes the QR checksum verification.
|
| 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.
| hammock wrote:
| More non-deterministic than I would have thought. Your
| explanation makes sense tho thanks
| dwheeler wrote:
| Shouldn't one of the URLs implement a Rick Roll :-) ?
| jdoe1337halo wrote:
| 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.
|
| https://dualqrcode.com/
| HenryBemis wrote:
| 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 wrote:
| Doesn't scan from a screenshot in
| https://f-droid.org/packages/com.atharok.barcodescanner/
| either
| jdoe1337halo wrote:
| 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!
| sschueller wrote:
| I had some trouble with the QR codes readers on my android but I
| found this demo app[1] (they make a code reader SDK) that work
| really well (you can also enable continues scanning so you can
| see the result change as you change the angle).
|
| [1]
| https://play.google.com/store/apps/details?id=com.scandit.de...
| mkl wrote:
| 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.
| Joker_vD wrote:
| Wait, don't QR codes have checksums in them? Or do they have some
| sort of error correction built-in? That's the only way I can
| imagine it can possibly work with e.g. 85% of one QR-code blended
| with 15% of another one.
| blueflow wrote:
| Yes, but the checksum is in the same bitstream, so it is
| swapped together with the data.
| soheil wrote:
| With the same link you can serve different content, QR codes
| typically resolve to links.
|
| Looking up user IP (and thus country of origin), user agent, etc
| is enough to determine what content needs to be served.
| andrewla wrote:
| 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).
| danvoell wrote:
| 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.
| trebligdivad wrote:
| 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...
| johnea wrote:
| 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...
___________________________________________________________________
(page generated 2025-01-24 23:01 UTC)