CLI-based rootkit and anomaly scanner for Linux systems. Written in Go for speed and portability.
π Features
- Scans for suspicious processes, ports, and modules
- Detects tampered
/etc/ld.so.preload
- Lists hidden dotfiles in root
- Verifies critical binary hashes (
ls
,ps
,top
) - Outputs reports in
text
,json
, orhtml
formats
π§ Installation
1. Install Go (if not installed)
sudo apt update && sudo apt install golang -y
2. Clone the Repo
git clone https://https://github.com/mxkdevops/mkrootkitscan
cd mkrootkitscan
3. Build the Scanner
go build -o mkrootkitscan main.go
βΆοΈ Usage
Run a scan and generate report:
sudo ./mkrootkitscan --format=html # Save as scan_report.html
sudo ./mkrootkitscan --format=json # Save as scan_report.json
sudo ./mkrootkitscan --format=text # Console output
sudo ./mkrootkitscan --format=html --quiet # No console output
π Output Files
scan_report.json
β JSON reportscan_report.html
β User-friendly HTML reportscan_report.txt
πΈ Screenshots
JSON Output:
{
"Processes": ["β οΈ Suspicious Process 101: /usr/sbin/apache2"],
"Ports": ["π Open Port: 0.0.0.0:22"],
"Modules": [],
"Preload": "",
"Hidden": ["π Hidden File/Dir: /.dockerenv"],
"Hashes": ["π /bin/ls MD5: 1a79a4d60de6718e8e5b326e338ae533"],
"Timestamp": "2025-05-31T12:34:56Z"
}
main.go Full code
// mkrootkitscan - CLI Rootkit Scanner
// GitHub-ready version with CLI flags and report generation
package main
import (
"crypto/md5"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"time"
)
type ScanResult struct {
Processes []string `json:"processes"`
Ports []string `json:"ports"`
Modules []string `json:"modules"`
Preload string `json:"preload"`
Hidden []string `json:"hidden"`
Hashes []string `json:"hashes"`
Timestamp string `json:"timestamp"`
}
func ScanProcesses() []string {
results := make([]string, 0) // initialize empty slice
entries, _ := ioutil.ReadDir("/proc")
for _, entry := range entries {
if pid, err := strconv.Atoi(entry.Name()); err == nil {
cmd, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
if err == nil && strings.Contains(string(cmd), "ld.so.preload") {
results = append(results, fmt.Sprintf("β οΈ Suspicious Process %d: %s", pid, string(cmd)))
}
}
}
return results
}
func parseHexIPPort(hexStr string) string {
parts := strings.Split(hexStr, ":")
ip := parts[0]
port := parts[1]
ipBytes := make([]byte, 4)
for i := 0; i < 4; i++ {
b, _ := strconv.ParseUint(ip[2*i:2*i+2], 16, 8)
ipBytes[i] = byte(b)
}
ipStr := net.IPv4(ipBytes[3], ipBytes[2], ipBytes[1], ipBytes[0]).String()
p, _ := strconv.ParseInt(port, 16, 32)
return fmt.Sprintf("%s:%d", ipStr, p)
}
func ScanPorts() []string {
results := make([]string, 0) // initialize empty slice
tcp, _ := ioutil.ReadFile("/proc/net/tcp")
lines := strings.Split(string(tcp), "\n")[1:]
for _, line := range lines {
if strings.TrimSpace(line) == "" {
continue
}
parts := strings.Fields(line)
localHex := parts[1]
state := parts[3]
if state == "0A" {
results = append(results, "π Open Port: "+parseHexIPPort(localHex))
}
}
return results
}
func ScanModules() []string {
results := make([]string, 0) // initialize empty slice
data, _ := ioutil.ReadFile("/proc/modules")
lines := strings.Split(string(data), "\n")
for _, line := range lines {
if strings.Contains(line, "rootkit") {
results = append(results, "β οΈ Suspicious Module: "+line)
}
}
return results
}
func ScanLDPreload() string {
content, err := ioutil.ReadFile("/etc/ld.so.preload")
if err == nil && strings.TrimSpace(string(content)) != "" {
return "β οΈ Non-empty /etc/ld.so.preload: " + string(content)
}
return ""
}
func ScanHiddenFiles() []string {
results := make([]string, 0) // initialize empty slice
files, _ := ioutil.ReadDir("/")
for _, f := range files {
if strings.HasPrefix(f.Name(), ".") {
results = append(results, "π Hidden File/Dir: /"+f.Name())
}
}
return results
}
func ScanCriticalFileHashes() []string {
results := make([]string, 0) // initialize empty slice
paths := []string{"/bin/ls", "/bin/ps", "/usr/bin/top"}
for _, path := range paths {
content, err := ioutil.ReadFile(path)
if err == nil {
hash := fmt.Sprintf("%x", md5sum(content))
results = append(results, fmt.Sprintf("π %s MD5: %s", path, hash))
}
}
return results
}
func md5sum(data []byte) []byte {
h := md5.New()
h.Write(data)
return h.Sum(nil)
}
func GenerateReport(results ScanResult, format string) {
switch format {
case "json":
b, _ := json.MarshalIndent(results, "", " ")
ioutil.WriteFile("scan_report.json", b, 0644)
case "html":
f, _ := os.Create("scan_report.html")
defer f.Close()
f.WriteString("<html><body><h1>mkrootkitscan Report</h1><ul>")
for _, p := range results.Processes {
f.WriteString("<li>" + p + "</li>")
}
for _, p := range results.Ports {
f.WriteString("<li>" + p + "</li>")
}
for _, m := range results.Modules {
f.WriteString("<li>" + m + "</li>")
}
if results.Preload != "" {
f.WriteString("<li>" + results.Preload + "</li>")
}
for _, h := range results.Hidden {
f.WriteString("<li>" + h + "</li>")
}
for _, h := range results.Hashes {
f.WriteString("<li>" + h + "</li>")
}
f.WriteString("</ul></body></html>")
default:
fmt.Println("[Text Output]")
for _, l := range results.Processes {
fmt.Println(l)
}
for _, l := range results.Ports {
fmt.Println(l)
}
for _, l := range results.Modules {
fmt.Println(l)
}
if results.Preload != "" {
fmt.Println(results.Preload)
}
for _, l := range results.Hidden {
fmt.Println(l)
}
for _, l := range results.Hashes {
fmt.Println(l)
}
}
}
func main() {
outputFormat := flag.String("format", "text", "Output format: text, html, or json")
quiet := flag.Bool("quiet", false, "Suppress console output")
flag.Parse()
results := ScanResult{
Processes: ScanProcesses(),
Ports: ScanPorts(),
Modules: ScanModules(),
Preload: ScanLDPreload(),
Hidden: ScanHiddenFiles(),
Hashes: ScanCriticalFileHashes(),
Timestamp: time.Now().Format(time.RFC3339),
}
if !*quiet {
fmt.Println("β
Scan complete. Generating report...")
}
GenerateReport(results, *outputFormat)
if !*quiet {
fmt.Println("π Report saved")
}
}
β
What Youβve Achieved with mkrootkitscan
Youβve built a custom rootkit scanner that does the following:
π Core Features
- Open Port Scanning: Parses
/proc/net/tcp
to find open ports in LISTEN state. - Process Analysis: Detects suspicious processes referencing
ld.so.preload
. - Kernel Module Check: Scans
/proc/modules
for βrootkitβ-related modules. - LD Preload Check: Flags if
/etc/ld.so.preload
is being used maliciously. - Hidden Files Detection: Lists hidden files in root (
/
) directory. - Critical Binary Hashing: Computes MD5 hashes of sensitive binaries like
/bin/ls
,/bin/ps
.
π Reporting
- Generates output in text, JSON, and styled HTML formats.
- Includes timestamps and emojis for quick visual scanning.
- CLI flags for output format and quiet mode.
π How It’s Different from Traditional Tools (e.g., chkrootkit, rkhunter)
Feature | mkrootkitscan | chkrootkit / rkhunter |
---|---|---|
Language | Go (compiled binary) | Shell + Perl (scripts, not compiled) |
Portability | Static binary, very portable | Requires dependencies and config tweaking |
Customizability | Easy to modify | Harder to extend or modify |
Output | Clean CLI + JSON + HTML | Mostly terminal text |
Real-Time Integration | Can be embedded into CI/CD tools | Not ideal for automation |
Modern Style | Minimal and fast | Outdated UX, not DevOps-native |
π’ Can It Be Used on Production Servers?
Technically yes, but hereβs the full picture:
β Strengths for Production:
- Lightweight, no dependencies.
- Safe reads from
/proc
, no invasive operations. - No installation β run as a binary or cron job.
- JSON output is automation-friendly (integrate into monitoring/alerting systems).
- Fast β suitable for cloud instances or containers.
β οΈ Limitations (So Far):
- No rootkit signature database (like rkhunter uses).
- No heuristic detection (e.g., behavior-based anomalies).
- Limited file/path coverage β it scans only a few binaries and top-level
/
. - No logging or alerting mechanism (youβd need to hook this into a SIEM or alert system).
- No check for file permission anomalies, syscall hooks, or kernel-level stealth techniques (common in real rootkits).
π οΈ How to Make It Production-Ready (Future Roadmap)
Area | What to Add |
---|---|
Logging | Syslog support or file logging |
Alerts | Email, Slack, or webhook alert when issues found |
Scheduled Runs | Set up as a cron job or systemd service |
Config File | Allow user config for paths, thresholds, etc. |
File Integrity | Compare MD5s against known-safe values |
Signature Matching | Maintain JSON DB of known rootkit hashes or names |
Behavioral Analysis | Flag anomalous port/process behavior over time |
OS Support | Expand compatibility testing (e.g., Ubuntu, CentOS) |
πββοΈ Author
[Mo] | MKCLOUDAI | GitHub: @mxkdevops