Originally Posted by ErV
counters = malloc(sizeof(char*)*length + 1);
I expect you meant
counters = malloc(sizeof(char*)*(length + 1));
In case this was homework, I don't think it was appropriate to provide a nearly complete solution.
I assume the thing that was confusing the OP is how to nest a variable number of loops.
You have demonstrated the professional answer: use an array of loop control variables (your "counters").
But I think it would have been better in this case to suggest the easier method, which is a recursive function. That isn't a better method (I think your method is better) but it is easier to understand how it works and easier for a beginner to apply to a wider range of variable nested loops.
Also, the problem statement doesn't say which sequence the strings should come out in, but maybe lexical sequence is assumed.
You varied the leftmost position fastest and rightmost slowest, which makes things a little easier in either your structure or a recursive function, but maybe the extra effort for the more expected sequence would be worth it.
I'm happy to answer questions about the concepts for making this a recursive function if the OP requests, but I don't think doing it is appropriate. (Very likely easier to do than explain, just not as helpful).
Edit: After a look at darkangel29's other posts, this looks less like homework and more like a real attempt to learn, so I'll say more right away regarding the recursive approach, using the simpler version with leftmost char varying fastest.
1) The top level caller allocates a big enough destination buffer and stores the terminating null at the end of it and
2) calls the recursive routine passing a pointer to one character before that terminating null and a count of characters per password, and if you like ErV's idea of a char array to control the character set, pass that as well (otherwise the character set is determined in the code of recursive function).
3) The recursive function loops through each possible char value storing it where the pointer tells it to.
4) In each loop step, after storing the character, if the length it was passed was 1, it prints the string. If the length it was passed is not 1, it calls itself recursively, passing each of pointer and length as one less than the value it received.