Sad Rendel

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Создано при поддержке моего канала

Ссылка на таск без vpn конфига не работает
И его сорцы

Немного истории

Таск был взят с Moscow School Ctf 2021, на которой я играл в команде Shadow Kitties (8 место).
Удивлен, что его решило всего 2 команды.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Решил я только этот таск.
В свое оправдание скажу, что в тот день писал олимпиаду Верченко по криптографии, пол дня провел в городе и приехал домой мертвый.

Решение

Заглянем в main.py и обнаружим условие получения флага.

from cookies import encrypt_cookie, decrypt_cookie


def get_user(request):
    cookie = request.cookies.get('sess')
    if cookie is None:
        return None
    session_data = decrypt_cookie(cookie)
    return int(session_data["user_id"])

async def get_flag(request):
    user = get_user(request)

    if user is None:
        return templates.TemplateResponse('flag_page.html', {
            'request': request,
            'flag': 'Flag is not available to anonymous users'
        })
    if user == 1:
        return templates.TemplateResponse('flag_page.html', {
            'request': request,
            'flag': FLAG
        })
    else:
        return templates.TemplateResponse('flag_page.html', {
            'request': request,
            'flag': 'Only admin can read a flag'
        })

Разобравшись, что get_user берет информацию о пользователе из куки и возвращает его user_id, используя при этом функцию decrypt_cookie, идем в cookies.py к этой самой функции.

def decrypt_cookie(encrypted_cookie):
    envelope_data = json.loads(base64.b64decode(encrypted_cookie))

    iv = base64.b64decode(envelope_data['iv'])
    cbc = AES.new(KEY2, AES.MODE_CBC, iv)
    second_layer = base64.b64decode(envelope_data['data'])
    first_layer = cbc.decrypt(second_layer)

    nonce = base64.b64decode(envelope_data['nonce'])
    ctr = AES.new(KEY1, AES.MODE_CTR, nonce=nonce)
    data = ctr.decrypt(first_layer)
    return json.JSONDecoder().raw_decode(data.decode('utf8', errors='replace'))[0]

Так как нам доступен IV, мы можем провести атаку bit flip на CBC.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Суть в том, что P = Dec(C) ⊕ IV, соответственно мы можем подменить часть IV (в нашем случае 1 байт, потому что user_id занимает именно столько), чтобы получить желаемый плейнтекст (нам известен исходный P , поэтому проблем не возникнет).

Второй этап расшифрования не представляет угрозы, так как при CTR шифротекст не используется в функции Dec.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Итак, заходим в бурп, логинимся, берем куку, подменяем в ней IV и забираем флаг.

Вот код решения.

import json from base64 import b64decode, b64encode iv_user_id_index = json.dumps({"user_id":9}).index('9') # всегда 12 def bit_flip(cook, user_id=0): user_id ^= 1 recv_json = json.loads(b64decode(cook).decode()) iv = recv_json['iv'] iv = list(b64decode(iv)) iv[iv_user_id_index] ^= user_id recv_json['iv'] = b64encode(bytes(iv)).decode() return b64encode(json.dumps(recv_json).encode()) cook = input() user_id = int(input()) print(bit_flip(cook, user_id).decode())

Опа флаг

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Я до конца думал, что это unintended решение (иначе зачем класть таск на крипту вместе с тасками на веб), но содержимое флага развеяло сомнения

Эпилог

Мой первый райтап, мог написать дичь, не стесняйтесь критиковать.