Immutability is not really the issue here. It is possible to write a mutable string class where this still would be \$O(n^2)\$ or an immutable string class of lower complexity (Sun could have done the latter anyway). The problem here is that at each iteration, the algorithm is rereading the string from the previous iteration in order to produce a new string.
To reduce it by an order of magnitude, you need to shift references. In some cases, the compiler very well might do this anyway, depending upon the sophistication of code optimization. In the case of the class String however, I think it still does it the expensive way, by design.
StringBuffer may be mutable, but I don't see how that is what makes it more efficient in this case. What matters is that at each iteration, it simply appends a reference to the list of characters instead of reads each item for any reason, whether to copy them into a new string instance as String does in this example, or for some other silly reason. But to focus on this as a mutability/immutability issue I think misses the point.
Simple illustration of what I mean. Input a string of letters. At each iteration, you read the previous string to create a new string, and then the current index of the input string to append to the new string. The general relation is the length of the string produced by the previous iteration plus one, which gives you a complexity of \$n^2 - 1\$, or \$O(n^2)\$.