Problem 01

https://hack.levels.fyi/ctf_01

<html data-theme="dark">
<link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/@picocss/pico/css/pico.min.css>">
<link rel="icon" href="<https://img.icons8.com/?id=VzU7PPLtpY2i>">
<meta name="viewport" content="width=device-width">
<title>Levels.fyi Hackathon</title>
<style>
  :root, h2 { --pico-font-weight:lighter }
</style>
<app-root style="display:block; padding:1rem">
  <hgroup>
    <h2>Levels.fyi Hackathon</h2>
    <p>Round 02 - CTF</p>
  </hgroup>
  <!-- ctf_f2dd3fa0bec04d8191376645d2b50dd7 -->
</app-root>

Solution: The flag is directly visible in the HTML source code of the page.

$ curl -s <https://hack.levels.fyi/ctf_01> | grep ctf_
<!-- ctf_f2dd3fa0bec04d8191376645d2b50dd7 -->

Takeaway: Never store secrets directly in HTML source code. They are easily visible to users. As a best practice, avoid writing secrets directly in any code. Instead, use environment variables or secure vault storage and access secrets through appropriate interfaces.

Problem 02

https://hack.levels.fyi/ctf_02

<html data-theme="dark">
<link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/@picocss/pico/css/pico.min.css>">
<link rel="icon" href="<https://img.icons8.com/?id=VzU7PPLtpY2i>">
<meta name="viewport" content="width=device-width">
<title>Levels.fyi Hackathon</title>
<style>
  :root, h2 { --pico-font-weight:lighter }
</style>
<app-root style="display:block; padding:1rem">
  <hgroup>
    <h2>Levels.fyi Hackathon</h2>
    <p>Round 02 - CTF</p>
  </hgroup>
  <input type="number" placeholder="Enter &quot;CTF&quot;" onkeyup="submit(this, event)">
</app-root>
<script>
  function submit(input, event) {
    if (event.key !== "Enter") return
    fetch("<https://hack.log10.workers.dev>", {
      method: "POST",
      body: JSON.stringify({ query: input.value }),
    })
      .then(response => response.text())
      .then(alert)
  }
</script>

Solution: The input box prompts to enter "CTF", but the input type is set to "number", preventing text entry. There are two ways to solve this:

  1. Inspect the page, change type="number" to type="text", then submit "CTF".
  2. Send the text manually by calling the API via cURL:
$ curl -sd '{"query":"CTF"}' <https://hack.log10.workers.dev>
ctf_b8aff142efd64275a4fa6b5058ea7fc4

Takeaway: Frontend validations should be used for providing better user experience and error messages, not for securing inputs. Never trust input coming from the client-side; always validate on the server-side.

Reference: https://en.wikipedia.org/wiki/CURL

Problem 03

https://hack.levels.fyi/ctf_03

<html data-theme="dark">
<link rel="stylesheet" href="<https://cdn.jsdelivr.net/npm/@picocss/pico/css/pico.min.css>">
<link rel="icon" href="<https://img.icons8.com/?id=VzU7PPLtpY2i>">
<meta name="viewport" content="width=device-width">
<title>Levels.fyi Hackathon</title>
<style>
  :root, h2 { --pico-font-weight:lighter }
</style>
<app-root style="display:block; padding:1rem">
  <hgroup>
    <h2>Levels.fyi Hackathon</h2>
    <p>Round 02 - CTF</p>
  </hgroup>
  <form style="max-width:500px">
    <input name="username" placeholder="Enter Username" required>
    <input name="password" placeholder="Enter Password" required>
    <button type="submit">Login</button>
  </form>
</app-root>
<script>
  document.querySelector("form").addEventListener("submit", function(event) {
    event.preventDefault()
    fetch("<https://hack.log10.workers.dev>", {
      method: "POST",
      body: JSON.stringify({
        username: event.target.username.value,
        password: event.target.password.value,
      }),
    })
      .then(response => response.text())
      .then(alert)
  })
</script>