I don't feel like digging for the sources of 25-year old shells, but
It could be a format-string vulnerability.
If the shell contains code like
str is some string taken from the user's input, the contents of the string will be the format string that
printf uses. The
printf to print a string pointed to by an argument. If the arguments aren't given (as above, there's only the format string), the function will read some other data from the stack, and follow them as pointers. Probably accessing unmapped memory and crashing the process.
In a way, I think the wording of the message hints at a solution like this, too. If you type
!xxx, what the shell visibly does is print an error message like
!xxx: event not found. From there, it's not a big leap to trying to also print
!xxx%s%s%s%s%s%s%s%s: event not found, with the implication of a format string vulnerability.
I shouldn't have, but I took a peek at the source here (
4.3BSD-Tahoe/usr/src/bin/csh, dates are from 1988).
findev(cp, anyarg) in
sh.lex.c looks it might be the function to find a matching history event: it
walks through a linked list of
struct Hist called
Histlist. If it doesn't find anything, it calls
seterr2(cp, ": Event not found"); through
cp here looks to be the string being searched for in the history.
seterr2() sets the variable
err as concatenation of the arguments, and
err is used as
if (err) error(err); in a couple of places in
sh.err.c) contains a classic format string vulnerability:
if (s) printf(s, arg), printf(".\n");
In some other places,
error() is called with an argument, like
error("Unknown user: %s", gpath + 1);, so clearly the idea is that the first argument to
error() may be a format string.
I wouldn't be honest if I said I understood the history substitution functions in full. It's pretty much uncommented manual string handling in C.
% does have a special meaning in history substitution, but I can only see it being handled specially as the first character (as in
!%) or after
findev() is called.