I think you’re misunderstanding what Cull does.
Each triangle has a facing, which is determined by the winding order as it appears on screen. Basically if the vertices of a triangle are in a clockwise order when seen by the camera, that’s the “front” of the triangle, and if it’s counter-clockwise it’s the “back” of the triangle. Cull determines if either side is skipped if it’s facing the camera, where Cull Back hides the back of the triangle, Cull Front hides the front, and Cull Off shows both sides.
That’s it.
I suspect what you’re describing is not backface culling. I suspect what you’re describing is overlapping front faces are visible and you want the ones in “the back” to not be. That’s depth rejection.
See this image from Unity’s old documentation on culling and depth testing.

Both of these meshes are using shaders with back face culling.
The difference is the one on the right is using that extra Pass you posted above that you’re using with your Surface Shader.
That extra Pass is writing to the depth buffer (aka Z buffer) which is normally used for opaque depth sorting, but can also be used like this. The depth buffer’s main reason to exist is when a triangle is rendered, it calculates its depth, and for each pixel of the triangle that’s on screen it checks if it’s further away than the value in the depth buffer. If it is, it skips rendering that pixel of the triangle. If it’s closer, it renders that pixel and replaces the depth value in the depth buffer with the new closer value. You can control that behavior with ZTest and ZWrite if you want to change it, but the above is the default behavior if you don’t specify.
And that explains why your “unlit” vertex fragment shader is working like you want it. You probably set a Blend mode to make it transparent, but aren’t changing the ZWrite behavior. However you’re also getting lucky that all (most?) of the triangles closer to the camera in the mesh you’re using are the ones rendering first. I suspect if you rotate around your model using that “unlit” shader you’ll see seemingly random faces “in the back” show up still, just because the order of the triangles in your mesh won’t and can’t be perfectly sorted for all camera angles (for all but some very specific concentric convex shape setups). If you add ZWrite Off, as is usual for most transparent shaders, it’ll look more like the Surface Shader did w/o the extra pass.
So I said ZWrite On is the default behavior if there’s no ZWrite line in the shader. That’s kind of a lie, but also isn’t. Surface Shaders are vertex fragment shader generators (as is Shader Graph), and setting a shader to be transparent by adding alpha to the #pragma surface line adds ZWrite Off to the generated shader passes. You could put ZWrite On in the shader at the start of the SubShader, but that won’t do anything since the setting in the generated passes overrides whatever settings exist in the SubShader. However adding an additional pass yourself gets around that because it’s not one being generated by the Surface Shader itself.
It’s also important that it’s the first Pass in the shader because otherwise it’ll be writing to the depth buffer after the rest of the passes have rendered. It’s also the “correct” way to solve this problem because it ensures only the closest triangle surfaces for that one mesh are ever rendered regardless of the order they’re in. So really you want to do this even for your “unlit” vertex fragment shader.