Select the same elements from lists

I believe my approach is fairly similar to yours, but uses Association's and some built in operations on them to make the code somewhat simpler. (The fact that several of these functions have operator forms makes the code especially clean.)

ClearAll[multiIntersection]
multiIntersection[lists__List] := (
  Counts /@ {lists}
       // KeyIntersection
      // Merge[Min]
     // KeySort
    // KeyValueMap[ConstantArray]
   // Catenate
  )

You can remove the KeySort if you don't want the returned elements to be sorted.

multiIntersection[a, b, c]
{2, 3, 3}

Edit: Eliminated the needlessly cumbersome ConstantArray[#1, #2] & in favor of ConstantArray.


We can use Intersection directly if we label each list value with a number indicating how many times that value has occurred in the list so far:

label[list_] := Module[{n}, n[_] = 0; #[++n[#]]& /@ list]

label[a]
(* {1[1], 2[1], 2[2], 3[1], 3[2], 3[3], 4[1], 4[2], 4[3]} *)

So then:

(Intersection@@(label /@ {a, b, c}))[[All, 0]]
(* {2, 3, 3} *)

Catenate[Table@@@Normal[Merge[KeyIntersection[Counts/@{a,b,c}],Min]]]

{2, 3, 3}