This method is messy indeed, I'll try to explain.
LWJGL 2.x:
The version that takes the result_size parameter is meant to be used when the vertex attribute is sourced from a buffer in client memory. It retrieves the buffer pointer and creates a ByteBuffer that starts at the pointer address and has a capacity of result_size bytes. You're seeing a null return value because you use it on a vertex attrib that's being sourced from a VBO at offset 0. The native implementation interprets the 0 offset as a null pointer, hence you get a null ByteBuffer back.
The version that takes a ByteBuffer parameter is meant to be used when the vertex attribute is sourced from a VBO. The VBO offset is written at the current position() of the ByteBuffer parameter. You can use it like so:
ByteBuffer offsetBuf = BufferUtils.createByteBuffer(PointerBuffer.getPointerSize());
glGetVertexAttribPointer(attribIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER, offsetBuf);
long offset = PointerBuffer.is64Bit() ? offsetBuf.getLong(0) : offsetBuf.getInt(0);
LWJGL 3.0:
The new LWJGL codebase follows the original function interfaces strictly (in general, not only for this method). It also provides lots of utilities for direct memory access, so all 3 alternatives for this method return a pointer value (or offset).
Using a ByteBuffer (cleaned-up version of the above)
ByteBuffer offsetBuf = BufferUtils.createByteBuffer(POINTER_SIZE);
glGetVertexAttribPointer(attribIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER, offsetBuf);
long offset = PointerBuffer.get(offsetBuf, 0);
Using a PointerBuffer (this is how it should have been in 2.x if PointerBuffer had been implemented earlier)
PointerBuffer offsetBuf = BufferUtils.createPointerBuffer(1);
glGetVertexAttribPointer(attribIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER, offsetBuf);
long offset = offsetBuf.get(0);
Using the "convenient" alternative (this uses an LWJGL-managed buffer internally)
long offset = glGetVertexAttribPointer(attribIndex, GL_VERTEX_ATTRIB_ARRAY_POINTER);