본문 바로가기

IT & AI

LinkedIn 채용 과제에 숨은 악성코드, 개발자 컴퓨터를 노렸어요

728x90

LinkedIn 채용 과제에 숨은 악성코드, 개발자 컴퓨터를 노렸어요

LinkedIn 채용 과제 악성코드 보안 썸네일
LinkedIn 채용 과제 악성코드 보안 썸네일

LinkedIn에서 받은 코딩 과제를 실행했다가 개발 환경이 공격당한 사례가 공개됐어요. 겉보기에는 평범한 React와 Web3 프로젝트였지만, `tailwind.config.js` 안에는 원격 제어와 정보 탈취로 이어지는 난독화 코드가 숨어 있었어요. 낯선 저장소를 개인 컴퓨터에서 바로 실행하는 습관이 왜 위험한지 구체적으로 보여 주는 사례예요. 1

핵심 요약

구분핵심왜 볼 만한가요
공격 경로LinkedIn 채용 제안 뒤 비공개 GitHub 과제를 전달했어요개발자가 평소 신뢰하는 채용 절차와 개발 도구를 함께 악용했어요
은닉 방식30,987바이트짜리 `tailwind.config.js`의 공백 뒤에 약 27KB 난독화 코드를 숨겼어요설정 파일도 Node가 실행하는 코드라는 점을 노렸어요
악성 기능원격 제어, 브라우저·지갑 탈취, 파일 검색, 클립보드 감시가 작동했어요SSH 키와 토큰, 소스 코드, 지갑 정보까지 한 번에 노렸어요
관찰 결과격리 VM에서 약 10분 동안 파일 업로드 요청 2,588건이 확인됐어요짧게 실행해도 유출 범위가 빠르게 커질 수 있어요
대응 원칙과제 저장소는 비밀 정보가 없는 일회용 VM에서 먼저 확인해야 해요삭제만으로 끝내지 말고 노출된 키와 세션도 교체해야 해요

1. 코딩 과제처럼 보였지만 개발 환경 전체를 노렸어요

공격자는 LinkedIn에서 소프트웨어 엔지니어 직무를 제안한 뒤 비공개 GitHub 저장소를 과제로 보냈어요. 프로젝트는 브랜드 대시보드와 NFT 캠페인 기능을 갖춘 React·Express 앱처럼 구성돼 있었어요. 지원자에게 요구한 작업도 MetaMask의 체인 ID를 읽어 네트워크 이름을 표시하는 정도였어요. 저장소를 복제하고 개발 서버를 켜는 행동이 자연스럽도록 주변 기능과 데모 데이터까지 만들어 둔 셈이에요. 2

이상 징후는 `tailwind.config.js`에서 발견됐어요. 파일 크기는 30,987바이트였지만 편집기에는 97줄만 보였고, 95번째 줄 부근의 수천 개 공백 뒤에 약 27KB의 난독화 JavaScript가 붙어 있었어요. Tailwind 설정은 단순한 텍스트가 아니라 Node가 불러와 평가하는 모듈이에요. 개발 서버나 빌드 도구가 이 파일을 읽는 순간 숨은 코드도 같은 사용자 권한으로 실행될 수 있어요.

728x90

암호화된 2단계 코드를 받아 실행했어요

첫 단계 코드는 원격 서버에서 암호화된 데이터를 받아 AES-256-CBC로 풀었어요. 복호화한 결과를 임시 폴더의 `pack` 파일에 저장한 뒤 `node pack`으로 실행했어요. 내려받은 코드의 서명이나 해시를 확인하는 절차는 없었어요. 원격 서버가 보내는 JavaScript가 개발자 계정 권한으로 곧바로 실행되는 구조였어요.

AISafe 연구자는 일회용 Windows 11 ARM VM에 미끼 SSH 키와 브라우저 데이터, 지갑 자료를 넣고 약 10분 동안 동작을 관찰했어요. 네트워크 기록에서는 한 포트로 파일 업로드 요청 2,588건이 확인됐어요. 다른 포트는 Socket.IO 원격 제어와 브라우저 데이터 전송에 쓰였어요. 짧은 실행만으로도 공격자가 여러 수집 경로를 동시에 열 수 있다는 뜻이에요. 2

백도어와 정보 탈취기가 역할을 나눴어요

2단계 페이로드는 네 가지 프로그램을 가동했어요. 첫 번째는 터미널과 SSH, 화면, 키보드, 마우스를 다루는 원격 제어 백도어였어요. 두 번째는 Chrome과 Edge, Brave 같은 브라우저의 데이터베이스와 MetaMask를 비롯한 지갑 확장 저장소를 찾았어요.

세 번째 프로그램은 사용자 폴더와 드라이브를 재귀적으로 훑었어요. `.env`, `.pem`, `.secret`, 셸 기록, 문서, 소스 코드뿐 아니라 `.ssh`, `.aws`, `.azure` 같은 폴더도 대상이었어요. 네 번째는 클립보드가 바뀔 때마다 내용을 원격 서버로 보냈어요. 개발자가 복사하는 API 토큰과 일회용 코드, 개인 키, 배포 비밀이 그대로 노출될 수 있었어요.

이 공격은 Windows에만 한정되지 않았어요. 분석된 코드에는 macOS와 Linux의 브라우저 경로와 클립보드 명령도 들어 있었어요. 개인 브라우저 프로필, SSH 키, 클라우드 자격 증명이 같은 계정 아래 있다면 운영체제와 관계없이 피해가 커질 수 있어요.

백신과 AI 코딩 도구만 믿기 어려웠어요

원문에 따르면 전통적인 백신은 해당 저장소를 탐지하지 못했어요. 과제를 푸는 데 사용한 인기 AI 코딩 도구도 숨은 악성코드를 찾아내지 못했어요. 정상 프로젝트 코드가 충분히 많았고, 악성 부분은 사람들이 지나치기 쉬운 설정 파일의 긴 공백 뒤에 숨겨져 있었어요.

AI 코딩 도구가 저장소를 읽었다고 해서 안전성 검토까지 끝난 것은 아니에요. 도구가 수정 대상 파일만 좁게 보거나 난독화된 블롭을 분석하지 않으면 공격 경로를 놓칠 수 있어요. 저장소의 출처와 실행 환경을 분리하는 기본 통제가 먼저 필요해요.

실행 전에는 저장소 전체를 비신뢰 코드로 다뤄야 해요

낯선 과제는 개인 개발 환경 대신 일회용 VM이나 격리된 컨테이너에서 열어야 해요. 개인 브라우저 프로필과 비밀번호 관리자, SSH 키, GitHub 토큰, 클라우드 키, 암호화폐 지갑을 마운트하지 않은 환경이 필요해요. Web3 과제라면 실제 자금이 없는 테스트 지갑과 테스트넷만 써야 해요.

`npm install`, `yarn start`, 빌드 명령을 실행하기 전에는 `package.json`의 수명 주기 스크립트와 설정 파일을 먼저 확인해야 해요. 비정상적으로 큰 설정 파일, 긴 공백, 난독화된 문자열, `child_process` 호출, 외부 IP 접속, 임시 폴더 실행 코드는 멈춰서 확인할 신호예요. 편집기에서 보이는 줄 수만 믿지 말고 파일 크기와 변경 이력도 함께 보는 편이 안전해요.

이미 실행했다면 컴퓨터를 먼저 네트워크에서 분리해야 해요. 의심 프로세스와 연결, 임시 파일을 확인하고 필요한 증거를 보존한 뒤 정리해야 해요. 저장소와 악성 파일을 지우는 것만으로는 부족해요. SSH 키와 GitHub·npm 토큰, 클라우드 키, 브라우저 비밀번호, 활성 세션을 깨끗한 장치에서 폐기하거나 교체해야 해요. 지갑 키가 노출됐을 수 있다면 새 장치에서 만든 지갑으로 자산을 옮겨야 해요.

왜 중요한가요

개발자는 다른 직군보다 외부 코드를 실행할 일이 많아요. 채용 과제, 오픈소스 예제, 버그 재현 저장소는 모두 `npm install`과 빌드 명령을 자연스럽게 요구해요. 공격자는 이 익숙한 절차를 이용해 악성 첨부파일 대신 정상적으로 보이는 프로젝트를 전달했어요. 2

설정 파일도 실행 코드라는 점을 기억해야 해요. Tailwind, Vite, Webpack, ESLint 같은 도구의 JavaScript 설정은 불러오는 과정에서 임의 코드를 실행할 수 있어요. 패키지 설치 스크립트만 꺼도 모든 위험이 사라지지 않아요. 빌드와 편집기 통합, CI가 설정 파일을 읽는 경로까지 신뢰 경계에 포함해야 해요.

팀 차원에서는 외부 저장소를 여는 전용 격리 환경을 마련하는 편이 좋아요. 비밀 정보가 없는 임시 계정과 짧게 살아 있는 VM을 쓰면 공격이 성공해도 유출 범위를 줄일 수 있어요. 채용 담당자와 개발팀도 과제 저장소의 소유자, 커밋 이력, 연락한 계정과 회사 도메인을 별도로 확인해야 해요.

참고 자료

  1. 개발자를 해킹하는 LinkedIn 채용 과제 사기 — GeekNews
  2. The LinkedIn scam that gets you hacked — AISafe Labs
728x90