Description
Picker III adds a primitive menu with helper functions for reading and writing names. Overwrite getRandomNumber with win so option 4 triggers the flag routine.
Setup
Run the script locally and issue the help command to list the numbered actions.
Identify option 3 (write_variable) and option 4 (get_flag). Overwriting getRandomNumber with win unlocks the final option.
wget https://artifacts.picoctf.net/c/526/picker-III.pypython3 picker-III.pySolution
- Step 1Use write_variableChoose menu item 3. When prompted for the variable name, enter getRandomNumber and when prompted for the new value, enter win. This hijacks the pointer used by option 4.
Learn more
Picker III presents a structured menu interface rather than raw eval, which means direct code injection is no longer possible. Instead, the vulnerability is in the write_variable option: it allows users to overwrite named variables in the program's namespace. Since Python functions are first-class objects stored in the same namespace as variables, overwriting
getRandomNumberwith the function objectwineffectively replaces one function with another.This is a form of function pointer hijacking- a concept that appears in both scripting language exploits and compiled binary exploitation. In C, function pointers stored in writable memory can be overwritten by buffer overflows or use-after-free vulnerabilities to redirect execution to attacker-controlled code. Picker III demonstrates the same concept in a Python context, where the "function pointer" is just a variable name bound to a function object.
The root cause is that the
write_variablefunction uses Python'sglobals()dictionary (orsetattr()) without any validation of what names or values are allowed. A secure implementation would restrict which variable names can be modified and would validate that the new value is of the expected type (e.g., an integer, not a function reference). - Step 2Trigger the modified functionBack in the main menu pick option 4. Because getRandomNumber now resolves to win, the service prints the flag (again as a stream of hex bytes).
nc saturn.picoctf.net 49706Learn more
Option 4 calls whatever
getRandomNumbercurrently refers to. Before the exploit, that is the legitimate random number function. After usingwrite_variableto rebind it towin, option 4 silently callswin()instead. The user interface gives no indication that the function has been replaced - it still says "get flag" or similar, but now it actually delivers the flag.This technique is called hooking in the context of software security and reverse engineering. Attackers hook functions to intercept calls, modify arguments, or redirect execution. In legitimate contexts, hooking is used for profiling, debugging, and monkey-patching in tests. The difference between legitimate and malicious hooking is authorization and intent - which is why environments that need integrity (kernel modules, security software) use code signing, kernel patches, and other mechanisms to prevent unauthorized hooking.
The challenge also demonstrates why exposing internal variable names through an API is dangerous. The
write_variablemenu option is essentially an unrestricted reflection API - it exposes the program's internal structure to external callers. Real APIs sometimes have similar issues: overly permissive object deserialization, unrestricted reflection in Java, or Python'ssetattrcalled on user-supplied strings without a whitelist. - Step 3Decode the hex outputPaste the 0x-prefixed values into CyberChef (From Hex) or use xxd -r to turn them into ASCII. The decoded string is the final flag.
Learn more
As in Picker I and II, the flag is returned as a sequence of hex-encoded bytes rather than plaintext. This is a consistent design decision across the Picker series - it reinforces the hex-to-ASCII conversion skill alongside the primary exploitation concept.
For the hex output format used here (space-separated
0x??values), a convenient Python one-liner is:bytes([int(x, 16) for x in output.split()]).decode(). This splits the output on whitespace, converts each token from hex to an integer, collects the integers into a bytes object, and decodes the bytes as UTF-8 (or ASCII). This approach is more robust than xxd when the input includes the0xprefix, which xxd's plain mode does not expect.Completing the Picker series has taken you through three progressively hardened versions of the same underlying application. The progression - from unrestricted eval, to a string blacklist bypass, to function pointer hijacking through a write API - models how real vulnerability research works: each fix introduces a new, slightly more subtle vulnerability that requires a different approach to exploit. This iterative cat-and-mouse between defenders and attackers is a central dynamic in security.
Flag
picoCTF{7h15_15_wh47_w3_g37_w17h_u53r5_1n_ch4...dd285}
Once getRandomNumber points at win, every subsequent run leaks the same hex-encoded flag.