Web Packaging Methodology in CTF
Before you get into specific vulnerabilities, you need a system. Without it, you will spend an hour on phase-sharing directories when the answer lies in the commentary to HTML. Here is my order of action on any web-tass:
The first 60 seconds - open the site in the browser, watch the source code of the page (Ctrl+U), check the answer headings via DevTools. Often directly in the comments or in the title X-Powered-By already visible stack: PHP, Flask, Express. This immediately narrows the vector. On the SANREN CTF, for example, it is recommended to try to contact index.php, index.cgi, index.html - according to the server's answer, it is clear that under the hood.
The next 5 minutes - if there is source code (and in modern CTFs is increasingly given), read the server part. Not all code, but entry points: routers, POST-request handlers, middleware. This is where the injections are hidden. If there are no source, I start ffuf -u http://target/FUZZ -w common.txt to search for hidden endpoints, including .git/HEAD, /backup, /admin.
In terms of MITRE ATT&CK, this whole process is Exploit Public-Facting Application (T1190), Initial Access). But in the CTF, we usually go further: after primary operation, you need to read the file, get to the internal service or raise privileges.
SQL Injection in CTF tasks
SQLi remains the most frequent vulnerability in the CTF web category. But if you think it's about ' OR 1=1 -- - so I haven't played in a while. Modern boards are built around bypasses of filters, non-standard injection points and situations where automation is powerless.
UNION-based: when you see the conclusion
Classic script: The app takes data from the database to the page and you can “glue” your request via UNION SELECT. The first thing to determine is the number of columns. I do it through ORDER BY N, increasing N until I get the error. Did you find that column 3? Excellent: ' UNION SELECT 1,2,3-- - and you're looking at what number it was on the page. That position is your “window” to extract data.
A typical brideg Mistake - Try to read the table immediately users. In the CTF, the structure of the base can be any. First find out the names of the tables through information_schema.tables, then the columns through information_schema.columns. Yes, it’s a basic SQL, but the number of people who miss this step and spend time guessing is still amazing.
On Hack TheBox in the car Gavel (according to 0xdf) was an interesting thing: SQL injection through PDO with backttick-quot sequenced prepared. Standard payloads did not work - it was necessary to figure out exactly how PDO shields input data, and find a bypass specifically for backtick-quoting. That’s why reading the source is more important than automatic scanning.
Blind SQL injection: when there is no conclusion
If the application does not show the result of the request, but reacts differently to the true/false conditions - this is blind SQLi. On the MireaCTF, one of the patches contained just such a vulnerability in cookies: with the truth on the page, the word "Welcome" appeared on the page, with false - no.
Manual Algorithm: Form a Condition ' AND SUBSTRING(password,1,1)='a'-- and you're going through the symbols. It's painfully slow with your hands, so you're writing a script. But here's the catch: sqlmap with parameters --technique=B --dbms=MySQL --level=5 --risk=3 on the same pull did not work - the injection point was in cookies with non-standard processing. Sqlmap expects certain patterns of answers, and when the application behaves atypically, you have to write custom exploit.
For sqlmap in such cases, it is worth trying to save the request to the file via Burp (sqlmap -r request.txt) and mark the injection point with a symbol * directly within the meaning of cookies. Useful flags --string="Welcome" (Text with true) and --not-string="Error". But if it doesn't help - write your script in Python with requests, binary search by ASCII-codes and real-time output. Honestly, it's faster than fighting with automation.
Extracting data through blind SQLi is the result of operation of T1190 (Exploit Public-Facting Application, Initial Access). T1213.006 (Databases) describes data collection in the legitimate access to the DBMS, which is not about the injection.
SSRF in CTF tasks - operation analysis
Server-Side Request Forgey is my favorite class of vulnerabilities in the CTF. The reason is simple: SSRF is rarely the ultimate goal. It’s always a springboard – to reading internal files, to metadata clouds, to internal services without authentication. According to MITRE ATT&CK, the initial operation of the SSRF - T1190 (Exploit Public-Facting Application, Initial Access). Scanning inland ports via SSRF (as in the example with Jenkins) - T1046 (Network Service Discovery). T1090 (Proxy) is about routing C2 traffic, SSRF is not related to SSRF mechanics.
SSRF through Next.js Middleware: real case CVE-2025-57822
One of the best examples of SSRF in the CTF is the CatFlix AI task with Intigriti Monthly Challenge (August 2025). Application on Next.js, source code is attached. This is how the course of thought went when I figured out a similar task.
The first thing that catches the eye in middleware.ts - the code adds secure headers to the answer. But there is a block that checks the UTM parameters in the query. When utm_source is present, NextResponse.next() is caused without explicit transfer of the request object - and the user headers are thrown on the server incorrectly. Transmission of the heading Location evokes a server redirect to an arbitrary URL. Three lines of code, and here you are SSRF.
These are CVE-2025-57822 - SSRF in Next.js to the versions 14.2.32 and 15.4.7, CVSS 6.5 (MEDIUM), CVSS vector:3.1/AV:N/A:X/N/N/NUI:N/C/C/I:L/A:N. The Root Problem – CWE-918 (Server-Side Request Forgey). The complexity of the attack is marked as High (AC:H) because you need a specific configuration of middleware - when next() caused without the transmission of the request object. In real-world conditions, CVSS 6.5 (MEDIUM) reflects precisely this limitation: with a pentest, you must first confirm the presence of a vulnerable configuration. In the CTF, the authors of the task guarantee this condition - thank you for this.
Proof of concept elementary: send a GET request from ?utm_source=meta and headline Location: http://localhost:3000/, and instead of a normal response, you get the contents of the internal service. According to writeup with Intigri, then over the ports (ffuf Jenkins was found on the port of 8080 without authentication, and through its Groovy Script Console - the execution of commands:
Code:
GET /?utm_source=meta HTTP/2
Host: challenge-0825.intigriti.io
Location: http://localhost:8080/script
Content-Type: application/x-www-form-urlencoded
script=println('cat /app/flag.txt'.execute().text)
RCE through Jenkins is a classic pattern that is found not only in the CTF, but also on real projects.
Bypass filters localhost in CTF
If the authors are not full of beginners, direct http://localhost will be blocked. Here are the techniques of bypass that I worked in real competitions:
DNS rebinding and alternative IP impressions. Instead of 127.0.0.1 Try http://127.1, http://0x7f000001, http://2130706433 (decimal), http://[::1] (IPv6). On MireaCTF, one of the patches gave the flag when referring to http://0:80/flag - the filter checked the line "localhost" and "127.0.0.1", but not alternative records. The fence also says “entry is prohibited”.
Redirects via an external server. Raise your server that returns 302 to http://127.0.0.1:порт. The application checks the URL before the request, sees your domain - everything is clean. But the HTTP client follows the redirect and falls on the localhost. For a quick solution on the CTF ngrok or a disposable Flask server.
URL schemes. If SSRF is implemented through curl or analogue, try file:///etc/passwd to read local files. Or gopher:// to send arbitrary TCP packets - this allows, for example, to send a SQL request directly to MySQL on localhost:3306 without a client. Gopher in 2025 - who would have thought that this piece of the 90s will still come in handy.
Deserviration in CTF - a vulnerability that is often missed
Insecure deserialization (CWE-502: Deserialization of Untrusted Data, category A08:2021 - Software and Data Integrity Failures According to OWASP Top 10 2021) is when the application restores an object from user data without validation, and the attacker can replace the object’s data or trigger a chain of destructive methods. In the CTF, this is found in two main variants: PHP and Java.
PHP: Magical methods as an input point
In PHP, deseresenization through unserialize() Dangerous because of magical methods. When calling unserialize() PHP is plucking wakeup() (or unserialize() PHP 8.0+, which has priority). When the object is destroyed - __destruct(). If in any of these methods there is a contact to the file system, executing commands or writing data is a potential RCE.
My CTF method: I find a challenge unserialize() in the source, then look for all classes with destruct, wakeup, toString, call. I build a “gadget chain” – a chain of objects where one’s property is an object of another class, and when the magic method is triggered by the logic I need. Model Example: Class Logger c __destruct()which records $this->logFile with contents $this->logData. Replace logFile on the way to the PHP file, logData - on webcilla, serialize, send - and we get Web Shell (T1505.003, Persistence on MITRE AT&CK). Beauty.
Another important point is the format phar://. Even if the direct call unserialize() No, but there is a file operation (file_exists(), fopen(), is_dir()), you can download a PHAR archive with a serialized object in metadata. When addressing phar://uploads/evil.jpg PHP automatically deseresenes metadata - itself, without demand. This technique was used, according to the best-web-ctf-writements collection on GitHub, in the PDF Creator task with CCCamp 2019.
Java: ysoserial and reality
Java-deserdication is heavy artillery. If the application accepts serialized Java objects (marker AC ED 00 05 in hex or rO0AB in Base64), this is almost guaranteed by RCE in the presence of vulnerable libraries in classpath.
Tool ysoserial generates payload for known gadget chains: CommonsCollitudes, Spring, Groovy and others. The original ysoserial (frohoff) has not been supported since ~21 and is incompatible with Java 17+ due to the limitations of the modular system - use current forks (ysoserial-all, java-deserialization-scanner in Burp). In practice, in CTF I run java -jar ysoserial.jar CommonsCollections5 'команда' | base64 and substitutes the result into a vulnerable parameter. Please note: Commons Collections5 only works with commons-collements 3.x; 4.x needs chains CommonsCollections2 or CommonsCollitudes4.
But not everything is so simple - you need to guess what library is on the server. If the source is available, look pom.xml or build.gradle for commons-collections, spring-core, groovy. If not, I foolishly go through the main chains. The method of scientific pumpkin, but works.
On HackTheBox (according to 0xdf) a Java-deserialization machine – for example, Cereal, Ophiuchi – often require not just the launch of ysoberial, but an understanding of how serialized data is processed by a specific framework. In Ophiuchi, the vulnerability was in SnakeYAML - the YAML parser for Java, where a specially formed YAML-document caused the loading of an arbitrary class. Deserialization is like one class of vulnerabilities, and you can kill a new zoo every time.
Chening of vulnerabilities - when one bug is not enough
In good CTF tasks, one bug does not give the flag. We need a chain. Here are the combinations I’ve met most often:
SSRF + internal service without authentication. Already dismantled on the example of CatFlix AI: SSRF in Next.js middleware → Jenkins without a password → Groovy RCE. Search on the domestic ports of Redis (6379), Elasticsearch (9200), Docker API (2375), Kubernetes API (6443).
SQL injection + file read/write. In MySQL through LOAD_FILE('/etc/passwd') or INTO OUTFILE '/var/www/html/shell.php' You can move from reading the base to RCE. FILE privileges are needed, but they are often in the CTF. Check the value @@secure_file_priv through UNION SELECT @@secure_file_priv - if empty ('') - no restrictions; if the path is indicated - record/read only in that directory; if NULL - FILE operations are prohibited in full. In MySQL 8.0+ default value - /var/lib/mysql-files/.
LFI + log poisoning. Found Local File Inclusion, but no interesting files? Send a request with a PHP code to User-Agent, then connect the Apache log via LFI: ?page=../../../var/log/apache2/access.log. PHP code from User-Agent will be executed when connecting the log. Dirty trick, but it works like a clock.
NoSQL injection + SSRF. On CatFlix AI, in addition to SSRF, there was also NoSQL injection in the registration endpoint - raw input was skating in the MongoDB-request. NoSQLi itself did not give access to the file system, but in combination with the SSRF would allow you to manipulate the data for further escalation.
On the HTB Guardian (according to 0xdf) the chain was even longer: IDOR in chat function → credentials leak for Gitea → XSS analysis through malicious XLSX in PhpSpreadsheet → session theft → CSRF to create an ad account → LFI with PHP filter network → RCE. Six links. It is these tasks that distinguish the average CTF from the top.
Frequent Mistakes and Why First Attempts Fail
Over the years of the CTF, I have identified patterns that stably break beginners:
Attempts to automate everything. Sqlmap, Nikto, disrearch are great tools, but they are not a replacement for the brain. On MireaCTF sqlmap did not find blind SQLi in cookies, and manual verification confirmed the vulnerability in a minute. Automation - amplifier, not crutch.
Ignoring the source code. If the childba gives the source, this is a clue. Don't jump past. On Intigriti CatFlix AI, the key to the solution was in three lines middleware.ts. I saw people who fasked directories for 40 minutes when the vulnerability lay in the open code. It's a shame.
Fixing on one vector. Spent 15 minutes on SQLi and nothing? Switch. Maybe it's not SQLi, it's STI. Or command injection. Or IDOR. Diversify the checks in the first 5 minutes, then delve into the most promising vector.
Misunderstanding of the flag format. Sounds silly, but regularly people find a line similar to a flag, and can not pass it - did not turn into a format CTF{...} or not decoded from Base64. Always check if there is an additional code layer - Deobfuscate/Decode Files or Information (T1140, Defense Evasion) also works in the opposite direction.
Before you get into specific vulnerabilities, you need a system. Without it, you will spend an hour on phase-sharing directories when the answer lies in the commentary to HTML. Here is my order of action on any web-tass:
The first 60 seconds - open the site in the browser, watch the source code of the page (Ctrl+U), check the answer headings via DevTools. Often directly in the comments or in the title X-Powered-By already visible stack: PHP, Flask, Express. This immediately narrows the vector. On the SANREN CTF, for example, it is recommended to try to contact index.php, index.cgi, index.html - according to the server's answer, it is clear that under the hood.
The next 5 minutes - if there is source code (and in modern CTFs is increasingly given), read the server part. Not all code, but entry points: routers, POST-request handlers, middleware. This is where the injections are hidden. If there are no source, I start ffuf -u http://target/FUZZ -w common.txt to search for hidden endpoints, including .git/HEAD, /backup, /admin.
In terms of MITRE ATT&CK, this whole process is Exploit Public-Facting Application (T1190), Initial Access). But in the CTF, we usually go further: after primary operation, you need to read the file, get to the internal service or raise privileges.
SQL Injection in CTF tasks
SQLi remains the most frequent vulnerability in the CTF web category. But if you think it's about ' OR 1=1 -- - so I haven't played in a while. Modern boards are built around bypasses of filters, non-standard injection points and situations where automation is powerless.
UNION-based: when you see the conclusion
Classic script: The app takes data from the database to the page and you can “glue” your request via UNION SELECT. The first thing to determine is the number of columns. I do it through ORDER BY N, increasing N until I get the error. Did you find that column 3? Excellent: ' UNION SELECT 1,2,3-- - and you're looking at what number it was on the page. That position is your “window” to extract data.
A typical brideg Mistake - Try to read the table immediately users. In the CTF, the structure of the base can be any. First find out the names of the tables through information_schema.tables, then the columns through information_schema.columns. Yes, it’s a basic SQL, but the number of people who miss this step and spend time guessing is still amazing.
On Hack TheBox in the car Gavel (according to 0xdf) was an interesting thing: SQL injection through PDO with backttick-quot sequenced prepared. Standard payloads did not work - it was necessary to figure out exactly how PDO shields input data, and find a bypass specifically for backtick-quoting. That’s why reading the source is more important than automatic scanning.
Blind SQL injection: when there is no conclusion
If the application does not show the result of the request, but reacts differently to the true/false conditions - this is blind SQLi. On the MireaCTF, one of the patches contained just such a vulnerability in cookies: with the truth on the page, the word "Welcome" appeared on the page, with false - no.
Manual Algorithm: Form a Condition ' AND SUBSTRING(password,1,1)='a'-- and you're going through the symbols. It's painfully slow with your hands, so you're writing a script. But here's the catch: sqlmap with parameters --technique=B --dbms=MySQL --level=5 --risk=3 on the same pull did not work - the injection point was in cookies with non-standard processing. Sqlmap expects certain patterns of answers, and when the application behaves atypically, you have to write custom exploit.
For sqlmap in such cases, it is worth trying to save the request to the file via Burp (sqlmap -r request.txt) and mark the injection point with a symbol * directly within the meaning of cookies. Useful flags --string="Welcome" (Text with true) and --not-string="Error". But if it doesn't help - write your script in Python with requests, binary search by ASCII-codes and real-time output. Honestly, it's faster than fighting with automation.
Extracting data through blind SQLi is the result of operation of T1190 (Exploit Public-Facting Application, Initial Access). T1213.006 (Databases) describes data collection in the legitimate access to the DBMS, which is not about the injection.
SSRF in CTF tasks - operation analysis
Server-Side Request Forgey is my favorite class of vulnerabilities in the CTF. The reason is simple: SSRF is rarely the ultimate goal. It’s always a springboard – to reading internal files, to metadata clouds, to internal services without authentication. According to MITRE ATT&CK, the initial operation of the SSRF - T1190 (Exploit Public-Facting Application, Initial Access). Scanning inland ports via SSRF (as in the example with Jenkins) - T1046 (Network Service Discovery). T1090 (Proxy) is about routing C2 traffic, SSRF is not related to SSRF mechanics.
SSRF through Next.js Middleware: real case CVE-2025-57822
One of the best examples of SSRF in the CTF is the CatFlix AI task with Intigriti Monthly Challenge (August 2025). Application on Next.js, source code is attached. This is how the course of thought went when I figured out a similar task.
The first thing that catches the eye in middleware.ts - the code adds secure headers to the answer. But there is a block that checks the UTM parameters in the query. When utm_source is present, NextResponse.next() is caused without explicit transfer of the request object - and the user headers are thrown on the server incorrectly. Transmission of the heading Location evokes a server redirect to an arbitrary URL. Three lines of code, and here you are SSRF.
These are CVE-2025-57822 - SSRF in Next.js to the versions 14.2.32 and 15.4.7, CVSS 6.5 (MEDIUM), CVSS vector:3.1/AV:N/A:X/N/N/NUI:N/C/C/I:L/A:N. The Root Problem – CWE-918 (Server-Side Request Forgey). The complexity of the attack is marked as High (AC:H) because you need a specific configuration of middleware - when next() caused without the transmission of the request object. In real-world conditions, CVSS 6.5 (MEDIUM) reflects precisely this limitation: with a pentest, you must first confirm the presence of a vulnerable configuration. In the CTF, the authors of the task guarantee this condition - thank you for this.
Proof of concept elementary: send a GET request from ?utm_source=meta and headline Location: http://localhost:3000/, and instead of a normal response, you get the contents of the internal service. According to writeup with Intigri, then over the ports (ffuf Jenkins was found on the port of 8080 without authentication, and through its Groovy Script Console - the execution of commands:
Code:
GET /?utm_source=meta HTTP/2
Host: challenge-0825.intigriti.io
Location: http://localhost:8080/script
Content-Type: application/x-www-form-urlencoded
script=println('cat /app/flag.txt'.execute().text)
RCE through Jenkins is a classic pattern that is found not only in the CTF, but also on real projects.
Bypass filters localhost in CTF
If the authors are not full of beginners, direct http://localhost will be blocked. Here are the techniques of bypass that I worked in real competitions:
DNS rebinding and alternative IP impressions. Instead of 127.0.0.1 Try http://127.1, http://0x7f000001, http://2130706433 (decimal), http://[::1] (IPv6). On MireaCTF, one of the patches gave the flag when referring to http://0:80/flag - the filter checked the line "localhost" and "127.0.0.1", but not alternative records. The fence also says “entry is prohibited”.
Redirects via an external server. Raise your server that returns 302 to http://127.0.0.1:порт. The application checks the URL before the request, sees your domain - everything is clean. But the HTTP client follows the redirect and falls on the localhost. For a quick solution on the CTF ngrok or a disposable Flask server.
URL schemes. If SSRF is implemented through curl or analogue, try file:///etc/passwd to read local files. Or gopher:// to send arbitrary TCP packets - this allows, for example, to send a SQL request directly to MySQL on localhost:3306 without a client. Gopher in 2025 - who would have thought that this piece of the 90s will still come in handy.
Deserviration in CTF - a vulnerability that is often missed
Insecure deserialization (CWE-502: Deserialization of Untrusted Data, category A08:2021 - Software and Data Integrity Failures According to OWASP Top 10 2021) is when the application restores an object from user data without validation, and the attacker can replace the object’s data or trigger a chain of destructive methods. In the CTF, this is found in two main variants: PHP and Java.
PHP: Magical methods as an input point
In PHP, deseresenization through unserialize() Dangerous because of magical methods. When calling unserialize() PHP is plucking wakeup() (or unserialize() PHP 8.0+, which has priority). When the object is destroyed - __destruct(). If in any of these methods there is a contact to the file system, executing commands or writing data is a potential RCE.
My CTF method: I find a challenge unserialize() in the source, then look for all classes with destruct, wakeup, toString, call. I build a “gadget chain” – a chain of objects where one’s property is an object of another class, and when the magic method is triggered by the logic I need. Model Example: Class Logger c __destruct()which records $this->logFile with contents $this->logData. Replace logFile on the way to the PHP file, logData - on webcilla, serialize, send - and we get Web Shell (T1505.003, Persistence on MITRE AT&CK). Beauty.
Another important point is the format phar://. Even if the direct call unserialize() No, but there is a file operation (file_exists(), fopen(), is_dir()), you can download a PHAR archive with a serialized object in metadata. When addressing phar://uploads/evil.jpg PHP automatically deseresenes metadata - itself, without demand. This technique was used, according to the best-web-ctf-writements collection on GitHub, in the PDF Creator task with CCCamp 2019.
Java: ysoserial and reality
Java-deserdication is heavy artillery. If the application accepts serialized Java objects (marker AC ED 00 05 in hex or rO0AB in Base64), this is almost guaranteed by RCE in the presence of vulnerable libraries in classpath.
Tool ysoserial generates payload for known gadget chains: CommonsCollitudes, Spring, Groovy and others. The original ysoserial (frohoff) has not been supported since ~21 and is incompatible with Java 17+ due to the limitations of the modular system - use current forks (ysoserial-all, java-deserialization-scanner in Burp). In practice, in CTF I run java -jar ysoserial.jar CommonsCollections5 'команда' | base64 and substitutes the result into a vulnerable parameter. Please note: Commons Collections5 only works with commons-collements 3.x; 4.x needs chains CommonsCollections2 or CommonsCollitudes4.
But not everything is so simple - you need to guess what library is on the server. If the source is available, look pom.xml or build.gradle for commons-collections, spring-core, groovy. If not, I foolishly go through the main chains. The method of scientific pumpkin, but works.
On HackTheBox (according to 0xdf) a Java-deserialization machine – for example, Cereal, Ophiuchi – often require not just the launch of ysoberial, but an understanding of how serialized data is processed by a specific framework. In Ophiuchi, the vulnerability was in SnakeYAML - the YAML parser for Java, where a specially formed YAML-document caused the loading of an arbitrary class. Deserialization is like one class of vulnerabilities, and you can kill a new zoo every time.
Chening of vulnerabilities - when one bug is not enough
In good CTF tasks, one bug does not give the flag. We need a chain. Here are the combinations I’ve met most often:
SSRF + internal service without authentication. Already dismantled on the example of CatFlix AI: SSRF in Next.js middleware → Jenkins without a password → Groovy RCE. Search on the domestic ports of Redis (6379), Elasticsearch (9200), Docker API (2375), Kubernetes API (6443).
SQL injection + file read/write. In MySQL through LOAD_FILE('/etc/passwd') or INTO OUTFILE '/var/www/html/shell.php' You can move from reading the base to RCE. FILE privileges are needed, but they are often in the CTF. Check the value @@secure_file_priv through UNION SELECT @@secure_file_priv - if empty ('') - no restrictions; if the path is indicated - record/read only in that directory; if NULL - FILE operations are prohibited in full. In MySQL 8.0+ default value - /var/lib/mysql-files/.
LFI + log poisoning. Found Local File Inclusion, but no interesting files? Send a request with a PHP code to User-Agent, then connect the Apache log via LFI: ?page=../../../var/log/apache2/access.log. PHP code from User-Agent will be executed when connecting the log. Dirty trick, but it works like a clock.
NoSQL injection + SSRF. On CatFlix AI, in addition to SSRF, there was also NoSQL injection in the registration endpoint - raw input was skating in the MongoDB-request. NoSQLi itself did not give access to the file system, but in combination with the SSRF would allow you to manipulate the data for further escalation.
On the HTB Guardian (according to 0xdf) the chain was even longer: IDOR in chat function → credentials leak for Gitea → XSS analysis through malicious XLSX in PhpSpreadsheet → session theft → CSRF to create an ad account → LFI with PHP filter network → RCE. Six links. It is these tasks that distinguish the average CTF from the top.
Frequent Mistakes and Why First Attempts Fail
Over the years of the CTF, I have identified patterns that stably break beginners:
Attempts to automate everything. Sqlmap, Nikto, disrearch are great tools, but they are not a replacement for the brain. On MireaCTF sqlmap did not find blind SQLi in cookies, and manual verification confirmed the vulnerability in a minute. Automation - amplifier, not crutch.
Ignoring the source code. If the childba gives the source, this is a clue. Don't jump past. On Intigriti CatFlix AI, the key to the solution was in three lines middleware.ts. I saw people who fasked directories for 40 minutes when the vulnerability lay in the open code. It's a shame.
Fixing on one vector. Spent 15 minutes on SQLi and nothing? Switch. Maybe it's not SQLi, it's STI. Or command injection. Or IDOR. Diversify the checks in the first 5 minutes, then delve into the most promising vector.
Misunderstanding of the flag format. Sounds silly, but regularly people find a line similar to a flag, and can not pass it - did not turn into a format CTF{...} or not decoded from Base64. Always check if there is an additional code layer - Deobfuscate/Decode Files or Information (T1140, Defense Evasion) also works in the opposite direction.