Summary
Security audit identified 5 vulnerabilities (2 Critical, 2 High, 1 Medium) in NOJ's authorization logic, primarily in the API endpoints.
Findings
1. API Submission Info IDOR - Source Code Disclosure (CRITICAL)
File: app/Http/Controllers/Api/SubmissionController.php:15
The POST /api/submission/info endpoint returns the full submission record including solution (source code) for ANY submission. The api.submission.exist middleware only checks that the submission exists (Submission::find($request->sid)) but performs no ownership check.
Any authenticated API user can read any other user's source code by iterating submission IDs — enabling cheating during contests.
Contrast: The web Ajax/SubmissionController@detail correctly uses getJudgeStatus() which nulls out the solution field for non-owned submissions. The API bypasses this entirely.
2. API fetchVerdict IDOR - Source Code Disclosure (CRITICAL)
File: app/Http/Controllers/Api/ProblemController.php:38
POST /api/problem/fetchVerdict explicitly returns "solution" => $submission->solution for ANY submission. Same root cause — api.submission.exist middleware lacks ownership check.
3. Contest PDF Download Missing Authorization (HIGH)
File: app/Http/Controllers/Ajax/ContestController.php:114-132
GET /ajax/contest/downloadPDF allows any authenticated user to download any contest's PDF. No clearance check is performed, unlike other contest admin functions that check judgeClearance($cid, Auth::user()->id) == 3.
4. Contest Account Generation via GET (CSRF + No Max) (HIGH)
File: app/Http/Controllers/Ajax/GroupAdminController.php:17-37
GET /ajax/group/generateContestAccount creates user accounts via GET (no CSRF protection). Unlike ContestAdminController which validates max:100, this endpoint has no upper bound on the num parameter.
5. OAuth Unbind via GET (CSRF) (MEDIUM)
Files: app/Http/Controllers/OAuth/GithubController.php:113-128, OAuth/AAuthController.php:114-126
OAuth unbind endpoints use GET requests for state-changing operations, enabling CSRF attacks that could lock users out of their accounts.
Root Cause
The API endpoints were built without the ownership/share checks that the web Ajax endpoints correctly implement. The api.submission.exist middleware should be extended to verify submission ownership or share status.
Recommended Fix
// In Exist.php middleware, add ownership check:
if ($submission->user_id !== auth()->user()->id && !$submission->share) {
return response()->json(['success' => false, 'message' => 'Unauthorized']);
}
For the contest PDF, add clearance check matching other admin endpoints.
Found during security research. This report covers vulnerability classes and fixes without providing exploitation details.
Summary
Security audit identified 5 vulnerabilities (2 Critical, 2 High, 1 Medium) in NOJ's authorization logic, primarily in the API endpoints.
Findings
1. API Submission Info IDOR - Source Code Disclosure (CRITICAL)
File:
app/Http/Controllers/Api/SubmissionController.php:15The
POST /api/submission/infoendpoint returns the full submission record includingsolution(source code) for ANY submission. Theapi.submission.existmiddleware only checks that the submission exists (Submission::find($request->sid)) but performs no ownership check.Any authenticated API user can read any other user's source code by iterating submission IDs — enabling cheating during contests.
Contrast: The web
Ajax/SubmissionController@detailcorrectly usesgetJudgeStatus()which nulls out thesolutionfield for non-owned submissions. The API bypasses this entirely.2. API fetchVerdict IDOR - Source Code Disclosure (CRITICAL)
File:
app/Http/Controllers/Api/ProblemController.php:38POST /api/problem/fetchVerdictexplicitly returns"solution" => $submission->solutionfor ANY submission. Same root cause —api.submission.existmiddleware lacks ownership check.3. Contest PDF Download Missing Authorization (HIGH)
File:
app/Http/Controllers/Ajax/ContestController.php:114-132GET /ajax/contest/downloadPDFallows any authenticated user to download any contest's PDF. No clearance check is performed, unlike other contest admin functions that checkjudgeClearance($cid, Auth::user()->id) == 3.4. Contest Account Generation via GET (CSRF + No Max) (HIGH)
File:
app/Http/Controllers/Ajax/GroupAdminController.php:17-37GET /ajax/group/generateContestAccountcreates user accounts via GET (no CSRF protection). UnlikeContestAdminControllerwhich validatesmax:100, this endpoint has no upper bound on thenumparameter.5. OAuth Unbind via GET (CSRF) (MEDIUM)
Files:
app/Http/Controllers/OAuth/GithubController.php:113-128,OAuth/AAuthController.php:114-126OAuth unbind endpoints use GET requests for state-changing operations, enabling CSRF attacks that could lock users out of their accounts.
Root Cause
The API endpoints were built without the ownership/share checks that the web Ajax endpoints correctly implement. The
api.submission.existmiddleware should be extended to verify submission ownership or share status.Recommended Fix
For the contest PDF, add clearance check matching other admin endpoints.
Found during security research. This report covers vulnerability classes and fixes without providing exploitation details.