class Child(Core): def parse_args(self, dn, con_id, first_pkt, dbg=0, ssh_p=22): self.dn = dn self.con_id = str(con_id) self.first_pkt = first_pkt self.ssh_p = ssh_p Automaton.parse_args(self, debug=dbg) def master_filter(self, pkt): if (Core.master_filter(self, pkt) and pkt[IP].src == self.ip_client): qname = Core.parse_qname(self, pkt) if len(qname) >= 4: if qname[-2].isdigit() and qname[-3] == self.con_id: self.msg_type = qname[-4] if len(qname) == 4 and self.msg_type in [_IWT]: return True if len(qname) > 4 and self.msg_type in [ _ACK, _DATA, _FAST, _DONE ]: self.arg = qname[-5] self.payload = qname[:-5] return True return False def calculate_limit_size(self, pkt): s = self.pkt_max_size - len( pkt[DNS]) - 2 * len(DNSRR()) - 3 * len(self.dn) - len("ns.") - 10 if pkt[DNSQR].qtype == TXT: max_size = 512 s -= len(str(s)) else: max_size = self.qname_max_size return min((s, 1)[s < 1], max_size) def fragment_data(self, data, limit_size, qtype): if qtype == CNAME: qname = [] rest = data while len(rest) > 0: d = rest[:limit_size] qname.append('.'.join([ d[i:i + self.label_size] for i in range(0, len(d), self.label_size) ])) rest = rest[limit_size:] elif qtype == TXT: qname = [ data[i:i + limit_size] for i in range(0, len(data), limit_size) ] return qname def compress(self, l): """ [1,2,4,12,7,11,3,14,13] => '1-4.7.11-14' """ l.sort() temp = [[l[0]]] result = [] j = 0 for i in range(1, len(l)): if l[i] == l[i - 1] + 1: temp[j] += [l[i]] else: temp.append([l[i]]) j += 1 for r in temp: if len(r) > 1: result.append("{0}-{1}".format(r[0], r[-1])) else: result.append(str(r[0])) return ".".join(result) @ATMT.state(initial=True) def START(self): self.label_size = 63 self.qname_max_size = 253 self.pkt_max_size = 512 self.recv_data = {} self.ip_client = self.first_pkt[IP].src self.is_first_wyw_pkt = True self.iwt_pkt = None raise self.TICKLING() @ATMT.state() def TICKLING(self): s = socket.socket() s.connect(("127.0.0.1", self.ssh_p)) self.stream = StreamSocket(s, Raw) ssh_msg = self.stream.recv() raise self.CON(ssh_msg.load) @ATMT.state() def CON(self, ssh_msg): if ssh_msg == "": raise self.TICKLING() s = self.calculate_limit_size(self.first_pkt) qtype = self.first_pkt[DNSQR].qtype self.frag_reply = self.fragment_data(b64encode(ssh_msg), s, qtype) if len(self.frag_reply) == 1: pkt = Core.forge_packet( self, self.first_pkt, "{0}.{1}.0.{2}".format(_CON, self.con_id, self.frag_reply[0])) else: pkt = Core.forge_packet( self, self.first_pkt, "{0}.{1}.{2}".format(_CON, self.con_id, str(len(self.frag_reply) - 1))) send(pkt, verbose=0) raise self.WAITING() @ATMT.state() def WAITING(self): pass @ATMT.timeout(WAITING, 600) def timeout_reached(self): raise self.END() @ATMT.receive_condition(WAITING) def data_pkt(self, pkt): if self.msg_type in [_ACK, _FAST]: pkt_nb = self.arg if pkt_nb.isdigit(): raise self.DATA_RECEPTION(pkt, int(pkt_nb)) @ATMT.receive_condition(WAITING) def iwt_pkt(self, pkt): if self.msg_type == _IWT: raise self.IWT(pkt) @ATMT.receive_condition(WAITING) def ttm_pkt(self, pkt): if self.msg_type == _DATA: asked_pkt = self.arg if asked_pkt.isdigit(): raise self.DATA_EMISSION(pkt, int(asked_pkt)) @ATMT.receive_condition(WAITING) def done_pkt(self, pkt): if self.msg_type == _DONE: code = self.arg if code == _ACK or code == _DATA: raise self.DONE(pkt, code) @ATMT.state() def DATA_RECEPTION(self, pkt, pkt_nb): if not self.recv_data.has_key(pkt_nb): self.recv_data[pkt_nb] = "".join(self.payload) if self.msg_type == _ACK: ack_pkt = Core.forge_packet(self, pkt, "{0}.{1}".format(_ACK, pkt_nb)) send(ack_pkt, verbose=0) raise self.WAITING() elif self.msg_type == _FAST: self.fast_pkt = pkt self.to_ack = [pkt_nb] @ATMT.receive_condition(DATA_RECEPTION) def got_data(self, pkt): if self.msg_type == _FAST: if self.arg.isdigit(): self.fast_pkt = pkt pkt_nb = int(self.arg) if not self.recv_data.has_key(pkt_nb): self.recv_data[pkt_nb] = "".join(self.payload) if pkt_nb not in self.to_ack: self.to_ack.append(pkt_nb) @ATMT.timeout(DATA_RECEPTION, 0.5) def ack(self): #TODO check the limit size l = self.compress(self.to_ack) ack_pkt = Core.forge_packet(self, self.fast_pkt, "{0}.{1}".format(_FAST, l)) send(ack_pkt, verbose=0) raise self.WAITING() @ATMT.state() def IWT(self, pkt): """IWT (I Want This) state of the Child automaton. After receiving a WYW (What You Want) pkt from the client, the server says how many DNS pkts he needs to send the reply """ if self.iwt_pkt is not None: send(self.iwt_pkt, verbose=0) else: ssh_reply = self.stream.sniff(count=1, timeout=0.1) iwt_pkt = Core.forge_packet(self, pkt, _DONE) if len(ssh_reply) > 0: qtype = pkt[DNSQR].qtype s = self.calculate_limit_size(pkt) self.frag_reply = self.fragment_data( b64encode(ssh_reply[0].load), s, qtype) self.iwt_pkt = Core.forge_packet( self, pkt, "{0}.{1}".format(_IWT, str(len(self.frag_reply)))) iwt_pkt = self.iwt_pkt send(iwt_pkt, verbose=0) raise self.WAITING() @ATMT.state() def DATA_EMISSION(self, pkt, asked_pkt): if asked_pkt <= len(self.frag_reply): data_pkt = Core.forge_packet( self, pkt, "{0}.{1}.{2}".format(_DATA, str(asked_pkt), self.frag_reply[-(asked_pkt + 1)])) send(data_pkt, verbose=0) raise self.WAITING() @ATMT.state() def DONE(self, pkt, code): if code == _ACK: if self.recv_data.keys() == range(0, len(self.recv_data)): d = "".join(self.recv_data.values()) ssh_request = Raw(b64decode(d)) self.stream.send(ssh_request) self.recv_data.clear() send(Core.forge_packet(self, pkt, _DONE), verbose=0) elif code == _DATA: self.iwt_pkt = None send(Core.forge_packet(self, pkt, _DONE), verbose=0) raise self.WAITING() @ATMT.state(final=True) def END(self): pass
def sendFlag(ip): s = socket.socket() s.connect((ip, 2997)) ss = StreamSocket(s, Raw) ss.send(Raw("Flag is: l0053_lips_m1ght_s1nk_sh1ps ")) s.close()
class Child(Core): def parse_args(self, dn, con_id, first_pkt, dbg=0, ssh_p=22): self.dn = dn self.con_id = str(con_id) self.first_pkt = first_pkt self.ssh_p = ssh_p Automaton.parse_args(self, debug=dbg) def master_filter(self, pkt): if (Core.master_filter(self, pkt) and pkt[IP].src == self.ip_client): qname = Core.parse_qname(self, pkt) if len(qname) >= 4: if qname[-2].isdigit() and qname[-3] == self.con_id: self.msg_type = qname[-4] if len(qname) == 4 and self.msg_type in [_IWT]: return True if len(qname) > 4 and self.msg_type in [ _ACK, _DATA, _FAST, _DONE ]: self.arg = qname[-5] self.payload = qname[:-5] return True return False def calculate_limit_size(self, pkt): s = self.pkt_max_size - len(pkt[DNS]) - 2*len( DNSRR() ) - 3*len(self.dn) - len("ns.") - 10 if pkt[DNSQR].qtype == TXT: max_size = 512 s -= len(str(s)) else: max_size = self.qname_max_size return min((s, 1)[s < 1], max_size) def fragment_data(self, data, limit_size, qtype): if qtype == CNAME: qname = [] rest = data while len(rest) > 0: d = rest[:limit_size] qname.append( '.'.join( [ d[i:i+self.label_size] for i in range( 0, len(d), self.label_size, ) ], ), ) rest = rest[limit_size:] elif qtype == TXT: qname = [ data[i:i+limit_size] for i in range(0, len(data), limit_size) ] return qname def compress(self, l): """ [1,2,4,12,7,11,3,14,13] => '1-4.7.11-14' """ l.sort() temp = [[l[0]]] result = [] j = 0 for i in range(1, len(l)): if l[i] == l[i-1]+1: temp[j] += [l[i]] else: temp.append([l[i]]) j += 1 for r in temp: if len(r) > 1: result.append("{0}-{1}".format(r[0], r[-1])) else: result.append(str(r[0])) return ".".join(result) @ATMT.state(initial=True) def START(self): self.label_size = 63 self.qname_max_size = 253 self.pkt_max_size = 512 self.recv_data = {} self.ip_client = self.first_pkt[IP].src self.is_first_wyw_pkt = True self.iwt_pkt = None raise self.TICKLING() @ATMT.state() def TICKLING(self): s = socket.socket() s.connect(("127.0.0.1", self.ssh_p)) self.stream = StreamSocket(s, Raw) ssh_msg = self.stream.recv() raise self.CON(ssh_msg.load) @ATMT.state() def CON(self, ssh_msg): if ssh_msg == "": raise self.TICKLING() s = self.calculate_limit_size(self.first_pkt) qtype = self.first_pkt[DNSQR].qtype self.frag_reply = self.fragment_data(b64encode(ssh_msg), s, qtype) if len(self.frag_reply) == 1: pkt = Core.forge_packet( self, self.first_pkt, "{0}.{1}.0.{2}".format( _CON, self.con_id, self.frag_reply[0], ), ) else: pkt = Core.forge_packet( self, self.first_pkt, "{0}.{1}.{2}".format( _CON, self.con_id, str(len(self.frag_reply)-1), ), ) send(pkt, verbose=0) raise self.WAITING() @ATMT.state() def WAITING(self): pass @ATMT.timeout(WAITING, 600) def timeout_reached(self): raise self.END() @ATMT.receive_condition(WAITING) def data_pkt(self, pkt): if self.msg_type in [_ACK, _FAST]: pkt_nb = self.arg if pkt_nb.isdigit(): raise self.DATA_RECEPTION(pkt, int(pkt_nb)) @ATMT.receive_condition(WAITING) def iwt_pkt(self, pkt): if self.msg_type == _IWT: raise self.IWT(pkt) @ATMT.receive_condition(WAITING) def ttm_pkt(self, pkt): if self.msg_type == _DATA: asked_pkt = self.arg if asked_pkt.isdigit(): raise self.DATA_EMISSION(pkt, int(asked_pkt)) @ATMT.receive_condition(WAITING) def done_pkt(self, pkt): if self.msg_type == _DONE: code = self.arg if code == _ACK or code == _DATA: raise self.DONE(pkt, code) @ATMT.state() def DATA_RECEPTION(self, pkt, pkt_nb): if pkt_nb in self.recv_data: self.recv_data[pkt_nb] = "".join(self.payload) if self.msg_type == _ACK: ack_pkt = Core.forge_packet( self, pkt, "{0}.{1}".format(_ACK, pkt_nb), ) send(ack_pkt, verbose=0) raise self.WAITING() elif self.msg_type == _FAST: self.fast_pkt = pkt self.to_ack = [pkt_nb] @ATMT.receive_condition(DATA_RECEPTION) def got_data(self, pkt): if self.msg_type == _FAST: if self.arg.isdigit(): self.fast_pkt = pkt pkt_nb = int(self.arg) if pkt_nb in self.recv_data: self.recv_data[pkt_nb] = "".join(self.payload) if pkt_nb not in self.to_ack: self.to_ack.append(pkt_nb) @ATMT.timeout(DATA_RECEPTION, 0.5) def ack(self): # TODO check the limit size l = self.compress(self.to_ack) ack_pkt = Core.forge_packet( self, self.fast_pkt, "{0}.{1}".format(_FAST, l), ) send(ack_pkt, verbose=0) raise self.WAITING() @ATMT.state() def IWT(self, pkt): """IWT (I Want This) state of the Child automaton. After receiving a WYW (What You Want) pkt from the client, the server says how many DNS pkts he needs to send the reply """ if self.iwt_pkt is not None: send(self.iwt_pkt, verbose=0) else: ssh_reply = self.stream.sniff(count=1, timeout=0.1) iwt_pkt = Core.forge_packet(self, pkt, _DONE) if len(ssh_reply) > 0: qtype = pkt[DNSQR].qtype s = self.calculate_limit_size(pkt) self.frag_reply = self.fragment_data( b64encode(ssh_reply[0].load), s, qtype, ) self.iwt_pkt = Core.forge_packet( self, pkt, "{0}.{1}".format(_IWT, str(len(self.frag_reply))), ) iwt_pkt = self.iwt_pkt send(iwt_pkt, verbose=0) raise self.WAITING() @ATMT.state() def DATA_EMISSION(self, pkt, asked_pkt): if asked_pkt <= len(self.frag_reply): data_pkt = Core.forge_packet( self, pkt, "{0}.{1}.{2}".format( _DATA, str(asked_pkt), self.frag_reply[-(asked_pkt+1)], ), ) send(data_pkt, verbose=0) raise self.WAITING() @ATMT.state() def DONE(self, pkt, code): if code == _ACK: if self.recv_data.keys() == range(0, len(self.recv_data)): d = "".join(self.recv_data.values()) ssh_request = Raw(b64decode(d)) self.stream.send(ssh_request) self.recv_data.clear() send(Core.forge_packet(self, pkt, _DONE), verbose=0) elif code == _DATA: self.iwt_pkt = None send(Core.forge_packet(self, pkt, _DONE), verbose=0) raise self.WAITING() @ATMT.state(final=True) def END(self): pass