Exploring Crystal language

These days I decided to explore the Crystal programming language, a high-performance, statically-typed programming language with Ruby-inspired syntax. To do so, I decided to port NativeDump and TrickDump to it.

Repositories:



Motivation

Crystal can execute Windows API calls, including NTAPI calls, by leveraging its C bindings and low-level system capabilities.

It provides the lib keyword, which allows defining these bindings. Using it, you can map the Windows API functions from kernel32.dll, user32.dll, or NTAPI functions from ntdll.dll into your Crystal code.

The @[Link] annotation is typically required when you want to explicitly link with a specific library that is not automatically linked by the system. For example, when calling functions from ntdll.dll or other custom shared libraries.

This is an example for defining the NtQueryInformationProcess NTAPI function:

@[Link("ntdll")]
lib Ntdll
  fun NtQueryInformationProcess(
    process_handle : Void*,
    process_information_class : UInt32,
    process_information : Void*,
    process_information_length : UInt32,
    return_length : Pointer(UInt32)
  ) : Int32
end

Then you can call NtQueryInformationProcess, for example to get the “Info Class” PROCESS_BASIC_INFORMATION (value 0) from the current process (value -1):

status = Ntdll::NtQueryInformationProcess(Pointer(Void).new(-1), 0, Pointer(Void).null, 0, Pointer(UInt32).null)
puts "Status: #{status}"

Knowing this, I created CrystalDump, a NativeDump port entirely written in Crystal; and a port of TrickDump to Crystal as well.

Exploring Crystal’s potential was inspired by Rastamouse’s blog post “Crystal Malware” from earlier this year: Crystal Malware.



CrystalDump

CrystalDump is a port of NativeDump written in Crystal lang, designed to dump the lsass process using only NTAPI functions:

esquema

  • NtOpenProcessToken and NtAdjustPrivilegesToken to enable the SeDebugPrivilege privilege
  • NtGetNextProcess and NtQueryInformationProcess to get a handle to the lsass process
  • RtlGetVersion to get OS information
  • NtReadVirtualMemory and NtQueryInformationProcess to get modules information
  • NtQueryVirtualMemory and NtQueryInformationProcess to get memory regions information

The tool supports remapping ntdll.dll using a process created in debug mode. For this it uses the NTAPI functions NtQueryInformationProcess, NtReadVirtualMemory, NtProtectVirtualMemory, NtClose, NtTerminateProcess and NtRemoveProcessDebug; and the Kernel32 function CreateProcessW.



Usage

crystaldump.exe [-o OUTPUTFILE ] [-r]
  • Output file (-o, optional): Dump file name

  • Remap ntdll (-r, optional): Remap the ntdll.dll library


By default it creates a file named “crystal.dmp”:

crystaldump.exe

img1

Using the parameter -r it remaps the ntdll.dll library:

crystaldump.exe -r

img2

The parameter -o is used to change the output file name:

crystaldump.exe -r -o document.pptx

img3



Build

To build the binary, use a command like:

crystal build crystaldump.cr --release



TrickDump - Crystal port

This branch implements the same functionality as the main branch using Crystal lang.

lock.exe  [-j JSON_NAME ] [-r]
shock.exe [-j JSON_NAME ] [-r]
barrel.exe [-j JSON_NAME ] [-z ZIP_NAME] [-r]

Options:

  • JSON file name (-j, optional): JSON file name

  • ZIP file name (-z, optional): ZIP file name

  • Remap ntdll (-r, optional): Remap the ntdll.dll library


By default the programs do not remap the ntdll.dll and create the files “lock.json”, “shock.json”, “barrel.json” and “barrel.zip”:

img1

It is possible to remap the ntdll.dll library using the parameter -r and change the default file names with -j and -z:

img2

Then use the create_dump.py script to generate the Minidump file in the attack system:

python3 create_dump.py [-l LOCK_JSON] [-s SHOCK_JSON] [-b BARREL_JSON] [-z BARREL_ZIP] [-o OUTPUT_FILE]

img3



Trick: All in one

If you prefer to execute only one binary, Trick.exe generates a ZIP file containing the 3 JSON files and the ZIP file with the memory regions:

trick.exe [-z ZIPNAME] [-r]

It creates the ZIP file locally, optionally using a ntdll.dll overwrite method:

img4

With a ZIP file like this, you can unzip it and create the Minidump file using create_dump.py later:

img5



Build

First resolve and install dependencies (crystal-garage/crystal-zip64 is the only one):

shards install

Then build the binaries you need:

crystal build lock.cr --release
crystal build shock.cr --release
crystal build barrel.cr --release
crystal build trick.cr --release


Written on December 20, 2024