← All articles

VirLabs Blog

knock-outd: an undetected port-knocking backdoor in the wild

By Ivan Kwiatkowski ·

CLOSEDQUORUM offers a glimpse of LLM-directed malware in the making, complete with hardcoded decisions, broken injection and a decision loop that only decides once. Autonomy remains a work in progress.

I recently came across an interesting Linux sample on VirusTotal (0 detections, uploaded May 25, 2026). It binds to the network interface, waiting for a packet sequence, and launches another executable with an IP address and a selected port. Unfortunately, I wasn't able to recover that second executable (/sbin/xfs_timesupdate). I have to assume it is a reverse shell of some sort, but in any case that leaves us with half a backdoor. The half we have is worth documenting though: port-knocking is a cool technique that we don't see too often, and the activation mechanism can be understood without recovering the payload.

I will refer to the launcher as knock-outd (VirLabs analysis), after the original source filename preserved in its ELF symbol table.

A promiscuous backdoor

The sample is a statically linked x86-64 Linux executable containing libpcap. Its worker requires UID 0 and takes the capture interface from the command line. On startup, it overwrites its visible process arguments with bash and spaces, changes its working directory to /, and forks the packet-monitoring process. For reliability, the parent implements a poor man's watchdog where it replaces the worker periodically. Every two hours, it sends SIGTERM, calls waitpid, waits another second, and starts again.

On its end, the worker opens the supplied interface in promiscuous mode and installs a capture filter. Activation depends on packet headers: after a qualifying sequence, the callback waits three seconds and invokes:

/sbin/xfs_timesupdate <packet-source IPv4> <selected port>

...where the IP address is the source of the triggering traffic, and the port is derived from the source port of the last knock (see below).

Knock knock knock... Who's there? Not the payload, Unfortunately

The capture filter combines the interface's IPv4 address with two individual source ports and a source-port range. For an address of 192.0.X.Y, it becomes:

dst host 192.0.X.Y and src port 35646 or 46589 or src portrange 41232-41238

There are missing parentheses for this filter to properly guard the destination but it doesn't matter too much in practice. The port knocking sequence expects two packets that match this filter, and then a third one in the selector range to determine the port argument to provide to /sbin/xfs_timesupdate. Packets must be received within 15 seconds of the previous one:

stateDiagram-v2
    direction TB
    state "Idle" as Idle
    state "One packet accepted" as First
    state "Waiting for selector" as Second
    state "/sbin/xfs_timesupdate" as Launch

    [*] --> Idle
    Idle --> First: Admitted IPv4 packet / arm 15s timer
    First --> Second: Same source / refresh timer
    First --> Idle: Different source or timeout
    Second --> Launch: Same source and source port 41232 to 41238
    Second --> Idle: Different source or timeout
    Launch --> Idle: Reset sequence

Note that some non-selector packets leave the callback waiting: for example, source port 35646 at the final stage does not complete or reset the sequence. Also state is global, so unrelated admitted traffic from another source could in theory interrupt a pending activation.

The final source port selects an argument from a table named rever_port:

Final source portArgument passed to the helper
41232443
4123380
4123453
41235110
412368080
4123725
412380, read beyond the table 😬

For source port 41238, that reads four bytes at 0x532138, immediately beyond the object -- fortunately for the malware author it happens to always be 0 due to padding.

Conclusion

For people interested in hunting, the executable has an original STT_FILE record for knock-outd.c, followed by application-local symbols. Names such as kernelfuc, rever_port, and srcip_previous.6119 also occur verbatim in the original symbol table, but none of these strings allowed me to discover additional samples. There are precedents for the general port-knocking architecture, such as the Linux backdoor analyzed after the 2014 freenode compromise or cd00r... yet as far as I can tell knock-outd is not related to either of these. A Yara rule is also provided below.

Anyway, it was a nice little piece of malware. I'm not surprised it evaded detection up to now, considering that Linux typically receives less attention (I have more coming) and the architecture carefully splits the port-knocking and the payload into separate components. At this stage, there is no way of knowing if this is related to APT activities, talented lone-hacker tooling, or anything in the middle. If you find more, be sure to drop me a line!

Indicators of Compromise

7ccdbf6d7cfbd987a4247583bbcff308cd31734d12071dfc26c7999266602cfa

Yara rule

rule VirLabs_Linux_Knockoutd_Hunting
{
    meta:
        author = "VirLabs"
        description = "Linux packet-triggered xfs_timesupdate launcher"
        hashes = "7ccdbf6d7cfbd987a4247583bbcff308cd31734d12071dfc26c7999266602cfa"

    strings:
        $elf64_le = { 7F 45 4C 46 02 01 }

        $helper = "/sbin/xfs_timesupdate %s %d" ascii
        $filter = "dst host %s and src port %u or %u or src portrange %u-%u" ascii

        // Little-endian uint32 values: 443, 80, 53, 110, 8080, 25.
        $helper_ports = {
            BB 01 00 00 50 00 00 00 35 00 00 00
            6E 00 00 00 90 1F 00 00 19 00 00 00
        }

        // Little-endian uint32 values: 35646, 46589, 41232.
        $trigger_ports = {
            3E 8B 00 00 FD B5 00 00 10 A1 00 00
        }

    condition:
        $elf64_le at 0 and
        (
            $helper or
            $filter or
            (
                $helper_ports and
                $trigger_ports
            )
        )
}