Sitemap
Ryca

Ryca

Top 1% on TryHackMe. Curious learner exploring cybersecurity from offense to defense.

Web Frameworks: Code Review | TryHackMe

4 min readJul 20, 2026

--

Read web app source like an attacker: trace user input to dangerous sinks, triage with Semgrep.

Press enter or click to view image in full size

Learning Objectives

  • Distinguish black-box, grey-box, and white-box testing, and recognise when source access changes the approach
  • Orient quickly in an unfamiliar codebase using a fixed reading order
  • Map the attack surface from routing and configuration without running the application
  • Trace user-controlled input from source to sink in Python web code
  • Triage a codebase with grep, ripgrep, and Semgrep
  • Recognise SQL injection, command injection, Server-Side Template Injection (SSTI), insecure deserialisation, path traversal, broken access control, and hardcoded secrets in source
  • Apply the full method to a purpose-built vulnerable Flask application

Prerequisites

Answer the questions below

  • At step 2 of the reading order we check the dependency manifest, because a pinned old version is a lead we can search against a CVE database. Which file conventionally holds that manifest in a Python project?

requirements.txt

  • Which decorator marks a function as a route, and therefore an entry point, in a Flask application?

@app.route

  • We read configuration before any route handler because a setting like DEBUG = True changes how every later bug behaves. In a Flask app, which file conventionally holds that debug flag and the secret key?

config.py

  • Tracing backwards from a cursor.execute call, we walk the value back to the point where it entered the application from the client. What do we call that entry point?

source

  • render_template_string and subprocess.run(..., shell=True) are both examples of which type of location in our source-to-sink model?

sink

  • In the worked example, request.args.get("name") flows into render_template_string with no validation. Which vulnerability class does that source-to-sink path create?

SSTI

  • Our first triage pass is a single grep that searches every Python file recursively and prints the line number of each hit so we can jump straight to it. Which two-flag combination gives us recursion plus line numbers?

-rn

  • Semgrep matches code structure rather than text, and we point it at a community ruleset such as p/owasp-top-ten. Which command-line flag selects that ruleset?

- -config

  • With subprocess.run(), the difference between safe and exploitable is whether the input is parsed by /bin/sh. Which keyword argument, when present, hands the whole command string to the shell and so opens command injection? Give it as written in code, including its value.

shell=True

  • Insecure deserialisation reaches code execution because the deserialiser can be told to call arbitrary objects through __reduce__. Which Python function in the task is the worst offender, loading attacker bytes back into objects?

pickle.loads

  • We read send_file(os.path.join(UPLOAD_DIR, filename)) on user input as the dangerous shape for path traversal. Which Flask function is its safe counterpart, routing the path through safe_join and returning a 404 when it escapes the directory?

send_from_directory

  • Reading a handler, we find Vault.query.get(item_id) keyed on the URL id with no owner_id = current_user condition in the filter. What is the common acronym for the access-control flaw this creates?

IDOR

  • The search query filters on the logged-in user and selects two columns, so reach the system_flags table with a UNION of a matching column count. Exploit the SQL injection to extract the stored flag. What is the flag?

THM{un10n_b4s3d_sql1_dump3d}

  • The same handler echoes the query back through render_template_string. Climb from {{7*7}} to command execution and read the FLAG2 value from the application's environment. What is the flag?

THM{j1nj4_ss71_to_rc3}

  • The download handler joins our filename onto the upload directory with no validation. Walk out of that directory to read /flag3.txt. What is the flag?

THM{send_file_tr4v3rs4l_w1n}

Get Ryca’s stories in your inbox

Join Medium for free to get updates from this writer.

CONCLUSION

White-box testing is a reading problem before it is an exploitation problem. The exploits at the end of this room were short, because the work was in the reading that came first. The method holds together as five steps we now own: orient in the codebase, map the attack surface from routing and configuration, trace user input from source to sink, triage with grep and Semgrep, then verify each candidate by hand.

What we exercised here repeats on real targets:

  • The first ten minutes are reading order, not bug hunting. Manifest, config, routes, auth, then handlers.
  • A source reaching a sink with no safe step between them is the shape of almost every injection bug.
  • grep and Semgrep produce candidates, never confirmed findings. Manual review is the confirmation step.
  • The safe and dangerous counterparts (render_template against render_template_string, send_from_directory against send_file, placeholders against f-strings) are what let us read a bug on sight.
  • Access control and hardcoded secrets are found by noticing what is missing, not by tracing data.

The language and framework only change the names: the sink is a raw query whether it is built with an f-string here, with Spring’s JdbcTemplate in Java, or with string concatenation in C#; the template engine differs but server-side template injection reads the same. We learned a way to read code for vulnerabilities, not a Python trick, so carry the method to whatever stack the next engagement hands us. If we want to drill into a single bug class, TryHackMe has dedicated rooms for SQL injection, SSTI, and the remaining OWASP Top 10 categories. On a real engagement with source in hand, start every review the same way: read the config, list the routes, and follow the input.

--

--

Ryca
Ryca

Top 1% on TryHackMe. Curious learner exploring cybersecurity from offense to defense.