After an uneventful first half, Milan scored four unanswered second-half goals having gone 2-0 down to Juve strikes from Adrien Rabiot and Cristiano Ronaldo.
Ibrahimovic’s 62nd-minute penalty offered hope, before Franck Kessie and Rafael Leao completed a five-minute turnaround.
Ante Rebic added a fourth to secure a memorable victory with 10 minutes remaining.
Defeat sees Maurizio Sarri’s side miss the chance to move 10 points clear of second-placed Lazio, who lost 2-1 to relegation-threatened Lecce earlier on Tuesday.
It took Maurizio Sarri’s visitors until first-half injury time to register their first shot on target, but Rabiot’s spectacular finish from the edge of the box – after running with the ball from his own half – sparked the contest into life.
Ronaldo, who became the first player to reach 25 Serie A goals in a season for the club since 1961 on Saturday, met Juan Cuadrado’s pass to score for a fifth successive game and leave Juve in control.
However, Ibrahimovic’s spot-kick, which was awarded after Leonardo Bonucci was penalised for handball, flipped the momentum.
Milan were soon level after Kessie wriggled through the Juventus defence unchallenged, while Leao beat Wojciech Szczesny at his near post from a tight angle.
A fired-up Milan continued to attack Sarri’s shell-shocked side and were rewarded as Rebic swept home to seal three points which send a resurgent Milan up to fifth.