문제 출처 https://h4ckingga.me
https://github.com/W3rni0/RACTF_2020#dimensionless-loading

힌트 : Something is borken
문제 파일 : linefeed.png
사진파일 그림판으로 확인해보면 아래와 같다.

사진을 보고 RGB 관련 값에 문제가 있지 않을까 예상했다.
파일을 HXD 프로그램으로 열어보면 아래와 같이 나온다.

무엇인가 깨져있다고 했으니, 여기서 확인해봐야 할 요소는 아래와 같다.
1. PNG 확장자 시그니처
2. IHDR, 등 각 청크별 헤더 및 데이터
PNG 파일 청크 헤더
IHDR: 이미지 헤더. "49 48 44 52"로 시작.
PLTE: 팔레트. "50 4C 54 45"로 시작.
IDAT: 이미지 데이터. "49 44 41 54"로 시작.
IEND: 이미지의 끝. "49 45 4E 44"로 시작.
각 청크별 헤더는 이상이 없었다.
그렇다면 데이터를 살펴봐야 한다.
데이터를 어떻게 맞출 수 있을까? 를 생각 하기 전에 파일을 만든 사람도 생각할 것이다. 데이터가 깨지면 어떻게 맞출 수 있을까?
이래서 데이터 무결성을 위해 청크별 CRC가 존재한다.
청크별 해당하는 데이터로부터 CRC를 생성하므로, CRC가 이상이 없다는 가정(1)하에 데이터를 확인해보면 된다.
Cyclic Redundancy Check, 순환 중복 검사의 약자로 청크의 무결성을 확인하는 코드이다.
청크 앞에 CRC 코드가 존재한다.
파이썬에서 사용하는 calclate_crc32 메소드가 존재하는데, 아래 예시(실제 CRC 코드를 생성하는게 아니다. 이렇게 생성한다고 예시를 보여주기 위해 기술)
def calculate_crc32(data):
# CRC32 다항식
polynomial = 0xEDB88320
# 초기값
crc = 0xFFFFFFFF
# 데이터 처리
for byte in data:
crc ^= byte # 데이터 비트를 CRC 값과 XOR 연산
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ polynomial # 나머지가 남으면 다항식과 XOR 연산
else:
crc >>= 1
# 최종 CRC32 값
crc = crc ^ 0xFFFFFFFF # 최종 결과와 XOR 연산
return crc
# 예시 데이터
data = b'IHDR\x00\x00\x01F\x00\x00\x01F\x08\x02\x00\x00\x00\xDA@\xA2F'
# CRC32 값 계산
crc32_result = calculate_crc32(data)
# 결과 출력
print(hex(crc32_result))
IDHR 이미지 헤더는 PNG 파일의 기본적 구성을 17바이트 구성으로 가지고 있다.
아래와 같은 예시가 있다고 가정해보자.
49 48 44 52 00 00 01 F0 00 00 01 F0 08 02 00 00 00 DA 40 A2 46
PNG 파일 시그니처 : 49 48 44 52
1. Width: 00 00 01 F0 (16진수) = 500
2. Height: 00 00 01 F0 (16진수) = 500
3. Bit Depth: 08 (16진수) = 8 bits
4. Color Type: 02 (16진수) = Truecolor (RGB)
5. Compression Method: 00 (16진수) = Deflate/Inflate 압축
6. Filter Method: 00 (16진수) = Standard PNG filtering
7. Interlace Method: 00 (16진수) = 비 인터레이스 (Non-interlaced)
8. IHDR 청크 : 44 B4 48 DD (16진수) = 너비, 높이, 비트깊이, 컬러 타입 로 생성.
파일 시그니처 제외 17바이트로 구성되어 있다.
시그니처는 각 파일마다 가지고 있는 고유 포맷
PNG : 89 50 4E 47 0D 0A 1A 0A ~ 49 45 4E 44 AE 42 60 82
JPG : FF D8 FF E0 / FF D8 FF E1 / FF D8 FF DB ~ FF D9
BMP : 42 4D
PDF : 25 50 44 46
GIF : 47 49 46 38
WAV : 52 49 46 46
ZIP : 50 4B 03 04
PPTX : 50 4B 03 04 14 00 06 00
MP3 : 49 44 33

무작위 대입방식을 사용해 너비와 높이 값을 임의 범위에서 지정 후 파이썬 crc32메소드 결과로 현재 파일의 CRC값인 0x44b448dd가 나오는지 확인했다.
from zlib import crc32
file_path = r"C:\linefeed.png" # 파일 경로를 적절히 수정
# 'corrupted.png' 파일을 이진 모드로 읽어옴
data = open(file_path, 'rb').read()
# IHDR 청크의 시작 인덱스 설정
index = 12
# IHDR 청크의 바이트 배열 생성
ihdr = bytearray(data[index:index + 17])
# 너비와 높이의 인덱스 설정
width_index = 7
height_index = 11
# 가능한 모든 너비와 높이의 조합을 시도
for x in range(1, 2000):
height = bytearray(x.to_bytes(2, 'big'))
for y in range(1, 2000):
width = bytearray(y.to_bytes(2, 'big'))
# 현재 조합의 너비와 높이를 IHDR 청크에 적용
for i in range(len(height)):
ihdr[height_index - i] = height[-i - 1]
for i in range(len(width)):
ihdr[width_index - i] = width[-i - 1]
# 현재 조합의 CRC32 값이 목표 값과 일치하면 출력하고 종료
if hex(crc32(ihdr)) == '0x44b448dd':
print("width: {} height: {}".format(width.hex(), height.hex()))
# 너비를 초기화하여 다음 반복에서 다른 값을 시도
for i in range(len(width)):
ihdr[width_index - i] = bytearray(b'\x00')[0]


해당하는 값을 HxD 프로그램으로 너비와 높이를 수정한 후 그림판으로 다시 파일을 열었더니 플래그 값이 나왔다.
성공했다.