zeus gameover malware
Para penguna internet tentunya sudah resah dengan adanya virus yang kian merebak luas mengapa demikian karena dapat merusak file-file penting kita dan mengambil informasi dari kita .Namun ada satu virus yang munngkinnakan membuat kamu jengkel ya seperti namanya virus ini bernama zeus gameover.
Tapi sebenarnta apasih virus zeus gameover itu?
Jadi zeus game over merupakan salah satu virus botnet peer-to-peer berdasarkan komponen dari trojan ZeuS sebelumnya. Malware ini adalah rancanganol peretas Rusia Evgeniy Mikhailovich Bogachev. membuat virus ini telah menyebar melalui penggunaan botnet Cutwail.
Virus ini adalah rancagan dari seorang dengan nama vgeniy Bogachev dan kelompok kejahatannya dengan menularkan sejumlah virus komputer–Gameover Zeus—ke dalam sistem komputer seluruh dunia sehingga mereka bisa menjelajah dan bahkan mengendalikan komputer-komputer itu dari manapun.
Gameover Zeus terrancang untuk mencuri nomor rekening bank dan kata kunci para pemilik komputer yang terkena serangan. Para korban melakukan transaksi perbankan secara daring (dalam jaringan).Kemudian tidak menyadari bahwa komputer mereka malah mengirimkan nomor rekening dan kata kunci bank kepada kelompok Evgeniy Bogachev. Para penjahat itu kemudian menggunakan informasi penting untuk untuk membobol rekening korban secara daring dan menguras isinya.
Kerjasama Lintas Batas
Ternyata sebaran virus ini telah merebak keseluruh dunia bahkan FBI telah berhasil menyita dan menyalin salinan komando gameover zeus yang berada tepat Negara ukraina.Pada tanggal 7 mei,dan kemudian tanggal 14 mei surat penangkapan berhasil keluar dari jaksa penuntut umum ke Negara bagaian Pennsylvania.
Kemudian FBI dan tim penegak keadilan dari berbagai Negara seperti Kanada, Prancis, Jerman, Luxembourg, Belanda, Ukraina, dan Inggris. Memulai penyitaan server komputer seluruh dunia yang menjadi tulang punggung kejahatan Gameover Zeus dan Cryptolocker telah berhasil tertangkap
Ternyata Trojan ini telah berhasil meretas berbagai jenis nomor rekeing bank dan karta sandi dari berbagai komputer yang berhasil terhack .Ternyata tujuan rancangan Trojan ini adalah untuk mencuri data pokok ,nomor rekening dan nomor sandi korban yang telah berhasil di retas.
contoh source zeus gameover:
# ZeusGameover.py # # import sys import pefile import re import struct import random import socket import md5 import zlib class ZeusGameoverError(Exception): pass class ZeusGameover: """ post process zeus gameover memdumps: - extract static peers - query static peers for config - enumerate p2p network """ # number of static peers in memdump NUM_PEER_ENTRIES = 20 # length of peer entry PEER_ENTRY_LEN = 45 # senderID and incoming rc4 key SENDER_ID = "c9a370355e879b521171b90d22ea4f15f7b1b556".decode("hex") # max response packet size MAX_PACKET_SIZE = 4096 # socket timeout SOCK_TIMEOUT = 2 # additional new peers threshold, percent NEW_PEER_THRES = 0.50 def __init__(self, memdump): self.pe = self.get_pe(memdump) self.memdump_data = self.pe.get_memory_mapped_image() self.memdump_config = self.get_memdump_config() self.memdump_rc4_key = self.get_memdump_rc4_key() self.static_peers = self.get_static_peers() self.peers = self.enumerate_peers() def get_pe(self, memdump): """ parse memdump into PE file format """ pe = None try: pe = pefile.PE(data=memdump) except pefile.PEFormatError as err: raise ZeusGameoverError("get_pe: %s" % err) return pe def get_memdump_config(self): """ find and decrypt memdump config """ # find function based on opcodes # AML-11294187.rsrc-38979806.dynamic.memdump 0x424BA2 # push esi # push edi # mov edi, 56Ch # push edi # mov esi, ecx # push offset stru_406FC8 match = re.search("\x56\x57\xbf.{2}\x00\x00\x57\x8b\xf1\x68", self.memdump_data) if not match: raise ZeusGameoverError("get_memdump_config: config not found - no function match") func_offset = match.start() # extract config config_len = self.pe.get_dword_from_offset(func_offset+0x3) config_addr = self.pe.get_dword_from_offset(func_offset+0xb) config_offset = config_addr - self.pe.OPTIONAL_HEADER.ImageBase config = self.memdump_data[config_offset:config_offset+config_len] if not config: raise ZeusGameoverError("get_memdump_config: config not found - no data @ 0x%x" % config_addr) # extract key xor_key = self.get_memdump_config_xor_key(config_len) # decrypt config plain = [] for offset, enc_byte in enumerate(config): key_byte = xor_key[offset] plain_byte = ord(enc_byte) ^ ord(key_byte) plain.append(chr(plain_byte)) return plain def get_memdump_config_xor_key(self, config_len): """ get xor key for memdump config """ key = [] for section in self.pe.sections: if section.Name.startswith(".reloc"): key = self.memdump_data[section.VirtualAddress:section.VirtualAddress+config_len] break return key def get_memdump_rc4_key(self): """ extract rc4 key from memdump config """ rc4_key_offset = self.get_rc4_key_offset() if not rc4_key_offset: raise ZeusGameoverError("get_memdump_rc4_key: unknown rc4 offset") key = self.memdump_config[rc4_key_offset:rc4_key_offset+258] return key def get_rc4_key_offset(self): """ extract rc4 key offset from memdump """ # @TODO opcodes need further validation # AML-11294187.rsrc-38979806.dynamic.memdump 0x40fbe6 # lea ecx, [esp+1Ch] # call sub_blah # lea eax, [esp+454h] # push # @TODO if we get too many, it would be worth integrating an asm module and matching on instructions # opcode case #1 code_offset = re.search(r"\x8d\x4c\x24(.{1})\xe8.{4}\x8d\x84\x24(.{4})\x6a", self.memdump_data) # opcode case #2 code_offset2 = re.search(r"\x8d\x4c\x24(.{1})\xe8.{4}\x8d\x44\x24(.{1})\x6a", self.memdump_data) if code_offset: x = struct.unpack("B", code_offset.groups()[0])[0] y = struct.unpack("I", code_offset.groups()[1])[0] elif code_offset2: x = struct.unpack("B", code_offset2.groups()[0])[0] y = struct.unpack("B", code_offset2.groups()[1])[0] else: raise ZeusGameoverError("get_rc4_key_offset: rc4 key not found") offset = y-x return offset def get_static_peers(self): """ extract static peers from memdump and query for version and config """ peer_array_offset = self.get_peer_array_offset() if not peer_array_offset: raise ZeusGameoverError("get_static_peers: unknown peer array offset") peers = [] for i in range(self.NUM_PEER_ENTRIES): offset = i*self.PEER_ENTRY_LEN+peer_array_offset peer = self.get_peer(self.memdump_config, offset) peers.append(peer) return peers def get_peer_array_offset(self): """ extract peer array offset from memdump """ # @TODO opcodes need further validation # AML-11294187.rsrc-38979806.dynamic.memdump 0x413906 # lea ecx, [esp+88h] # call sub_blah # push 14h # lea edi, [esp+11bh] code_offset = re.search(r"\x8d\x8c\x24(.{4})\xe8.{4}\x6a.{1}\x8d\xbc\x24(.{4})", self.memdump_data) x = struct.unpack("I", code_offset.groups()[0])[0] y = struct.unpack("I", code_offset.groups()[1])[0] offset = y-x-4 return offset def get_peer(self, data, offset, quick=False): """ extract a peer from a chunk of data """ peer = {} peer_entry = data[offset:offset+self.PEER_ENTRY_LEN] key = "".join(peer_entry[0x1:0x1+0x14]) peer["key"] = key ip = ".".join(["%s" % ord(byte) for byte in peer_entry[0x15:0x15+0x4]]) peer["ip"] = ip port = struct.unpack("H", "".join(peer_entry[0x19:0x19+0x2]))[0] peer["port"] = port if not quick: peer = self.query_peer_for_version(peer) if "tcp_port" in peer and self.memdump_rc4_key: peer = self.query_peer_for_config(peer) return peer def query_peer_for_version(self, peer): """ query peer for its version info, 0x00 command """ p2p_header, junk_size = self.get_p2p_header(0x00) # 0x00 cmd data = "" junk = self.get_junk(junk_size) command = p2p_header + data + junk sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.SOCK_TIMEOUT) encrypted_command = self.rc4(peer["key"], command) response = None try: sock.sendto(encrypted_command, (peer["ip"], peer["port"])) response = sock.recv(self.MAX_PACKET_SIZE) except: pass sock.close() if response: decrypted_response = self.rc4(self.SENDER_ID, response) peer = self.parse_version_response(peer, decrypted_response) return peer def query_peer_for_config(self, peer): """ query peer for its config, 0x68 command """ p2p_header, junk_size = self.get_p2p_header(0x68) # 0x68 cmd data = "" junk = self.get_junk(junk_size) command = p2p_header + data + junk sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(self.SOCK_TIMEOUT) encrypted_command = self.rc4(peer["key"], command) response = "" try: sock.connect((peer["ip"], peer["tcp_port"])) sock.send(encrypted_command) while 1: segment = sock.recv(self.MAX_PACKET_SIZE) if not segment: break response += segment except: pass sock.close() if response: decrypted_response = self.rc4(self.SENDER_ID, response) peer = self.parse_config_response(peer, decrypted_response) return peer def get_p2p_header(self, cmd): """ generate a P2P header """ #rand_byte = random.randint(1, 255) # 1 byte, random value, not 0 rand_byte = 0x44 # @TODO rand_byte, ttl, and ssid are related somehow, use hardcoded for now header = struct.pack("B", rand_byte) #ttl = random.randint(0, 255) # 1 byte, TTL field or random value (when not used) ttl = 0x73 # @TODO rand_byte, ttl, and ssid are related somehow, use hardcoded for now header += struct.pack("B", ttl) junk_size = random.randint(0, 255) # 1 byte, number of extra bytes to append to end of packet header += struct.pack("B", junk_size) header += struct.pack("B", cmd) # 1 byte, cmd #for i in range(20): # SSID, 20 bytes # ssid_byte = random.randint(0, 255) # header += struct.pack("B", ssid_byte) header += "\xbb\x8c\x79\xa8\x5a\xf1\xe1\x94\xe0\x19\xae\x72\x56\x68\xfc\x1b\x42\xf7\xda\x3a" # @TODO rand_byte, ttl, and ssid are related somehow, use hardcoded for now header += self.SENDER_ID # senderID, 20 bytes return header, junk_size def get_junk(self, junk_size): """ get junk bytes """ junk = "" for i in range(junk_size): # junk_size junk bytes junk_byte = random.randint(0, 255) junk += struct.pack("B", junk_byte) return junk def rc4(self, key, in_buf): """ rc4 encrypt/decrypt """ out_buf = [] i = 0 j = 0 S = self.ksa(key) for byte in in_buf: (i, j, S, K) = self.prga(i, j, S) new_byte = ord(byte) ^ K out_buf.append(chr(new_byte)) return "".join(out_buf) def rc4_keystate(self, key_state, in_buf): """ rc4 decrypted with exisiting KSA """ out_buf = [] i = ord(key_state[256]) j = ord(key_state[257]) S = [ord(byte) for byte in key_state[:256]] for byte in in_buf: (i, j, S, K) = self.prga(i, j, S) new_byte = ord(byte) ^ K out_buf.append(chr(new_byte)) return "".join(out_buf) # the key-scheduling algorithm (KSA) def ksa(self, key): S = [] # init to identity permutation for i in range(256): S.append(i) j = 0 for i in range(256): # equal: j = (j + S[i] + ord(key[i % len(key)])) & 255 j = (j + S[i] + ord(key[i % len(key)])) % 256 S = self.swap(S, i, j) return S # swap list elements def swap(self, S, i, j): S[i], S[j] = S[j], S[i] return S # the pseudo-random generation algorithm (PRGA) def prga(self, i, j, S): i = (i + 1) % 256 j = (j + S[i]) % 256 S = self.swap(S, i, j) K = S[(S[i] + S[j]) % 256] return (i, j, S, K) def parse_version_response(self, peer, response): """ parse version response """ # sanity check, make sure response command is 0x1 if ord(response[0x3]) != 0x1: raise ZeusGameoverError("parse_version_response: bad response command: %x" % ord(response[0x3])) data = self.strip_response(response) peer["binary_ver"] = struct.unpack("I", "".join(data[0:4]))[0] peer["config_ver"] = struct.unpack("I", "".join(data[4:8]))[0] peer["tcp_port"] = struct.unpack("H", "".join(data[8:10]))[0] return peer def parse_config_response(self, peer, response): """ parse config response """ length = struct.unpack("I", "".join(response[0:4]))[0] rc4_decrypted_response = self.rc4_keystate(self.memdump_rc4_key, response[4:]) plain = self.dexor(rc4_decrypted_response) # sanity checks # total length if length != len(plain): raise ZeusGameoverError("parse_config_response: bad total length") # config length -- subtract trailing rsa key at end calculated_len_of_conf = len(plain)-256 len_of_conf = struct.unpack("I", "".join(plain[20:24]))[0] if calculated_len_of_conf != len_of_conf: raise ZeusGameoverError("parse_config_response: bad config length") # md5 check calculated_hash_of_conf = "%04x%04x%04x%04x" % struct.unpack(">IIII", md5.new("".join(plain[48:len(plain)-256])).digest()) hash_of_conf = "%04x%04x%04x%04x" % struct.unpack(">IIII", "".join(plain[32:32+16])) if calculated_hash_of_conf != hash_of_conf: raise ZeusGameoverError("parse_config_response: bad md5 check") # @TODO complete config parser # parse config peer["config"] = self.parse_config(plain) peer["config_len"] = len_of_conf return peer def parse_config(self, plain): """ parse zeus gameover config @TODO complete config parser """ config_version = struct.unpack("I", "".join(plain[28:32]))[0] # chop off StorageHeader and trailing rsa key items_blob = plain[48:-256] items_blob_len = len(items_blob) current_position = 0 config = "" while current_position < items_blob_len: # get config entry pieces item_number = struct.unpack("I", "".join(items_blob[current_position:current_position+4]))[0] item_type = struct.unpack("I", "".join(items_blob[current_position+4:current_position+8]))[0] item_size_packed = struct.unpack("I", "".join(items_blob[current_position+8:current_position+12]))[0] item_size_unpacked = struct.unpack("I", "".join(items_blob[current_position+12:current_position+16]))[0] item_data = "".join(items_blob[current_position+16:current_position+16+item_size_packed]) # decrypt data xor_key = (item_size_packed << 0x10) | (item_number & 0xFFFF) | (config_version << 8) & 0xffffffff xor_key_str = struct.pack("I", xor_key) data = [] for i in range(len(item_data)): plain_byte = ord(item_data[i]) ^ ord(xor_key_str[i % 4]) data.append(chr(plain_byte)) # decompress if necessary if item_type & 0x1 == 1: data = zlib.decompress("".join(data), -15) # format entry config += "[start item number: %d, type: 0x%x, packed size: %d, unpacked size: %d]\n" % \ (item_number, item_type, item_size_packed, item_size_unpacked) config += "".join(data) config += "\n" config += "[end item number: %d]\n" % item_number current_position += 16 + item_size_packed return config def dexor(self, message): """ dexor message, aka visual decrypt in zeus-talk """ plain = [] for i in range(len(message)-1, 0, -1): plain_byte = ord(message[i]) ^ ord(message[i-1]) plain.append(chr(plain_byte)) plain.append(message[0]) plain.reverse() return plain def strip_response(self, response): """ strip off p2p header and trailing junk bytes """ junk_size = ord(response[0x2]) data = response[0x2c:-junk_size] return data def get_static_peers_list(self): """ return the list of static peers """ return self.static_peers def get_peers_list(self): """ return the list of peers """ return self.peers def enumerate_peers(self): """ enumerate p2p network, breadth first traversal """ all_peers = [] old_peers = [] last_len = 0 # init with static peers for peer in self.static_peers: all_peers.append(peer) old_peers.append(peer) while old_peers: # break if we're adding new peers too slowly percent = (len(all_peers) - last_len) / (len(all_peers) * 1.0) if percent < self.NEW_PEER_THRES: break last_len = len(all_peers) new_peers = [] for old_peer in old_peers: peers = self.query_peer_for_peers(old_peer) if peers: for peer in peers: if peer not in all_peers: all_peers.append(peer) new_peers.append(peer) old_peers = new_peers return all_peers def query_peer_for_peers(self, peer): """ query peer for its peers, 0x02 command """ p2p_header, junk_size = self.get_p2p_header(0x02) # 0x02 cmd data = peer["key"] # reqID, 20 bytes for i in range(8): # randomFill, 8 bytes random_fill = random.randint(1, 255) # non-zero random bytes data += struct.pack("B", random_fill) junk = self.get_junk(junk_size) command = p2p_header + data + junk sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(self.SOCK_TIMEOUT) encrypted_command = self.rc4(peer["key"], command) response = "" try: sock.sendto(encrypted_command, (peer["ip"], peer["port"])) response = sock.recv(self.MAX_PACKET_SIZE) except: pass sock.close() peers = [] if response: decrypted_response = self.rc4(self.SENDER_ID, response) peers = self.parse_peers_response(decrypted_response) return peers def parse_peers_response(self, response): """ parse peers response """ peers = [] # sanity check, make sure response command is 0x3 if ord(response[0x3]) != 0x3: raise ZeusGameoverError("parse_peers_response: bad response command: %x" % ord(response[0x3])) data = self.strip_response(response) for i in range(len(data)/self.PEER_ENTRY_LEN): offset = i*self.PEER_ENTRY_LEN peer = self.get_peer(data, offset, quick=True) peers.append(peer) return peers def format_peer_entry(self, peer): """ pretty format a peer entry """ entry = [] entry += [" ip: %s, udp port: %d, rc4 key: %s" % \ (peer["ip"], peer["port"], "".join(peer["key"]).encode('hex'))] if "binary_ver" in peer: entry += [" binary version: %d, config version: %d, tcp port: %d" % \ (peer["binary_ver"], peer["config_ver"], peer["tcp_port"])] if "config" in peer: entry += [" config saved (%d actual bytes)" % peer["config_len"]] entry += [""] return entry if __name__ == "__main__": fp = open(sys.argv[1], "rb") memdump = fp.read() fp.close() try: zeus_gameover = ZeusGameover(memdump) except ZeusGameoverError as msg: print "Error: %s" % msg sys.exit(1) static_peers = zeus_gameover.get_static_peers_list() formatted = [] for i, peer in enumerate(static_peers): formatted += ["static peer #%d" % (i+1)] formatted += zeus_gameover.format_peer_entry(peer) print "\n".join(formatted) # save configs for peer in static_peers: if "config" in peer: filename = "%s.config" % peer["ip"] fp = open(filename, "wb") fp.write(peer["config"]) fp.close() peers = zeus_gameover.get_peers_list() formatted = [] for i, peer in enumerate(peers): formatted += ["peer #%d" % (i+1)] formatted += zeus_gameover.format_peer_entry(peer) print "\n".join(formatted)
Komentar
Posting Komentar