How can I use legend in ContourPlot3D and add arrowheads for axis?

Try this:

    sf1[x_, y_, z_] := x (1/(1 + 0.06))^3 + y (1/(1 + 0.06))^2 - z/3;
sf2[x_, y_, z_] := 
  x (1 + 5 0.06) (1/((1 + 5 0.06) + 0.06))^3 + 
   y (1 + 5 0.06) (1/((1 + 5 0.06) + 0.06))^2 - z/3;


Show[{
  ContourPlot3D[{sf1[x, y, z] == 0, sf2[x, y, z] == 0}, {x, 0, 
    150}, {y, 0, 100}, {z, 0, 200}, Ticks -> None, 
   BoundaryStyle -> Red, Boxed -> False, AxesOrigin -> {0, 0, 0}],
  Graphics3D[{Red, Thickness[0.005], Arrowheads[0.03], 
    Arrow[{{0, 0, 0}, {153, 0, 0}}], Arrow[{{0, 0, 0}, {0, 0, 203}}], 
    Arrow[{{0, 0, 0}, {0, 102, 0}}]}],
  Graphics3D[{Text[
     Style["x", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {149, 0, 16}], 
    Text[Style["y", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {0, 96, 22}], 
    Text[Style["z", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {0, 0, 215}]}]
  }]

It should give this:

enter image description here

With another ViewPoint though. I turned a bit to make the y axis visible.

Have fun!

Edit: to address your question. I also did not succeed to use the Epilog->Insetstructure for this purpose. However, you may simply draw the necessary objects "by hand" the same way as the axes and labels were drawn. Try this:

  Show[{ContourPlot3D[{sf1[x, y, z] == 0, sf2[x, y, z] == 0}, {x, 0, 
    150}, {y, 0, 100}, {z, 0, 200}, Ticks -> None, 
   BoundaryStyle -> Red, Boxed -> False, AxesOrigin -> {0, 0, 0}, 
   ViewPoint -> {1.54, -2.77, 1.19}], 
  Graphics3D[{Red, Thickness[0.005], Arrowheads[0.03], 
    Arrow[{{0, 0, 0}, {153, 0, 0}}], Arrow[{{0, 0, 0}, {0, 0, 203}}], 
    Arrow[{{0, 0, 0}, {0, 102, 0}}]}], 
  Graphics3D[{Text[
     Style["x", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {149, 0, 16}], 
    Text[Style["y", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {0, 96, 22}], 
    Text[Style["z", 18, Italic, Bold, 
      FontFamily -> "Times New Roman"], {0, 0, 215}],
    Text[Style["b=0", 20, Darker@Brown], {20, 20, 180}],
    Text[Style["b=5", 20, Darker@Blue], {83, 47, 128}]
    }],
  Graphics3D[{Arrowheads[0.03], Thickness[0.005], 
    Arrow[{{20, 20, 173}, {25, 25, 129}}], 
    Arrow[{{83, 47, 132}, {74, 34, 188}}]}]
  }]

you should obtain the following:

enter image description here

The advantage is that bot the letters and arrows are 3D objects and you may rotate the image to visualize, say, where the second arrow points to. If the image rotation is not necessary, you may think about playing with the coordinates and placing the text b=5and the corresponding arrow into another position, where the arrow is more visible.

Have fun!


Modifying the function axes in this answer:

axes3D[x_, y_, z_, f_, a_] := With[{min = Min[x, y, z]},
  Graphics3D[Join[{Arrowheads[a]}, Arrow[{{0, 0, 0}, #}] & /@ {{x, 0, 0}, {0, y, 0}, {0, 0, z}},
    Text[Style[#, FontSize -> Scaled[f]], #2] & @@@
     Transpose[{{"x", "y", "z"}, {{x/2, 0, 0}, {0, y/2, 0}, {0, 0, z/2}} /. (0) -> (-.05 min)}]],
   Boxed -> False]]

cp = ContourPlot3D[{sf1[x, y, z] == 0, sf2[x, y, z] == 0}, {x, 0, 150}, {y, 0, 100}, {z, 0, 200},
   BoundaryStyle -> Red, ContourStyle -> {Directive[Opacity[.8], Yellow], 
     Directive[Opacity[.8], Green]}, Mesh -> None, 
   PlotLegends -> SwatchLegend[{Yellow, Green}, {"b=0", "b=5"}]];

Show[axes3D[##&@@(Last/@ PlotRange[cp[[1]]]), .05, .03], cp, BoxRatios -> 1, Lighting -> "Neutral"]

enter image description here