Well, technically Linux is just the kernel, it doesn't need a shell for anything. But to do anything useful, you need something running in userspace. If you managed to integrate everything you need into one process, you could drop it in place of
/sbin/init and have no other userspace processes on the system.
In practice you might want the equivalent of
udev and other plumbing, possibly a DHCP client. (Static IPv4 configuration can be set on the kernel command line.)
With the traditional sysvinit style initialization, everything depended on shell scripts (the ones in
/etc/init.d/, with links from
/etc/rc*.d), so using that is right out. But one of the points of systemd is that it doesn't rely on the shell as much, so that might be doable, if all services you're running have systemd unit files (instead of
init.d scripts), and they don't use shell scripts for anything else.
On one Ubuntu system (17.10) I have, at least
keyboard-setup and Postfix seem to have shell scripts in their unit files. The first two aren't probably important since you not logging in on a console without a shell, but there might be others that need it...
If a shell-less system was really wanted, I'd probably start from scratch or from something more minimal than a full-blown system like Ubuntu. It would be an interesting experiment to remove
/bin/sh and see how far the system gets, though.
You mentioned security as one motivation for this. Without a shell, any shellcode that relies on starting a shell would fail, which might thwart simpler attackers. But in principle, when we get to the point where the attacker can run arbitrary code, there's nothing to stop them from doing what they want manually, without a shell, or from just loading a shell on the system and running it. It's just a question of how much time your attackers are prepared to use.
An obvious disadvantage of a shell-less system would be that configuring it would be harder, since you couldn't just log in to start an editor, or restart services. You'd need to boot the system from a "rescue disk" or rebuild it completely to make changes.
Note that disabling the usual ways of logging in to the system (
gettys on virtual consoles, SSH, whatever) don't do much to stop attackers. They're more likely to exploit bugs in any services you're running, than to ask nicely if your system would please let them log in.