Diego Forlan missed a penalty in the 13th minute after Chelsea defender David Luiz wrestled Diego Lugano to the ground.
Brazil took the lead in the 40th minute when Neymar latched onto to a long ball from Paulinho and his deflected shot fell to Fred, who bundled the ball home.
It took Uruguay three minutes of the second half to draw level.
The Brazilian defence repeatedly failed to clear the ball and when Thiago Silva's weak pass fell to Edinson Cavani he made no mistake with an angled drive.
The second half threw up few good chances but Paulinho rose to head home a Neymar corner in the 86th minute and give Brazil victory, with Spain and Italy playing in Thursday's second semi.
The final is at Rio de Janeiro's Maracana on Sunday.