소개
사이버 보안 분야에서 네트워크 스캔 결과를 이해하고 분석하는 것은 안전한 인프라를 유지하는 데 매우 중요합니다. Nmap (Network Mapper) 은 네트워크 탐색 및 보안 감사를 위해 가장 널리 사용되는 도구 중 하나입니다. 이 튜토리얼은 XML 형식의 Nmap 스캔 결과를 해석하는 과정을 안내하여, 사이버 보안 요구 사항에 맞게 이 강력한 도구를 활용하는 데 필요한 기술을 제공합니다.
이 랩을 마치면, XML 출력을 사용하여 Nmap 스캔을 실행하고, XML 데이터의 구조를 이해하며, 명령줄 도구와 Python 스크립트를 사용하여 귀중한 정보를 추출하고, 스캔 결과에서 잠재적인 보안 문제를 식별하는 방법을 알게 될 것입니다.
Nmap 설치 및 기본 XML 스캔 실행
Nmap 이란 무엇인가요?
Nmap (Network Mapper) 은 네트워크 탐색 및 보안 감사를 위한 무료 오픈 소스 유틸리티입니다. 전 세계의 보안 전문가들은 네트워크에서 어떤 장치가 실행 중인지 식별하고, 사용 가능한 호스트와 제공하는 서비스를 발견하며, 열린 포트를 찾고, 보안 취약점을 탐지하기 위해 이 도구를 사용합니다.
Nmap 설치
시스템에 Nmap 을 설치하는 것으로 시작해 보겠습니다. 터미널 창을 열고 다음 명령을 입력합니다.
sudo apt update
sudo apt install nmap -y
설치가 완료되면 Nmap 버전을 확인하여 Nmap 이 올바르게 설치되었는지 확인합니다.
nmap --version
다음과 유사한 출력을 볼 수 있습니다.
Nmap version 7.80 ( https://nmap.org )
Platform: x86_64-pc-linux-gnu
Compiled with: liblua-5.3.3 openssl-1.1.1f libssh2-1.8.0 libz-1.2.11 libpcre-8.39 nmap-libpcap-1.9.1 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: epoll poll select
XML 출력을 사용한 기본 Nmap 스캔 실행
Nmap 은 스캔 결과를 XML 형식으로 저장할 수 있으며, 이는 데이터를 프로그래밍 방식으로 분석하는 구조화된 방법을 제공합니다. 로컬 머신에 대한 기본 스캔을 실행하고 결과를 XML 형식으로 저장해 보겠습니다.
sudo nmap -A -T4 -oX ~/project/localhost_scan.xml localhost
이 명령은 다음을 수행합니다.
-A: OS 탐지, 버전 탐지, 스크립트 스캔 및 traceroute 를 활성화합니다.-T4: 타이밍 템플릿을 "aggressive"로 설정합니다.-oX: 출력이 XML 형식이어야 함을 지정합니다.localhost: 스캔 대상 (자신의 머신)
스캔을 완료하는 데 1~2 분 정도 걸릴 수 있습니다. 완료되면 터미널에 스캔 결과 요약이 표시됩니다.
XML 스캔 결과 보기
방금 생성한 XML 파일을 살펴보겠습니다.
cat ~/project/localhost_scan.xml
출력은 스캔에 대한 자세한 정보를 포함하는 구조화된 XML 문서가 됩니다. 처음에는 압도적으로 보일 수 있지만, 다음 단계에서 이를 해석하는 방법을 배우게 됩니다.
head 명령을 사용하여 XML 파일의 기본 구조도 확인해 보겠습니다.
head -n 20 ~/project/localhost_scan.xml
이것은 XML 파일의 처음 20 줄을 보여주며, 구조를 엿볼 수 있습니다.
XML 출력 구조 검토
Nmap XML 형식 이해
Nmap XML 출력은 스캔 정보를 논리적인 방식으로 구성하는 계층적 구조를 따릅니다. 이 구조의 주요 요소를 살펴보겠습니다.
<nmaprun>: 모든 스캔 정보를 포함하는 루트 요소<scaninfo>: 스캔 유형 및 매개변수에 대한 세부 정보<host>: 각 스캔된 호스트에 대한 정보<status>: 호스트가 작동 중인지 여부<address>: IP 및 MAC 주소<hostnames>: DNS 이름<ports>: 스캔된 포트에 대한 세부 정보<port>: 특정 포트에 대한 정보<state>: 포트가 열려 있는지, 닫혀 있는지 또는 필터링되었는지 여부<service>: 서비스 정보 (사용 가능한 경우)
<os>: 운영 체제 탐지 결과<times>: 스캔에 대한 타이밍 정보
명령줄 도구를 사용하여 정보 추출
XML 파일은 원시 형식으로 읽기가 어려울 수 있습니다. 몇 가지 명령줄 도구를 사용하여 스캔 결과에서 특정 정보를 추출해 보겠습니다.
먼저, grep과 wc를 사용하여 열린 포트 수를 세어 보겠습니다.
grep -c "state=\"open\"" ~/project/localhost_scan.xml
이 명령은 XML 파일에서 state="open"의 인스턴스를 검색하고 개수를 셉니다.
다음으로, -A 옵션과 함께 grep을 사용하여 열린 포트와 해당 서비스를 식별하여 일치하는 줄을 표시합니다.
grep -A 3 "state=\"open\"" ~/project/localhost_scan.xml
이렇게 하면 열린 포트의 각 인스턴스와 그 뒤에 오는 3 줄이 표시되며, 일반적으로 서비스 정보가 포함됩니다.
xmllint를 사용하여 XML 파일을 더 쉽게 읽을 수 있도록 형식을 지정할 수도 있습니다. 먼저 설치해 보겠습니다.
sudo apt install libxml2-utils -y
이제 XML 파일의 형식을 지정해 보겠습니다.
xmllint --format ~/project/localhost_scan.xml > ~/project/formatted_scan.xml
형식이 지정된 파일을 살펴보겠습니다.
head -n 50 ~/project/formatted_scan.xml
이렇게 하면 형식이 지정된 XML 파일의 처음 50 줄이 표시되어 훨씬 쉽게 읽을 수 있습니다.
마지막으로, XPath 를 사용하여 xmllint를 사용하여 호스트 상태에 대한 특정 정보를 추출해 보겠습니다.
xmllint --xpath "//host/status/@state" ~/project/localhost_scan.xml
이 명령은 XPath 를 사용하여 호스트 요소 아래의 모든 상태 요소의 상태 속성을 추출합니다.
Python 을 사용하여 Nmap XML 파싱
Python 을 사용한 XML 파싱 소개
Python 은 XML 파일을 파싱하기 위한 강력한 라이브러리를 제공합니다. 이 단계에서는 Nmap 스캔 결과를 파싱하고 더 읽기 쉬운 형식으로 표시하는 간단한 Python 스크립트를 만들 것입니다.
기본 XML 파서 생성
xml.etree.ElementTree 모듈을 사용하여 Nmap XML 파일을 파싱하는 Python 스크립트를 만들어 보겠습니다. 이 모듈은 Python 표준 라이브러리에 포함되어 있으므로 추가로 설치할 필요가 없습니다.
프로젝트 디렉토리에 parse_nmap.py라는 새 파일을 만듭니다.
nano ~/project/parse_nmap.py
다음 코드를 복사하여 편집기에 붙여넣습니다.
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import sys
def parse_nmap_xml(xml_file):
try:
## Parse the XML file
tree = ET.parse(xml_file)
root = tree.getroot()
## Print scan information
print("Nmap Scan Report")
print("=" * 50)
print(f"Scan started at: {root.get('startstr')}")
print(f"Nmap version: {root.get('version')}")
print(f"Nmap command: {root.get('args')}")
print("=" * 50)
## Process each host in the scan
for host in root.findall('host'):
## Get host addresses
for addr in host.findall('address'):
if addr.get('addrtype') == 'ipv4':
ip_address = addr.get('addr')
print(f"\nHost: {ip_address}")
## Get hostname if available
hostnames = host.find('hostnames')
if hostnames is not None:
for hostname in hostnames.findall('hostname'):
print(f"Hostname: {hostname.get('name')}")
## Get host status
status = host.find('status')
if status is not None:
print(f"Status: {status.get('state')}")
## Process ports
ports = host.find('ports')
if ports is not None:
print("\nOpen Ports:")
print("-" * 50)
print(f"{'PORT':<10}{'STATE':<10}{'SERVICE':<15}{'VERSION'}")
print("-" * 50)
for port in ports.findall('port'):
port_id = port.get('portid')
protocol = port.get('protocol')
## Get port state
state = port.find('state')
port_state = state.get('state') if state is not None else "unknown"
## Skip closed ports
if port_state != "open":
continue
## Get service information
service = port.find('service')
if service is not None:
service_name = service.get('name', '')
service_product = service.get('product', '')
service_version = service.get('version', '')
service_info = f"{service_product} {service_version}".strip()
else:
service_name = ""
service_info = ""
print(f"{port_id}/{protocol:<5} {port_state:<10}{service_name:<15}{service_info}")
## Get OS detection information
os = host.find('os')
if os is not None:
print("\nOS Detection:")
for osmatch in os.findall('osmatch'):
print(f"OS: {osmatch.get('name')} (Accuracy: {osmatch.get('accuracy')}%)")
except ET.ParseError as e:
print(f"Error parsing XML file: {e}")
return False
except Exception as e:
print(f"Error: {e}")
return False
return True
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <nmap_xml_file>")
sys.exit(1)
xml_file = sys.argv[1]
if not parse_nmap_xml(xml_file):
sys.exit(1)
Ctrl+O를 누른 다음 Enter를 눌러 파일을 저장하고 Ctrl+X로 nano 를 종료합니다.
이제 스크립트를 실행 가능하게 만듭니다.
chmod +x ~/project/parse_nmap.py
파서 실행
이전에 생성한 Nmap XML 파일에서 Python 스크립트를 실행해 보겠습니다.
python ~/project/parse_nmap.py ~/project/localhost_scan.xml
다음과 같은 스캔 결과가 깔끔하게 형식화된 출력을 볼 수 있습니다.
- 기본 스캔 정보
- 호스트 세부 정보
- 열린 포트 및 서비스
- 사용 가능한 경우 운영 체제 탐지 결과
이 형식화된 출력은 원시 XML 파일보다 훨씬 읽기 쉽고 스캔에서 가장 중요한 정보를 강조 표시합니다.
파서 코드 이해
Python 스크립트가 수행하는 작업을 검토해 보겠습니다.
xml.etree.ElementTree를 사용하여 XML 파일을 파싱합니다.- 루트 요소에서 일반적인 스캔 정보를 추출합니다.
- 스캔에서 발견된 각 호스트에 대해:
- IP 주소와 호스트 이름을 추출합니다.
- 호스트가 작동 중인지 여부를 결정합니다.
- 포트 번호, 프로토콜, 서비스 이름 및 버전을 포함하여 모든 열린 포트를 나열합니다.
- 사용 가능한 경우 OS 탐지 정보를 추출합니다.
이 구조화된 접근 방식을 통해 XML 복잡성을 무시하면서 가장 관련성이 높은 정보에 집중할 수 있습니다.
보안 관련 정보 추출
Nmap 스캔에서 얻는 보안 통찰력
이제 Nmap XML 데이터를 파싱할 수 있으므로 스크립트를 확장하여 보안 관련 정보를 추출해 보겠습니다. 여기에는 다음이 포함됩니다.
- 잠재적으로 위험한 열린 포트 식별
- 구식 서비스 버전 감지
- 보안 문제 요약
보안 분석에 초점을 맞춘 파서의 향상된 버전을 만들어 보겠습니다.
보안 분석 스크립트 생성
security_analysis.py라는 새 파일을 만듭니다.
nano ~/project/security_analysis.py
다음 코드를 복사하여 붙여넣습니다.
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import sys
import datetime
## Define potentially risky ports
HIGH_RISK_PORTS = {
'21': 'FTP - File Transfer Protocol (often unencrypted)',
'23': 'Telnet - Unencrypted remote access',
'25': 'SMTP - Email transfer (may allow relay)',
'445': 'SMB - Windows file sharing (potential target for worms)',
'3389': 'RDP - Remote Desktop Protocol (target for brute force)',
'1433': 'MSSQL - Microsoft SQL Server',
'3306': 'MySQL - Database access',
'5432': 'PostgreSQL - Database access'
}
## Services with known security issues
OUTDATED_SERVICES = {
'ssh': [
{'version': '1', 'reason': 'SSHv1 has known vulnerabilities'},
{'version': 'OpenSSH 7', 'reason': 'Older OpenSSH versions have multiple CVEs'}
],
'http': [
{'version': 'Apache httpd 2.2', 'reason': 'Apache 2.2.x is end-of-life'},
{'version': 'Apache httpd 2.4.1', 'reason': 'Apache versions before 2.4.30 have known vulnerabilities'},
{'version': 'nginx 1.14', 'reason': 'Older nginx versions have security issues'}
]
}
def analyze_security(xml_file):
try:
## Parse the XML file
tree = ET.parse(xml_file)
root = tree.getroot()
## Prepare the report
report = []
report.append("NMAP SECURITY ANALYSIS REPORT")
report.append("=" * 50)
report.append(f"Report generated on: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append(f"Scan started at: {root.get('startstr')}")
report.append(f"Scan command: {root.get('args')}")
report.append("=" * 50)
## Track security findings
high_risk_services = []
potentially_outdated = []
exposed_services = []
## Process each host in the scan
for host in root.findall('host'):
## Get host addresses
ip_address = None
for addr in host.findall('address'):
if addr.get('addrtype') == 'ipv4':
ip_address = addr.get('addr')
hostname = "Unknown"
hostnames = host.find('hostnames')
if hostnames is not None:
hostname_elem = hostnames.find('hostname')
if hostname_elem is not None:
hostname = hostname_elem.get('name')
report.append(f"\nHOST: {ip_address} ({hostname})")
report.append("-" * 50)
## Process ports
ports = host.find('ports')
if ports is None:
report.append("No port information available")
continue
open_ports = 0
for port in ports.findall('port'):
port_id = port.get('portid')
protocol = port.get('protocol')
## Get port state
state = port.find('state')
if state is None or state.get('state') != "open":
continue
open_ports += 1
## Get service information
service = port.find('service')
if service is None:
service_name = "unknown"
service_product = ""
service_version = ""
else:
service_name = service.get('name', 'unknown')
service_product = service.get('product', '')
service_version = service.get('version', '')
service_full = f"{service_product} {service_version}".strip()
## Check if this is a high-risk port
if port_id in HIGH_RISK_PORTS:
high_risk_services.append(f"{ip_address}:{port_id} ({service_name}) - {HIGH_RISK_PORTS[port_id]}")
## Check for outdated services
if service_name in OUTDATED_SERVICES:
for outdated in OUTDATED_SERVICES[service_name]:
if outdated['version'] in service_full:
potentially_outdated.append(f"{ip_address}:{port_id} - {service_name} {service_full} - {outdated['reason']}")
## Track all exposed services
exposed_services.append(f"{ip_address}:{port_id}/{protocol} - {service_name} {service_full}")
report.append(f"Open ports: {open_ports}")
## Add security findings to report
report.append("\nSECURITY FINDINGS")
report.append("=" * 50)
## High-risk services
report.append("\nHIGH-RISK SERVICES")
report.append("-" * 50)
if high_risk_services:
for service in high_risk_services:
report.append(service)
else:
report.append("No high-risk services detected")
## Potentially outdated services
report.append("\nPOTENTIALLY OUTDATED SERVICES")
report.append("-" * 50)
if potentially_outdated:
for service in potentially_outdated:
report.append(service)
else:
report.append("No potentially outdated services detected")
## Exposed services inventory
report.append("\nEXPOSED SERVICES INVENTORY")
report.append("-" * 50)
if exposed_services:
for service in exposed_services:
report.append(service)
else:
report.append("No exposed services detected")
## Write the report to a file
report_file = "security_report.txt"
with open(report_file, 'w') as f:
f.write('\n'.join(report))
print(f"Security analysis complete. Report saved to {report_file}")
## Display a summary
print("\nSummary:")
print(f"- High-risk services: {len(high_risk_services)}")
print(f"- Potentially outdated services: {len(potentially_outdated)}")
print(f"- Total exposed services: {len(exposed_services)}")
except ET.ParseError as e:
print(f"Error parsing XML file: {e}")
return False
except Exception as e:
print(f"Error: {e}")
return False
return True
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <nmap_xml_file>")
sys.exit(1)
xml_file = sys.argv[1]
if not analyze_security(xml_file):
sys.exit(1)
Ctrl+O를 누른 다음 Enter를 눌러 파일을 저장하고 Ctrl+X로 nano 를 종료합니다.
스크립트를 실행 가능하게 만듭니다.
chmod +x ~/project/security_analysis.py
보안 분석 실행
Nmap XML 파일에서 보안 분석 스크립트를 실행해 보겠습니다.
cd ~/project
./security_analysis.py localhost_scan.xml
스크립트는 스캔 결과를 분석하고 잠재적인 취약점에 초점을 맞춘 보안 보고서를 생성하여 security_report.txt라는 파일에 저장합니다.
보고서의 내용을 살펴보겠습니다.
cat ~/project/security_report.txt
보안 분석 이해
보안 분석 스크립트는 몇 가지 중요한 기능을 수행합니다.
고위험 포트 식별: 공격자가 자주 사용하는 FTP(21), Telnet(23), RDP(3389) 와 같은 일반적으로 악용되는 포트를 식별합니다.
구식 서비스 감지: 알려진 보안 취약점이 있을 수 있는 SSH, Apache, nginx 와 같은 서비스의 이전 버전을 확인합니다.
노출된 서비스 인벤토리: 모든 열린 포트 및 서비스의 완전한 인벤토리를 생성하며, 이는 보안 감사에 유용합니다.
위험 범주화: 보안 개선의 우선 순위를 정하는 데 도움이 되도록 위험 수준별로 결과를 정리합니다.
이러한 유형의 분석은 공격자가 네트워크를 악용하기 전에 보안 전문가가 잠재적인 취약점을 식별하는 데 매우 중요합니다.
분석 확장
실제 시나리오에서는 다음을 수행하여 이 분석을 확장할 수 있습니다.
- 감지 목록에 더 많은 고위험 포트 추가
- 최신 취약성 정보로 구식 서비스 정의 업데이트
- 알려진 CVE(Common Vulnerabilities and Exposures) 를 확인하기 위해 취약성 데이터베이스와 통합
- 감지된 문제에 대한 해결 권장 사항 추가
Nmap XML 데이터를 프로그래밍 방식으로 분석하는 기능은 사이버 보안 전문가에게 강력한 기술이며, 자동화된 취약성 평가 및 더 큰 보안 모니터링 시스템과의 통합을 가능하게 합니다.
요약
Nmap 스캔 결과를 XML 형식으로 분석하는 이 랩을 완료하신 것을 축하드립니다. 몇 가지 중요한 기술을 익히셨습니다.
Nmap 설치 및 실행: Nmap 을 설치하고 XML 출력을 사용하여 스캔을 실행하는 방법을 배웠으며, 이는 네트워크 정찰의 기반을 제공합니다.
XML 구조 이해: Nmap XML 파일의 구조를 탐색하고 명령줄 도구를 사용하여 특정 정보를 추출하여 스캔 결과를 빠르게 분석할 수 있는 능력을 갖게 되었습니다.
Python 을 사용한 XML 파싱: Python 스크립트를 생성하여 Nmap 스캔 결과를 읽기 쉬운 형식으로 파싱하고 표시하여 구조화된 데이터를 프로그래밍 방식으로 처리하는 방법을 보여주었습니다.
보안 분석: Python 기술을 확장하여 보안 문제에 대한 스캔 결과를 분석하고 잠재적으로 위험한 서비스를 식별하며 포괄적인 보안 보고서를 생성했습니다.
이러한 기술은 네트워크 평가, 취약성 스캔 및 보안 감사를 수행해야 하는 사이버 보안 전문가에게 필수적입니다. Nmap 결과 분석을 자동화하는 기능은 보다 효율적이고 철저한 보안 모니터링을 가능하게 합니다.
다음과 같은 방법으로 이러한 기술을 더욱 향상시킬 수 있습니다.
- 더 발전된 Nmap 스캔 기술 탐색
- 스캔 결과를 다른 보안 도구와 통합
- 더 정교한 분석 알고리즘 생성
- 스캔 데이터를 위한 시각화 도구 개발
무단 스캔은 불법적이거나 비윤리적일 수 있으므로, 네트워크 스캔은 자신이 소유하거나 스캔할 명시적인 권한이 있는 네트워크에서만 수행해야 합니다.



