Predict Choice / Limited Rand

the challenge I give is to win 50 times in a row with the client recorder against this RockPaperScissor-PythonServer

import SocketServer,threading,os,string
import random, time
f = open('secret.txt')
offset = int(f.readline().strip())

choices = {
        'r': 'rock',
        'p': 'paper',
        's': 'scissors'
}

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        rnd = random.Random()
        # Initialize the random number generator to some secret value
        # Note: the value of offset is too big to guess/bruteforce you need to find a better way :)
        rnd.seed(int(time.time() + offset))
        self.request.sendall("Rock paper scissors is back\n")
        win_count = 0
        play_again = True
        while play_again:
            while win_count < 50:
                self.request.sendall("choose one [r] rock, [p] paper, [s] scissors: ")
                your_choice = self.request.recv(1024).strip()
                if not your_choice in 'rps':
                    continue
                self.request.sendall("Your choice %s\n" % choices.get(your_choice))
                my_choice = rnd.choice("rps")
                self.request.sendall("My choice %s\n" % choices.get(my_choice))
                if my_choice == your_choice:
                    self.request.sendall("Its a tie, sorry you need to win 50 times in a row, a tie is simply not good enough.\nWho ever said life was fair?\n")
                    break
                if ((my_choice == 'r' and your_choice == 'p') or 
                        (my_choice == 'p' and your_choice == 's') or 
                        (my_choice == 's' and your_choice == 'r')):
                    win_count += 1
                    self.request.sendall("Arghhh. you beat me %s times\n" % win_count)
                else:
                    self.request.sendall("You loose!\n")
                    break

            if win_count == 50:
                self.request.sendall("50 times in a row?!? are you some kind of mind reader?\n")
                return
            else:
                win_count = 0
                answer = ''
                while answer not in ('y','n'):
                    self.request.sendall("Play again? (y/n): ")
                    answer = self.request.recv(1024).strip().lower()
                    if answer == 'n':
                        return

SocketServer.TCPServer.allow_reuse_address = True
server = ThreadedTCPServer(("0.0.0.0", 1178), MyTCPHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()

      

I read in the python random.py doc and on various sites that the kernel random number generator that uses the random pythons class (MersenneTwister) is not good for security-related stuff because it is predictable when an attacker manages to get 624 sequential numbers.

I already have a client playing 624 times of rock and in each round it detects a server selection, converts it to the corresponding array index in [rps], and writes that number to a file. So in the end a long file contains many 0s, 1s and 2s like this

0
1
0
2
2
0
....

      

The most important line in the server code for me seems to be

my_choice = rnd.choice("rps")

      

which is implemented as (extract from random.py):

def choice(self, seq):
  """Choose a random element from a non-empty sequence."""
  return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty

      

There I read that for the prediction of the following numbers I need to record 624 consecutive numbers and restore the state by the change / cancellation of certain changes, but I believe that this requires a direct conclusion rng core, which is a float between [0.0, 1.0] ...

To get the main rng output from the sequence index, it seems like I just need to exactly flip the above "choice ()" function code, which would be something like

seq_value = seq[int(core_rng_out * len(seq))]
seq_index = int(core_rng_out * len(seq))
int^-1(seq_index) = core_rng_out * len(seq)
int^-1(seq_index) / len(seq) = core_rng_out
core_rng_out = int^-1(seq_index) / 3

      

The above should be something like solving a math equation for a specific variable. Divided by 3 because sequence of 3 sizes ("rps") but what is the inverse of pythons int (...)?!? Above, I tried to abstractly mark it as inverse by making it ^ -1.

And besides, it is even possible to get rng float at all?!?, Because in pythons int-doc it says when int (...) is assigned to float some truncation will / might happen ...?!

Or could it be completely wrong approach and I can beat the server in an easier way?

+3


source to share


1 answer


It seems to me that you can trick the server by initiating two connections at the same time (within the same second).

If they are initiated within the same second, the random seed will be the same (because of this line: rnd.seed(int(time.time() + offset))

This way the server will generate the same options for the two clients.



So what needs to be done is simply ask one client to go for any (possibly losing) strategy by recording the first 50 computers first. Then, knowing them, you can play on a different connection with exactly winning moves.

+1


source







All Articles