Problem is that you are calling C-function that uses cdecl calling convention (caller must clean the stack). This means that after the call, you must add the size of the arguments (in bytes) to the stackpointer. Your first printf takes 2 dword arguments (8 bytes) so you must add 8 to stackpointer after the call. Your second call passes one dword so you add 4 to the stackpointer.
This should work:
SECTION .data
message: db "Value = %d", 10, 0
message2: db "End", 10, 0
SECTION .bss
SECTION .text
extern printf
global main
main:
push ebp
mov ebp, esp
mov ebx, 0
call loop
push message2
call printf
add esp, 4
mov esp, ebp
pop ebp
ret
loop:
push ebx
push message
call printf
add esp, 8
inc ebx
cmp ebx, 10000
jne loop
ret