Skip to content

[BUG] Error executing script: name 'job_message_buffer' is not defined #827

Description

@ToshY

Is there an existing issue for this?

  • I have searched the existing issues

Empire Version

v6.6.0

Python Version

docker

Operating System

Ubuntu 24.04.4 LTS

Database

MySQL

Current Behavior

Summary

Running any background-job module that streams output via job_message_buffer(...) on a Linux/macOS CPython agent (multi/launcher, Language=python) aborts with:

[!] Error executing script: name 'job_message_buffer' is not defined

Affected modules include python/collection/linux/keylogger,
python/collection/linux/xkeylogger,
python/collection/linux/sniffer,
python/collection/linux/port_scanner, and any custom module that calls
job_message_buffer(msg) from inside the script body it hands to the
agent.

Root cause

PR #821 ("Fix agent background python jobs", 354741f0) rewrote MainAgent.python_job_func in empire/server/data/agent/agent.py to exec the module-supplied script inside a purpose-built globals dict containing only print:

output_capture = io.StringIO()
script_globals = {'print': lambda *a, **k: print(*a, **k, file=output_capture)}
try:
    exec(data, script_globals)

Several built-in modules stream output by calling job_message_buffer as a bare name from inside that script. For example, empire/server/modules/python/collection/linux/xkeylogger.yaml references the name at lines 554, 625, 628, 654, 685, 689, and 755. Under the new exec scope the name is unresolved, the script raises NameError, and the operator sees [!] Error executing script: name 'job_message_buffer' is not defined.

Additional complication that informs the fix shape: MainAgent keeps a string instance attribute self.job_message_buffer = "" (the accumulating buffer) and defines a method def job_message_buffer(self, message) with the same name. The instance attribute shadows the method, so even self.job_message_buffer(message) raises 'str' object is not callable. That tangle predates the regressions discussed here and is best fixed separately. This patch sidesteps it.

Expected Behavior

No error.

Steps To Reproduce

  1. Start Empire v6.6.0.
  2. Deploy a Linux CPython agent: multi/launcher with Language=python,
    against a multi/http listener.
  3. (Apply the separate task-50/51 dispatch fix first so the module can
    be observed end-to-end; without it, the operator hits the
    'MainAgent' object has no attribute 'job_list' error before
    reaching this one.)
  4. Run python/collection/linux/xkeylogger against the agent.

Expected: xkeylogger registers as a running job and streams keystrokes
via job_message_buffer.

Actual on stock v6.6.0: the task output shows
[!] Error executing script: name 'job_message_buffer' is not defined.

Anything else?

Inject a small closure named job_message_buffer into script_globals that appends to the string buffer instance attribute directly, bypassing the shadowed method:

         output_capture = io.StringIO()
-        script_globals = {'print': lambda *a, **k: print(*a, **k, file=output_capture)}
+        def _append_job_message(msg):
+            try:
+                self.job_message_buffer += str(msg)
+            except Exception as e:
+                print("[!] Error adding job output to buffer: %s" % e, file=output_capture)
+        script_globals = {
+            'print': lambda *a, **k: print(*a, **k, file=output_capture),
+            'job_message_buffer': _append_job_message,
+        }

The existing checkin path (get_job_message_buffer) already flushes self.job_message_buffer back to the server on every poll and resets it; that side is unchanged.

Only python_job_func (task 112, background python jobs) is touched. dynamic_code_execute_wait_nosave (task 110) and dynamic_code_execution_wait_save (task 111) — immediate-execute paths — are intentionally left as-is; they do not run modules that stream output via job_message_buffer.

See attached patch: 0002-expose-job_message_buffer-in-script-globals.patch


Debug and PR summary with Claude Opus 4.7; Tested fix locally by extending Dockerfile.

Dockerfile
ARG EMPIRE_IMAGE_VERSION
FROM bcsecurity/empire:${EMPIRE_IMAGE_VERSION:-v6.6.0}

# from 826
COPY 0001-fix-agent-task-list-stop-task-dispatch.patch /tmp/p1.patch
COPY 0002-expose-job_message_buffer-in-script-globals.patch /tmp/p2.patch

RUN set -eu; \
    for p in /tmp/p1.patch /tmp/p2.patch; do \
      patch -p1 --forward --silent -d /empire < "$p" \
        || (echo "[empire-patch] $p did not apply cleanly; check upstream state" && exit 1); \
    done; \
    rm -f /tmp/p1.patch /tmp/p2.patch

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions