셸코드(shellcode)는 컴퓨터 보안 분야에서 소프트웨어 취약점을 이용한 공격(익스플로잇)을 위해 제작된 작은 크기의 기계어 코드 조각을 의미한다. 일반적으로 어셈블리어로 작성된 후 기계어로 변환되어 사용된다.
명칭의 유래
"셸코드"라는 이름은 이 코드가 주로 공격 대상 시스템의 명령 셸(Command Shell, 예: bash, cmd.exe)을 실행시켜 공격자에게 시스템 제어 권한을 부여하는 데 사용되었기 때문에 붙여졌다. 공격자는 셸을 획득함으로써 해당 시스템에 명령을 내릴 수 있게 된다. 다만, 현대에 이르러 셸코드의 기능이 단순히 셸을 실행하는 것에 그치지 않고 다양한 작업(파일 읽기/쓰기, 네트워크 연결, 권한 상승 등)을 수행할 수 있게 되면서 "셸코드"라는 용어가 충분히 설명적이지 않다는 지적도 있었으나, 대체 용어가 널리 채택되지는 않았다.
특징 및 동작 방식
셸코드는 일반적으로 다음과 같은 특징을 가진다.
- 작은 크기: 제한된 메모리 공간에 주입되어 실행되므로 가능한 한 작은 크기로 작성된다.
- 기계어 형태: 메모리에 직접 주입되어 실행될 수 있도록 CPU가 직접 해석하는 기계어(바이트코드) 형태로 존재한다.
- null 바이트 회피: 문자열 복사 함수(strcpy 등)를 통한 주입 시 문자열 종결자(null 바이트, \x00)가 삽입되면 코드가 중간에 잘릴 수 있으므로, 셸코드 내부에는 null 바이트가 포함되지 않도록 작성하는 것이 일반적이다.
주요 유형
- execve 셸코드:
/bin/sh등의 셸을 실행시키는 전통적인 형태의 셸코드. - ORW 셸코드(Open-Read-Write): 파일을 열고(open), 읽고(read), 출력(write)하는 방식으로 시스템 콜을 직접 호출하여 동작하는 셸코드.
- 리버스 셸코드(Reverse Shell): 공격자의 시스템으로 역방향 연결을 시도하여 셸을 전달하는 셸코드.
- 바인드 셸코드(Bind Shell): 공격 대상 시스템에서 특정 포트를 열어 공격자의 연결을 기다리는 셸코드.
- 스테이지드 셸코드(Staged Shellcode): 크기가 큰 셸코드를 여러 단계에 걸쳐 메모리에 로드하기 위해 작은 크기의 초기 셸코드가 이후 단계의 코드를 불러오는 방식.
사용 맥락
셸코드는 주로 버퍼 오버플로우(Buffer Overflow), 힙 오버플로우(Heap Overflow), 포맷 스트링 취약점(Format String Vulnerability) 등 메모리 손상 취약점을 이용한 공격에서 페이로드(Payload)의 일부로 사용된다. 공격자는 취약한 프로그램의 메모리에 셸코드를 주입한 후, 프로그램의 실행 흐름을 조작하여 해당 셸코드가 위치한 메모리 주소로 분기시킴으로써 셸코드를 실행한다.
셸코드는 보안 연구, 취약점 분석, 모의 침투 테스트 등 합법적인 보안 연구 목적으로도 사용되며, Dreamhack 등의 국내 보안 교육 플랫폼에서도 체계적으로 다루어지고 있다.