At the CPU level they are not variables, they are simply addresses, registers and other locations. The stack is not necessarily the place where they are stored.
While you might have higher level code that says
Add `variable1` to `variable2`
At the machine code level this would be broken down to simpler instructions (using pseudo-code, not true x86 assembly)
Read memory address of `variable1` into register A
Read memory address of `variable2` into register B
Add resister A to register B
Store result in memory address of `variable3`
Variable names are only there for you to use as a human being, the CPU does not care about them and they will be replaced with relevant program memory addresses by the assembler.
As far as I remember (it has been 20 years) the stack is not meant for random access. You can push values onto the stack or pop off the last item but saying give me item 9 on the stack
is not how it works. At best it would be
Pop item from stack into register A
Pop (next) item from stack into register B
Add register A to register B
...
If you need item 9 on the stack then you would have to pop the first 8 items off it first and presumably do whatever work was necessary with them first. It is a queue, not random storage. Specifically it is a LIFO (Last In First Out) queue.
So as an example if you are going to call a function you may well push two items onto the stack to pass as "variables". The first thing the receiving code would do is pop those off the stack:
Main code:
Push variable2 onto stack
Push variable1 onto stack
Jump to code at address (AddNumbers)
AddNumbers:
Pop variable 1 from stack to register A
Pop variable 2 from stack to register B
Add register A to register B
(Note the reverse order of pushing items onto the stack)
How variables are kept as local and global is not really something for the CPU to care about. It is an issue of semantics and descriptive language used by the assembler program to decide where in memory it is going to arrange named variables before replacing their name with an address in the machine code, as well as on giving the ability to re-use simple names in a local scope.
Assembler code might be "close to the bare metal" but there are still obfuscation such as variable names, local and global scopes, and other things used to make them simpler for humans to handle. They still need a program (the assembler) to rewrite them in actual machine code before the CPU can use them.