Decoding the obfuscated bash script on a Uniqlo t-shirt

(tris.sherliker.net)

514 points | by speerer 4 hours ago

39 comments

  • estebarb 58 minutes ago
    "Uniqlo x Akamai sells another design of shirt in the same range which is plainly incomplete"

    Imagine having to return a t-shirt because that malfunction!

    — I don't understand why are you returning this, was the size wrong or you didn't like it?

    — No, there is a syntax error at line 37 that makes it impossible to run, and I'm concerned people on the street may think I promote unsafe bash scripting.

    • cromka 49 minutes ago
      Oh the Karens these days!
  • wbh1 2 hours ago
    I love this shirt! Here's a nice video from the actual designer about the process of making this shirt (including intentionally making it hard to OCR): https://youtu.be/jocGLiecpjU?t=526
    • speerer 1 hour ago
      Author here. Thank you so much for the link which I hadn't seen! I'm very happy to see this and I'm gratified that it was deliberately difficult to OCR, not just me.
  • forinti 10 minutes ago
    This reminds me of a T-shirt I once saw that read:

              perl -e '
         "$a="etbjxntqrdke";
      $a=~s/(.)/chr(ord($1)+1)/eg;
            print "$a\n;"'
    
    It's cursing. Don't run it if it might offend you.

    Upon seeing this, I decided to golf and came up with a shorter version:

      perl -e "print chr 1+ ord for split //,'etbjxntqrdke'"
  • Tiberium 3 hours ago
    OCRing this is a nightmare and is a good benchmark to any self-proclaimed good OCR/vision model.

    I think though it could likely be easily OCR'd if you give the image to any decent agentic harness with a good vision model, e.g. newest Claude/GPT ones, and tell them to split the image per lines, and then just OCR each line individually.

    I wonder if the script itself was written by an LLM before obfuscation? There seem to be a lot of comments in it, but in this case it's still ok :)

    • lemagedurage 2 hours ago
      I don't think it was written by an LLM, some things stand out:

      The congratulations text is both in English and Japanese. Contains a single heart emoji.

      There was an intention to have a cyan to orange gradient, but the range starts in an ANSI block, ends halfway through the 256 color block and 256 terminal colors are not arranged like a gradient at all.

      There's no sleep at the end of the loop where I feel like an LLM would add that defensively.

      • n2j3 2 hours ago
        Human here. I added a sleep 0.5 at the end, it's too fast to read otherwise. Makes for a nice terminal screensaver!
        • INTPenis 1 hour ago
          Hi fellow human, I got the same idea. Just a sleep 0.1 before the echo "" makes it readable. Otherwise it scrolls way too fast.
      • make3 2 hours ago
        "the code is not quite detail oriented enough to be AI", times are changing
        • DaSHacka 1 hour ago
          More like 'not boilerplate-y enough'
        • lemagedurage 1 hour ago
          Ehh, AI makes plenty mistakes but they have a different vibe to it.

          In my mind an AI would do something the most popular way even when that's not appropriate.

          A human might do things in an unpopular way even when that's not appropriate.

    • OtherShrezzing 2 hours ago
      Safari's copy-text-from-image feature manages the entire base64 part of the string, except for the first character (I instead of a T). Weirdly, it gets much worse performance if you try to copy the entire string, including the hashbang part.

      I wonder what it's doing under the hood to get such good performance?

      • khurs 2 hours ago
        Didn't know Safari had this.

        Looked it up, you put mouse over text, then just select and copy it - very cool!

        https://support.apple.com/en-gb/guide/safari/ibrw20183ad7/ma...

        • al_borland 1 minute ago
          It’s actually a system feature, not strictly a Safari feature. It also works in Photos, Preview, etc.

          On meetings I will often take a screenshot of the URL someone is presenting. I’m then able to just open the image and click the URL in the image.

        • iamflimflam1 1 hour ago
          There’s a whole bunch of hidden features that no one seems to be aware of.

          Preview has pretty good background removal.

          Notes will transcribe audio from audio files.

        • agys 2 hours ago
          Preview has it too… And it works extremely well.
    • underyx 48 minutes ago
      I gave the photo to Opus 4.8 and it reconstructed the same script in one shot. Although it did say it had to correct some parts of it based on context where it suspected OCR mistakes.
    • shakna 1 hour ago
      > I wonder if the script itself was written by an LLM before obfuscation?

      From the prototype shown here [0], and the way they talk about their process, I sincerely doubt it. Especially as they mention trying to make it hard for AI to handle the output.

      [0] https://youtu.be/jocGLiecpjU?t=567

      • cb321 1 hour ago
        I watched that whole video link - thank you for that - and he doesn't really say. In fact, he spends much more time on the beige color harkening to computer case plastics of the 80s & 90s.

        What made me wonder, personally, was that the output seems identical if you use "♥PEACE♥FOR♥ALL" instead of the version with internal repeats. IF there is any point to that "manual expansion of the cycles", IMO that deserves a comment much more so than "# Calculate length of text; text_length=".

        Also, that `echo -n ...` followed by `echo ""` instead of just plain `echo` in the first place seems like the kind of copy-pasta code LLMs generate. Then again, regular devs also write pretty bad copy-pasta code.

        There is also this the weirdly "broken down" calculation with 3 `bc` invocations not 1 as if it was translated from a language with more arithmetic/special function power than bash.

        There is also the color scale stuff done in the loop instead of outside (except the one color=$(..)) which seems very unnatural and also very like machine translation.

        Also, at least for me, on my bash-5.3.15(1), `char="${text:t % text_length:1}"` does not work to slice out the multi-byte UTF8 heart symbols, but it sure does look like the kind of thing an LLM would do translating from a python3 script (such as something like https://news.ycombinator.com/item?id=48830669) into bash.

        Another thing is, as others here have observed, there is nothing "gradual" about the xterm-256 color cube. So, "gradient" is a misnomer and exactly the kind of weird things LLMs do when they cobble text together.

        Finally, all the tput stuff the script does instead of just "print x spaces" really smells like a human description of the side scroll in the video game graphic he shows inspired him somehow LLM-corrupted/complexified into the vertical scroll terminals do, but with a lot of extraneous complexity.

        None of this is conclusive, but the video mentions 2023..2025 as when he did it and given that he was a designer and his concerns more visual than code-oriented, I'd have to say I disagree with your sincere doubt and I do strongly suspect the decoded script was very likely LLM-circa2024-generated, possibly with light post-edits.

        The AI not handling the output relates to the final base64 output on the T-shirt (which other comments in this thread mention manually keying in or TFA discusses in the context of OCR). So, that is just not relevant to the question.

    • netsharc 2 hours ago
      The last time Internet people were obsessed with OCRing some base64 was a few months ago when the DoJ released tons of emails from some guy who died, but they were released as rasterized PDFs.

      Can't remember his name now, there's been so many distractions...

    • da_grift_shift 26 minutes ago
      >I wonder if the script itself was written by an LLM before obfuscation?

      I seem to recall seeing an Akamai-branded base64'd shell script on a white shirt pre-2021(?), so unless they've changed the code since then, I doubt it...

    • IshKebab 3 hours ago
      Definitely LLM. No humans write that many comments.
      • ivolimmen 1 minute ago
        Since LLM's are mimicking our code my guess we do...
      • ChrisMarshallNY 2 hours ago
        Ahem...

        My code usually clocs at 50/50 (or thereabouts)[0]. Has, since my very first real engineering project (in 1987)[1]. I discuss in detail, here[2].

        But one reason that I like LLMs, is that they help me to write even more documentation. I have found that I can instruct an LLM to revise my documentation, and make it even more effective.

        [0] https://github.com/ChrisMarshallNY (My GH profile. Pretty much everything there, is like that -has, since long before LLMs were a broken rubber on the drug store shelf).

        [1] https://littlegreenviper.com/wp-content/uploads/2022/07/TF30... (Downloads a PDF)

        [2] https://littlegreenviper.com/leaving-a-legacy/

      • petu 2 hours ago
        Human could write that many comments to get enough base64 text for a design. Maybe to even get some of the highlighted characters in places they want (roughly equally spaced apart).
      • latexr 2 hours ago
        > No humans write that many comments.

        Especially in a case like this, I would definitely write a lot of comments to aid in understanding, thus increasing trust so people would try it out and tinker with it.

        • boomboomsubban 2 hours ago
          Plus the main point of this code is to have people look at it, the function is secondary to being an easter egg.
      • Tiberium 3 hours ago
        Honestly it's a bit of a shame. I checked and they could've shortened their base64 payload by 304 chars by removing all comments except the top two congratulatory ones, or by 524 if they removed those too.
        • OtherShrezzing 2 hours ago
          Would they still get the highlighted "PEACE FOR ALL" text throughout the shortened string? It looks like the length, and presence of those characters, was an explicit design choice.
        • lemagedurage 2 hours ago
          Maybe they added the comments to get a longer payload for the sake of the shirt's design.

          The comments can be more cute/awe inspiring for people who aren't as familiar with bash but like solving puzzles as well.

        • yborg 2 hours ago
          The HN optimizing T-shirt compiler is the next stage here :D
        • saidnooneever 3 hours ago
          im just sad it didnt render a qr code leading to malware :'). the different ways ppl look at obfuscated codes and scripts hah
  • world2vec 2 hours ago
    Oh wow I saw that tshirt at the store and said to my girlfriend "no way that script is functional, probably just for show". I should have persevered.
    • actionfromafar 2 hours ago
      An easy miss. :-) Most of the time our thoughts are on autopilot, since we are not calm.
  • khernandezrt 6 minutes ago
    Ive been to 3 Uniqlos in my are and i havent been blessed with a bash shirt :(
  • chrysoprace 1 hour ago
    My old colleague had one with a Go program[0] which I always thought was quite cool.

    [0] https://github.com/GL-Kageyama/UNIQLO_Akamai_T-shirt_Code

    • mdgld 1 hour ago
      I wasn’t sure if you meant a Go solver or Go the language. Would be fun if someone wrote a Go program in Go
      • psd1 1 hour ago
        Or a pong clone in Racket.
    • ExoticPearTree 1 hour ago
      I got one this year with the Go code. Never actually thought it is legit code, just some random stuff.
  • qiqitori 1 hour ago
    I once wrote a tool that helps with finding mistakes in OCR'd fixed width text, https://blog.qiqitori.com/2023/03/ocring-hex-dumps-or-other-...

    Basically it just clusters same characters and asks the human to find the problems, which is easy when you're looking at a series of pictures like ssssss5sss.

    The UI is kinda least-effort. Should ask a modern AI agent to make it look nice and intuitive, sometime maybe.

  • haileys 3 hours ago
    I thought it was funny that the author used a variety of OCR tools with mixed success before spending a lot of time manually fixing up the output from the best one, rather than just typing it in
    • christoph 2 hours ago
      That was also my thought… but I grew up mashing rubber keys for hours copying “games” out of magazines and books! Then hours after fixing all the typos!
      • forinti 4 minutes ago
        I spent hours typing 6502 assembly. It went a lot better when someone dictated: LDA, STA, BEQ, LDY, STY...
    • rtldg 2 hours ago
      Took me almost 2 minutes for 4 lines (and I missed a character in one of them!). I would opt for OCR too, obviously so I'm prepared for the next bash t-shirt I'd come across...
      • OtherShrezzing 2 hours ago
        I think this is a case where two people can successfully complete the task manually faster than one attempting to automate it. Get a ruler, read five centimetres of characters to your colleague, have them type it in as you go, then repeat that five centimetres back to you. Correct as you go. Format your string with the same line-breaks as the t-shirt, and remove them at the end, so you can be sure you've got the correct length on each row. Trial-and-error adjust the five-cm distance depending on your success rate as you go along

        All in, you should have a non-corrupted string in 10-15 min.

    • acters 2 hours ago
      I ran it through paddle paddle OCR and it flawlessly did it. Google's OCR through my phone's Google lens had also worked at getting a very good extraction but not 100% correct. Definitely would spend less time fixing it than hand copying.

      IDK what the author was using but I feel like he could have shared how his OCR attempt went, but I am thinking he tried some naive OCR tools.

      • speerer 1 hour ago
        Author here - that's a good idea actually, it shouldn't be too hard to compare the various attempts. The tools I used were whatever my Android built-in is (likely Google Gemini, but I can't tell whether this is something Samsung has replaced in OneUI); tesseract; tesseract with various tweaks and charsrt restrictions; Claude; and finally, manual fixes based on disagreements between all the previous.
    • grumbel 1 hour ago
      Gemini3.5 Flash didn't have a problem OCR'ing and base64 decoding it, despite the OCR step having errors, it just fixed them in the base64 decoding step.
    • mayas_ 2 hours ago
      "just typing it" would be more error prone for the average human
    • duskdozer 2 hours ago
      I'm guilty of this, but for me this kind of thing is optimizing over annoyance rather than time.
    • speerer 1 hour ago
      (Author here) Yes I agree. It was a fun side-quest though. Reminds me of https://xkcd.com/1205/
  • teo_zero 13 minutes ago
    I don't know... I prefer unobfuscated text that you can immediately grok. The other day I saw this on a T-shirt:

    > May the m×s/t² be with you

  • chrisweekly 17 minutes ago
    Great post! It's interesting, detailed but concise, and well-written. Also, I appreciate the "no cookies or tracking" and attractive, functional and performant site design.
  • DrewADesign 2 hours ago
    > I guess Uniqlo is run through Windows though: one thing that struck me was the font, which I’m almost certain is Consolas,

    Surely this would use whatever font the virtual terminal profile was set to? I don’t know of any method to choose a virtual terminal font from bash and don’t see any code that addresses it?

    • nisiddharth 2 hours ago
      They're referring to the font on the T-shirt.
      • tym0 2 hours ago
        Thank you for spelling it out for me because I thought I was looking at a completely hallucinated AI article...
        • speerer 8 minutes ago
          Author here. All hallucinations are my own. Now you point it out, I see why the jump in context from the terminal back to the tshirt font would give the wrong impression.
  • cb321 1 hour ago
    For anyone that cares, this is a slightly less stupid Python version:

        #!/usr/bin/env python3
        from os   import environ; E = environ.get
        from math import sin
        from time import sleep
        text = "♥PEACE♥FOR♥ALL" # The text to sine-scroll animate
        nText  = len(text)      # Number of utf8 chars
        freq   = 0.2            # Frequency scaling factor
        color0 = 12             # xt256 Color cube segment 12..<208
        color1 = 208; nColor = color1 - color0
        (w, h) = (int(E("COLUMNS", 80)), int(E("LINES", 24)))
        t = 0
        while True:
            x = (w/2) + (w/4)*sin(t*freq)           # x pos via sine value
            x = max(0, min(w - 1, int(x + 0.5)))    # bound to tty width
            color = color0 + ((nColor*t)//h)%nColor # cycle colors
            ch = text[t%nText]  # Get char & Use xterm-256 color escs
            print("%*s\033[38;5;%sm%s\033[m\n" % (x, "", color, ch))
            t += 1
            sleep(0.1)   # original used bc shell outs to rate-limit
    
    As mentioned in https://news.ycombinator.com/item?id=48830326 , the heart symbols did not otherwise even work for my bash and some have commented on liking the screen saver.
  • alexpotato 24 minutes ago
    Fascinating that we have base64 but not error correction for it!
  • _flux 1 hour ago
    On one hand it's nice how it's clean and commented, but on the other hand some golfing could have made the encoded block a lot more reasonable to actually manually enter.
    • speerer 7 minutes ago
      It might not have filled up the whole shirt then?
    • puttycat 59 minutes ago
      The comments just mean they used AI to do that in 3 seconds
  • brightball 57 minutes ago
    Nice!

    Might have to do something like that for a verse on the next Carolina Code Conference shirt. Been trying to figure out a good way to pull in cybersecurity.

  • shim__ 1 hour ago
    Could have saved 50% with 'base64 -d | gzip -d'
    • speerer 7 minutes ago
      Maybe useful for those XS sizes.
  • sixtyj 56 minutes ago
    > Interesting. I told my wife "that’s basically how people ship viruses’ and bought it.

    It’s a movie plot.

  • preetham_rangu 1 hour ago
    The real threat model here isn't the base64 payload, it's Uniqlo turning a T-shirt into a QR code that requires a human OCR pipeline to redeem.
  • willejs 55 minutes ago
    Looks like it has a few shellcheck issues, and no set -euo pipefail? ;)
  • high_byte 3 hours ago
    what if it contained a zero day for tesseract and the script you thought you got is just a throwaway
  • dylanzhangdev 3 hours ago
    Cool! I bought one a few months ago as soon as I spotted it at a Uniqlo store, and later ordered a larger size online—I really love wearing them. But it never occurred to me to look into the story behind them.
  • brazzy 2 hours ago
    After being primed by the article, I read the author's name as "Shirtliker"...
    • speerer 4 minutes ago
      That's a new one and oddly apt :)
  • FijiBY 1 hour ago
    Nice investigation, thx
  • doppp 2 hours ago
    Thanks for the post! Love Easter Eggs like these!
  • khurs 2 hours ago
    Brilliant marketing when you can get people to pay to walk around advertising with your logo!!
  • kijin 2 hours ago
    Well at least they're not instructing consumers to run curl | bash.

    That's better than half the tech howtos out there.

    • INTPenis 1 hour ago
      No, they're instructing their customers to run unknown base64 encoded code instead. :D
  • tantalor 1 hour ago
    TIL Consolas is a Windows font
  • l337h4x0rz 2 hours ago
    there's no newline between the shebang and the actual code
  • icevl 2 hours ago
    Base64 without error correction turns the t-shirt itself into a lossy transport layer, so the OCR/transcription step becomes the actual challenge.
  • bryanrasmussen 3 hours ago
    Why does the shirt have an obfuscated bash script on the back?
  • breppp 1 hour ago
    Feels very reminiscent of the style of old DeCSS tshirts

    https://www.wired.com/2000/08/court-to-address-decss-t-shirt...

  • devnull810 27 minutes ago
    [dead]
  • BeatrizPerez 6 minutes ago
    [dead]
  • tancop 2 hours ago
    [dead]
  • huflungdung 1 hour ago
    [dead]
  • lloydatkinson 2 hours ago
    P ./cool.sh: line 31: bc: command not found ./cool.sh: line 34: bc: command not found ./cool.sh: line 37: bc: command not found E ./cool.sh: line 31: bc: command not found ./cool.sh: line 34: bc: command not found ./cool.sh: line 37: bc: command not found

    Very wow. Shame they assumed everyone has "bc"...

    • em500 2 hours ago
      Why would that be a shame? "bc" is a mandatory POSIX command, while /bin/bash isn't (/bin/sh is the standard).
    • greazy 2 hours ago
      Which distro are you running? Perchance did you run the shell script in alpine Linux (docker)?
    • comradesmith 2 hours ago
      You are fun.
      • lloydatkinson 1 hour ago
        Are we really at the "redditor insult" type comments stage of HN now? There is nothing wrong with saying a piece of code is broken.
        • deciduously 41 minutes ago
          Broken seems a little hyperbolic, it has an implicit dependency on a standard POSIX tool.
          • lloydatkinson 10 minutes ago
            I suppose, but my Debian didn’t seem to ship with it.
  • koiueo 30 minutes ago
    > I ran OCR in a few ways: First, using the built-in OCR of the circle-to-search feature on Android, which is often very good. Second, by using Tesseract with a few options and tweaks. And third by running it through Claude. After diffing the three to look for mismatches and getting Claude to output a table of locations for quick scanning, it became trivial but time-consuimg to tidy up the remainder

    I bet 10$ I'd spend less time typing it from the t-shirt. And I wouldn't boil two kettles of water in the process.

    But hey, AI makes you 10x more productive, I suppose

    • speerer 5 minutes ago
      (Author here) for unrelated reasons my typing is very slow at the moment, so I was keen to automate. I see that people are getting different results from Claude than I did though.
    • freedomben 21 minutes ago
      You may want to retract that bet: https://news.ycombinator.com/item?id=48830846
      • koiueo 15 minutes ago
        Yeah, I read that later.

        My bet is against manual OCR with various engines + finding mistakes later using an LLM.