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.
Setup
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.
wget https://artifacts.picoctf.net/c/509/vm1 && unzip vm1# Open the .dae file in Blender: File -> Import -> Collada (.dae)nc <HOST_FROM_INSTANCE> <PORT_FROM_INSTANCE>Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Open the model in Blender and expose the gear trainObservationI 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 filebash# Click a housing object, press X -> Delete to remove itbash# Orbit the camera to inspect each gear stagebash# Count teeth by slowly rotating each gear in Edit ModeWhat 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.
Step 2
Calculate the compound gear ratioObservationI 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.pythonpython3 - <<'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 PYExpected 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.
Step 3
Connect to the server and answer the rotation questionObservationI 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.bashnc <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 = 206263001What 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.