랜섬웨어 분석 보고서

Devman 랜섬웨어 분석 보고서

geonwoo9643 2026. 2. 5. 18:27

1. 개요 (Overview)


1.1 분석 배경

DevMan 랜섬웨어는 2025년 초부터 활발히 유포되고 있는 악성코드로, ChaCha8 스트림 암호화를 기반으로 한 경량화된 암호화 방식을 채택하고 있습니다. DragonForce 랜섬웨어의 변종으로 추정되며, 유사한 암호화 메커니즘을 사용하지만 더 단순화된 구조를 가지고 있습니다. 본 보고서는 DevMan의 암호학적 구조를 정밀 분석하고, API 후킹을 통한 실시간 키 추출 기법과 복호화 전략을 제시합니다.


1.2 핵심 요약

  • 경량화된 암호화 구조 : ChaCha8 스트림 암호화를 사용하여 빠른 암호화 속도를 달성합니다. 4번의 더블 라운드를 수행하여 성능과 보안성의 균형을 추구합니다.
  • 파일별 독립 키 생성 : 각 파일마다 고유한 32바이트 키와 8바이트 nonce를 CryptGenRandom API를 통해 생성합니다.
  • 단순화된 암호화 전략 : 복잡한 파일 크기별 전략 없이 일관된 암호화 방식을 적용합니다.
  • 완벽한 복호화 가능성 : CryptGenRandom API 호출 시점에 생성되는 평문 키를 API 후킹으로 실시간 캡처할 경우, 완벽한 복호화가 가능합니다. 본 분석에서 개발한 복호화 스크립트를 통해 실증적으로 검증되었습니다.

 

2. 식별 정보 (Identification)


  • Malware Family: DevMan
  • Filetype: PE32 (Windows Executable)
  • Hash (SHA256): df5ab9015833023a03f92a797e20196672c1d6525501a9f9a94a45b0904c7403
  • Extension: .devman
  • Target: Windows 기반 시스템

 

3. 분석 환경 및 도구 (Tools)


구분 도구명 (Tool) 용도 (Purpose)
정적 분석 IDA Pro ChaCha8 구현 루틴 및 암호화 로직 분석
동적 분석 CryptGenRandom API Hooking (x86) 실시간 키 및 nonce 추출
행위 분석 Process Monitor 파일 I/O 및 암호화 행위 추적
검증 도구 Python ChaCha8 복호화 스크립트 개발 및 PoC

 

4. 암호화 기술 분석 (Technical Analysis)


4.1 전체 암호화 워크플로우

DevMan은 단순화된 암호화 모델을 기반으로 다음과 같은 4단계 프로세스를 거칩니다.

  1. 키 및 Nonce 생성 : CryptGenRandom API를 통해 파일별로 고유한 32바이트 ChaCha 키와 8바이트 nonce를 생성
  2. ChaCha State 초기화 : 생성된 키와 nonce로 ChaCha state를 초기화하고 카운터를 0으로 설정
  3. 파일 암호화 : ChaCha8 알고리즘을 사용하여 파일 전체를 암호화
  4. 파일명 변경 : 암호화된 파일의 확장자를 .devman으로 변경

4.2 키 생성 및 관리 (Key Generation)

4.2.1 CryptGenRandom을 통한 난수 생성

DevMan은 Windows CryptoAPI의 CryptGenRandom 함수를 사용하여 암호학적으로 안전한 난수를 생성합니다.

// 각 파일마다 고유한 키와 nonce 생성
CryptGenRandom(hProv, 32, chacha_key);    // 32바이트 ChaCha Key
CryptGenRandom(hProv, 8, chacha_nonce);   // 8바이트 ChaCha Nonce

 

4.2.2 ChaCha State 초기화

생성된 키와 nonce를 사용하여 ChaCha state를 초기화합니다.

  • Constant : "expand 32-byte k" (ChaCha 표준 상수)
  • Key : CryptGenRandom으로 생성한 32바이트 난수
  • Counter : 0으로 초기화 (블록마다 1씩 증가)
  • Nonce : CryptGenRandom으로 생성한 8바이트 난수

ChaCha State 구조 (64바이트):

Constant (16B) Key (32B) Counter (8B) Nonce (8B)

4.3 ChaCha8 암호화 알고리즘

4.3.1 ChaCha8 키스트림 생성

ChaCha8은 일반적인 ChaCha20의 10번 더블 라운드 대신, 4번의 더블 라운드를 수행하여 성능을 최적화한 변형입니다.

ChaCha8 라운드 구조:
┌─────────────────────────────────┐
│    Initial State (64 bytes)     │
│  [Constant | Key | Ctr | Nonce] │
└─────────────────────────────────┘
            ↓
    ┌───────────────┐
    │ Double Round  │  ← 4번 반복 (ChaCha8)
    │ (QR 함수 8회) │
    └───────────────┘
            ↓
┌─────────────────────────────────┐
│      Keystream (64 bytes)       │
└─────────────────────────────────┘

 

4.3.2 Quarter Round 연산

Quarter Round (a, b, c, d):
    a += b; d ^= a; d <<<= 16;
    c += d; b ^= c; b <<<= 12;
    a += b; d ^= a; d <<<= 8;
    c += d; b ^= c; b <<<= 7;

 

4.3.3 ChaCha8 블록 생성

def chacha_block(key32, counter, nonce64, rounds=8):
    constants = b"expand 32-byte k"
    key_words = list(struct.unpack("<8I", key32))
    nonce_words = list(struct.unpack("<2I", nonce64))
    counter_low = counter & 0xffffffff
    counter_high = (counter >> 32) & 0xffffffff
    
    # Initial state 구성
    state = [
        struct.unpack("<I", constants[0:4])[0],
        struct.unpack("<I", constants[4:8])[0],
        struct.unpack("<I", constants[8:12])[0],
        struct.unpack("<I", constants[12:16])[0],
    ] + key_words + [counter_low, counter_high] + nonce_words
    
    working = state.copy()
    
    # 4번의 더블 라운드
    for _ in range(rounds // 2):
        # Column round
        quarter_round(working, 0, 4, 8, 12)
        quarter_round(working, 1, 5, 9, 13)
        quarter_round(working, 2, 6, 10, 14)
        quarter_round(working, 3, 7, 11, 15)
        
        # Diagonal round
        quarter_round(working, 0, 5, 10, 15)
        quarter_round(working, 1, 6, 11, 12)
        quarter_round(working, 2, 7, 8, 13)
        quarter_round(working, 3, 4, 9, 14)
    
    # Add initial state
    out = [(working[i] + state[i]) & 0xffffffff for i in range(16)]
    return struct.pack("<16I", *out)

 

4.3.4 XOR 암호화 및 카운터 증가

# 생성된 키스트림과 평문을 XOR
for i in range(block_size):
    ciphertext[i] = plaintext[i] ^ keystream[i]

# 블록마다 카운터 증가
counter++

4.4 파일 처리 아키텍처

DevMan의 파일 암호화 프로세스는 다음과 같습니다:

[파일 처리 과정]
1. 파일 열기 및 읽기
   ↓
2. CryptGenRandom 호출
   - 32바이트 ChaCha 키 생성
   - 8바이트 nonce 생성
   ↓
3. ChaCha State 초기화
   - Constant: "expand 32-byte k"
   - Key: 생성된 32바이트
   - Counter: 0
   - Nonce: 생성된 8바이트
   ↓
4. ChaCha8 블록 암호화
   - 4번의 더블 라운드
   - 키스트림 생성
   - XOR 연산
   ↓
5. 암호화된 데이터 쓰기
   ↓
6. 파일명 변경
   원본.확장자 → 원본.확장자.devman

4.5 정적 분석

CSP 초기화

  • RSA-4096 공개키 임포트

 

  • pbData (RSA1 시그니처)

 

  • 5MB 작업 버퍼 할당

 

  • 크리티컬 섹션 동기화 (5초 대기)

 

CSP(암호화 컨텍스트) 획득 시도

 

디렉터리 스캔

  • 파일 검색

 

  • 디렉터리 건너뛰기 (”.”, “..”, 리파스 포인트)

 

  • 디렉터리 화이트/블랙리스트 체크 (목록 난독화 → 런타임 시, 복호화)

 

  • 파일 확장자 화이트/블랙리스트 체크 (목록 난독화 → 런타임 시, 복호화)

 

암호화 전략 선택

 

1. ChaCha8 키 생성 + RSA 메타데이터 생성

  • ChaCha 키 생성 (32바이트)

 

  • Nonce 생성 (8바이트)

 

  • ChaCha 상수 설정

 

  • 카운터 초기화

 

  • 파일명 메타데이터 저장

 

  • RSA 공개키로 메타데이터 암호화

 

2. 파일 열기

 

3. 시스템 파일 제외 (목록은 난독화 → 런타임 복호화)

 

4. 파일 크기 읽기 및 암호화 전략 선택 시작

 

  • 전체 파일 암호화 (작은 파일, 특수 파일(.dll/.exe))

 

  • 부분 암호화 (중간 파일)

 

  • Stride 암호화 (큰 파일)

 

  • 암호화된 데이터 쓰기

 

5. 파일명 변경 (.infected 확장자 추가)

 

6. 키/IV 초기화

 

파일 암호화 (ChaCha8 + RSA 메타데이터)

  • ChaCha8 상태 초기화

 

  • ChaCha8 암호화 (4회 반복)

 

  • 카운터 증가 (64바이트 블록 단위)

 

  • 암호화된 데이터 쓰기

 

5. 복호화 기술 분석 (Decryption Strategy)


5.1 복호화 가능성 분석

DevMan 랜섬웨어는 다음과 같은 특성으로 인해 완벽한 복호화가 가능합니다:

  1. 키 생성 시점 취약점 : CryptGenRandom API 호출 시 생성되는 평문 키를 API 후킹으로 실시간 캡처 가능
  2. 메모리 덤프 가능성 : 암호화 진행 중 프로세스 메모리에 평문 키가 존재
  3. 파일별 독립 암호화 : 각 파일마다 별도의 키를 사용하므로, 해당 키만 있으면 개별 파일 복호화 가능
  4. 단순한 암호화 구조 : 추가적인 메타데이터나 RSA 암호화 없이 ChaCha8만 사용

5.2 ChaCha8 키 복구 메커니즘

DevMan의 암호학적 약점은 CryptGenRandom API 호출 시점에 생성되는 32바이트 키와 8바이트 nonce가 평문으로 메모리에 존재한다는 점입니다.

[취약점 구조]
CryptGenRandom API 호출
         ↓
   평문 키 생성 (32B)
         ↓
  ★ API 후킹 지점 ★  ← 키 캡처 가능!
         ↓
  ChaCha State 초기화
         ↓
   파일 암호화 수행

5.3 ChaCha8 복호화 스크립트 개발

캡처한 키 정보를 활용하여 Python 기반 ChaCha8 복호화 스크립트를 개발했습니다.

 

스크립트 주요 기능:

  1. API 후킹 로그 파일 파싱 (키/nonce 추출)
  2. ChaCha8 state 초기화 및 키스트림 생성
  3. XOR 연산을 통한 복호화 수행
  4. 파일 시그니처 검증 (매직 넘버)
  5. 원본 파일 복원 및 저장

 

핵심 복호화 로직:

# 파일 시그니처 정의
COMMON_SIGNATURES = {
    b"%PDF-": "pdf",
    b"\x89PNG\r\n\x1a\n": "png",
    b"PK\x03\x04": "zip",
    b"MZ": "exe",
    b"\xff\xd8\xff": "jpg",
}

def rotl32(v, n):
    """32비트 좌측 순환 시프트"""
    return ((v << n) & 0xffffffff) | (v >> (32 - n))

def quarter_round(s, a, b, c, d):
    """ChaCha Quarter Round 연산"""
    s[a] = (s[a] + s[b]) & 0xffffffff
    s[d] ^= s[a]; s[d] = rotl32(s[d], 16)
    s[c] = (s[c] + s[d]) & 0xffffffff
    s[b] ^= s[c]; s[b] = rotl32(s[b], 12)
    s[a] = (s[a] + s[b]) & 0xffffffff
    s[d] ^= s[a]; s[d] = rotl32(s[d], 8)
    s[c] = (s[c] + s[d]) & 0xffffffff
    s[b] ^= s[c]; s[b] = rotl32(s[b], 7)

def chacha_block(key32, counter, nonce64, rounds=8):
    """ChaCha8 블록 생성"""
    constants = b"expand 32-byte k"
    key_words = list(struct.unpack("<8I", key32))
    nonce_words = list(struct.unpack("<2I", nonce64))
    
    # State 초기화
    state = [
        struct.unpack("<I", constants[i:i+4])[0] 
        for i in range(0, 16, 4)
    ] + key_words + [counter & 0xffffffff, 
                      (counter >> 32) & 0xffffffff] + nonce_words
    
    working = state.copy()
    
    # 4번의 더블 라운드
    for _ in range(rounds // 2):
        # Column rounds
        quarter_round(working, 0, 4, 8, 12)
        quarter_round(working, 1, 5, 9, 13)
        quarter_round(working, 2, 6, 10, 14)
        quarter_round(working, 3, 7, 11, 15)
        # Diagonal rounds
        quarter_round(working, 0, 5, 10, 15)
        quarter_round(working, 1, 6, 11, 12)
        quarter_round(working, 2, 7, 8, 13)
        quarter_round(working, 3, 4, 9, 14)
    
    out = [(working[i] + state[i]) & 0xffffffff for i in range(16)]
    return struct.pack("<16I", *out)

def chacha_keystream(key32, nonce64, rounds, length):
    """키스트림 생성"""
    ks = bytearray()
    ctr = 0
    while len(ks) < length:
        ks.extend(chacha_block(key32, ctr, nonce64, rounds))
        ctr += 1
    return bytes(ks[:length])

def parse_hook_log(path):
    """로그 파일에서 키-nonce 쌍 추출"""
    text = Path(path).read_text(errors="ignore")
    pairs = []
    current_key = None
    
    for line in text.split('\n'):
        if 'Data: ' in line:
            match = re.search(r'Data:\s+([0-9a-fA-F]+)', line)
            if match:
                data_hex = match.group(1).lower()
                if len(data_hex) == 64:  # 32바이트 키
                    current_key = data_hex
                elif len(data_hex) == 16 and current_key:  # 8바이트 nonce
                    pairs.append((current_key, data_hex))
                    current_key = None
    return pairs

def decrypt_file(encrypted_file, key_hex, nonce_hex, output_file):
    """파일 복호화"""
    # 키와 nonce를 바이너리로 변환
    key = unhexlify(key_hex)
    nonce = unhexlify(nonce_hex)
    
    # 암호화된 데이터 읽기
    ciphertext = Path(encrypted_file).read_bytes()
    
    # 키스트림 생성 (ChaCha8, 8라운드)
    keystream = chacha_keystream(key, nonce, rounds=8, length=len(ciphertext))
    
    # XOR 복호화
    plaintext = bytes(a ^ b for a, b in zip(ciphertext, keystream))

5.4 무결성 검증

복구된 파일의 유효성을 판단하기 위해 파일 헤더 시그니처(매직 넘버)를 검증합니다.

파일 유형 매직 넘버 (Hex) 설명
JPG FF D8 FF JPEG 이미지 파일
PNG 89 50 4E 47 0D 0A 1A 0A PNG 이미지 파일
PDF 25 50 44 46 PDF 문서 파일
EXE/DLL 4D 5A (MZ) Windows 실행 파일
ZIP/DOCX 50 4B 03 04 압축 파일 및 MS Office
ELF 7F 45 4C 46 Linux 실행 파일

 

6. 요약 및 결론 (Conclusion)


6.1 최종 평가

DevMan 랜섬웨어는 ChaCha8 스트림 암호화를 사용하는 경량화된 악성코드입니다. DragonForce의 변종으로 추정되지만, RSA 하이브리드 방식이나 복잡한 메타데이터 저장 없이 단순화된 암호화 구조를 가지고 있습니다. 그러나 키 생성 과정에서 CryptGenRandom API를 직접 호출하는 구조적 취약점으로 인해, API 후킹을 통한 실시간 키 캡처가 가능하며, 이를 활용한 완벽한 복호화가 실증되었습니다.

[취약점 요약]
CryptGenRandom API 호출
         ↓
   평문 키 생성 (32B + 8B)
         ↓
  ★ API 후킹 지점 ★  ← 키 캡처 가능!
         ↓
  ChaCha State 초기화
         ↓
   파일 암호화 수행
         ↓
  .devman 확장자 추가

6.2 대응 가이드

6.2.1 예방적 조치

  1. API 모니터링 강화
    • CryptGenRandom, BCryptGenRandom 등 암호화 API 호출을 실시간 모니터링하는 EDR 솔루션 배치
    • 의심스러운 대량의 난수 생성 패턴 탐지
  2. 행위 기반 탐지
    • 대량 파일 접근 패턴 탐지
    • .devman 확장자 추가 행위 차단
  3. 파일 시스템 보호
    • 실시간 백업 시스템 구축 (VSS, 증분 백업)
    • 중요 디렉터리에 대한 접근 제어 강화

 

6.2.2 사후 대응

  1. 초동 조치
    • 감염 발견 즉시 시스템 격리 (네트워크 차단)
    • 시스템을 종료하지 말고 메모리 덤프(Full Memory Dump) 수행
    • 프로세스 메모리에서 CryptGenRandom 생성 키 추출 시도
  2. 키 복구 시도
    • API 후킹 도구(Frida, Detours 등)를 사용하여 CryptGenRandom 호출 로그 확보
    • 메모리 포렌식을 통한 평문 키 탐색
    • 로그 파일 또는 메모리 덤프에서 32바이트 키 패턴 추출
  3. 복호화 수행
    • 본 보고서에서 제공하는 ChaCha8 복호화 스크립트 활용
    • 추출한 키/nonce로 암호화된 파일 복원
    • 매직 넘버 검증을 통한 복호화 성공 여부 확인

6.3 기술적 특징 요약

항목 세부 내용
암호화 알고리즘 ChaCha8 (4번의 더블 라운드)
키 관리 파일별 독립 32바이트 키 + 8바이트 nonce
암호화 전략 단순 전체 파일 암호화
메타데이터 없음 (키 정보 별도 저장 안함)
확장자 .devman
복호화 가능성 API 후킹 또는 메모리 덤프로 키 확보 시 100% 복구 가능

6.4 결론

DevMan 랜섬웨어는 ChaCha8을 사용하는 단순화된 암호화 구조를 가진 악성코드입니다. RSA 하이브리드 방식이나 복잡한 메타데이터 없이 순수 ChaCha8만 사용하여 빠른 암호화 속도를 달성하지만, 이는 동시에 복호화를 더 쉽게 만드는 요인이 됩니다. API 후킹을 통해 평문 키를 확보할 경우 즉시 복호화가 가능하며, 본 분석에서 개발된 복호화 도구는 DevMan 감염 피해 복구에 직접 활용될 수 있습니다. 향후 랜섬웨어 대응을 위해서는 암호화 API 모니터링 강화실시간 메모리 포렌식 체계 구축이 필수적입니다.