DGA는 매일 수많은 도메인 주소를 무작위로 생성
특정 날짜에 생성되는 도메인 주소를 예측할 수 있도록 되어있고 그 도메인 주소 중 하나를 미리 도메인 등록을 해두는 방식을 사용한다.
Locky 랜섬웨어는 파일 암호화에 사용할 키 값을 얻기위해 내부에 저장된 IP로 접속을 시도한다 IP로 접속 실패 시, 2차로 고유한 DGA 함수를 통해 생성한 도메인 주소로 접속을 시도한다 IP로 접속 실패 시, 2차로 고유한 DGA함수를 통해 생성한 도메인 주소로 접속을 시도한다 DGA 함수는 현재 날짜 정보를 통해 생성되는 구조이며 매일 생성되는 도메인이 변경되는 특징을 가지고 있다.
config = {
1: {
# md5: 81e85dcaf482aba2f8ea047145490493
#sha256:9afb127@733b01952f00a8174291d39f740b6df2c90d0422b4d6f2e2e6bc7d1a
#sample: <https://virustotal.com/en/file/9afb127e733b01952f00a8174291d39f740b6df2c90d042264d6f2e2e6bc7d1a/analysis/>
'seed': 7077,
'shift': 7,
'tlds': ['ru','xyz''info','biz','click','su','work','pl','org','pw','xyz']
},
2:{
# md5: 5fb8f8f75342ff68ed8c79cc375f0cd8
# sha256: bc7c4565a05f3f0deea162578e45d5fb64c9aa72a81395083509c0f78b6ae1de
#sample:<https://malwr.com/analysis/NZF1YZRKOWZhZD1iNDZm/ThkMzkzMjU22mE50DUzMjE/>
'seed': 5566,
'shift':7,
'tids': ['ru','xyz''info','biz','click','su','work','pl','org','pw','xyz']
},}
Locky 랜섬웨어는 seed값( 4바이트 상수)로 되어있으며 가변적인 형태로 값이 달라지고 있다. DGA는 현재 날짜와 seed값을 통해 도메인 값을 계속 변경하고 있다.
def rol32(v,s):
v &= 0xFFFFFFFF
return ((v << s)) | (v >> (32-s))) & 0xFFFFFFFF
def dga(date, config_nr, domain_nr):
c = config[ config_nr]
seed_shifted = rol32(c[ 'seed'], 17)
dhr_shifted = rol32(domain_nr, 21)
k = 0
year = date. year
for in range(7):
t_0= ror32(0xB11924E1 * (year + k + 0x1BF5), c['shift']) & 0xFFFFFFFF
t_1 = ((t_0 + 0x27100001) ^ k) & 0xFFFFFFFF
t_2 = (ror32(0xB11924E1 *(t_1 + c[' seed' ]), c['shift'])) & 0xFFFFFFFF
t_3 = ((t_2 + 0x27100001) ^ t_1) & 0xFFFFFFFF
t_4 = (ror32(0x811924E1 *(date.day//2 + t_3), c['shift' ])) & 0xFFFFFFFF
t_5 = (0xD8EFFFFF - t_4 + t_3) & 0xFFFFFFFF
t_6 = (ror32(0xB11924E1 * (date.month + t_5 - 0x65CAD), c['shift'])) & 0xFFFFFFFF
t_7 = (t_5 + t_6 + 0x27100001) & 0xFFFFFFFF
t_8 = (ror32(0xB119241 * (t_7 + seed_shifted + dnr_shifted), c['shift'])) & 0xFFFFFFFF
k = ((t_8 + 0x27100001) ^ t_7) & 0xFFFFFFFF
year +=1
length = (k % 11) + 7
domain = ""
for i in range(length):
k : (ror32(0×B11924E1*Pol32(k, i), c['shift ']) + 0x27100001) & 0xFFFFFFFF
domain += chr(k % 25 + ord('a'))
domain += '.'
k = ror32(k*0xB11924E1, c['shift' ])
tids = c['tlds' ]
tld_i = ( (k + 0x27100001) & 0xFFFFFFFF) % len(tids)
domain += tlds[tld_i]
return domain
Locky 랜섬웨어에서 사용하는 DGA는 여러 버전이 존재하고 있으며 해당 소스코드에서 암호화 방식을 알 수 있다고 해도 seed값이 바뀐다면 전혀 다른 값을 생성할수 있기 때문에 방어자 입장에서는 정확한 DGA값을 도출할 수 없다.
import argparse
from datetime import datetime
#import datetime
from rc4 import rc4
import struct
#now=datetime.datetime.now()
config = {
1: {
# md5: 81e85dcaf482aba2f8ea047145490493
# sha256: 9afb127e733b01952f00a8174291d39f740b6df2c90d0422b4d6f2e2e6bc7d1a
# sample: <https://virustotal.com/en/file/9afb127e733b01952f00a8174291d39f740b6df2c90d0422b4d6f2e2e6bc7d1a/analysis/>
'seed': 7077,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
2: {
# md5: 5fb8f8f75342ff68ed8c79cc375f0cd8
# sha256: bc7c45b5a05f3f0deea162578e45d5fb64c9aa72a81395083509c0f78b6ae1de
# sample: <https://malwr.com/analysis/NzFlYzRkOWZhZDliNDZmMThkMzkzMjU2ZmE5ODUzMjE/>
'seed': 5566,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
3: {
# md5: 19079496f6abfafd9a99d02098556a79
# sha256: 5dd6188efe13268bb9ac20ecdb257085e7d62163
# sample: <https://malwr.com/analysis/Yzg5ZWMyZDZmNGFhNGU0YWJjMzY3YjBmMjY4Y2JlMDM/>
'seed': 9106,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
4: {
# md5: 0e223d578eaddec361498591ec8c1a19
# sha256:
# sample:
'seed': 5579,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
5: {
# md5: 6cb11f4066f74556dd14d27008d867b4
# sha256: 353ea18baa6c9c796a7b48bcccbf4c9c3c6aa63f8b4dd55d796c65e22028b77b
# sample: <https://malwr.com/analysis/N2Q4YWUyM2I3Y2VlNGQwYzllMjczNzE2Njc1ZTFhZWI/>
'seed': 111,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
6: {
# md5: ed534695e79a2a70e8b15f8873cdfa02
# sha256:
# sample: <https://www.sophos.com/en-us/threat-center/threat-analyses/viruses-and-spyware/Troj~Ransom-CUL/detailed-analysis.aspx>
'seed': 9044,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
7: {
# md5: bfb5fec661318f07b0eca8520bb8c53f
# sha256: 92a1d459194d0bf86ff26103a9c92d64059e1caa9d98e1ed9002058a0da8f53d
# sample: <https://malwr.com/analysis/Mzc0MTU0MTQ1YjRlNGVhYzgzMmQ0MGQ3YWY1NWUzZDg/>
'seed': 9099,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
8: {
# md5: a075610a69e196ab74f79508dbcf5eef
# sha256: caa6e59e98c22a3f9159412a612ad170d2683640e1845afb6f533f279f5e6577
# sample: <https://malwr.com/analysis/OWJhZjQ5ZmE4YjY4NDFhYmFhNjIxZDcyYmFkYzlhYTM/>
'seed': 9047,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
9: {
# md5: 6c3e68307d01e4340c83fac94f237237
'seed': 9133,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
10: {
# md5: 17bf0d1776de896315cb2d63118f9667
# sha256: 98d6ebd37c861beaf7420494aa8dfd43e4904145bac62c607965b3a8d92967c1
# sample: <https://malwr.com/analysis/NWJiNjJhZWU2YjU2NGRlYjg5Njk2ZGJlMzZkZDcxYWI/>
'seed': 9199,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
11: {
# md5:
# sha256:
# sample:
'seed': 9190,
'shift': 7,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
12: {
# md5:
# sha256:
# sample: <https://malwr.com/analysis/NzY4YmRjZDA1MTYwNGEzMzg2MWZkNmUyODIzMWRhMDM/>
'shift': 7,
'seed': 9088,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
13: {
# md5: 6eb8865bf055ba30cc9e2843f16ee461
# sha256:
# sample:
'shift': 7,
'seed': 9998,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
14: {
# md5: b5660f65369abc67cfa4a65e7d0d65d9
# sha256: 478ab3b1f465dc1088b0d1e7cef8cab1f3b736856f6be279d4e7a8113ad065d5
# sample: <https://www.virustotal.com/en/file/478ab3b1f465dc1088b0d1e7cef8cab1f3b736856f6be279d4e7a8113ad065d5/analysis/>
'shift': 7,
'seed': 9992,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
15: {
# md5: aceec3d6334e925297efc8d4232473c2
# sha256: 5c18ab258a3a89980aaa9d673a07851fcab4443733a00c4fbf14d21906b65c9e
# sample: <https://www.virustotal.com/en/file/5c18ab258a3a89980aaa9d673a07851fcab4443733a00c4fbf14d21906b65c9e/analysis/1463993646/>
'shift': 7,
'seed': 1511,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
16: {
# md5:
# sha256:
# sample: (downloaded by) <https://malwr.com/analysis/ODU3OWM4ZDMxMmE2NDllZWE4MWQ3ZGQ2ZTBjZTc4MWI/>
'shift': 7,
'seed': 1513,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
17: {
# md5: 89f35a5d22088e3ca8664697e895b7a5
# sha256:
# sample:
'shift': 7,
'seed': 1517,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
18: {
# md5: a9d09406e0982d652b6db291558df71a
# sha256:
# sample:
'shift': 7,
'seed': 9056,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
19: {
# md5:
# sha256:
# sample:
# ref: <https://nominum.com/locky-back/>
'shift': 7,
'seed': 7773,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
20: {
# md5:
# sha256:
# sample:
# ref: <https://nominum.com/locky-back/>
'shift': 7,
'seed': 7743,
'tlds': ['ru', 'info', 'biz', 'click', 'su', 'work', 'pl', 'org', 'pw',
'xyz']
},
}
def ror32(v, s):
v &= 0xFFFFFFFF
return ((v >> s) | (v << (32-s))) & 0xFFFFFFFF
def rol32(v, s):
v &= 0xFFFFFFFF
return ((v << s) | (v >> (32-s))) & 0xFFFFFFFF
def dga(date, config_nr, domain_nr):
c = config[config_nr]
seed_shifted = rol32(c['seed'], 17)
dnr_shifted = rol32(domain_nr, 21)
k = 0
year = date.year
for _ in range(7):
t_0 = ror32(0xB11924E1 * (year + k + 0x1BF5), c['shift']) & 0xFFFFFFFF
t_1 = ((t_0 + 0x27100001) ^ k) & 0xFFFFFFFF
t_2 = (ror32(0xB11924E1 * (t_1 + c['seed']), c['shift'])) & 0xFFFFFFFF
t_3 = ((t_2 + 0x27100001) ^ t_1) & 0xFFFFFFFF
t_4 = (ror32(0xB11924E1 * (date.day//2 + t_3), c['shift'])) & 0xFFFFFFFF
t_5 = (0xD8EFFFFF - t_4 + t_3) & 0xFFFFFFFF
t_6 = (ror32(0xB11924E1 * (date.month + t_5 - 0x65CAD), c['shift'])) & 0xFFFFFFFF
t_7 = (t_5 + t_6 + 0x27100001) & 0xFFFFFFFF
t_8 = (ror32(0xB11924E1 * (t_7 + seed_shifted + dnr_shifted), c['shift'])) & 0xFFFFFFFF
k = ((t_8 + 0x27100001) ^ t_7) & 0xFFFFFFFF
year += 1
length = (k % 11) + 7
domain = ""
for i in range(length):
k = (ror32(0xB11924E1*rol32(k, i), c['shift']) + 0x27100001) & 0xFFFFFFFF
domain += chr(k % 25 + ord('a'))
domain += '.'
k = ror32(k*0xB11924E1, c['shift'])
tlds = c['tlds']
tld_i = ((k + 0x27100001) & 0xFFFFFFFF) % len(tlds)
domain += tlds[tld_i]
return domain
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--date",
help="date for which to generate domains")
parser.add_argument("-c", "--config", choices=list(range(1,len(config)+1)),
help="config nr", type=int, default=1)
args = parser.parse_args()
if args.date:
d = datetime.strptime(args.date, "%Y-%m-%d")
else:
d = datetime.now()
for i in range(12):
print( dga(d, args.config, i) )
"""
UNPREDICTABLE DGA OF RECONYC
from sample: d7e3bbdc38aa18b214fed3518798b86b
unpacks to: db0820a974dbea22d981bd39ddb19518
"""
import random
class Mersenne:
def __init__(self, seed):
self.n = 624
self.w = 32
self.wm = (2**32) - 1
self.r = 31
self.f = 1812433253
self.a = 0x9908B0DF
self.u = 11
self.s = 7
self.t = 15
self.d = 0xFFFFFFFF
self.b = 0x9D2C5680
self.c = 0xEFC60000
self.l = 18
self.m = 397
self.seed = seed
self.seed_b = ~seed
self.mt = self.n*[0]
self.lower_mask = (1 << self.r) - 1
self.upper_mask = (1 << self.r)
def seed_mt(self):
self.mt[0] = self.seed
for i in range(self.n-1):
self.mt[i+1] = (self.f * ((self.mt[i] >> 30) ^ self.mt[i]) + i+1) & self.wm
def extract_number(self):
if self.seed != self.seed_b:
self.index = self.n + 1
if self.index >= self.n:
if self.index == self.n + 1:
self.seed_mt()
self.seed = ~self.seed
self.seed_b = self.seed
self.twist()
self.index = 0
y = self.mt[self.index]
y ^= (y >> self.u) & self.d
y ^= (y << self.s) & self.b
y ^= (y << self.t) & self.c
y ^= (y >> self.l)
self.index += 1
return y
def twist(self):
for i in range(self.n-1):
a = (self.mt[i] & self.upper_mask)
b = (self.mt[(i+1) % self.n]) & self.lower_mask
x = a + b
x = x & self.wm
xA = (x >> 1)
if x % 2:
xA = (xA ^ self.a)
self.mt[i] = self.mt[(i+self.m) % self.n] ^ xA
self.index = 0
def randint(mersenne, nr):
x = mersenne.extract_number()
return (nr*x) >> 32
charset = "iHRYg79zJXaGw1CF5K0d3vZobhAlx6StUBnjOIMpe2yVuPr4sL8DqmQTkEcWNf"
seed = random.randint(0, 1000*3600*24)
if __name__ == "__main__":
mersenne = Mersenne(seed)
for nr in range(100):
domain = ""
for i in range(10):
c = charset[randint(mersenne, len(charset))]
domain += c
domain += ".com"
print(domain)
DGA생성은 했지만 매일같이 바뀌는 DGA를 패턴분석기반으로 찾아내기는 힘들다.