Juventus had a chance to go ten points clear at the top after second-placed Lazio also lost 2-1 to bottom side Lecce earlier.
The first half ended goal-less after Ibrahimović had a goal ruled out for offside shortly before the break.
Adrien Rabiot broke the deadlock two minutes after the restart by curling a strike into the top right corner. And Cristiano Ronaldo doubled Juve's lead six minutes later.
That seemed to rattle the hosts who halved the deficit through a 62nd-minute penalty by Ibrahim before quickfire goals from Franck Kessié and substitute Rafael Leão. Milan then wrapped the three points with an 80th-minute strike from Ante Rebic.
In Rome, Lazio went down 2-1 to relegation-threatened Lecce who also missed a penalty and had a goal disallowed.
Fabio Lucioni and Khouma Babacar scored for Lecce after Felipe Caicedo had given Lazio an early lead.
Lazio's bad night was made worse when defender Patric was sent off for biting Giulio Donati in an incident that could lead to a lengthy ban.
Title-chasing Lazio was just a point behind Juventus when the season was halted but they are now seven points adrift after losing three of their five matches after football resumed.
Lecce moved out of the relegation zone with 28 points, a point above Genoa, which hosts Napoli on Wednesday.
Lecce thought it had taken the lead after two minutes through Marco Mancosu but it was ruled out for handball in the buildup. Lazio then took the lead moments later when Caicedo tapped into an empty net after Lecce keeper Gabriel had parried out a Ciro Immobile strike.
Lecce levelled on the half-hour mark when Babacar was left unmarked, eight yards out, to head in Filippo Falco's cross.
The hosts' Mancosu blasted a penalty over the bar, following a handball from Patric. However, Lecce did go in front two minutes after the break when Lucioni headed in a corner.
Serie A Tuesday results
- AC Milan 4-2 Juventus
- Lecce 2-1 Lazio