Virtual Machine 1 picoCTF 2023 Solution

Published: April 26, 2023

Description

A harder version of Virtual Machine 0. Instead of a simple two-gear system, this challenge hides a multi-stage bevel gear train inside a COLLADA (.dae) 3D model. Open the model in Blender, count the teeth on every gear in the train, compute the compound ratio, and submit the output rotation to a remote server to receive the flag.

Download and extract the challenge archive to get the .dae model file.

Install Blender (free, from blender.org) to open the COLLADA model.

Connect to the remote server when you are ready to submit your answer.

bash
wget https://artifacts.picoctf.net/c/509/vm1 && unzip vm1
bash
# Open the .dae file in Blender: File -> Import -> Collada (.dae)
bash
nc <HOST_FROM_INSTANCE> <PORT_FROM_INSTANCE>

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Open the model in Blender and expose the gear train
    Observation
    I noticed the challenge distributed a COLLADA (.dae) 3D model file, which indicated the puzzle was embedded in a 3D mechanical assembly, and that Blender's COLLADA importer and Edit Mode would be the right tool to visually inspect and count the gear teeth.
    Import the .dae file into Blender. The scene contains a multi-stage bevel gear train surrounded by casing parts. Select and delete non-gear objects (the housing, axle shafts, decorative pieces) to reveal all gears clearly. You need to count the teeth on every gear in the chain from the red input axle to the blue output axle.
    bash
    # In Blender:
    bash
    # File -> Import -> Collada (.dae) -> select the model file
    bash
    # Click a housing object, press X -> Delete to remove it
    bash
    # Orbit the camera to inspect each gear stage
    bash
    # Count teeth by slowly rotating each gear in Edit Mode
    What didn't work first

    Tried: Trying to read the gear tooth counts directly from the COLLADA XML with a text editor or grep instead of opening the model in Blender.

    COLLADA stores mesh geometry as raw vertex coordinates, not semantic tooth-count attributes. You will see thousands of floating-point numbers with no label indicating which cluster of vertices forms one tooth. Blender's Edit Mode lets you visually orbit and count tooth peaks directly on the rendered mesh, which is the only practical approach.

    Tried: Using FreeCAD or another 3D viewer instead of Blender to inspect the model.

    FreeCAD can open .dae files, but it often fails to correctly import COLLADA scenes with multiple instanced objects and may collapse the gear train into a single merged mesh, making individual gear identification impossible. Blender's COLLADA importer preserves the scene hierarchy and lets you isolate each object by clicking it in the Outliner panel.

    Learn more

    COLLADA (.dae) is an XML-based 3D interchange format. Blender imports it via File -> Import -> Collada. The Virtual Machine 1 model is a Lego-style mechanical assembly with bevel gears at multiple stages, meaning gears whose axes are not parallel (bevel gears transfer rotation between shafts at an angle, like the gears in a car differential).

    The gear ratio for each meshing pair is simply the tooth count of the driven gear divided by the tooth count of the driving gear. A compound gear train multiplies the ratios of every stage together. If stage 1 gives 7:1, stage 2 gives 191:1, and stage 3 gives 7:1, the total ratio is 7 x 191 x 7 = 9359. That means one full turn of the input axle produces 9359 full turns of the output axle.

  2. Step 2
    Calculate the compound gear ratio
    Observation
    I noticed from Blender that the model contained multiple bevel gear stages rather than a single pair, which suggested I needed to multiply the individual stage ratios together to find the total compound ratio before submitting to the server.
    Walk every gear mesh from input to output and record the driven/driving tooth ratio at each stage. Multiply all the stage ratios together. For this challenge the gear train works out to a compound ratio of 9359.
    python
    python3 - <<'PY'
    # Example of how to combine per-stage gear ratios:
    # Stage 1: driving gear has D1 teeth, driven gear has d1 teeth -> ratio = d1 / D1
    # Repeat for every stage, then multiply all ratios.
    stages = [
        (8, 56),   # stage 1: 56-tooth driven by 8-tooth -> 7x
        (1, 191),  # stage 2: 191-tooth driven by 1-tooth -> 191x  (adjust to match your count)
        (8, 56),   # stage 3: 56-tooth driven by 8-tooth -> 7x
    ]
    ratio = 1
    for driving, driven in stages:
        ratio *= driven / driving
    print(f"Compound ratio: {ratio}")   # should be 9359
    PY

    Expected output

    Compound ratio: 9359.0
    What didn't work first

    Tried: Dividing the driving tooth count by the driven tooth count instead of the other way around, producing a fractional ratio less than 1.

    Gear ratio is always driven/driving (output/input). If the driven gear has more teeth than the driving gear, the output shaft spins slower - a reduction. If the driven gear has fewer teeth, the output spins faster - an increase. Inverting the fraction gives the reciprocal ratio, so instead of 9359 you would compute roughly 0.000107, which the server will reject.

    Tried: Assuming the chain has only two gear stages because two gears are most visible after removing the housing.

    The Virtual Machine 1 model is a three-stage compound train. One intermediate stage is tucked behind the main bevel pair and is easy to miss if the housing is only partially removed. Missing a stage means you multiply only two ratios (for example 7 x 191 = 1337) instead of all three, producing the wrong final count. Remove every non-gear object to a hidden collection and inspect from multiple angles before finalising your count.

    Learn more

    The ratio of 9359 comes from a three-stage bevel gear train. Each bevel gear pair works the same way as a spur gear pair for counting purposes: divide the driven tooth count by the driving tooth count. The challenge authors chose numbers whose product (9359) is large enough that you cannot guess it but small enough to count by hand or with Blender's vertex inspector.

    If your count differs from 9359, re-examine each stage. A common mistake is confusing which gear is driving and which is driven (the ratio inverts), or missing a stage hidden inside the housing. Move every non-gear object to a hidden collection in Blender so nothing obstructs your view.

  3. Step 3
    Connect to the server and answer the rotation question
    Observation
    I noticed the remote server prompted for how many times the output axle rotates given a random input count, which meant I needed to apply the compound ratio of 9359 I had already computed by multiplying it against whatever integer the server supplied.
    The remote server presents a large input rotation count and asks how many times the output axle will rotate. Multiply the input by the gear ratio (9359) and submit the result. The server validates the answer and prints the flag.
    bash
    nc <HOST_FROM_INSTANCE> <PORT_FROM_INSTANCE>
    bash
    # Server prompt example:
    bash
    #   How many times does the output rotate if the input rotates 22039 times?
    bash
    # Answer: 22039 * 9359 = 206263001
    What didn't work first

    Tried: Submitting the gear ratio itself (9359) as the answer instead of multiplying it by the server-provided input rotation count.

    The server asks how many times the OUTPUT rotates given a specific INPUT count, not what the ratio is. The prompt changes every session with a different random input integer. Submitting 9359 will be wrong for any session where the server chose a different input value. You must parse the integer from the server's prompt and compute input * 9359 to get the correct output rotation count.

    Tried: Hard-coding the example host and port (saturn.picoctf.net:51650) into the socket script instead of using the values from your own challenge instance.

    picoCTF spins up per-team instances; a host and port copied from anywhere other than your own challenge panel belong to a different session and will likely be unreachable or already closed. Always use the HOST and PORT shown in your own challenge panel, and substitute them into the nc command or the Python socket script before running.

    Learn more

    The server generates a random input value each session, so you will not see 22039 every time. The computation is always the same: output = input * 9359. The Python snippet below handles arbitrarily large numbers (picoCTF often uses big-integer inputs):

    import socket
    
    RATIO = 9359
    HOST, PORT = "saturn.picoctf.net", 51650  # replace with your instance values
    
    with socket.create_connection((HOST, PORT)) as s:
        data = s.recv(4096).decode()
        print(data)
        # Parse the input number from the prompt (last integer on the line)
        n = int(data.strip().split()[-1].rstrip("?"))
        answer = str(n * RATIO)
        s.sendall((answer + "\n").encode())
        print(s.recv(4096).decode())

    This challenge demonstrates that "reverse engineering" is not limited to binary analysis. Understanding the mechanism of a physical machine, whether real or simulated in a 3D model, is also reverse engineering. The gear train is the "program" and counting teeth is the "disassembly."

Interactive tools
  • Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.

Flag

Reveal flag

picoCTF{m0r3_g34r5_3g4d_...}

The compound gear ratio is 9359. Multiply the server-provided input rotation count by 9359 and submit the result to receive the flag.

Key takeaway

Compound gear trains multiply individual stage ratios together, which is the same composition principle used in cryptographic key-derivation chains and multi-round ciphers where each stage applies a transformation whose combined effect is far larger than any single step. Accurately analyzing a multi-stage system requires tracing every link in the chain without missing or misordering a stage; the same discipline applies when reversing layered encoding schemes, nested obfuscation passes, or chained redirect flows in web security.

Related reading

Want more picoCTF 2023 writeups?

Useful tools for Reverse Engineering

What to try next