As noted by other answerers, the concept of a default branch is a GitHub thing doesn't really make sense (in the lofty sense) of Git (there's a reasonably good (if under-appreciated) comment here gets into it a bit: https://stackoverflow.com/a/65710958/2521092) but in practice, we all know what that means.
Since the original asking of this question, Git 2.28 added init.defaultBranch, allowing a different initial branch than master, and a lot of projects are now using main.  That's good.  Most of the answers here rely on checking a remote, but that relies on there actually being a remote, and that there exists a reliable and consistent naming scheme to those remotes.  Those are probably (increasingly?) reasonable assumptions, but it's not a guarantee, and I don't find that any of the major answers here fail nicely.
Moreover, my main use case is using the name of the default branch for various git aliases (e.g. lm = log main..HEAD).  I'd like to use the same aliases without thinking about it too much, regardless of an external repo using master or a local using main with no remote.  Git and its config can't really "store" information, so there's no way to set ahead of time what the current repository's main branch is.  As such, any alias that wants to, say, show commits between the main branch and HEAD can't assume git log master..HEAD or git log main..HEAD will work.
Thus, I define a default-branch alias in Git that figures out the default branch and then supply that to other aliases.  This is a pain since a simple lm = log main..HEAD has to instead become lm = "!git log $(git default-branch)..HEAD" but here we are:
default-branch = "!git branch --sort=-refname | grep -o -m1 '\\b\\(main\\|master\\|dev\\)\\b'"
This simply gets the branch names then finds the first one in a defined list.  If there's a main, use that; if not and there's a master, use that.  I also have dev in there as a tertiary option some folks use.
This is sort of similar to what @henrik-n does in https://stackoverflow.com/a/66622363/2521092, but I do it in Git itself rather than the shell, and I think it has some more optionality.