High level languages like C define a program to be working on an abstract machine. This abstract machine has no notion of different virtual address spaces, hardware interrupts, or process switching. Instead, it relies on such things as a stack to be set up.
Take for example the stack: Once the stack is set up correctly, the abstract machine can operate it, but there is no means in the abstract machine to say: "From now on, use this bit of memory as a stack, and start the stack pointer at this position." As such, at the very beginning of a program, a small snippet of assembler code is needed that sets the register which is used as a stack pointer to a sensible initial value. After that code snippet is run, it can hand over to code that uses the stack in the normal way.
One other thing that needs to be taken care of in assembler is interrupt handling: The hardware of your computer can signal to the CPU that it needs attention. The CPU responds to this by abandoning its current execution (this can happen between any two machine instructions!), and transfer execution to an interrupt handler. When this interrupt handler is entered, all the data the currently running process had in the CPU registers is still there and nowhere else. Worse, the interrupt handler typically has no idea what the program was actually intending to do with it. So the interrupt handler must save away all this state before it can do anything else, it would be impossible to resume the interrupted process if the interrupt handler failed to do this. This requires explicit access to all the registers, and a language that guarantees not to step on any unsaved state, so this must be done in assembler. Once the state is saved, control can be passed to routines written in C.
These are just two different examples of where and why assembler is needed even in modern systems. And I have not even touched on switching address spaces and the flushing TLBs that it requires...