Skip to content

sftp: don't crash when glob(3) results lack stat information - #707

Open
maycuatroi1 wants to merge 1 commit into
openssh:masterfrom
maycuatroi1:sftp-glob-null-statv
Open

sftp: don't crash when glob(3) results lack stat information#707
maycuatroi1 wants to merge 1 commit into
openssh:masterfrom
maycuatroi1:sftp-glob-null-statv

Conversation

@maycuatroi1

@maycuatroi1 maycuatroi1 commented Aug 15, 2026

Copy link
Copy Markdown

Summary

The sftp(1) client crashes with a NULL pointer dereference when a glob(3) result contains entries whose gl_statv[] stat pointer is NULL. Two consumers of the glob result in do_globbed_ls() dereference these entries without NULL checks, while the display loop in the same function already handles NULL correctly ("no stat information", sftp.c:1046).

How NULL stat entries arise

do_globbed_ls() runs glob(3) with GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT (sftp.c:980). When one brace alternative matches real entries while another alternative matches nothing (e.g. ls {*,zzz}), the NOCHECK fallback appends the non-matching literal via globextend(..., NULL), which stores a NULL gl_statv[] entry for it (openbsd-compat/glob.c:563, 850-851). The stat callbacks fudge_stat/fudge_lstat (sftp-glob.c:110-133) fail whenever the SFTP server replies failure to SSH2_FXP_STAT/LSTAT, so a malicious or compromised server fully controls which entries carry NULL stat pointers. This is server-controlled data, no server bug needed.

Confirmed sinks

  1. collect_ids_from_glob() (sftp-usergroup.c:158) dereferences gl_statv[i]->st_uid / st_gid. Reached from a plain ls <glob> whenever the server advertises users-groups-by-id@openssh.com (feature since 9.1).
  2. sglob_comp() (sftp.c:950-956, 961) dereferences gl_statv[a] / gl_statv[b for ls -t / ls -S sorting. No extension required; comparator present since at least 8.x.

Reproduction (deterministic, minimal-impact)

Mock SFTP subsystem (Python, stdio protocol, fixed fake data, no filesystem access): READDIR returns poc_a/poc_b/poc_c; STAT/LSTAT succeed for /data and /data/poc_*, return SSH_FX_FAILURE for anything else (the zzz literal). Loopback sshd, container build.

  • OpenSSH master @ 5280556, clang ASan build:
    • ls {*,zzz} with extension advertised: SEGV read at 0x1c (struct stat .st_uid offset), stack collect_ids_from_glob <- parse_dispatch_command sftp.c:1039
    • ls -t {*,zzz}: SEGV read at 0x58 (.st_mtim offset), stack sglob_comp sftp.c:951 <- qsort <- sftp.c:1035
  • Ubuntu 24.04 stock client (1:9.6p1-3ubuntu13.18): both commands Segmentation fault, exit 139
  • With this patch: both complete normally (exit 0); ls -l {*,zzz} still prints "no stat information for zzz"; -S/-ltr order the no-stat entry deterministically

Impact: remote client-side crash (read of a NULL-derived address; no write primitive). Severity low/medium DoS - but it is a memory-safety violation on server-controlled data in code that clearly intends to handle the NULL case elsewhere.

Code changes

  • sftp.c sglob_comp(): order entries without stat information last (reverse-aware via rmul)
  • sftp-usergroup.c collect_ids_from_glob(): skip entries with NULL stat

Report credit: Binhna3

glob(3) run with GLOB_KEEPSTAT can leave gl_statv[i] NULL for result
entries that could not be stat'ed, e.g. for a GLOB_NOCHECK literal
appended for a non-matching brace expansion when the remote SFTP
server fails SSH2_FXP_STAT/LSTAT for it. The listing code already
handles this case ("no stat information"), but two other consumers
of the glob result did not:

- sglob_comp(), the comparator used by ls -t/-S sorting, dereferenced
  gl_statv[a]/gl_statv[b] unconditionally
- collect_ids_from_glob(), which collects uids/gids for the
  users-groups-by-id@openssh.com extension, dereferenced
  gl_statv[i]->st_uid/st_gid unconditionally

A malicious or compromised SFTP server could crash the client this
way (NULL deref, read-only). Order entries without stat information
last in the comparator and skip them during id collection.

Reproduced on master @ 5280556 and
on Ubuntu 24.04 stock 9.6p1 client against a mock SFTP server that
returns SSH_FX_FAILURE for STAT/LSTAT of a non-matching brace
alternative; with this change both ls {*,zzz} (extension advertised)
and ls -t {*,zzz} complete normally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant