You're modifying hitInfo at every light (it's declared outside the foreach loop), this will trash the calculations at the second light. Basically, it'd do the first light right, and then on the second light, it'll incorrectly flip the hitInfo.ray vector once again, messing up the specular term (and not the diffuse, as you observed, since it doesn't use that variable). The third light would come out right, since the vector is flipped in the right direction once again, the fourth one will be wrong, and so on.
Easiest way to fix it would be to just get a temporary HitInfo variable inside the loop's scope, and make it equal to your original hitInfo at the start of each iteration, though in this case you might also be better off just hoisting the vector flip outside the loop, though that's less flexible and might bite you later if you modify more of this variable.
By the way, I have triangles now! I will write an .obj loader soon, and I will have 3D meshes!!!
Nice! Though if you want to go into really complex meshes you'll need some sort of scene graph to accelerate ray-triangle intersections, like an octree or a bounding volume hierarchy, otherwise it's going to take forever! But that's for later