Robinho's first half penalty was enough to decide a tight match, and surge Milan up the table, while leaving Juve with a precarious four point lead at the top.
The Bianconeri suffered only their second defeat in the league this season, after failing to respond to a Robinho penalty after 31 minutes in what was a closely contested match from both sides, which nevertheless lacked much action in front of goal.
Juventus maintains a four point lead at the top of the table, but could see that gap cut to a single point should Inter come away with victory at Parma on Monday, while Milan moves to eighth place with 18 points, jumping five spots in the standings.
Robinho won the race to start up front for the home side ahead of Bojan, with Mattia de Sciglio and Kevin Constant manning both fullback spots in the absence of Ignazio Abate and Luca Antonini.
Juve made only one change from the side that dismantled Chelsea 3-0 in the Champions League midweek, with Isla replacing Stephan Lichtsteiner as the right wing back.
De Sciglio was in the thick of things early on, dragging a shot from the edge of the area wide after five minutes, before forcing Gianluigi Bufon to work to stop his attempt from an angle with his legs, after he was played in by Robinho.
Still, the game struggled to get off the ground, as both teams, despite plenty of effort and encouragement from the crowd, found it hard to settle into a flow in the attacking third.
Antonio Nocerino became the first player booked after a quarter of an hour, after he was caught being a little to eager when attempting to win a loose ball ahead of Caceres.
But the better openings in the match were going Milan's way, and the club found some fortune just after the half hour mark when a soft penalty appeal for handball against Isla went in favor of the home team, after the defender struggled to clear under pressure from Nocerino.
Robinho took responsibilities from the spot, and his shot squeezed into the bottom left corner, despite Buffon getting a strong hand to the attempt.
Juve was simply not good enough in the first half, as despite controlling the majority of possession, looked decidedly out of ideas in attack.
Minutes into the second half, a terrible pass out of defense from Buffon nearly sold Andrea Barzagli short and allowed Stephan El Shaarawy to pounce, but the defender was able to slide in for a crucial tackle.
The introduction of Sebastian Giovinco for Fabio Quagliarella around the hour mark finally sparked Juve into life, as Milan found it hard to advance out of its own half and was guilty of leaving a few gaps on the flanks for the the visiting side to exploit.
The diminutive Italian set up a great opportunity for Mirko Vucinic not long after his introduction, but the Montenegrin could not convert his cross from a good position inside the area.
Philippe Mexes then had to be alert to intercept a very dangerous cross from substitute Simone Padoin, but Juve was still made to respect Milan's threat on the break as a rare second half attack for the home side saw Nocerino charge forward and crash a powerful drive just over.
Yepes forced a save from Buffon with a header from a corner, but Milan were soon forced back into some desperate defending as Juve made a late surge to get something from the match in the closing stages.
With seven minutes remaining, Giovinco again picked out Vucinic inside the area, but Amelia was in the right place to make a superb save to deny the striker from close range.
But Juve could not change its stripes that late in the game, and failed to find another breakthrough as Milan held on to record three vital points to help the recovery of the team's season.
Follow GOAL.COM USA on