back

Claude Now Tags Its Own Content. Here's How I Stripped It.

reverse-engineeringaic2pa

So Anthropic finally did it. They’ve been teasing this whole “AI content detection” thing for a while, making it sound like they built something that could identify AI-generated content from word choice and writing patterns. People on my Twitter feed are losing their minds over it, talking about canceling their subscriptions and all that.

And I’m sitting here thinking, let me just look at what they actually shipped.

Twitter post about Claude's content tagging

I went ahead and asked Claude to generate me a png that contains “Hello World” text, because nothing else comes to a programmer’s mind except hello world.

Hello World image generated by Claude

Then I made a copy of it and renamed the png to .txt like a caveman, just to see if there were some strings like “Anthropic” hiding inside to understand how the mechanism works.

File explorer showing the PNG and its text copy

After I opened the txt, I didn’t even need to scroll. It’s directly looking at me: “Anthropic Claude Content Signing”.

Anthropic Claude Content Signing string visible in the raw file

So I looked up what this thing is called. Anthropic has a check content page where they check if a file was made by Claude using something called C2PA. They check our metadata, so I figured I’d check theirs. Reverse uno card.

In the txt file I found this string: Claude.aigversione1.0.0worg.contentauth.c2pa_rsf0.90.0. That tells us they’re using a Rust crate called c2pa-rs version 0.90.0 to generate the signature. So I went to the GitHub repo and looked at how it actually works. In the README I found these lines:

  • JPEG: Wraps into multi-segment APP11 data with JPEG XT headers
  • JPEG XL: Wraps into a single ISOBMFF jumb superbox containing the C2PA manifest store
  • PNG: Wraps into a caBX chunk with CRC

Source

So pngs have chunks just like .exe files? I never heard about this to be honest. So I wrote a simple python script to see if that’s the chunk that’s betraying our school projects:

import struct

with open("hello_world_text.png", "rb") as f:
    f.read(8)
    while chunk := f.read(8):
        length, type = struct.unpack(">I4s", chunk)
        data = f.read(length)
        crc = f.read(4)
        if type.decode() == "caBX":
            print(type.decode(), length, data)

Script output showing the caBX chunk data

There it is. Now we just need to write the same png to a new file without including that chunk.

import struct

with open("hello_world_text.png", "rb") as f:
    sig = f.read(8)
    chunks = []
    while chunk := f.read(8):
        length, type = struct.unpack(">I4s", chunk)
        data = f.read(length)
        crc = f.read(4)
        if type.decode() != "caBX":
            chunks.append(chunk + data + crc)

with open("clean.png", "wb") as f:
    f.write(sig)
    for c in chunks:
        f.write(c)

Then I verified both images on Anthropic’s own check content page:

Before: detected as ClaudeAfter: no signs of Claude

So yeah, before you start thinking this is some next-level technology that analyzes writing patterns and detects everything Claude has ever touched, maybe it’s just a signature slapped onto the file. Don’t be lazy to research and improve yourselves, my friends.

That said, turns out Anthropic actually is building text watermarking that works at the token level by biasing word choices during generation. That’s the real deal and way more interesting than a metadata chunk. That’s probably why their check content page only supports media files (JPG, PNG, GIF, WEBP, MP4, etc.) and not .txt or any coding extensions, the text watermarking isn’t fully public yet. When it is though, maybe we’ll get to crack that next.

And if there’s already some tool out there that does this, I honestly don’t care. I enjoyed doing this project and when I tried the obvious stuff like exiftool -all= it didn’t work anyway, so here we are.

I also made a small cli tool just for png files if you want to use it: claude-png-unsign. Other formats like jpeg and jxl store C2PA data differently so I didn’t bother, but PRs are open if anyone wants to add support.

Hope to see you guys in the next blogs.

~/emro.cat

$ ls -la posts/

Apr 26
-rwxr--r--

How I Broke the Anti-Bot Behind Nike, Kick, and Twitch