LIT CTF 2026 Writeup: no way out

608 words
3 minutes
LIT CTF 2026 Writeup: no way out

The no way out Google Form challenge
The no way out Google Form challenge

Final flag: LITCTF{y0u_3sc4ped_th3_f0rm_6bq3}

The title, no way out, is the first hint: the important part is not just answering questions, but understanding how the form navigates between sections.

1. Testing the form#

The first question asks:

What’s the first part of the flag?

I tried submitting:

LITCTF{

The form replied:

nope, that’s wrong

That tells us the form is validating answers on the client side. Since this is Google Forms, the next step is to inspect what the browser already received.

2. Reading the form source#

Using DevTools, search the page source for:

FB_PUBLIC_LOAD_DATA_

This is Google Forms’ public form data. It contains much more than the visible questions:

  • questions and choices;
  • validation rules;
  • section routes;
  • hidden section text;
  • the form’s navigation state.

The expected first answer is exposed in the validation data:

LITCTF{y0u_

So the flag begins with:

LITCTF{y0u_

The important lesson is simple:

Client-side validation is not secret. If the browser needs the expected answer, the expected answer is usually present somewhere in the page data.

The same source also exposes the hidden form state, including pageHistory.

The public Google Forms data containing the challenge structure
The public Google Forms data containing the challenge structure

3. Recovering 3sc4ped#

After the first part, the form asks for the next letter several times. Each page presents many choices, but most choices route backward. Only one choice continues forward.

The routes in FB_PUBLIC_LOAD_DATA_ make this visible. Conceptually:

a -> old section
b -> old section
c -> old section
3 -> next section
4 -> old section
...

There is no need to brute-force the answer. Pick the choice whose destination differs from the old/backward section.

Repeating this process gives:

3 s c 4 p e d

At this point, the flag is:

LITCTF{y0u_3sc4ped

The same progress can also appear in the hidden partialResponse field after the answers are submitted.

4. The trap page#

The form eventually shows:

You’re trapped here. You can still access the past, but not the future.

That message is the next hint.

Google Forms tracks visited sections with a hidden input:

<input type="hidden" name="pageHistory" value="0,1,2,3,4,5,6,7,8">

pageHistory is not the flag. It is the list of pages that Google Forms believes we have already visited.

Because this state is client-side, we can edit it.

At the trap page, change:

0,1,2,3,4,5,6,7,8

to:

0,1,2,3,4,5,6,7,8,9

Then use the form’s back navigation. This lets us enter the first hidden future page, where the next fragment is revealed:

_th3_f0rm_

After continuing forward, the form gives:

6b
q3

The last page contributes:

}

The hidden page-history state used to reach the next section
The hidden page-history state used to reach the next section

5. Why add only 9?#

This is the key detail.

We do not need to write:

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14

We only need to fake access to the first hidden future page. Once we enter that page, Google Forms continues its normal routing and updates pageHistory itself.

So:

0,1,2,3,4,5,6,7,8,9

means:

Let me enter the first future page.

Adding many more page numbers manually can reveal decoy sections. More pages do not mean more correct flag characters.

The correct strategy is:

  • use FB_PUBLIC_LOAD_DATA_ to understand the real routes;
  • use pageHistory to reach the hidden path;
  • let the form’s own navigation reveal the valid fragments.

6. Shortest solving script#

Once the form structure is understood, the solve can be reduced to a small parser:

import re, json, requests
s = requests.get("https://forms.gle/kniocxKJN1RiDaTU7").text
d = json.loads(re.search(r"FB_PUBLIC_LOAD_DATA_ = (.*?);", s, re.S)[1])[1][1]
flag = d[0][4][0][4][0][2][0]
for x in d:
if (x[1] or "").startswith("What's the next letter"):
flag += next(c[0] for c in x[4][0][1] if c[2] != 588010028)
sec = [0] + [i for i, x in enumerate(d) if x[3] == 8]
for i in (9, 16, 11, 20):
flag += re.sub(r"Here's.*flag:\s*|\s+", "", d[sec[i]][2])
print(flag)

Output:

LITCTF{y0u_3sc4ped_th3_f0rm_6bq3}

Final flag#

Combining the valid fragments:

LITCTF{y0u_
3sc4ped
_th3_f0rm_
6b
q3
}

gives:

LITCTF{y0u_3sc4ped_th3_f0rm_6bq3}

Takeaway#

The form says there is no way out, but the browser already has the map.

The solve uses three client-side observations:

FB_PUBLIC_LOAD_DATA_ -> form structure, answers, and routing
partialResponse -> saved answers
pageHistory -> editable visited-page history

The essential bypass is changing:

0,1,2,3,4,5,6,7,8

to:

0,1,2,3,4,5,6,7,8,9

That is enough to enter the hidden future path. From there, the form’s own routing leads to the remaining real flag fragments.

Share Article

If this article helped you, please share it with others!

LIT CTF 2026 Writeup: no way out
https://multiflora-rose.xyz/posts/litctf-2026/
Author
Multiflora_Rose
Published at
2026-08-09
Profile Image of the Author
Multiflora_Rose
Cybersecurity / CTF / Research
Categories
Tags
Latest Moments
Site Statistics
Posts
3
Categories
1
Tags
6
Total Words
18,657
Running Days
40 days
Last Activity
0 days ago