# CRYPTO-2 Оценка: 20 ## Описание В данном задании необходимо придумать алгоритм, который будет отличать зашифрованный вывод от незашифрованного. ## Решение В данном задании участнику предоставляется сервис по хэшированию сообщения. Участник запрашивает бит флага, который будет захэширован и передан участнику (неограниченное количество раз). При этом, для шифрования нулевого бита и единичного бита будут выбираться разные алгоритмы хэширования, которые значительно различаются по времени. Участнику надо проанализировать медианное время хэширование нулевого и единичного бита, а после найти ответ. Участникам предоставлялось условие задачи на языке программирования python: ``` from flask import Flask, render_template, request from flag import flag from Crypto.Util.number import * from random import randint assert flag.endswith(b'}') flag = bytes_to_long(flag) flag = bin(flag)[2:] assert flag[-2] == '0' n = getPrime(512) * getPrime(512) app = Flask(__name__) @app.route('/') def intro(): return render_template('intro.html', n = n) @app.route('/guess_bit', methods=['GET']) def guess_bit(): args = request.args if 'bit' not in args.keys(): return {"error": "Bit needed to be guessed"} index = abs(int(args['bit'])) if index >= len(flag): return {"error": "Index overflow"} bit = flag[index] if bit == '1': return {"guess": pow(7, getPrime(300), n)} else: return {"guess": randint(n//2, n)} def main(): app.run(host='0.0.0.0', port=1177) if __name__ == "__main__": main() ``` Для решения данной задачи участнику было нужно: 1) Найти длину флага при помощи бинарного поиска (или просто перебора); 2) Понять, что если выбранный бит флага равен единице, то операция будет сильно сложнее, чем операция при бите флага равном нулю. Ниже представлено решение: ``` import requests import time from Crypto.Util.number import * r = requests.Session() url = 'http://127.0.0.1:5000' # Данная функция возвращает бит ответа по индексу def guess_bit(index:int): return r.get(f"{url}/guess_bit?bit={index}").json() # Данная функция возвращает бит ответа по индексу и время, которое на это ушло def guess_bit_timing(index:int): time_start = time.time_ns() ans = r.get(f"{url}/guess_bit?bit={index}").json() time_end = time.time_ns() return ans, time_end - time_start # Данная функция реализует бинарный поиск по длине ответа, возвращает количество бит в ответе def find_length(): msg = guess_bit(0) low = 0 #i think 1000 bits for flag enough high = 1000 #binary search for smart people :D while low <= high: middle = (low + high)//2 msg = guess_bit(middle) if "error" in msg.keys(): high = middle - 1 else: low = middle + 1 #from zero to length of flag - 1 return low length = find_length() # Данная функция находит среднее время, необходимое, чтобы достать бит по индексу. def medium_time(index, attempts = 50): timings = [] # hardcoded 50 is a random number for _ in range(attempts): timings.append(guess_bit_timing(index)[1]) return sum(timings)/len(timings) # Находим среднее время для нулевого бита MED_ZERO_TIME = medium_time(length - 2) # Находим среднее время для единичного бита MED_ONE_TIME = medium_time(0) # Находим медианное время MED_TO_DIVIDE = (MED_ZERO_TIME + MED_ONE_TIME)/2 # Данная функция находит бит, сравнивая среднее время для бита по индексу и медианное время def timing_attack_for_bit(index): bit_time = medium_time(index, 10) return '1' if bit_time > MED_TO_DIVIDE else '0' # Данная функция находит каждый бит флага def retrieve_flag(): flag = '' for i in range(length): flag += timing_attack_for_bit(i) return long_to_bytes(int(flag,2)).decode() if __name__ == "__main__": print(retrieve_flag()) ```