공격자가 “Legitimate” 소프트웨어에 멀웨어를 숨기는 방법 이해: 기술 심층 분석
Source: Dev.to
위에 제공된 소스 링크 아래에 번역하고자 하는 전체 텍스트를 알려주시면, 요청하신 대로 한국어로 번역해 드리겠습니다. (코드 블록, URL, 마크다운 형식 및 기술 용어는 그대로 유지됩니다.)
The Story Begins: Curiosity About Malware
WannaCry에 대해 읽은 뒤, 모두가 그가 이용한 취약점—EternalBlue, SMB 결함 등—에 대해 이야기합니다. 하지만 저를 더 흥미롭게 만든 것은 악성코드가 실제로 어떻게 자신을 숨기는가였습니다.
공격자는 어떻게 악성 코드를 정상 소프트웨어처럼 보이게 할까요? 의심을 일으키지 않으면서 어떻게 실행을 보장할까요?
이것이 바로 트로잔의 핵심입니다: 겉으로는 합법적으로 보이지만 숨겨진 페이로드를 가지고 있는 프로그램. 이러한 행동을 이해하는 것은 사이버 보안을 진지하게 고민하는 사람에게 필수적이며, 방어는 공격을 이해하는 것에서 시작됩니다.
그래서 저는 통제된, 윤리적인, 교육적인 프로젝트로 이 개념을 탐구하기로 했습니다: 내 자체 바이너리 바인더 만들기.
교육 목표
목표는 트로이 목마 스타일 전달 메커니즘을 이해하는 것이었습니다.
안전한 환경에서 다음 상황이 발생할 때를 시뮬레이션하고 싶었습니다:
- 하나의 실행 파일이 다른 실행 파일을 내부에 포함하고 있음
- 숨겨진 프로그램이 보이는 프로그램이 시작된 후 실행됨
- OS를 깨뜨리거나 오류를 일으키지 않으면서 실행 흐름이 조정됨
본질적으로, 바인더는 두 개의 실행 파일을 하나로 합치면서 모든 기능을 유지합니다.
바인더 작동 방식 (기술 개요)
단일 파일이 하나의 프로그램처럼 보이지만 실제로는 두 개의 실행 파일을 비밀히 포함하고 있다고 상상해 보세요. 바로 제가 만든 것이 바로 그것입니다.
최종 병합 파일 구조
[ Python runtime stub - visible program ]
#--MAGIC_DELIMITER--
[ Hidden binary 1 ]
#--MAGIC_DELIMITER--
[ Hidden binary 2 ]
런타임 스텁
스텁은 프로그램의 “얼굴”이며, 사용자가 보고 실행하는 부분입니다. 스텁의 역할은 다음과 같습니다.
- 자신을 바이너리 모드로 열어 임베디드 페이로드를 구분하는 구분자를 찾는다.
- 숨겨진 바이너리를 임시 디렉터리에 추출한다.
- 추출한 바이너리를 순차적으로 실행한다.
- 모든 임시 파일을 정리한다.
with open(sys.argv[0], 'rb') as f:
data = f.read()
parts = data.split(MAGIC_DELIMITER)
이것이 트로이 목마 방식 페이로드 전달의 핵심 아이디어입니다: 눈에 보이는 프로그램이 숨겨진 동작을 조정합니다.
왜 이것이 중요한가: Python 파싱 문제
Python은 실행하기 전에 전체 소스 파일을 파싱합니다. 원시 바이너리 데이터를 그대로 추가하면 Python은 SyntaxError를 발생시키는데, 이는 Python이 비‑Python 바이트를 해석하려고 시도하기 때문입니다.
--binary bytes--
이를 해결하려면:
- 구조화된 구분자를 사용합니다 (바이트 안전하고 페이로드에 나타날 가능성이 낮은 것).
- 스텁이 실행된 직후
os._exit(0)으로 Python 실행을 즉시 종료합니다. - 바이너리는 임시 디렉터리에서 실행하도록 하여 파일 시스템의 혼란을 방지합니다.
이러한 기법은 악성코드가 페이로드를 안전하게 풀어 사용자에게 경고를 주거나 실행을 손상시키지 않도록 하는 방법과 동일합니다.
진입점 관리
모든 실행 파일은 entry point를 가지고 있으며, 이는 실행이 시작되는 메모리 주소입니다.
내 바인더에서는 stub becomes the entry point가 흐름을 제어합니다: 먼저 숨겨진 바이너리, 그 다음 두 번째 바이너리.
이는 droppers가 다단계 악성코드에서 흐름을 재지정하는 방식과 개념적으로 유사합니다.
진입점을 이해하는 것은 다음에 중요합니다:
- 리버스 엔지니어링
- 악성코드 탐지
- 안전한 소프트웨어 패키징
임시 실행 환경
import tempfile, subprocess
with tempfile.TemporaryDirectory() as temp_dir:
subprocess.run(["python3", bin1_path])
왜 임시 디렉터리를 사용하나요?
- 디스크에 남는 아티팩트를 줄입니다.
- 일시적인 악성코드 스테이징을 모방합니다.
- 중요한 시스템 파일을 덮어쓰는 것을 방지합니다.
보안 분석가들은 종종 다음과 같은 행동을 모니터링합니다:
- 단명 바이너리
- 자체 추출 페이로드
- 예기치 않은 자식 프로세스
직접 구현하면서 이러한 행동이 실제 환경에서 어떻게 나타나는지와 방어자가 이를 어떻게 탐지하는지에 대한 통찰을 얻었습니다.
권한 및 실행
import os
os.chmod(bin1_path, 0o755)
Linux 바이너리는 실행 권한이 필요합니다. 파일 권한을 이해하는 것은 다음을 위해 기본적입니다:
- 안전한 배포
- 악용 방지
- 안전한 샌드박스
교육적 시사점
바인더를 구축하면서 다음과 같은 실무 경험을 얻었습니다:
- 바이트 수준 파일 조작
- 실행 흐름 조정
- 자기 참조 로딩 (프로그램이 자신을 읽음)
- 프로세스 생성 및 임시 환경
- 실행 파일의 진입점 관리
- 트로이 목마형 행동에 대한 실용적 이해
사이버 보안 관점에서 악성코드를 효과적으로 탐지하거나 방어하려면 공격자가 이를 어떻게 설계하는지 이해해야 합니다. 이 프로젝트를 통해 공격 메커니즘을 안전하게 체험할 수 있었으며, 방어 전략을 설계하는 능력을 강화했습니다.
윤리 및 보안 고려사항
- 이 바인더는 교육용이며 악성 페이로드를 포함하고 있지 않습니다.
- 실제 악성 코드를 삽입하거나 허가받지 않은 시스템에서 코드를 실행하지 마세요.
- 공격 기술에 대한 책임 있는 이해는 사이버 보안 경력에 필수적입니다.
향후 확장
보다 깊이 학습하려면 다음을 시도할 수 있습니다:
- 헤더 수준에서 실제 ELF 바이너리를 병합합니다.
- Windows PE 형식에 대한 지원을 추가합니다.
- 숨겨진 페이로드의 실행 순서 제어를 허용합니다.
- 페이로드 암호화 및 무결성 검증을 구현합니다.
이러한 확장은 실제 악성코드 기술을 시뮬레이션하여 탐지 및 방지에 대한 통찰을 제공합니다.
프로젝트 및 사례 연구
전체 코드, 구현 세부 사항 및 기술 문서:
GitHub Repository