mirror of
https://github.com/System-End/my-blog.git
synced 2026-04-19 16:18:16 +00:00
security bugs
This commit is contained in:
parent
da5c75a9b4
commit
188abb9e08
4 changed files with 158 additions and 0 deletions
BIN
src/assets/oauth-history.png.png
Normal file
BIN
src/assets/oauth-history.png.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/oauth-url.png
Normal file
BIN
src/assets/oauth-url.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
76
src/content/posts/spaces-bug.md
Normal file
76
src/content/posts/spaces-bug.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
title: "Finding a CSRF Vuln in Hack Club Spaces"
|
||||
description: "How I found a bug by reading source code"
|
||||
pubDate: 15 Jan 2026
|
||||
---
|
||||
|
||||
Was planning on using spaces for my APCSA class and show it to my peers, teacher asked me if the code was secure and asked me to check it.... i ended up finding a CSRF bug in the admin panel. Here's how.
|
||||
|
||||
## The Bug
|
||||
|
||||
Admin endpoints read auth tokens from the request body instead of headers:
|
||||
|
||||
```js
|
||||
// src/middlewares/admin.middleware.js line 6
|
||||
const { authorization } = req.body; // bad
|
||||
|
||||
// vs everywhere else
|
||||
const authorization = req.headers.authorization; // good
|
||||
```
|
||||
|
||||
Why does this matter? HTML forms can set body data, but they can't set custom headers. So I can make a form on any website that submits to their admin API.
|
||||
|
||||
## The Attack
|
||||
|
||||
```html
|
||||
<form method="POST" action="https://spaces-api.com/api/v1/admin/users/1/update">
|
||||
<input type="hidden" name="authorization" value="STOLEN_TOKEN">
|
||||
<input type="hidden" name="is_admin" value="true">
|
||||
<button>free robux</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
If an admin clicks that button (or I auto-submit it with JS), their browser sends the request. No CORS issues, no preflight - just a normal form submission.
|
||||
|
||||
## Proving It
|
||||
|
||||
Saved the HTML locally, opened it, clicked the button:
|
||||
|
||||
- `Origin: null` - request came from a file, not their site
|
||||
- `Sec-Fetch-Site: cross-site` - browser confirmed it's cross-origin
|
||||
- Response: `401 Invalid authorization token` - server processed it, just rejected the fake token
|
||||
|
||||
With a real token, this executes. Game over.
|
||||
|
||||
## How Would You Get The Token?
|
||||
|
||||
The OAuth flow leaks it in the URL fragment after login (`/#oauth_success=true&user_data={"authorization":"xxx"}`). This gets saved in browser history, can be logged by analytics, or leak via Referer header. Any XSS would also grab it.
|
||||
|
||||
## The Fix
|
||||
|
||||
One line change:
|
||||
|
||||
```diff
|
||||
- const { authorization } = req.body;
|
||||
+ const authorization = req.headers.authorization;
|
||||
```
|
||||
|
||||
# Status
|
||||
|
||||
Reported on 15-01-26
|
||||
|
||||
On 15-01-26 Ivie (the maintainer of Space) contacted me stating:
|
||||
|
||||
"Hey End!
|
||||
|
||||
Re: the two security bounties you submitted
|
||||
|
||||
Neither are these are eligible for a payout, due to spaces being in a private beta state.
|
||||
|
||||
However, even if it was in production, they would not likely get a payout as:
|
||||
|
||||
The admin csrf requires you to already have an admin token, and if you had an admin token you could just use the admin endpoints, no csrf needed
|
||||
|
||||
Both will be fixed before spaces goes to production though, thanks for the report!"
|
||||
|
||||
---
|
||||
82
src/content/posts/spaces-bug2.md
Normal file
82
src/content/posts/spaces-bug2.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
title: "OAuth Token Leak in Hack Club Spaces"
|
||||
description: "Your auth token is in your browser history"
|
||||
pubDate: 15 Jan 2026
|
||||
heroImage: /src/assets/oauth-url.png
|
||||
---
|
||||
|
||||
# OAuth Token Leak in Hack Club Spaces
|
||||
|
||||
Second bug I found while poking around Spaces. This one's simpler but arguably worse.
|
||||
|
||||
## The Problem
|
||||
|
||||
After you login with Hack Club OAuth, the app redirects you to:
|
||||
|
||||
```
|
||||
https://spaces/#oauth_success=true&user_data={"authorization":"your_secret_token","username":"you"}
|
||||
```
|
||||
|
||||
Your full auth token. Right there in the URL.
|
||||
|
||||
## Why That's Bad
|
||||
|
||||
URLs stick around:
|
||||
|
||||
1. **Browser history** - anyone who opens your history sees it
|
||||
2. **Analytics** - if they run Google Analytics or whatever, full URLs get logged
|
||||
3. **Referer header** - click a link right after login, the next site might see it
|
||||
4. **Extensions** - browser extensions can read URLs
|
||||
5. **Shoulder surfing** - it's literally on screen
|
||||
|
||||
The token gives full account access. Your spaces, your data, everything.
|
||||
|
||||
## The Code
|
||||
|
||||
```js
|
||||
// src/api/oauth/oauth.route.js lines 184-186
|
||||
const encodedData = encodeURIComponent(JSON.stringify(userData));
|
||||
res.redirect(`/#oauth_success=true&user_data=${encodedData}`);
|
||||
```
|
||||
|
||||
They're already setting an httpOnly cookie with the token on line 177. So why also put it in the URL? No idea.
|
||||
|
||||
## The Fix
|
||||
|
||||
Just... don't:
|
||||
|
||||
```diff
|
||||
- const encodedData = encodeURIComponent(JSON.stringify(userData));
|
||||
- res.redirect(`/#oauth_success=true&user_data=${encodedData}`);
|
||||
+ res.redirect('/#oauth_success=true');
|
||||
```
|
||||
|
||||
Frontend can fetch user data from a `/me` endpoint using the cookie. Problem solved.
|
||||
|
||||
## Proof
|
||||
|
||||
Logged in, checked browser history:
|
||||
|
||||

|
||||
|
||||
There it is. Permanently saved until you clear history.
|
||||
|
||||
# Status
|
||||
|
||||
Reported on 15-01-26
|
||||
|
||||
On 15-01-26 Ivie (the maintainer of Space) contacted me stating
|
||||
|
||||
"Hey End!
|
||||
|
||||
Re: the two security bounties you submitted:
|
||||
|
||||
Neither are these are eligible for a payout, due to spaces being in a private beta state.
|
||||
|
||||
However, even if it was in production, they would not likely get a payout as:
|
||||
|
||||
The account token stored in URL: This is an issue, but due to it requiring access to the users computer to actually exploit, it is likely out of scope.
|
||||
|
||||
Both will be fixed before spaces goes to production though, thanks for the report!"
|
||||
|
||||
---
|
||||
Loading…
Add table
Reference in a new issue