Closed guidovranken closed 5 years ago
Nice find! Could you please send a PR to fix it?
Given this works only without authentication, although not good, I don't think it's catastrophic either, fortunately. Public endpoint should always use CURVE/GSSAPI.
@guidovranken have you requested a CVE for this issue?
I've just requested one, will post the ID as soon as I get it.
CVE-2019-6250
thanks
Bug details
In
src/v2_decoder.cpp zmq::v2_decoder_t::eight_byte_size_ready()
, the attacker can provide anuint64_t
of his choosing:Then, in
src/v2_decoder.cpp zmq::v2_decoder_t::size_ready()
, a comparison is performed to check if this peer-suppliedmsg_size_
is within the bounds of the currently allocated block of memory:This is inadequate because a very large
msg_size_
will overflow the pointer (read_pos_
). In other words, the comparison will compute as 'false' even thoughmsg_size_
bytes don't fit in the currently allocated block.Exploit details
Now that
msg_size_
has been set to a very high value, the attacker is allowed to send this amount of bytes, and libzmq will copy it to its internal buffer without any further checks.This means that it's possible to write beyond the bounds of the allocated space.
However, for the exploit this is not necessary to corrupt memory beyond the buffer proper.
As it turns out, the space the attacker is writing to is immediately followed by a
struct content_t
block:So the memory layout is such that the receive buffer is immediately followed by
data
, thensize
, thenffn
, thenhint
, thenrefcnt
. Note that the receive buffer + thestruct content_t
is a single, solid block of memory; by overwriting beyond the designated receive buffer's bounds, no dlmalloc state variables in memory (likebk
,fd
) are corrupted (or, in other words, it wouldn't trigger AddressSanitizer).This means that the attacker can overwrite all these members with arbitrary values.
ffn
is a function pointer, that upon connection closure, is called with two parameters,data
andhint
.This means the attacker can call an arbitrary function/address with two arbitrary parameters.
In my exploit, I set
ffn
to the address ofstrcpy
, set the first parameter to somewhere in the executable's .data section, and the second parameter to the address of the character I want to write followed by a NULL character.So for instance, if i want to write a 'g' character, I search the binary for an occurrence of 'g\x00', and use this address as the second value to my
strcpy
call.For each character of the command I want to execute on the remote machine, I make a separate request to write that character to the .data section. So if I want to execute 'gnome-calculator', I first write a 'g', then a 'n', then an 'o', and so on, until the full 'gnome-calculator' string is written to .data.
In the next request, I overwrite the 'data' member of
struct content_t
with the address of the .data section (where nowgnome-calculator
resides), set theffn
member to thesystem
libc function, andhint
to NULL.In effect, this calls
system("gnome-calculator")
, by which this command is executed on the remote machine.Exploit
The following is a self-exploit, that demonstrates the exploit flow as explained above.
Notes
Crucial to this exploit is knowing certain addresses, like
strcpy
andsystem
, though the address ofstrcpy
could be replaced with any executable location that containsstosw / ret
or anything else that moves[rsi]
to[rdi]
, andsystem
might be replaced with code that executes the string atrsi
.I did not find any other vulnerabilities in libzmq, but if there is any information leaking vulnerability in libzmq, or the application that uses it, that would allow the attacker to calculate proper code offsets, this would defeat ASLR.
Resolution
Resolution of this vulnerability must consist of preventing pointer arithmetic overflow in
src/v2_decoder.cpp zmq::v2_decoder_t::size_ready()
.