As Dan D. says, letting numpy generate your bytes in one hit at C speed is going to be way faster than producing them one at a time at Python speed.
However, if you don't want to use numpy you can make your code a little more efficient. 
Building a string by concatenation eg buf = buf + chr(random.randint(0,255)) is very slow, since a new buf has to be allocated on every loop (remember, Python strings are immutable). The usual technique in Python for building a string from substrings is to accumulate the substrings in a list then to use the str.join() method to combine them in one go.
We can also save a little bit of time by pre-generating a list of our 1 byte strings rather than calling chr() for every byte we want.
from random import seed, choice
allbytes = [chr(i) for i in range(256)]
def random_bytes(n):
    bytes = []
    for _ in range(n):
        bytes.append(choice(allbytes))
    return ''.join(bytes)
We can streamline this and make it slightly more efficient by using a list comprehension:
def random_bytes(n):
    return ''.join([choice(allbytes) for _ in range(n)])
Depending on how you intend to use these random bytes, you may find it useful to put them into a bytearray or bytes object.
Here's a variant based on cristianmtr's new answer:
def random_bytes(n):
    return bytes(bytearray(getrandbits(8) for _ in xrange(n)))
You could use str() in place of bytes(), but bytes() is better for Python 3, since Python 3 strings are Unicode.