In C++ you are responsible to track object lifetimes.
This means you have to track the lifetimes of things that hold pointers and references to other things, and ensure they do not live as long as those things.
You failed in your task. You passed lambdas that capture pointers to objects around as if they where candy, and not a direct line into the guts of your object.
Solving lifetime problems by sprinkling shared pointer around is usually a bad idea. Making the lifetime of your objects more nebulous may reduce the incidents of immediate crashes, but a nebulous ball of object lifetimes does not make your program work. The nebulous ball either expands to encompass your entire program, which now can never actually shut down, or it circles back in on itself and self-perpetuates, leaking resources.
Shared pointers can be used in narrow situations where you have a defined lifetime relationship that is best modelled as a shared ownership. This is not at all the same as "I have objects going away before their pointers, so I should try shared pointer!" You have an object lifetime problem. You try shared pointer. Now you have two problems: the original object lifetime problem, and your shared pointer problem.
Callbacks are an example of a case where you need strict lifetime rules. How long do you callback? When do you stop? How urgently you recycle resources? How do you unregister the callback? Etc.
I have written callback systems that use shared and weak pointers. They aren't perfect. Here is one I found in google: broadcaster. The listener stores tokens to say "keep talking to me", when they go away the broadcaster stops yammering at it.