picoCTF 2017 – ciekawe zadania – LEVEL 2 SoRandom

Zawody picoCTF 2017 już dawno za nami ale nic nie stoi na przeszkodzie by spróbować swoich sił w tej zabawie dla początkujących pentesterów. W tym wpisie opiszę mój sposób rozwiązania jednego z wielu czekających na zawodników ciekawych zadań: [LEVEL 2] SoRandom.

Treść zadania:

SoRandom
We found sorandom.py running at shell2017.picoctf.com:23741. It seems to be outputting the flag but randomizing all the characters first. Is there anyway to get back the original flag?
Update (text only) 16:16 EST 1 Apr Running python 2 (same version as on the server)
HINTS
How random can computers be?
Skrypt sorandom.py:

#!/usr/bin/python -u
import random,string

flag = "FLAG:"+open("flag", "r").read()[:-1]
encflag = ""
random.seed("random")
for c in flag:
  if c.islower():
    #rotate number around alphabet a random amount
    encflag += chr((ord(c)-ord('a')+random.randrange(0,26))%26 + ord('a'))
  elif c.isupper():
    encflag += chr((ord(c)-ord('A')+random.randrange(0,26))%26 + ord('A'))
  elif c.isdigit():
    encflag += chr((ord(c)-ord('0')+random.randrange(0,10))%10 + ord('0'))
  else:
    encflag += c
print "Unguessably Randomized Flag: "+encflag

 

Jak widać mamy skrypt napisany w pythonie który odczytuje flagę z pliku ale przed zwróceniem jej na wyjście miesza znaki w "losowy" sposób.
Przeanalizujmy co się dzieje:
  1. Skrypt iteruje po każdym znaku naszej flagi, następnie generuje numer w tablicy ASCII danego znaku i odejmuje numer pierwszego znaku tak aby otrzymać numer znaku liczony od zera np.: "h" ma numer 104 więc odejmujemy 97 (numer znaku "a") i mamy 7 gdyż "h" to siódmy znak alfabetu licząc od zera.
  2. Następnie do numeru naszego znaku dodawana jest "losowa" liczba z zakresu od zera do 26 (26 małych liter w alfabecie łacińskim) a następnie używa operatora modulo na wypadek przekroczenia zakresu. np. dla litery "h" ma numer 7, jeśli dodamy 25 będziemy poza zakresem więc 32%26=6 czyli jedziemy od początku.
  3. Na końcu do naszego nowego "losowego" znaku dodajemy spowrotem 99 aby znów mieć poprawny numer z tabeli ASCII.
W tym przypadku "h" (kod ASCII 104) przy "losowej" liczbie 25 zostaje zakodowana do litery "g" (7 + 25 = 32, 32&26=6, 97+6=103).

Odwrócenie tego procesu wydaje się dość proste, wystarczy od numeru ASCII zakodowanej litery odjąć naszą "losową" liczbę generowaną przed każdą iteracją (z uwzględnieniem możliwości wyjścia poza dolny zakres).

Kluczowa jest tu funkcja random.seed() która jest używana do losowania liczby szyfrującej. Jak czytamy w manulu python'a:

The pseudo-random generators of this module should not be used for security purposes. Use os.urandom() or SystemRandom if you require a cryptographically secure pseudo-random number generator.

Funkcja ta nie jest bezpieczna, ów losowa liczba zwracana przez nią nie jest do końca losowa. W zasadzie dla podanego takiego samego seed'a jest zawsze taka sama dla każdego kolejnego wywołania. Jesteśmy zatem w domu jeśli znamy o jaką liczbę jest zmieniany kod ASCII każdego ze znaków w naszej fladze. Wystarczy odwrócić proces odejmując tą liczbę.

Nie trzeba nawet za wiele pisać, wystarcz zmienić zmienną na zawartość naszej zakodowanej flagi i + na - aby skrypt szyfrujący zamienił się w deszyfrujący 🙂

#!/usr/bin/python -u
import random,string

flag = "BNZQ:1l36de9583w5516fv3b8691102224f3e" 

decflag = ""
random.seed("random")
for c in flag:
	if c.islower():
		decflag += chr((ord(c)-97-random.randrange(0,26))%26 + 97)
	elif c.isupper():
		decflag += chr((ord(c)-65-random.randrange(0,26))%26 + 65)
	elif c.isdigit():
		decflag += chr((ord(c)-48-random.randrange(0,10))%10 + 48)
	else:
		decflag += c

print "Flag: "+decflag

 

Makulatura:

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *