At least part of the problem is that the code runs in a potentially "dirty" state, and has to know every possible thing which has to be "cleaned" from that state (and how to clean it). If it forgets to (or, as was the case here, fails to) remove even one of them, no matter how insignificant it is, it can potentially be exploited.
On the other hand, if the code ran in a "clean" state and only copied things it knew to be safe from the "dirty" state, the risk would be lower (if it forgets or fail to do a copy, the information is simply missing).
In other words, blacklists versus whitelists.
Unfortunately, that cannot be done in this case, since it has no way of knowing which environment variables the called program will need.