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