Bug 1152544 (CVE-2014-3699)

Summary: CVE-2014-3699 eDeploy: Remote code execution due to cPickle deserialization of untrusted data
Product: [Other] Security Response Reporter: David Jorm <djorm>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED WONTFIX QA Contact:
Severity: high Docs Contact:
Priority: high    
Version: unspecifiedCC: grocha, jrusnack, mjc, security-response-team, tdecacqu, weli
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2015-03-17 23:21:30 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1152549    

Description David Jorm 2014-10-14 11:42:05 UTC
It was found that under certain conditions, eDeploy would bind cPickle to a TCP port. A remote, unauthenticated attacker able to connect to this port could provide malicious serialized data, leading to remote code execution.

Comment 1 David Jorm 2014-10-14 11:43:00 UTC
Acknowledgements:

This issue was discovered by Kurt Seifried of Red Hat Product Security.

Comment 3 Kurt Seifried 2015-03-17 19:44:37 UTC
So in src/netdetect.py and src/health_protocol.py we send and receive pickled 
data, no authentication, so anyone with network access == code execution 

Code, snipped, basically:

src/netdetect.py

def start_sync_bench_server():
    '''Server is made for receiving keepalives and manage them.'''
    ''' Let's bind a server to the Multicast group '''
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

def start_discovery_server():
    '''Server is made for receiving keepalives and manage them.'''
    ''' Let's bind a server to the Multicast group '''
    ''' Until we got a synthesis list from another server '''
    while not synthesis:
        answer = {}
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

def start_client(mode, max_clients=0):
        ''' While we are in discovery mode, let's send keepalives '''
        while discovery:
            sys.stderr.write("Sending keepalive for %s\n" % my_mac_addr)
            sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT))

            sys.stderr.write("Sending Ready To Bench for %s\n" % my_mac_addr)
            sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT))
  
        sys.stderr.write("Sending Go !\n")
        sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT_GO))

def scrub_timestamp():
    '''Scrubing deletes server that didn't sent keepalive on time.'''
                    sock.sendto(cPickle.dumps(server_list),
                                (MCAST_GRP, MCAST_PORT))

                    sys.stderr.write("No remote system detected, exiting\n")
                    sock.sendto(cPickle.dumps(message),
                                (MCAST_GRP, MCAST_PORT))
                ''' It's time to send the synthesis to the other nodes '''
                leader = True
                sock.sendto(cPickle.dumps(server_list),
                            (MCAST_GRP, MCAST_PORT))

def wait_for_go():
    global ready_to_bench
    ''' Let's bind a server to the Multicast group '''
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

===============================================================================

src/health_protocol.py

def start_sync_bench_server():
    '''Server is made for receiving keepalives and manage them.'''
    ''' Let's bind a server to the Multicast group '''
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

def start_discovery_server():
    '''Server is made for receiving keepalives and manage them.'''
    ''' Let's bind a server to the Multicast group '''
    ''' Until we got a synthesis list from another server '''
    while not synthesis:
        answer = {}
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

def start_client(mode, max_clients=0):
    '''Client is made for generating keepalives.'''
    ''' Let's prepare the socket '''
        ''' While we are in discovery mode, let's send keepalives '''
        while discovery:
            sys.stderr.write("Sending keepalive for %s\n" % my_mac_addr)
            sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT))

        while ready_to_bench:
            sys.stderr.write("Sending Ready To Bench for %s\n" % my_mac_addr)
            sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT))

        sys.stderr.write("Sending Go !\n")
        sock.sendto(cPickle.dumps(host_info), (MCAST_GRP, MCAST_PORT_GO))

def scrub_timestamp():
    '''Scrubing deletes server that didn't sent keepalive on time.'''
                    sock.sendto(cPickle.dumps(server_list),
					
                    sys.stderr.write("No remote system detected, exiting\n")
                    sock.sendto(cPickle.dumps(message),

                ''' It's time to send the synthesis to the other nodes '''
                leader = True
                sock.sendto(cPickle.dumps(server_list),

def wait_for_go():
    global ready_to_bench
    ''' Let's bind a server to the Multicast group '''
        ''' Let's get keepalives from servers '''
        answer = cPickle.loads(sock.recv(10240))

Comment 4 Kurt Seifried 2015-03-17 23:21:30 UTC
This is now public https://github.com/enovance/edeploy/issues/229

Comment 5 Kurt Seifried 2015-03-19 04:17:09 UTC
Statement:

Red Hat does not currently ship eNovance edeploy in a product form and as such this issue has been filed upstream.