Updated:

1. Setup (once)

/team add red "Red"
/team add blue "Blue"
/team modify red color red
/team modify blue color blue
/team modify red friendlyFire false
/team modify blue friendlyFire false
/scoreboard objectives add flags dummy "Flags"
/scoreboard objectives add died deathCount
/scoreboard objectives setdisplay sidebar flags
/scoreboard players set Red flags 0
/scoreboard players set Blue flags 0

Put players on teams with /team join red NAME. See team command. Players can share plans with /teammsg (private chat commands).

2. Mark the flag spots

/summon marker ~ ~ ~ {Tags:["red_flag"]}
/summon marker ~ ~ ~ {Tags:["blue_flag"]}

Run each at the center of a base and place a banner block there as decoration.

3. Take the enemy flag (repeating)

These steps are for red taking the blue flag; mirror them for blue.

/execute at @e[type=marker,tag=blue_flag] unless entity @a[tag=has_blue] as @a[team=red,distance=..1.5,limit=1,sort=nearest] run tag @s add take_blue
/item replace entity @a[tag=take_blue] armor.head with blue_banner
/tag @a[tag=take_blue] add has_blue
/tag @a[tag=take_blue] remove take_blue

Only the nearest red player gets the flag, and only while nobody holds it. The banner replaces any helmet, so play without helmets.

4. Make the carrier glow (repeating)

/effect give @a[tag=has_blue] glowing 1 0 true

5. Score at your base (repeating)

/execute at @e[type=marker,tag=red_flag] as @a[tag=has_blue,distance=..2] run scoreboard players add Red flags 1
/execute at @e[type=marker,tag=red_flag] as @a[tag=has_blue,distance=..2] run tellraw @a {"text":"Red captured the flag!","color":"red"}
/execute at @e[type=marker,tag=red_flag] as @a[tag=has_blue,distance=..2] run item replace entity @s armor.head with air
/execute at @e[type=marker,tag=red_flag] as @a[tag=has_blue,distance=..2] run tag @s remove has_blue

Remove the tag last, or the earlier lines stop matching.

6. Return the flag on death (repeating)

/gamerule keep_inventory true
/execute as @a[scores={died=1..},tag=has_blue] run item replace entity @s armor.head with air
/tag @a[scores={died=1..}] remove has_blue
/scoreboard players reset @a[scores={died=1..}] died

Run the gamerule once (keepInventory in 1.21.10 and older) so the banner is not dropped and picked up. Once the tag is gone, step 3 can hand out the flag again.

7. Win check (repeating)

/execute if score Red flags matches 3.. run title @a title {"text":"Red wins!","color":"red"}

Stop the command blocks afterwards and reset scores to 0 before the next match.

Troubleshooting

Check your command

The Command Error Doctor checks common syntax mistakes in your browser.