The Bitcoin.org Developer Guide has a section about Initial Block Download (IBD) that goes into detail.
The basic principle in modern Bitcoin Core nodes is that the node connects to eight peers, tells them that it knows about the hardcoded Genesis Block (first block), and asks for the headers of subsequent blocks. Headers are only 80 bytes per block (about 4.2 MB per year of blocks), so it doesn’t take a lot of data for a node to get all of the headers up until the present.
Each header contains enough data for the node to see how much Proof-of-Work is contained in the corresponding block, allowing the node to select the block (headers) chain with the most cumulative proof of work.
Now that the node knows which chain has the most proof of work, the node requests each of its peers start sending it blocks from that chain. Blocks are requested in order* from the earliest blocks to the latest. If any block on the chain with the most proof of work is found to be invalid, that block and any blocks that descend from that block are considered invalid. The node will poll its peers again, if necessary, and find which other block (headers) chain has the most PoW. It will then begin downloading those blocks.
It continues to download blocks from its peers, validating each one, until it has all the blocks on the valid block chain with the most PoW.
* Modern Bitcoin Core nodes will receive blocks out of order, storing later blocks temporarily until earlier blocks are received in order to allow receiving blocks from multiple peers simultaneously. Still, because most later blocks depend on data added to earlier blocks, some key parts of block validation can only be done in series.