def test_request_get_register(self): # requesting register 1033 request_bytes = (0x80, 0x3F, 0x10, 0x01, 0x04, 0x09, 0x18, 0x6D, 0x0D) for i in range(0, len(request_bytes)): self.request_parser.add_byte(chr(request_bytes[i])) if i < len(request_bytes) - 1: # parser returns None until it can put together an entire message self.assertEqual(self.request_parser.get_request(), None) parsed_request = self.request_parser.get_request() response = self.command_responder.respond(parsed_request) self.assertEqual(len(response.registers), 1) self.assertEqual(response.registers[0].name, 1033) # we should have no left overs self.assertEqual(len(self.request_parser.bytes), 0) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", self.kamstrup_management_server.server.server_port)) s.sendall( chr_py3(0x80) + chr_py3(0x3F) + chr_py3(0x10) + chr_py3(0x01) + chr_py3(0x04) + chr_py3(0x09) + chr_py3(0x18) + chr_py3(0x6D) + chr_py3(0x0D) ) data = s.recv(1024) s.close() # FIXME: verify bytes received from server - ask jkv? pkt = [hex(data[i]) for i in range(len(data))] self.assertTrue(("0x40" in pkt) and ("0x3f" in pkt) and ("0xd" in pkt))
def initiate_session(self, data, address, session): if len(data) < 22: self.close_server_session() return if not (chr_py3(data[0]) == b"\x06" and data[2:4] == b"\xff\x07"): # check rmcp version, sequencenumber and class; self.close_server_session() return if chr_py3(data[4]) == b"\x06": # ipmi v2 session.ipmiversion = 2.0 session.authtype = 6 payload_type = chr_py3(data[5]) if payload_type not in (b"\x00", b"\x10"): self.close_server_session() return if payload_type == b"\x10": # new session to handle conversation serversession.ServerSession( self.authdata, self.kg, session.sockaddr, self.sock, data[16:], self.uuid, bmc=self, ) serversession.ServerSession.logged = logger return # data = data[13:] if len(data[14:16]) < 2: self.close_server_session() else: myaddr, netfnlun = struct.unpack("2B", data[14:16]) netfn = (netfnlun & 0b11111100) >> 2 mylun = netfnlun & 0b11 if netfn == 6: # application request if chr_py3(data[19]) == b"\x38": # cmd = get channel auth capabilities verchannel, level = struct.unpack("2B", data[20:22]) version = verchannel & 0b10000000 if version != 0b10000000: self.close_server_session() return channel = verchannel & 0b1111 if channel != 0xE: self.close_server_session() return (clientaddr, clientlun) = struct.unpack("BB", data[17:19]) level &= 0b1111 self.send_auth_cap( myaddr, mylun, clientaddr, clientlun, session.sockaddr )
def test_request_get_register(self): # requesting register 1033 request_bytes = (0x80, 0x3f, 0x10, 0x01, 0x04, 0x09, 0x18, 0x6d, 0x0d) for i in range(0, len(request_bytes)): self.request_parser.add_byte(chr(request_bytes[i])) if i < len(request_bytes) - 1: # parser returns None until it can put together an entire message self.assertEqual(self.request_parser.get_request(), None) parsed_request = self.request_parser.get_request() response = self.command_responder.respond(parsed_request) self.assertEqual(len(response.registers), 1) self.assertEqual(response.registers[0].name, 1033) # we should have no left overs self.assertEqual(len(self.request_parser.bytes), 0) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1', self.kamstrup_management_server.server.server_port)) s.sendall(chr_py3(0x80) + chr_py3(0x3f) + chr_py3(0x10) + chr_py3(0x01) + chr_py3(0x04) + chr_py3(0x09) + chr_py3(0x18) + chr_py3(0x6d) + chr_py3(0x0d)) data = s.recv(1024) s.close() # FIXME: verify bytes received from server - ask jkv? pkt = [hex(data[i]) for i in range(len(data))] self.assertTrue(('0x40' in pkt) and ('0x3f' in pkt) and ('0xd' in pkt))
def _got_request(self, data, address, session): if chr_py3(data[4]) in (b"\x00", b"\x02"): # ipmi 1.5 payload session.ipmiversion = 1.5 remsequencenumber = struct.unpack("<I", data[5:9])[0] if ( hasattr(session, "remsequencenumber") and remsequencenumber < session.remsequencenumber ): self.close_server_session() return session.remsequencenumber = remsequencenumber if ord(chr_py3(data[4])) != session.authtype: self.close_server_session() return remsessid = struct.unpack("<I", data[9:13])[0] if remsessid != session.sessionid: self.close_server_session() return rsp = list(struct.unpack("!%dB" % len(data), data)) authcode = False if chr_py3(data[4]) == b"\x02": # authcode in ipmi 1.5 packet authcode = data[13:29] del rsp[13:29] payload = list(rsp[14 : 14 + rsp[13]]) if authcode: expectedauthcode = session._ipmi15authcode( payload, checkremotecode=True ) expectedauthcode = struct.pack( "%dB" % len(expectedauthcode), *expectedauthcode ) if expectedauthcode != authcode: self.close_server_session() return session._ipmi15(payload) elif chr_py3(data[4]) == b"\x06": # ipmi 2.0 payload session.ipmiversion = 2.0 session.authtype = 6 session._ipmi20(data) else: # unrecognized data self.close_server_session() return
def send_auth_cap(self, myaddr, mylun, clientaddr, clientlun, sockaddr): header = b'\x06\x00\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10' headerdata = (clientaddr, clientlun | (7 << 2)) headersum = self._checksum(*headerdata) header += struct.pack('BBBBBB', *(headerdata + (headersum, myaddr, mylun, 0x38))) header += self.authcap bodydata = struct.unpack('B' * len(header[17:]), header[17:]) header += chr_py3(self._checksum(*bodydata)) self.session.stage += 1 logger.info('Connection established with %s', sockaddr) self.session.send_data(header, sockaddr)
def handle(self, sock, address): session = conpot_core.get_session('kamstrup_protocol', address[0], address[1], sock.getsockname()[0], sock.getsockname()[1]) logger.info('New Kamstrup connection from %s:%s. (%s)', address[0], address[1], session.id) session.add_event({'type': 'NEW_CONNECTION'}) self.server_active = True parser = request_parser.KamstrupRequestParser() try: while self.server_active: raw_request = sock.recv(1024) if not raw_request: logger.info('Kamstrup client disconnected. (%s)', session.id) session.add_event({'type': 'CONNECTION_LOST'}) break for x in raw_request: parser.add_byte(chr_py3(x)) while True: request = parser.get_request() if not request: session.add_event({'type': 'CONNECTION_LOST'}) break else: logdata = {'request': binascii.hexlify(bytearray(request.message_bytes))} response = self.command_responder.respond(request) # real Kamstrup meters has delay in this interval gevent.sleep(random.uniform(0.24, 0.34)) if response: serialized_response = response.serialize() logdata['response'] = binascii.hexlify(serialized_response) logger.info('Kamstrup traffic from %s: %s (%s)', address[0], logdata, session.id) sock.send(serialized_response) session.add_event(logdata) else: session.add_event(logdata) break except socket.timeout: logger.debug('Socket timeout, remote: %s. (%s)', address[0], session.id) session.add_event({'type': 'CONNECTION_LOST'}) sock.close()
def _got_rmcp_openrequest(self, data): request = struct.pack("B" * len(data), *data) clienttag = ord(chr_py3(request[0])) self.clientsessionid = list(struct.unpack("4B", request[4:8])) self.managedsessionid = list(struct.unpack("4B", os.urandom(4))) self.session.privlevel = 4 response = ( [clienttag, 0, self.session.privlevel, 0] + self.clientsessionid + self.managedsessionid + [ 0, 0, 0, 8, 1, 0, 0, 0, # auth 1, 0, 0, 8, 1, 0, 0, 0, # integrity 2, 0, 0, 8, 1, 0, 0, 0, # privacy ] ) logger.info("IPMI open session request") self.session.send_payload( response, constants.payload_types["rmcpplusopenresponse"], retry=False )
def serialize(self, message): final_message = list() # prefix message final_message.append(kamstrup_constants.RESPONSE_MAGIC) final_message.append(self.communication_address) # add the original content for c in message: final_message.append(c) # generate and append checksum crc = crc16.crc16xmodem(b''.join([chr_py3(item) for item in final_message[1:]])) final_message.append(crc >> 8) final_message.append(crc & 0xff) # trailing magic final_message.append(kamstrup_constants.EOT_MAGIC) escaped_message = self.escape(final_message) return escaped_message
def valid_crc(cls, message): supplied_crc = message[-2] * 256 + message[-1] calculated_crc = crc16.crc16xmodem( b"".join([chr_py3(item) for item in message[:-2]]) ) return supplied_crc == calculated_crc
def valid_crc(cls, message): supplied_crc = message[-2] * 256 + message[-1] calculated_crc = crc16.crc16xmodem(b''.join([chr_py3(item) for item in message[:-2]])) return supplied_crc == calculated_crc