H7CTF write up 및 후기(feat. RubiyaLab)

2025. 10. 21. 22:50review 및 write up

RubiyaLab에 들어가 처음으로 푼 CTF이자 처음으로 나가본 국제 마이너 CTF다. 아마 인도 대학쪽에서 진행한 CTF라 그런지,

자기 대학생이 아니라면 팀원제한은 없었고 그냥 즐겨보라는 식으로 개최한 느낌이였다. 처음에는 언어의 장벽이 있지않을까 심란했는데, 영어라고 해도 어차피 코드자체도 영어인지라 전혀 상관이 없었다. OSINT, Hardware, cloud등 다양한 카테고리가 출제되어 많은 경험이 된 대회라 라업을 남겨본다. (정작 새로운 카테고리 푼건 OSINT뿐이지만)

Crypto - Baby Sharingan

 

처음 나왔을 때는

 

🌀 Encoded Leaf Scroll: 0d6106061f343a2d13063c3d1433632b47602f13387a7d347f72347c272d7f257125131e2e612371366a7f73311a26143b397f22361b40
⚡ Encoded ANBU Report: 01170b15103e542572112a48603a782020674e0b3b726753161321723c2d7f397f56630d2d67237326131e62541e486738397d21311640

📜 Decoded Leaf Scroll (text): 45564552594f4e4520594f55204152452054414c4b494e472041424f55542048415320414c5245414459204245454e204b494c4c45442e
📜 Decoded ANBU Report (text): 492048415645204d414e59205448494e4753205448415420492057414e5420544f2050524f544543542041532041205348494e4f42492e

🔍 Chakra Signature (Leaf Scroll): 48374354467b7468335f73683472316e67346e5f733333735f33763372795f6d3076335f6233663072335f31745f68347070336e735f6e
🔍 Chakra Signature (ANBU Report): 48374354467b7468335f73683472316e67346e5f733333735f33763372795f6d3076335f6233663072335f31745f68347070336e735f6e

 

같은 형태로 되어있어 시그니처가 같다는 걸 보고, 두 암호문이 같은 시그니처를 썼다는 사실을 기반으로 생각했다.

실제로 암호문과 복호화된 텍스트를 xor해보면 둘 다 signature에 써있는 문자열과 같은 문자열이 나오게 되는데, 스트림 키 라는걸 알 수있는 부분이였다. 그래서 중복된 스트림키를 썼다는 것 자체가 취약점이라 시그니처를 ascii code로 바꿔보니 플래그 형식이 짤린채로 H7CTF{th3_sh4r1ng4n_s33s_3v3ry_m0v3_b3f0r3_1t_h4pp3ns_n 같은 형태로 나왔다

 

알고보니 문제가 잘못된거였고 바뀐 문제에서는

🔍 Chakra Signature (Leaf Scroll): 48377465787b7468335f73683472316e67346e5f72337633346c735f346c6c5f733363
🔍 Chakra Signature (ANBU Report): 48377465787b7468335f73683472316e67346e5f72337633346c735f346c6c5f733363723374737d4837746578

 

다음과 같은 형식으로 두 번쨰 시그니처가 중복된 문자열을 포함하는 형식으로 바뀌였는데 이를 ascii code로 바꿔보면

H7tex{th3_sh4r1ng4n_r3v34ls_4ll_s3cr3ts}H7tex와 같은 형태가 나오게된다.

대회플래그 형식은 H7CTF{ } 였지만 H7tex{th3_sh4r1ng4n_r3v34ls_4ll_s3cr3ts} 이부분을 넣었더니 정답처리됐다.

급하게 수정하느라 못바꾼듯?

OSINT - The Loard

OSINT 라는 카테고리는 이번 CTF에서 처음봤는데 특정 정보를 주어지면 검색해서 해당 하는 것을 찾는 문제였다.

이 문제의 설명은 다음과 같다


He prided himself on purity not of soul, but product. A ghost who ran factories where fog and tea conceal the blue crystals . What he made wasn’t just addictive it was alchemy, precision, perfection. He dealt in chemistry and corporations.

Things to uncover The guys name_Empires name_What was his revenue peak (eg 1_million)_What the officers named their major evidence_How much they spend on making stuffs(in myanmar only numbers)_How much they sold it for (in japan only numbers) _His passport number_Expiry date of passport(only numbers). All in lower case

GPT에게 설명을 돌리니까 tse chi lop라는 인물이 나왔는데, 문제 제목과 유사하게 아시아의 마약왕이라고 불린점, 미얀마와 일본의 판매가가 있다는 점에서 거의 확실시했다.

문제는 여권 번호와 만료일자는 개인정보상 나오지않았는데, 이 부분은 구글에 이미지를 뒤지다보니 모자이크 되지않은 여권이 남아있어 찾을 수 있었다.

다만 플래그 형식의 문제가 있어 답을 맞추고도 몇 시간동안 틀리다고 나와 해맸다

나중에야 플래그 형식이 공개가 됐는데, 만료일자가 DDMMYY 형식이여서 답을 찾고도 수 시간 뒤에 제출할 수 있었다. 

Forensics - RecoverFS

문제 파일을 받으니 깨진 디스크이미지 파일이 들어있었다. 

발견되는건 MBR 하나였고 이 부분을 HxD로 열어보니 

0x1BE ~ 0x1CD에서 00 00 02 00 EE FE BF 09 01 00 00 00 FF FF FF FF이 보여 GPT 파티션 디스크라는걸 알 수있었다

 

이후로는 wsl에서 testdisk를 깔고 photorec을 사용해서 복구 가능한 이미지가 있나 돌려봤으나 나오지않았는데 파일크기가 4기가인데 비어 있는 것처럼 보이는게 이상해서 HxD로 디스크이미지 열고 FF로 깨져있는섹터부터 섹터 1000개씩 돌아가면서 유효한 데이터가 남은게 있는지 하나하나 확인했다

그 결과 2112168섹터부터 RCRD 시그니처가 보였고 NTFS파일시스템의 유효한 데이터섹터인걸 확인해 다시 wsl에서 testDisk를 사용했다

sudo apt install testdisk -y
sudo testdisk /mnt/c/Users/user/Desktop/recovery.img

 

GPT 파티션이라는걸 확인했으니 EFI GPT를 선택해준다

이제 Analyse- Quick search를 누르면 파티션이 나오는 것을 확인할 수있다.

 

여기서 p를 누르면 디스크에 들은 파일리스트를 볼 수있는데

딱봐도 수상해보이는 secret이라는 폴더가 있어 카피 해오니 

깨진 png 파일이 하나 나왔다

이 png 파일을 hxd로 열어보니 헤더 시그니처와 IHDR 8바이트가 0으로 덮어 씌워진 상태여서 파이썬으로 빠르게 복구했다

 

exploit code

# /tmp/fix_png.py 로 저장 후 실행 (python3 /tmp/fix_png.py)
import sys

src = "C:/Users/user/Desktop/secret_is_here.png"
dst = "C:/Users/user/Desktop/fixed.png"

with open(src, "rb") as f:
    data = f.read()

# sRGB 청크의 길이+타입 패턴을 찾아 IHDR CRC 바로 뒤 위치를 잡는다.
marker = b"\x00\x00\x00\x01sRGB"  # 00 00 00 01 + 'sRGB'
idx = data.find(marker)
if idx < 0:
    print("sRGB 청크 시그니처를 찾지 못했습니다. 다른 오프셋 탐색이 필요합니다.")
    sys.exit(1)

# IHDR CRC는 sRGB 길이 필드 바로 앞의 4바이트
ihdr_crc_off = idx - 4
ihdr_data_off = ihdr_crc_off - 13  # IHDR 데이터는 13바이트

if ihdr_data_off < 0:
    print("IHDR 데이터 오프셋 계산 오류")
    sys.exit(1)

ihdr_data = data[ihdr_data_off:ihdr_crc_off]
if len(ihdr_data) != 13:
    print("IHDR 데이터 길이가 13이 아닙니다.")
    sys.exit(1)

# 확인용: IHDR 데이터의 구조 확인 (width, height, depth, color type 등)
width = int.from_bytes(ihdr_data[0:4], "big")
height = int.from_bytes(ihdr_data[4:8], "big")
bd, ct, comp, filt, inter = ihdr_data[8:]
print(f"Detected IHDR: {width=} {height=} bd={bd} ct={ct} comp={comp} filt={filt} inter={inter}")

# 새로운 파일 구성:
# PNG 시그니처(8) + IHDR length(4, 0x0000000D) + 'IHDR'(4) + IHDR data(13) + 기존의 IHDR CRC부터 끝까지
png_sig = b"\x89PNG\r\n\x1a\n"
ihdr_len = (13).to_bytes(4, "big")
ihdr_type = b"IHDR"

fixed = png_sig + ihdr_len + ihdr_type + ihdr_data + data[ihdr_crc_off:]

with open(dst, "wb") as f:
    f.write(fixed)

print(f"Wrote fixed PNG: {dst}")

 

고쳐진 fixed.png에서 플래그가 나온 모습을 확인할 수 있었다

 


대회가 마무리되자 우리 팀의 점수는 450팀 언저리 중 총합 5위였는데, 팀전에서 이렇게 높은 순위 받은건 처음이였다. 괜히 현 기준 CTFtime 한국1위 팀이 아니다. 미약하게나마 내가 푼 문제도 도움이 됐기를 바란다.

 

여담으로 CTF를 풀때까지는 try out 단계여서 2개월안에 벗어나지 못하면 강제퇴장 이였는데, 기준이 마이너CTF에서 1인분할 가능성으로 낮아져서 그런지 이 CTF를 푼 직후 바로 try out을 벗어날 수 있었다. 앞으로 정식으로 RubiyaLab으로 활동할 날이 기대된다.

'review 및 write up' 카테고리의 다른 글

ACDC2025 write up  (0) 2025.11.07
osu!CTF2025 [Forensics]map-dealer write up  (0) 2025.10.29
Fiesta2025 write up  (0) 2025.10.03
SPACE WAR - Earth Write UP 및 후기  (0) 2025.09.28
CCE2025 예선 write up 및 후기  (1) 2025.08.20