The only way to do this with a validation rule (i.e., without writing code) is to maintain a count of Attachments on the Opportunity. Since the relationship between Attachment and Opportunity is not master-detail, this must be done with customization.
You can write a trigger to do this, but it's easier to do it declaratively by installing Declarative Lookup Rollup Summaries. You can build a rollup using DLRS with the parent object as Opportunity and the child as Attachment; rollup operation as COUNT; any SOQL parameters you need to include; and a target Number field on the Opportunity, such as Count_of_Attachments__c
.
Once you do this and go through the activation process for DLRS, including setting the rollup to active and running it in real-time by having DLRS deploy its triggers (you'll probably also want to click Calculate to cover all existing Opportunities), you can write a validation rule on Opportunity something like this:
AND(Count_Of_Attachments__c = 0, ISPICKVAL(StageName, "YOUR_STAGE_NAME_HERE))
This is not a feature that you can get "out of the box". You're going to have to do some real customization to get there, but it's not difficult - you just need to go through several steps and effectively deploy these features to get to your objective.