balanced Shortest[] and string-patterns

This seems to work:

StringCases["blabla ...Hello Hello ... blabla ... Goobye Goobye ..", 
 Longest[___ ~~ a : "Hello"] ~~ b : Shortest[___ ~~ "Goobye"] :> a ~~ b]

Update

If there are multiple substrings to extract you can use recursion:

extractbetween[str_, x_, y_] := Module[{f},
  f[s_] := StringCases[s, 
   Longest[a___ ~~ x] ~~ b : Shortest[___ ~~ y] :> {f[a], x ~~ b}];
  Flatten@f@str]

extractbetween["blah X first Y blah X second Y X third Y", "X", "Y"]
(* {"X first Y", "X second Y", "X third Y"} *)

 shortestStringCases[str_String, from_String, to_String] := 
   StringCases[ str, (from ~~ mid___ ~~ to) /; StringFreeQ[mid, {from, to}]]
 shortestStringCases["blah X blah X first Y blah X blah X second Y", "X", "Y"]
 (* {"X first Y", "X second Y"} *)

A possible solution is just to replace your boundary words with single characters. I think what you are venturing into is something akin to look-behind, which I don't think is supported. Anyways here's how I would do it:

boundary = {"Hello", "Goobye"};
limits = {"\[FormalCapitalX]", "\[FormalCapitalY]"};

shift[str_, from_, to_] := StringReplace[str, Rule @@@ Transpose[{from, to}]]

Just to have more then one match, I changed the test string

test = "blabla ...Hello Hello ... blabla ... Goobye
            Goobye .... Hello ... blabla2... Goobye";

shift[#, limits, boundary] & /@ 
  StringCases[shift[test, boundary, limits], 
  Shortest[limits[[1]] ~~ (Except[limits[[1]]] ...) ~~ limits[[2]]]]
(* {"Hello ... blabla ... Goobye"  , "Hello ... blabla2... Goobye"}  *)

Depending on your input, the substitution characters might need to be more carefully selected.