์ง๋ ํฌ์คํ
์ ์ด์ด Qakbot ์
์ฑ์ฝ๋๋ฅผ ๋ถ์ํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ ํฌ์คํ
์์๋ ์
์ฑ์ฝ๋์ ๋ฆฌ์์ค ๋ณตํธํ ๋ฃจํด์ ํ์
ํ๊ณ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ์ฌ ํด๋น ๋ฆฌ์์ค์์ ์ด๋ค ์ ๋ณด๋ฅผ ์ถ์ถํ ์ ์๋์ง ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
์ํ ํด์(SHA256)๋ 73e4969db4253f9aeb2cbc7462376fb7e26cc4bb5bd23b82e2af0eaaf5ae66a8์
๋๋ค.
๊ณ์ ๋ถ์ํ๊ธฐ ์ํด sub_1000FB74 ์๋ธ๋ฃจํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๋ฌธ์์ด ํ ์ด๋ธ ๋์ฝ๋(mw_decode_string_table_2)๋ฅผ ํธ์ถํ๋ ๊ฒ์ ๋ฐ๊ฒฌํ์์ต๋๋ค. ์ง๋๋ฒ ์์ฑํ IDA Python ์คํฌ๋ฆฝํธ๋ฅผ ์ด์ฉํ์ฌ ๋ฌธ์์ด์ ๋ณตํธํํ์ฌ ์ฃผ์์ผ๋ก ์ถ๊ฐํด์ฃผ๊ฒ ์ต๋๋ค.
(์คํฌ๋ฆฝํธ ๋ด์ฉ์ '๋ฐ์ด๋๋ฆฌ์์ ์ํธํ๋ ๋ฌธ์์ด ์ถ์ถ(๋ณตํธํ)ํ๊ธฐ'์ ์์ธํ ์ค๋ช ๋์ด ์์ต๋๋ค.)
sub_1000FABA (mw_manipulate_persistence) ์๋ธ๋ฃจํด ๋ํ ๋ด๋ถ์ ๋ณตํธํ๋ ๋ฌธ์์ด์ด ์ฃผ์์ผ๋ก ์ถ๊ฐ๋์์ต๋๋ค.
๋ฌธ์์ด ๋ฐ ํธ์ถ๋๋ API๋ฅผ ํตํด ๋ ์ง์คํธ๋ฆฌ์ ์์ ์์ฝ์ ํตํด ์ง์์ฑ์ ์ค์ ํจ์ ์ ์ ์์ต๋๋ค.
Capa ๋ถ์์ ํตํด HTTP ์ํ ์ฝ๋๋ฅผ ํ์ธํ๋ ์๋ธ๋ฃจํด์ด ์กด์ฌํ๋ค๋ ๊ฒ์ ์๊ณ ์์์ต๋๋ค.
ํด๋น ์๋ธ๋ฃจํด sub_1000E815 (mw_HTTP_stuff)๋ฅผ ์ดํด๋ณด๋ฉด
WinINet API๋ค์ ํธ์ถํ๊ณ ์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ ์ฑ์ฝ๋๊ฐ WinINet API๋ฅผ ์ด์ฉํ์ฌ ํน์ IP ์ฃผ์์ธ C2์ ํต์ ํ๊ณ ์๋ค๋ ๊ฒ์ ๋๋ค.
๋ ์์ธํ ๋ถ์ํด๋ณด๊ธฐ ์ํด์ sub_1000E815 (mw_HTTP_stuff) ์๋ธ๋ฃจํด์ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ๋ฐ๋ผ๊ฐ๋ณด๊ฒ ์ต๋๋ค.
(mw_HTTP_stuff → sub_1000EBE2 → sub_1000EBE1 → sub_1000729B → sub_1000719F)
sub_1000EBE2 ์๋ธ๋ฃจํด์ HttpQueryInfoA, InternetReadFile ๋ฑ์ ํตํด C2 ํต์ ์ ์ํํ๊ณ
sub_1000EBE1์ sleepEx ํจ์๋ฅผ ํตํด sub_1000EBE2์ ํธ์ถ ๊ฐ๊ฒฉ์ ๊ด๋ฆฌํฉ๋๋ค.
sub_1000729B ์๋ธ๋ฃจํด์์ ํธ์ถํ๋ sub_1000719F๋ฅผ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
sub_1000719F (mw_SHA_RC4)
์ง๋ ๋ฒ ๋ดค๋ mersenne twister ํจ์๋ ์๊ณ , SHA1 ๊ด๋ จ ํจ์์ธ mw_w_SHA1 (sub_10007118)๋ ๋ณด์ ๋๋ค.
mw_w_SHA1๋ก ๋ค์ด๊ฐ๋ณด๊ฒ ์ต๋๋ค.
16๋ฒ์งธ ์ค mw_SHA1 (sub_1000F681) ์๋ธ๋ฃจํด ๋ด๋ถ ์ด๋ฐ ์ฝ๋ ๋ถ๋ถ์์ ์ํธํ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
mw_w_SHA1 ์๋ธ๋ฃจํด์ ๋ฐํ๊ฐ์ธ mw_RC4_init (sub_1000F353) ์๋ธ๋ฃจํด์์๋ ์ฝ๊ฐ ์์ ๋ KSA(Key-scheduling algorithm) ์๊ณ ๋ฆฌ์ฆ์ ๋ณผ ์ ์์ต๋๋ค.
S-Box(S_array) ์ด๊ธฐํ ๋ฐ ์๊ธฐ(์ฌ๋ฐฐ์ด) ๊ณผ์ ์ด ์ ๋ํ๋์์ต๋๋ค.
- KSA (Key-scheduling algorithm)
- RC4 ๊ฐ์ ์คํธ๋ฆผ ์ํธ์์ ์ฌ์ฉ๋๋ ํค ์ค์ผ์ค๋ง ์๊ณ ๋ฆฌ์ฆ
๋ค์ mw_SHA_RC4๋ก ๋์์์ 18๋ฒ์งธ ์ค์ mw_RC4_ PRGA ์๋ธ๋ฃจํด์ ๋ณด๋ฉด,
mw_w_SHA1์์ ๋ฐํ๋ ํค ์คํธ๋ฆผ key_stream์ ๋ค์ mw_RC4_ PRGA (sub_1000F3C5)ํจ์ ํธ์ถ์ ํ๋ผ๋ฏธํฐ๋ก ์ฐ์ด๊ณ ์์ต๋๋ค.
์ด๋ฅผ ํตํด mw_RC4_PRGA๊ฐ RC4์ PRGA ๋ฃจํด์ธ ๊ฒ์ ์ ์ ์์ต๋๋ค.
- PRGA (Pseudorandom Generation Algorithm)
- ์์ฌ๋์ ์์ฑ ์๊ณ ๋ฆฌ์ฆ
- RC4 ๊ฐ์ ์คํธ๋ฆผ ์ํธ๋ฅผ ์์ฑํ ๋ ํ์ํ ์์ฌ๋์๋ฅผ ์์ฑ
RC4 ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด์ ์ ์ฑ์ฝ๋๊ฐ ๋ฌด์จ ์ผ์ ํ๊ณ ์๋์ง ๊ถ๊ธํ๋ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
์ฌ๋ฌ ๋ฒ ํธ์ถ๋๊ณ ์๊ณ ๊ทธ์ค sub_10008F17 ์๋ธ๋ฃจํด์ผ๋ก ์ด๋ํด๋ณด๊ฒ ์ต๋๋ค.
sub_10008F17 (mw_w_RC4)์ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ์ธํ๋ฉด sub_100089C6 ์๋ธ๋ฃจํด์ด ์์ต๋๋ค.
sub_100089C6 (mw_ww_RC4) ๋ด๋ถ์๋ mw_SHA์ mw_w_RC4 ํธ์ถ์ด ์์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ mw_ww_RC4์ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ์ธํ์ ๋ ๋ฐ๊ฒฌํ sub_10008AC1 ์๋ธ๋ฃจํด์ ๋ค์ด๊ฐ์,
sub_10008AC1 ์๋ธ๋ฃจํด์ ๋ํ ๊ต์ฐจ ์ฐธ์กฐ ์ค sub_10002783๋ฅผ ์ ํํด๋ณด๊ฒ ์ต๋๋ค.
sub_10002783 ์๋ธ๋ฃจํด์ ์์ธํ ๋ณด๊ฒ ์ต๋๋ค.
sub_10002783 ์๋ธ๋ฃจํด ๋ด๋ถ์๋ resource๋ฅผ ๊ฐ์ ธ์ค๋ sub_1000A6CA (mw_get_resources) ์๋ธ๋ฃจํด๊ณผ
์ด๋ฏธ ๋ถ์ํ๋ ๋ฌธ์์ด ๋ณตํธํ ๋ฃจํด mw_w_decode_string_table_0 (sub_10001080),
๋๋ค๋ฅธ resource ๊ด๋ จ sub_10008AC1 (mw_w_resource_crypto) ์๋ธ๋ฃจํด์ ํธ์ถํ๊ณ ์์ต๋๋ค.
ํนํ sub_10008AC1 (mw_w_resource_crypto) ์๋ธ๋ฃจํด์ mw_ww_RC4์ ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ์ธํ์ ๋ ๋ฐ๊ฒฌํ, ์ฒ์์ ์๋ ๊ทธ ์๋ธ๋ฃจํด์ ๋๋ค.
sub_10002783 ์๋ธ๋ฃจํด ๋ด๋ถ ๋ณ์/์๋ธ๋ฃจํด๋ช ์ ์์ ํ์ฌ ์ฃผ๊ฒ ์ต๋๋ค.
mw_get_resources (sub_1000A6CA) ์๋ธ๋ฃจํด์ผ๋ก ๋ค์ด๊ฐ๋ณด๊ฒ ์ต๋๋ค.
ํด๋น ์๋ธ๋ฃจํด์ ๋ฐ์ด๋๋ฆฌ์ .rsrc ์น์ ์์ ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ์ฐพ์ ๋ก๋ํฉ๋๋ค.
- FindResourceA
- ๋ฆฌ์์ค ํ์ผ์์ ํน์ ๋ฆฌ์์ค๋ฅผ ์ฐพ๋ ๊ธฐ๋ฅ์ ์ ๊ณต
- FindResourceA ํจ์ ๊ตฌ์กฐ
- ๋ฆฌ์์ค ํ์ผ์์ ํน์ ๋ฆฌ์์ค๋ฅผ ์ฐพ๋ ๊ธฐ๋ฅ์ ์ ๊ณต
HRSRC FindResourceA(
HMODULE hModule, // ๋ฆฌ์์ค๊ฐ ํฌํจ๋ ๋ชจ๋์ ํธ๋ค
LPCSTR lpName, // ์ฐพ๊ณ ์ ํ๋ ๋ฆฌ์์ค์ ์ด๋ฆ
LPCSTR lpType // ๋ฆฌ์์ค์ ์ ํ (์: RT_ICON, RT_STRING ๋ฑ)
);
- SizeofResource
- ๋ฆฌ์์ค ํฌ๊ธฐ ๋ฐํ
- ๋ฆฌ์์ค ํฌ๊ธฐ ๋ฐํ
- LoadResource
- ๋ฆฌ์์ค๋ฅผ ๋ฉ๋ชจ๋ฆฌ์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ก๋ฉ
๋ค์ mw_get_resources๋ก ๋์๊ฐ์, ๊ต์ฐจ ์ฐธ์กฐ๋ฅผ ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
๊ทธ์ค sub_1000173B (mw_resource_decryptor)๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
58๋ฒ์งธ ์ค์์ ๋ฆฌ์์ค ์๋ณ์ 3719๊ฐ ์ ๊ณต๋ฉ๋๋ค.
59๋ฒ์งธ ์ค mw_get_resources๋ฅผ ํตํด ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
66๋ฒ์งธ ์ค์์ ํน์ ๋ฌธ์์ด์ด ๋ณตํธํ๋ฉ๋๋ค.
67๋ฒ์งธ ์ค์์ ๊ฐ์ ธ์จ ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ๋ณตํธํํฉ๋๋ค.
mw_w_resource_crypto_0๊ณผ mw_resource_decryptor ์๋ธ๋ฃจํด์ ํตํด ์ ์ ์๋ ๊ฒ์,
๋ฆฌ์์ค ์๋ณ์(ID) 5812, 3719๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ , ๊ทธ๊ฒ์ ๋ณตํธํํ๊ธฐ ์ํ mw_w_resource_crypto ํจ์, ๊ทธ๋ฆฌ๊ณ mw_ww_RC4 ์๋ธ๋ฃจํด์ ํธ์ถํ๋ค๋ ๊ฒ์
๋๋ค.
๋ํ SHA1 ๋ฐ RC4 ๊ด๋ จ ์๋ธ๋ฃจํด(mw_SHA, mw_w_RC4, mw_ww_RC4 ๋ฑ)์ ํตํด ์ ์ ์๋ ๊ฒ์,
ํค๊ฐ SHA1 ์๋ธ๋ฃจํด์ ์ ๊ณต๋๊ณ , SHA1 ์๋ธ๋ฃจํด์ ๊ฒฐ๊ณผ(key_stream)๊ฐ RC4 ์๋ธ๋ฃจํด์ ์ ๊ณต๋๋ฉฐ(์ด๋ SHA1๋ KDF), RC4 ์๋ธ๋ฃจํด์ ๋ฆฌ์์ค ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ค๋ ๊ฒ์
๋๋ค.
๋๋ฌด ๊ธธ๊ณ ๋ง์์ ๊น๋จน์ง ์๊ฒ ์ ๋ฆฌํด๋ดค์ต๋๋ค. (์ผ์ฝ;;)
์ด๋ฒ์๋ mw_resource_decryptor์ ๊ต์ฐจ ์ฐธ์กฐ ์ค sub_100019DE ์๋ธ๋ฃจํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
sub_100019DE ์๋ธ๋ฃจํด ๋ด๋ถ์ inet_ntoa ํจ์๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค.
- inet_ntoa
- IPv4 ์ฃผ์๋ฅผ ๋ฌธ์์ด ํ์์ผ๋ก ๋ณํ
inet_ntoa ํจ์๋ฅผ ํตํด IP์ฃผ์๋ฅผ ๋ฌธ์์ด ํ์(dotted-decimal)์ผ๋ก ๋ณํํ๋ ๊ฒ์ ๋๋ค. mw_resource_decryptor๊ฐ .rsrc์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๊น ํด๋น ์น์ ์ C2 ์ฃผ์๊ฐ ์ ์ฅ๋์ด ์์ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ฒ์๋ mw_resource_decryptor์ ๊ต์ฐจ ์ฐธ์กฐ ์ค sub_1000173B ์๋ธ๋ฃจํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค
๋ด๋ถ์ ๋ณด๋ ๋ฑ ๋ด๋ IP ์ฃผ์ ํ์์ ๋ฌธ์์ด์ด ์์ต๋๋ค: "%u.%u.%u.%u"%u"
์ด๋ ๊ฒ .rsrc ์น์ ์ ์ค์ํ ์ ๋ณด๋ค์ด ์์ผ๋ .rsrc ์น์ ์ ๋ณด๋ฅผ ์ถ์ถํ๊ณ ๋ณตํธํํด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
๋จผ์ ์ฝ๋์์๋ ํ์ธํ์ง๋ง Resource Hacker ํด๋ก ๋ค์ ํ ๋ฒ ๋ณด๋ฉด, ๋ ๊ฐ์ ๋ฆฌ์์ค ID๊ฐ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๋ถ์์ ํตํด ์๊ณ ์๋ ์ํฉ์
mw_w_RC4 ์๋ธ๋ฃจํด์ด ๋ ๋ฒ ํธ์ถ๋์ด ๋ ๋ฆฌ์์ค ID๋ฅผ ๋ณตํธํํ๊ณ ์๊ณ ,
mw_w_RC4 ํธ์ถ ์(mw_w_RC4(0x14u, (int)key, key + 20, resource_size - 20, *arg_key_stream);) ์ธ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ์์ 20์ ๋ํ๋ ๊ฒ์ ๋ณด๋ฉด ์ค์ ๋ฐ์ดํฐ๋ 21๋ฒ์งธ ๋ฐ์ดํธ๋ถํฐ ์์ํ๊ณ ์์ต๋๋ค.
SHA1์ ํด์๊ฐ์ด 20๋ฐ์ดํธ์ด๊ธฐ ๋๋ฌธ์ ์ด์ ์ฐ๊ด์ด ์์ ๊ฒ์ ๋๋ค.
์ด ์ ๋ณด๋ค์ ๊ธฐ๋ฐ์ผ๋ก Python ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ์ฌ ๋ณตํธํํด๋ณด๊ฒ ์ต๋๋ค. ("Alexandre Borges์ "Malware Analysis Series(MAS) – Article 2" ์ฐธ์กฐ)
import binascii
import pefile
import ipaddress
from Crypto.Cipher import ARC4
from Crypto.Hash import SHA1
# Key found in the code (doubled slashs because the interpretation)
key = b'\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
# SHA hash of the key
sha1_key = SHA1.new(data=key).digest()
# This routine extracts data from .rsrc section.
def extract_resource(filename, res_identification):
extracted_data = b""
pe = pefile.PE(filename)
for resource in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if hasattr(resource, 'directory'):
for res_id in resource.directory.entries:
if hasattr(res_id, 'name'):
if (str(res_id.name) == str(res_identification)):
offset = res_id.directory.entries[0].data.struct.OffsetToData
resid_size = res_id.directory.entries[0].data.struct.Size
extracted_data = pe.get_memory_mapped_image()[offset:offset+resid_size]
return extracted_data
# This routine decrypt resource data using RC4 + hashed key
def data_decryptor(key_data, data):
data_cipher = ARC4.new(key_data)
decrypted_config = data_cipher.decrypt(data)
return decrypted_config
# Main routine used to perform all necessary calls.
def main():
# Path to the malware
filename = r"C:\Users\user\Desktop\mas2_sample_unpack\rundll32_047A0000_from_VirtualProtect.bin"
# Extracts the second resource id (smaller) and decrypt it.
resource_data = extract_resource(filename, 5812)
hex_resource_data = binascii.hexlify(resource_data)
# Remember that the useful data strat at 21th byte onward.
decrypted_data = data_decryptor(sha1_key, resource_data)[20:]
# Prints SHA1 key
print('SHA1 KEY: ', end='')
print(binascii.hexlify(sha1_key))
print(60*'=')
# Prints decrypted resource : ID=5812
print('\nDECRYPTED BOTNET AND CAMPAIGN ID: ', end='')
print("\n" + 34*'-')
print(decrypted_data.decode('latin1'))
# extracts the first resource id and decrypt it.
resource_data = extract_resource(filename, 3719)
decrypted_data = data_decryptor(sha1_key, resource_data)
# Remember that the useful data start at 21th byte onward.
resource_item = decrypted_data[21:]
# Prints decrypted resource : ID=3719
# The format is: [4 bytes for IP address][2 bytes of part]
print('\nC2 IP ADDRESS LIST: ')
print(30*'-')
k = 0
i = 0
while (k < len(resource_item)):
ip_item = resource_item[k:k+4]
ip_port = resource_item[k+4:k+6]
print("IP[%d]: %s" % (i, ipaddress.IPv4Address(ip_item)), end=':')
print(int(binascii.hexlify(ip_port),16))
k = k+7
i = i+1
if __name__ == '__main__':
main()
์คํฌ๋ฆฝํธ๋ฅผ ํตํด ๋ด๋ท ์ด๋ฆ์ด obama150์ด๋ผ๋ ๊ฒ๊ณผ ์บ ํ์ธ ID๊ฐ 1640256791์ด๋ผ๋ ๊ฒ, ๊ทธ๋ฆฌ๊ณ C2 IP ์ฃผ์ 150๊ฐ๋ฅผ ์ป์์ต๋๋ค.
๋ค์ ํฌ์คํ ์์๋ ์ ์ฑ์ฝ๋๊ฐ Crypto API๋ฅผ ํธ์ถํ์ฌ C2 ํต์ ํ๋ ๋ถ๋ถ์ ๋ํด ๋ถ์ํด๋ณด๊ฒ ์ต๋๋ค.
Ref. "Malware Analysis Series(MAS) – Article 2", Alexandre Borges