Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
29c3:exploitation:minesweeper [2012/12/30 13:01] mooh |
29c3:exploitation:minesweeper [2017/04/09 15:33] (Version actuelle) |
||
---|---|---|---|
Ligne 4: | Ligne 4: | ||
Enough of reversing? Play this nice game and chill a bit, if you want, you can even save the game and enjoy it later! XX.XX.XX.XX:1024 | Enough of reversing? Play this nice game and chill a bit, if you want, you can even save the game and enjoy it later! XX.XX.XX.XX:1024 | ||
- | |||
- | http://dl.ctftime.org/57/193/minesweeper.rar | ||
==== Solution ==== | ==== Solution ==== | ||
Ligne 111: | Ligne 109: | ||
On recree une partie, mais cette fois avec **cat flag.txt** et on obtient le flag **29C3_TickTickBoom_YouFoundAMine** | On recree une partie, mais cette fois avec **cat flag.txt** et on obtient le flag **29C3_TickTickBoom_YouFoundAMine** | ||
+ | |||
+ | ==== Source ==== | ||
+ | <code python> | ||
+ | #!/usr/bin/env python | ||
+ | import bisect, random, socket, signal, base64, pickle, hashlib, sys, re, os | ||
+ | |||
+ | def load_encrypt_key(): | ||
+ | try: | ||
+ | f = open('encrypt_key.bin', 'r') | ||
+ | try: | ||
+ | encrypt_key = f.read(4096) | ||
+ | if len(encrypt_key) == 4096: | ||
+ | return encrypt_key | ||
+ | finally: | ||
+ | f.close() | ||
+ | except: | ||
+ | pass | ||
+ | |||
+ | rand = random.SystemRandom() | ||
+ | encrypt_key = "" | ||
+ | for i in xrange(0, 4096): | ||
+ | encrypt_key += chr(rand.randint(0,255)) | ||
+ | |||
+ | try: | ||
+ | f = open('encrypt_key.bin', 'w') | ||
+ | try: | ||
+ | f.write(encrypt_key) | ||
+ | finally: | ||
+ | f.close() | ||
+ | except: | ||
+ | pass | ||
+ | |||
+ | return encrypt_key | ||
+ | |||
+ | class Field: | ||
+ | def __init__(self, w, h, mines): | ||
+ | self.w = w | ||
+ | self.h = h | ||
+ | self.mines = set() | ||
+ | while len(self.mines) < mines: | ||
+ | y = random.randint(0, h - 1) | ||
+ | x = random.randint(0, w - 1) | ||
+ | self.mines.add((y, x)) | ||
+ | self.mines = sorted(self.mines) | ||
+ | self.opened = [] | ||
+ | self.flagged = [] | ||
+ | |||
+ | def calc_num(self, point): | ||
+ | n = 0 | ||
+ | for y in xrange(point[0] - 1, point[0] + 2): | ||
+ | for x in xrange(point[1] - 1, point[1] + 2): | ||
+ | p = (y, x) | ||
+ | if p != point and p in self.mines: | ||
+ | n += 1 | ||
+ | return n | ||
+ | |||
+ | def open(self, y, x): | ||
+ | point = (int(y), int(x)) | ||
+ | if point[0] < 0 or point[0] >= self.h: | ||
+ | return (True, "Illegal point") | ||
+ | if point[1] < 0 or point[1] >= self.w: | ||
+ | return (True, "Illegal point") | ||
+ | if point in self.opened: | ||
+ | return (True, "Already opened") | ||
+ | if point in self.flagged: | ||
+ | return (True, "Already flagged") | ||
+ | bisect.insort(self.opened, point) | ||
+ | if point in self.mines: | ||
+ | return (False, "You lose") | ||
+ | if len(self.opened) + len(self.mines) == self.w * self.h: | ||
+ | return (False, "You win") | ||
+ | if self.calc_num(point) == 0: | ||
+ | #open everything around - it can not result in something bad | ||
+ | self.open(y-1, x-1) | ||
+ | self.open(y-1, x) | ||
+ | self.open(y-1, x+1) | ||
+ | self.open(y, x-1) | ||
+ | self.open(y, x+1) | ||
+ | self.open(y+1, x-1) | ||
+ | self.open(y+1, x) | ||
+ | self.open(y+1, x+1) | ||
+ | return (True, None) | ||
+ | |||
+ | |||
+ | def flag(self, y, x): | ||
+ | point = (int(y), int(x)) | ||
+ | if point[0] < 0 or point[0] >= self.h: | ||
+ | return "Illegal point" | ||
+ | if point[1] < 0 or point[1] >= self.w: | ||
+ | return "Illegal point" | ||
+ | if point in self.opened: | ||
+ | return "Already opened" | ||
+ | if point in self.flagged: | ||
+ | self.flagged.remove(point) | ||
+ | else: | ||
+ | bisect.insort(self.flagged, point) | ||
+ | return None | ||
+ | |||
+ | def load(self, data): | ||
+ | self.__dict__ = pickle.loads(data) | ||
+ | |||
+ | def save(self): | ||
+ | return pickle.dumps(self.__dict__, 1) | ||
+ | |||
+ | def write(self, stream): | ||
+ | mine = 0 | ||
+ | open = 0 | ||
+ | flag = 0 | ||
+ | screen = " " + ("0123456789" * ((self.w + 9) / 10))[0:self.w] + "\n +" + ("-" * self.w) + "+\n" | ||
+ | for y in xrange(0, self.h): | ||
+ | have_mines = mine < len(self.mines) and self.mines[mine][0] == y | ||
+ | have_opened = open < len(self.opened) and self.opened[open][0] == y | ||
+ | have_flagged = flag < len(self.flagged) and self.flagged[flag][0] == y | ||
+ | screen += chr(0x30 | (y % 10)) + "|" | ||
+ | for x in xrange(0, self.w): | ||
+ | is_mine = have_mines and self.mines[mine][1] == x | ||
+ | is_opened = have_opened and self.opened[open][1] == x | ||
+ | is_flagged = have_flagged and self.flagged[flag][1] == x | ||
+ | assert(not (is_opened and is_flagged)) | ||
+ | if is_mine: | ||
+ | mine += 1 | ||
+ | have_mines = mine < len(self.mines) and self.mines[mine][0] == y | ||
+ | if is_opened: | ||
+ | open += 1 | ||
+ | have_opened = open < len(self.opened) and self.opened[open][0] == y | ||
+ | if is_mine: | ||
+ | c = "*" | ||
+ | else: | ||
+ | c = ord("0") | ||
+ | #check prev row | ||
+ | for m in xrange(mine - 1, -1, -1): | ||
+ | if self.mines[m][0] < y - 1: | ||
+ | break | ||
+ | if self.mines[m][0] == y - 1 and self.mines[m][1] in (x - 1, x, x + 1): | ||
+ | c += 1 | ||
+ | #check left & right | ||
+ | if mine > 0 and self.mines[mine - 1][0] == y and self.mines[mine - 1][1] == x - 1: | ||
+ | c += 1 | ||
+ | if have_mines and self.mines[mine][1] == x + 1: | ||
+ | c += 1 | ||
+ | #check next row | ||
+ | for m in xrange(mine, len(self.mines)): | ||
+ | if self.mines[m][0] > y + 1: | ||
+ | break | ||
+ | if self.mines[m][0] == y + 1 and self.mines[m][1] in (x - 1, x, x + 1): | ||
+ | c += 1 | ||
+ | c = chr(c) | ||
+ | elif is_flagged: | ||
+ | flag += 1 | ||
+ | have_flagged = flag < len(self.flagged) and self.flagged[flag][0] == y | ||
+ | c = "!" | ||
+ | else: | ||
+ | c = " " | ||
+ | screen += c | ||
+ | screen += "|" + chr(0x30 | (y % 10)) + "\n" | ||
+ | screen += " +" + ("-" * self.w) + "+\n " + ("0123456789" * ((self.w + 9) / 10))[0:self.w] + "\n" | ||
+ | stream.send(screen) | ||
+ | |||
+ | sock = socket.socket() | ||
+ | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
+ | sock.bind(('0.0.0.0', 1024)) | ||
+ | sock.listen(10) | ||
+ | |||
+ | signal.signal(signal.SIGCHLD, signal.SIG_IGN) | ||
+ | |||
+ | encrypt_key = load_encrypt_key() | ||
+ | |||
+ | while 1: | ||
+ | client, addr = sock.accept() | ||
+ | if os.fork() == 0: | ||
+ | break | ||
+ | client.close() | ||
+ | sock.close() | ||
+ | |||
+ | f = Field(16, 16, 20) | ||
+ | |||
+ | re_pos = re.compile("^. *([0-9]+)[ :;,]+([0-9]+) *$") | ||
+ | re_save = re.compile("^. *([0-9a-zA-Z+/]+=*) *$") | ||
+ | def handle(line): | ||
+ | if len(line) < 1: | ||
+ | return (True, None) | ||
+ | if len(line) == 1 and line[0] in "qxQX": | ||
+ | return (False, "Bye") | ||
+ | global f | ||
+ | if line[0] in "foFO": | ||
+ | m = re_pos.match(line) | ||
+ | if m is None: | ||
+ | return (True, "Usage: '([oOfF]) *([0-9]+)[ :;,]+([0-9]+) *', Cmd=\\1(Open/Flag) X=\\2 Y=\\3") | ||
+ | x,y = m.groups() | ||
+ | x = int(x) | ||
+ | y = int(y) | ||
+ | if line[0] in "oO": | ||
+ | return f.open(y,x) | ||
+ | else: | ||
+ | return (True, f.flag(y,x)) | ||
+ | elif line[0] in "lL": | ||
+ | m = re_save.match(line) | ||
+ | if m is None: | ||
+ | return (True, "Usage: '([lL]) *([0-9a-zA-Z+/]+=*) *', Cmd=\\1(Load) Save=\\2") | ||
+ | msg = base64.standard_b64decode(m.group(1)) | ||
+ | tmp = "" | ||
+ | for i in xrange(0, len(msg)): | ||
+ | tmp += chr(ord(msg[i]) ^ ord(encrypt_key[i % len(encrypt_key)])) | ||
+ | msg = tmp | ||
+ | if msg[0:9] != "4n71cH3aT": | ||
+ | return (True, "Unable to load savegame (magic)") | ||
+ | h = hashlib.sha1() | ||
+ | h.update(msg[9+h.digest_size:]) | ||
+ | if msg[9:9+h.digest_size] != h.digest(): | ||
+ | return (True, "Unable to load savegame (checksum)") | ||
+ | try: | ||
+ | f.load(msg[9+h.digest_size:]) | ||
+ | except: | ||
+ | return (True, "Unable to load savegame (exception)") | ||
+ | return (True, "Savegame loaded") | ||
+ | elif len(line) == 1 and line[0] in "sS": | ||
+ | msg = f.save() | ||
+ | h = hashlib.sha1() | ||
+ | h.update(msg) | ||
+ | msg = "4n71cH3aT" + h.digest() + msg | ||
+ | tmp = "" | ||
+ | for i in xrange(0, len(msg)): | ||
+ | tmp += chr(ord(msg[i]) ^ ord(encrypt_key[i % len(encrypt_key)])) | ||
+ | msg = tmp | ||
+ | return (True, "Your savegame: " + base64.standard_b64encode(msg)) | ||
+ | elif len(line) == 1 and line[0] in "dD": | ||
+ | return (True, repr(f.__dict__)+"\n") | ||
+ | else: | ||
+ | return (True, "Unknown Command: '" + line[0] + "', valid commands: o f q x l s") | ||
+ | |||
+ | data = "" | ||
+ | while 1: | ||
+ | f.write(client) | ||
+ | while 1: | ||
+ | pos = data.find("\n") | ||
+ | if pos != -1: | ||
+ | cont, msg = handle(data[0:pos]) | ||
+ | if not cont: | ||
+ | if msg is not None: | ||
+ | client.send(msg + "\n") | ||
+ | f.write(client) | ||
+ | client.close() | ||
+ | sys.exit(0) | ||
+ | if msg is not None: | ||
+ | client.send(msg + "\n") | ||
+ | data = data[pos+1:] | ||
+ | break | ||
+ | new_data = client.recv(4096) | ||
+ | if len(new_data) == 0: | ||
+ | sys.exit(0) | ||
+ | data += new_data | ||
+ | |||
+ | </code> | ||