A PIN with three binary digits has 8 possible combinations: 000, 001, 010, 011, 100, 101, 110, and 111. With that number, it’s not too time-consuming to brute force attack manually. Just hand-enter all the combinations and within a minute, you’ve got access.
With more possible digits, a PIN could end up taking hours, days, or even years to crack with brute force by hand. So, brute force attacks are often mounted by scripts—the attacker doesn’t have to spend all that time manually typing in different PIN values. The computer (or in our case, the micro:bit module) does the work, and the attacker can check back periodically to find out if the PIN has been cracked.
The Bank Vault Receiver micro:bit should still be on and running the bank_vault_receiver script.
# bank_vault_crack.py from microbit import * import radio radio.on() radio.config(channel=7) sleep(1000) digits = ['0','1'] display.show(Image.ARROW_W) while True: if button_a.was_pressed(): display.clear() for a in digits: for b in digits: for c in digits: pin = ''.join([a, b, c]) print("pin =", pin) for x in range(0, len(pin)): bit = int(pin[x]) brightness = bit * 9 display.set_pixel(x,4,9) for y in range(0, 4): display.set_pixel(x, y, brightness) response = None while response is None: radio.send(pin) sleep(100) response = radio.receive() print(response) if response == "Access granted.": while True: display.scroll(pin) sleep(4000) display.clear()
See how it keeps trying combinations until it reaches the correct 011 PIN?
The script starts by declaring a list named digits with single character strings '0' and '1'.
digits = ['0','1']
The display.show(Image.ARROW_W) call makes the LED display point at the A button as a prompt to press it to start the process. After that, the while True: loop repeatedly checks if the A button was pressed, and does nothing else until you press it. After the A button is pressed, the display.clear() call erases the arrow so that it can start displaying the 0/1 digits without any leftover LED pixels from the arrow.
display.show(Image.ARROW_W) while True: if button_a.was_pressed(): display.clear()
These three nested loops cycle through each possible combination in pin. The script starts setting a to '0', then b to '0', and c to '0'. Then, pin = ''.join([a, b, c]) creates the string '000' by combining the three characters. The for c in digits loop isn’t done yet so it sets c to '1'. Then, pin = ''.join([a, b, c]) repeats, and this time, the result is '001'.
for a in digits: for b in digits: for c in digits: pin = ''.join([a, b, c])
Now that the for c in digits loop is finished, the for b in digits loop has to do its next iteration, setting b to '1'. Since for c in digits is below and indented, it has to repeat that loop again. This time, a is still '0', but b is now '1'. So, when c is '0', the result of pin = ''.join([a, b, c]) is '010'. On the for c in digits second repetition, it sets c to '1' and the result of pin = ''.join([a, b, c]) is '011'. It continues this way through all the possible 0/1 combinations.
A lot more happens each time through the for c loop. First, it displays the current pin combination in the terminal.
print("pin =", pin)
Then, it displays the PIN as 0/1 digits with the LED display.
for x in range(0, len(pin)): bit = int(pin[x]) brightness = bit * 9 display.set_pixel(x,4,9) for y in range(0, 4): display.set_pixel(x, y, brightness)
It also checks for the radio response from the Vault Receiver micro:bit. This loop sends repeatedly and checks for a response each time. When it does get a response, it exits the while response is None:… loop.
response = None while response is None: radio.send(pin) sleep(100) response = radio.receive()
Still inside the for c in digits… loop. Once a response has been received, it prints to the terminal. Then, if the response is "Access granted.", it means the PIN was correct. At that point, the correct PIN scrolls through the PIN Pad Transmitter micro:bit’s display repeatedly.
print(response) if response == "Access granted.": while True: display.scroll(pin)
The response string could also be "Access denied." In that case, the script just waits 4 seconds, then clears its display and lets for c in digits…loop to repeat with the next pin string in the sequence.
sleep(4000) display.clear()
If you are doing these activities by yourself, or want to make it to where you won’t have any knowledge of the PIN you are trying to crack, then modify the bank_vault_receiver script to create a random pin.
# bank_vault_receiver_random_pin # <- change from microbit import * import radio import random # <- add radio.on() radio.config(channel=7) sleep(1000) # pin = '011' # <- comment pin = '' # <- add for n in range(3): # <- add number = str(random.randint(0,1)) # <- add pin += number # <- add print("Random pin =", pin) # <- add while True: display.set_pixel(2,2,9) message = radio.receive() if message: if message == pin: radio.send("Access granted.") for n in range(4): display.show(Image.YES) sleep(1000) display.clear() sleep(200) else: radio.send("Access denied.") display.show(Image.NO) sleep(3000) display.clear()
Each time you reset the Bank Vault micro:bit, it should display a new random PIN in the terminal. (Don’t worry if your random sequence is different from what’s shown here. Yours only has a 1 in 8 chance of matching for any given reset.)
In the place of a previously decided PIN, there is now an empty string and a for loop that repeats three times—randomly choosing a 0 or 1- and then appending the pin string with that digit.
# pin = '011' # <- comment pin = '' # <- add for n in range(3): # <- add number = str(random.randint(0,1)) # <- add pin += number # <- add print("Random pin =", pin) # <- add