Welcome to the blog! Yesterday (29/11/2025), I decided to go on a little hunt for vulns in Scrapbook, Hack Club's devlog publishing thing.

With that, I found two vulnerabilities, one of them, which I will post tomorrow, but this one is somewhat high severity.


The Hack Club security program

The Hack Club Security program was made by 3kh0 (aka the monero fox or smth) so people could report security vulns related to Hack Club's services. These can go from PII leaks, to Airtable injections, etc. Most of the issues that have been found are related to programs like Shiba or Juice and Clubs (yes...).

About the vulnerability

This vulnerability was probably the highest severity one of them all. After scanning over the codebase, I have found that the /api/web/post/delete endpoint allows me to delete any user's post, just by being logged in. I don't need to be the author of that post nor have admin permissions (i'm not even sure if those exist in scrapbook lol).

import { getServerSession } from 'next-auth/next';
import { authOptions } from '../../auth/[...nextauth]';
import prisma from '../../../../lib/prisma';
import s3 from '../../../../lib/s3';
import { DeleteObjectCommand } from '@aws-sdk/client-s3';

export default async (req, res) => {
  const session = await getServerSession(req, res, authOptions);

  if (!session?.user) {
    // console.log('Unauthorized access attempt');
    return res.status(401).send({message: "Unauthorized"});
  }

  try {
    const update = await prisma.updates.delete({
      where: { id: req.body.id },
    });

    for (let attachment of update.attachments) {
      // console.log("attachment", attachment);
      if (!attachment.includes("amazonaws.com")) continue;
      let s3ItemKey = attachment.split("/").pop(); // the last part of the url is the key of the object in s3
      // console.log("s3 item key", s3ItemKey);
      const s3DeleteResult = await s3.send(new DeleteObjectCommand({
        Bucket: 'scrapbook-into-the-redwoods',
        Key: s3ItemKey
      }));
      // console.log("s3 delete result", s3DeleteResult);
    }
    // console.log('API Response:', update);

    return res.status(200).send({message: "Post Deleted"});
  } catch (e) {
    // console.error('Error deleting post:', e);
    return res.status(500).json({ error: true, message: 'Internal Server Error' });
  }
};

I am allowed to delete anyone's scrapbook posts by just being logged in and knowing the Post ID, which I can quickly find by going in the network tab and finding the request to
/api/users/[username] and seeing the posts. I was able to test this out in scrapbook.hackclub.com. During my testing, I did not affect anyone else's post.

The impact

This allows any user to delete any other user's Scrapbook posts, without any permissions. This is a big deal, as there's a LOT of posts on Scrapbook, which date back to 2020.

This is what an attacker could do:

  1. Go on Scrapbook, and login as an user (let's call them User B) that is not the author of User A's post.
  2. Go to User A's profile and find your target post. Open the Network DevTools tab and look for the request to /api/users/[username], where the username is User A's username, and look for their post (0 is the most recent post, and so on), click on it and find the post ID.
  3. Copy that post ID, and do a simple POST request to /api/web/post/delete/, example:
userPostId = 'UserAPostID'

fetch('/api/web/post/delete', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ id: userPostId }),
  credentials: 'include'
  })
  1. Profit. It's that simple.

Vulnerability timeline

all the dates in here are in dd/mm/yyyy...

  • I discovered this vulnerability on the 29/11/2025, where I then reported it to the Security Program. Scrapbook's maintainer was informed of it on the 01/12/2025 and responded saying that these issues "would be addressed".
  • On the 26/12/2025, I received the OK to publish this blog post on 01/01/2026.
  • On the 29/12/2025, this vulnerability was fixed. See the commit here.
  • On the 01/01/2026 (happy new year!!), I posted it publically (that's why you're reading it now!!)

Final notes

The timeline isn't the best. I actually spoke with 3kh0 about this timeline (when I met up with them IRL in rainy (and very cold) Lisbon, Portugal.

Of course, I understand people have lives, but a month to fix a vulnerability isn't good, when you have a ton of history on the table.

This vulnerability was only fixed after me and the program's developer received the OK for this blog post to be posted in 7 days. Now, when you do a request to the API path, it returns "Unauthorized" when you aren't the post's owner.

http.cat

At the end I did not get any payout. I was warned that this might happen when I reported it, since Scrapbook doesn't have a budget allocated to it.
Understandable, but again, the Security program is opt-out, not opt-in, which annoys me a little bit, but I'm happy that I've at least participated in this, it's not about the money.

Thanks for reading this!


Not using an adblocker? Disqus puts advertisements below that I don't control :(