BMP Recovery

chwrld·2024년 3월 31일
0

dreamhack

목록 보기
12/19
post-thumbnail

🚩 문제 설명

BMP 파일에서 이런 중요한 값들을 지워버리다니!
빨리 복구해서 플래그를 읽어주세요!
플래그 형식은 DH{...}로, flag.bmp를 올바르게 복구하면 찾을 수 있습니다.

chal.py

with open('flag.bmp', 'rb') as f:
    data = bytearray(f.read())

data[:0x1C] = b'\x00' * 0x1C
data[0x22:0x36] = b'\x00' * 0x14

with open('flag.bmp.broken', 'wb') as f:
    f.write(data)

코드를 보면 0:1C 까지 0으로 채우고 다시 0x22:0x36까지 0으로 채운다.

즉, 우리가 복구해야 할 영역이다.

flag.bmp.broken

저 부분만 훼손하지 않았다. 픽셀당 바이트 수를 담은 영역이다. 24비트면 3바이트이고 1픽셀당 3바이트로 표현된다. 잘 기억해두자.


🚀 exploit.py

일단 코드를 보며 어떻게 복구해야 될지 알아보자.

with open('flag.bmp.broken', 'rb') as f:
    data = bytearray(f.read())

total_bytes = 14309622  # 전체 파일 바이트 크기
header_size = 54  # BMP 파일 헤더 크기
info_header_size = 40
bitplane_value = 1
bytes_per_pixel = 3  # 픽셀당 바이트 수 (24비트 = 3바이트)

# 이미지 데이터 크기 계산
image_data_size = total_bytes - header_size

# 전체 픽셀 수 계산
num_pixels = image_data_size // bytes_per_pixel

data[0x0] = 0x42
data[0x1] = 0x4D
data[0x2:0x6] = total_bytes.to_bytes(4, byteorder='little')     
data[0xA:0xE] = header_size.to_bytes(4, byteorder='little')
data[0xE:0x12] = info_header_size.to_bytes(4, byteorder='little')     
data[0x1A:0x1C] = bitplane_value.to_bytes(2, byteorder='little')     
data[0x22:0x26] = image_data_size.to_bytes(4, byteorder='little')       


# 가능한 가로와 세로 조합 찾기
for width in range(1, int(num_pixels**0.5) + 1):
    if num_pixels % width == 0:
        height = num_pixels // width
        data[0x12:0x16] = width.to_bytes(4, byteorder='little')
        data[0x16:0x1A] = height.to_bytes(4, byteorder='little')
        with open(f'flag_{width}.bmp', 'wb') as f:
            f.write(data)
        data[0x12:0x16] = height.to_bytes(4, byteorder='little')
        data[0x16:0x1A] = width.to_bytes(4, byteorder='little')
        with open(f'flag_{height}.bmp', 'wb') as f:
            f.write(data)
  1. 먼저 0x42, 0x4D 값이 들어간다 bmp 시그니처 (BM)
  2. 전체 파일 바이트 값
  3. 전체 헤더 바이트 값
  4. info header 바이트 값
  5. bitplane 값 1로 고정
  6. image 데이터 바이트 값

나머지는 리틀 엔디안과 몇 바이트 크기로 넣어줄 건지 정하고
가로x세로 조합을 찾으면 된다.


가로x세로 조합

가로x세로 조합의 경우의 수는 width x height 또는 height x width 2개이다. bmp 파일의 width 영역과 height 영역이 고정되어 있기 때문이다.

widthheight중 하나만 정해지면 나머지 값도 정해진다. 이 관계는 전체 파일 바이트 값과 관련이 있는데, 전체 파일 바이트 값을 100이라고 가정하면 가능한 가로x세로 조합은 10x10 5x20 4x25 2x50 1x100이다. 즉 widthheight 둘중 하나도 전체 파일 바이트 값의 제곱근을 넘길 수 없다.

따라서 다음과 같은 식이 탄생한다.

for width in range(1, int(num_pixels**0.5) + 1):
  • num_pixels?

# 전체 픽셀 수 계산
num_pixels = image_data_size // bytes_per_pixel

여기서 num_pixels변수는 전체 바이트 값에서 3을 나눈 값이다. 이유는 아까 기억하라고 했던 1픽셀당 3바이트로 표현되기 때문인데 3을 나눈 num_pixels값에서 가로x세로 값을 구하면 된다.

  • All Case

    하지만 이대로 하면 width 1개에 대한 경우만 구했기 때문에
    widthheight을 바꿔서 1번 더 복구해주면 모든 경우의 수가 구해진다.
    이제 여기서 flag가 들어있는 파일을 찾으면 된다.


profile
BoB 13th 최강포린이👮

0개의 댓글