[Global Vehicle Cybersecurity Competition 2025] #4 AutoGraph [RAMN] Write-Up

2025. 9. 14. 16:19·CTF
반응형

 

문제 정보 

Prompt: I downloaded a firmware update for RAMN’s ECU B and C from a secret OTA portal in development. Can you help me sign my own firmware files? I have attached a caringcaribou UDS RDBI dump log file, if that is any help.

(Note: flag is secret key in decimal format – not hexadecimal. It is NOT the password for the .zip file).

2 files are provided:  
1. challenge_signed_firmware_files.zip
2. challenge4_RDID_dump.log

 


문제 풀이 요약

  1. 주어진 log 파일 내에 0x7e3, 0x7eb CAN id를 사용하는 UDS가 존재함
    • 0x22 명령을 이용해서 브루투포싱하는 것으로 보임
  2. UDS 프레임 내에 public key를 추출하여 pub.pem 파일로 저장
  3. ECUB.hex.sig, ECUC.hex.sig 파일을 sigB.der, sigC.der 파일로 변환
  4. pub.pem과 der 파일들을 이용해 ECU*.hex 파일 검증 수행
  5. sig*.der 파일의 서명 바이트 구조를 확인하여 동일한 난수(r)값 사용 식별
  6. 연산을 통해 개인키(d)를 추출

문제 풀이 상세

1. 주어진 log 파일 내에 0x7e3, 0x7eb CAN ID를 사용하는 UDS frame이 있으며 did을 brute-force하여 출력하는 동작을 수행하고 있습니다.

 

• 출력된 주요 did를 정리해보면 아래와 같습니다.

22 1d f2
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzWvXZ0MMevWSHMMCzdJKOq2uXr1kg45NS1z0w8ZWc0Lr
n2qYn0QsXdR3aTXK9kDmPx2fH3wxt9OkhrwHnl+Hsg==
-----END PUBLIC KEY-----

22 1d f3
1234567890123456789012345678901234567890

22 1d f4
115792089237316195423570985008687907852837564279074904382605163141518161494337

22 1d f5
SECP256k1

 

2. 식별된 공캐키를 복구하여 검증을 수행합니다.

a. byte를 추출하여 pub.pem 파일로 저장

-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzWvXZ0MMevWSHMMCzdJKOq2uXr1kg45NS1z0w8ZWc0Lr
n2qYn0QsXdR3aTXK9kDmPx2fH3wxt9OkhrwHnl+Hsg==
-----END PUBLIC KEY-----

 

b. pub.pem 파일이 어떤 곡선 위에서 정의된 공개키인지 확인

• 검증 시 ASN1 OID가 DID : 0x1dF5에 쓰인 문자열 secp256k1와 일치

$ openssl ec -pubin -in pub.pem -text -noout -conv_form uncompressed
read EC key
Public-Key: (256 bit)
pub:
    04:cd:6b:d7:67:43:0c:7a:f5:92:1c:c3:02:cd:d2:
    4a:3a:ad:ae:5e:bd:64:83:8e:4d:4b:5c:f4:c3:c6:
    56:73:42:eb:9f:6a:98:9f:44:2c:5d:d4:77:69:35:
    ca:f6:40:e6:3f:1d:9f:1f:7c:31:b7:d3:a4:86:bc:
    07:9e:5f:87:b2
ASN1 OID: secp256k1

 

 

3. challenge_signed_firmware_files.zip 의 비밀번호를 hashcat을 이용해 cracking을 시도합니다.

• 사용된 dictionary는 여러 비밀번호 파일을 조합한 rockyou와 유사한 파일입니다.

$ hashcat -m 17225 -a 0 ./hash2.txt ../final_combo.txt --backend-ignore-cuda
hashcat (v6.2.5) starting
...
Dictionary cache hit:
* Filename..: ../final_combo.txt
* Passwords.: 29605980
* Bytes.....: 294047725
* Keyspace..: 29605980

$pkzip$4*1*1*0*0*24*c428*603a23180c25ce5d86ccbb95a7a5e5292381545dfcb146015c9b0acb1d7b70032bc8cd66*1*0*8*24*fc61*a8558f624cbb9eb41d07248cb6f1f04e9ff9dd59a1ac0dd0d364077876972f4e9a8d7c8d*1*0*8*24*1ec8*83f8ef4dce5e6fd80718b62388b3e165588b0ad28bff032be21d2d4dc36e4b6f5f15152e*2*0*4c*40*34d3532f*17894*2a*0*4c*34d3*3a2c342737bb82610c7aa3635a975d694d2ee223ed6be0797673024f41f88c79827102bda1700468df9b6b084402e13d26256b9d111881295bc375f7d32c5568c6dd29d836ee0d2cd4380103*$/pkzip$:password12345678
...

 

4. 해당 압축 파일을 해제하면 4개의 파일이 존재하는 것을 확인합니다.

ECUB.hex
ECUB.hex.sig
ECUC.hex
ECUC.hex.sig

 

5. .hex.sig 파일의 구조는 64바이트 RAW r||s인 것으로 추측되며 2개의 파일을 모두 .der로 변환을 수행합니다.

# make_der.py
# pip install cryptography

from math import ceil
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature

with open("ECUB.hex.sig","rb") as f:
    sig = f.read()
assert len(sig)==64, f"len={len(sig)}"

r = int.from_bytes(sig[:32], "big")
s = int.from_bytes(sig[32:], "big")
der = encode_dss_signature(r, s)

with open("sigB.der","wb") as f:
    f.write(der)
print("OK:", len(der), "bytes")

 

6. .der 파일과 .hex 파일을 이용해 검증을 수행하여 맞는 서명 파일인지 확인합니다.

• OpenSSL로 검증(SHA-256 가정)

$ openssl dgst -sha256 -verify pub.pem -keyform PEM -signature sig.der challenge_signed_firmware_files/ECUB.hex
Verified OK

 

7. 2개의 .der 서명 파일 구조를 확인한 결과 r 값이 동일하기 때문에 d값을 연산할 수 있습니다.

•  r 이 같은 경우 같은 nonce(k)를 사용했기 때문에 취약한 경우임

# der sign file format
30 LL ; SEQUENCE
  02 l_r rr..; INTEGER r
  02 l_s ss..; INTEGER s
$ openssl asn1parse -inform DER -in sigB.der
    0:d=0  hl=2 l=  70 cons: SEQUENCE
    2:d=1  hl=2 l=  33 prim: INTEGER           :CB8F2F4901E5DC0610A562309A0BA238289EDD5A6A819A115CA3B2AC802E7589
   37:d=1  hl=2 l=  33 prim: INTEGER           :90B2130178E5AA4501E0DA0FA9FA02A82C94F9F137664E43562D040D68B21262
$ openssl asn1parse -inform DER -in sigC.der
    0:d=0  hl=2 l=  70 cons: SEQUENCE
    2:d=1  hl=2 l=  33 prim: INTEGER           :CB8F2F4901E5DC0610A562309A0BA238289EDD5A6A819A115CA3B2AC802E7589
   37:d=1  hl=2 l=  33 prim: INTEGER           :AB711A35E232B03C5BAA9AD2D1F3F74D348381FF31203AB3CE66F5980DE2F945

 

8. 개인키(d)를 구하는 파이썬 코드를 작성하여 각 파일을 대입하면 FLAG를 얻을 수 있습니다.

# pip install cryptography
from hashlib import sha256
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
from cryptography.hazmat.primitives.serialization import load_pem_public_key

# 입력 파일 이름
sig1_path = "sigB.der"     # 첫 번째 서명 DER
msg1_path = "ECUB.hex"     # 첫 번째 메시지 바이너리
sig2_path = "sigC.der"     # 두 번째 서명 DER
msg2_path = "ECUC.hex"     # 두 번째 메시지 바이너리

# secp256k1 군 차수 n
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

# 1) r,s 추출
r1,s1 = decode_dss_signature(open(sig1_path,"rb").read())
r2,s2 = decode_dss_signature(open(sig2_path,"rb").read())
assert r1 == r2, "r가 다르면 k 재사용 아님"
r = r1

# 2) 해시 z 계산(SHA-256, big-endian 정수)
z1 = int.from_bytes(sha256(open(msg1_path,"rb").read()).digest(), "big")
z2 = int.from_bytes(sha256(open(msg2_path,"rb").read()).digest(), "big")

# 3) 모듈러 역원
def inv(x,m): return pow(x, -1, m)

# 4) k, d 복구
k = ((z1 - z2) * inv((s1 - s2) % n, n)) % n
d = ((s1 * k - z1) * inv(r, n)) % n

print("r =", hex(r))
print("s1=", hex(s1))
print("s2=", hex(s2))
print("k =", hex(k))
print("d =", hex(d))
print("d(dec) =", d)

 

• 코드 수행 결과는 아래와 같습니다.

FLAG : 58198666691408413489157977283298548714183388226985257069167369868995344613920

$ python .\cal_d.py
r = 0xcb8f2f4901e5dc0610a562309a0ba238289edd5a6a819a115ca3b2ac802e7589
s1= 0x90b2130178e5aa4501e0da0fa9fa02a82c94f9f137664e43562d040d68b21262
s2= 0xab711a35e232b03c5baa9ad2d1f3f74d348381ff31203ab3ce66f5980de2f945
k = 0x3a0c92075c0dbf3b8acbc5f96ce3f0ad2
d = 0x80ab472c8929b2190636f5a394aabf8d3c45ef7f4c6ccc9bb1257db73d4e4220
d(dec) = 58198666691408413489157977283298548714183388226985257069167369868995344613920

 

 

 


 

 

 

반응형

'CTF' 카테고리의 다른 글

[Global Vehicle Cybersecurity Competition 2025] #3 Password Change Policy [RAMN] Write-Up  (0) 2025.09.14
[Global Vehicle Cybersecurity Competition 2025] #1 Wired Keyless Entry [PowerPC] Write-Up  (0) 2025.09.14
[Global Vehicle Cybersecurity Competition 2025] #3 Firmware Reveal Write-Up  (0) 2025.09.14
[Global Vehicle Cybersecurity Competition 2025] #2 SAE EAS Write-Up  (0) 2025.09.14
[Global Vehicle Cybersecurity Competition 2025] #1 Red Alert Write-Up  (0) 2025.09.14
'CTF' 카테고리의 다른 글
  • [Global Vehicle Cybersecurity Competition 2025] #3 Password Change Policy [RAMN] Write-Up
  • [Global Vehicle Cybersecurity Competition 2025] #1 Wired Keyless Entry [PowerPC] Write-Up
  • [Global Vehicle Cybersecurity Competition 2025] #3 Firmware Reveal Write-Up
  • [Global Vehicle Cybersecurity Competition 2025] #2 SAE EAS Write-Up
tae3
tae3
  • tae3
    tae3log
    tae3
  • 링크

    • LinkedIn
  • 전체
    오늘
    어제
    • 전체보기 (50)
      • 보안 이론 (37)
        • 웹 보안 (13)
        • 네트워크 보안 (6)
        • 시스템 보안 (3)
        • 클라우드 보안 (15)
        • 차량 보안 (0)
      • CTF (12)
      • 리눅스 (1)
  • 태그

    /dpkg/lock
    AES
    ALB
    amazon linux
    argus
    Auto scaling
    Automotive Security
    awk
    AWS
    bWAPP
  • hELLO· Designed By정상우.v4.10.4
tae3
[Global Vehicle Cybersecurity Competition 2025] #4 AutoGraph [RAMN] Write-Up
상단으로

티스토리툴바