산업제어시스템의 취약점 파악을 위해 ICS-CERT 웹 사이트에서 권고문을 확인할 수 있는데요.
ICS-CERT Advisories by Vendor | CISA
각각의 권고문 웹 페이지를 직접 방문하지 않고도 취약점의 영향을 받는 제품을 엑셀로 한 눈에 확인할 수 있도록 자동화하려고 합니다.
로봇 배제 표준(robots.txt) 확인
먼저 해당 사이트의 robots.txt에서 /ics/ 경로가 Disallow에 포함되지 않음을 확인합니다.
※ robots.txt 규칙 정보 참조
파이썬 라이브러리 사용
웹 스크래핑, 엑셀 저장 기능을 위해 파이썬의 BeautifulSoup, requests와 openpyxl 라이브러리를 활용했습니다.
beautifulsoup, requests, openpyxl 설치
$ pip install beautifulsoup4
$ pip install requests
$ pip install openpyxl
웹 페이지 구조 확인
필요한 정보는 특정 제조사로 검색했을 때의 권고문 ID, 권고문 제목, 영향을 받는 제품 목록 입니다.
제조사 검색창을 이용하여 검색 수행, 페이징, 상세 페이지에서 영향을 받는 제품 목록을 확인하면서 웹 페이지의 구조를 파악해봤습니다.
목록 페이지 (ICS-CERT Advisories by Vendor)
1. 제조사 명 검색: URL에 GET 파라미터 glossary를 통해 전송
2. 페이지 이동: URL에 GET 파라미터 page를 통해 전송, 0부터 시작(1페이지=0),
페이지 범위를 초과해서 파라미터를 전송할 경우 빈 페이지가 조회됨
3. 권고문 ID 클래스 선택자: .item_list .views-field-field-ics-docid-advisory
4. 권고문 제목 클래스 선택자: .item_list .views-field-title
5. 권고문 상세 페이지 URL: 권고문 제목의 a 태그 href 속성 값
각각 권고문의 상세 페이지
6. 영향을 받는 제품들:
AFFECTED PRODUCT 텍스트가 포함된 h(제목) 태그 다음부터, 그 다음 h(제목) 태그 전까지의 내용
- 고려사항
각각 권고문 별로 발행시기에 따라 AFFECTED PRODUCTS 소제목 태그(h)는 h3, h4와 같이 depth가 다르고, AFFECTED PRODUCTS 내용이 아래 예시의 경우처럼 문단(p) 태그와 목록(ul) 태그 한 개씩인 경우이거나 여러 개로 구성되고 하위 소제목이 있는 경우도 존재함
코드 작성
확인한 웹 페이지 구조를 바탕으로 스크랩을 수행하고 엑셀로 저장하는 코드를 작성했습니다.
import re
import openpyxl
import requests
from bs4 import BeautifulSoup
def get_soup(url):
response = requests.get('https://us-cert.cisa.gov' + url)
source = response.text
soup = BeautifulSoup(source, 'html.parser')
return soup
# 엑셀 파일 생성
wb = openpyxl.Workbook()
ws = wb.active
ws.append(['VENDOR', 'ICSA_CD', 'ICSA_TITLE', 'AFFECTED_PRODUCTS'])
# 검색할 제조사 배열 선언
vendors = ['SIEMENS', 'ABB', 'GE']
# 제조사별 스크랩
for vendor in vendors:
url_vendor = '/ics/advisories-by-vendor?glossary=' + vendor + '&page='
# 마지막 페이지까지 탐색
page = 0
while True:
soup = get_soup(url_vendor + str(page))
# 권고문 ID, 권고문 제목 추출
icsa_cds = soup.select('.item-list .views-field-field-ics-docid-advisory')
icsa_titles = soup.select('.item-list .views-field-title')
# 페이지 범위를 초과하여 빈 페이지일 경우 중지
if len(icsa_cds) == 0: break
for i in range(len(icsa_cds)):
# 상세 페이지로 이동 (권고문 제목 a 태그의 href 속성)
soup = get_soup(icsa_titles[i].find('a').get('href'))
print(icsa_cds[i].text)
# AFFECTED PRODUCT 제목 찾음
headers = soup.find_all(text=re.compile('AFFECTED PRODUCT', re.I))
products = []
for header in headers:
# h3, h4와 같은 제목 태그일 경우에만 (p 태그 등 제외)
header_tag = header.parent.name
if header_tag[0].lower() == 'h':
# AFFECTED PRODUCT 내용 추출 (다음 제목 태그 전까지)
for product in header.find_all_next(['p', 'ul', header_tag]):
if product.name == header.parent.name: break
products.append(product.text)
break
# 엑셀에 정보 기록
ws.append([vendor,
icsa_cds[i].text,
icsa_titles[i].text,
'\n'.join(products)])
page += 1
# 엑셀 최종 저장
wb.save('results.xlsx')
wb.close()
결과 엑셀 파일 확인
ICS-CERT 권고문별 영향을 받는 제품 목록이 엑셀 파일로 저장되었습니다.
'Python' 카테고리의 다른 글
[BOJ] 7576, 7569: 토마토 BFS 풀이 (Python) (0) | 2021.04.28 |
---|---|
[CodeUp] 기초 100제 1099번 성실한 개미 풀이 (Python) (0) | 2021.02.27 |
[CodeUp] 기초 100제 1098번 2차원 배열 설탕과자 뽑기 풀이 (Python) (0) | 2021.02.27 |
[CodeUp] 기초 100제 1097번 2차원 배열 바둑알 십자 뒤집기 풀이 (Python) (0) | 2021.02.27 |
[CodeUp] 기초 100제 1096번 2차원 배열 바둑판에 흰 돌 놓기 풀이 (Python) (0) | 2021.02.27 |
댓글