Uru is a payload generation tool that enables you to create payload based on a configuration file.
To use all the functionnalities offered by Uru, it is recommended to install:
go install mvdan.cc/garble@latest
go get github.com/C-Sto/BananaPhone
To use the signing option (limelighter), the following packages are needed:
- openssl
- osslsigncode
The generate command is reponsible for payload generation and uses the following arguments:
Take as input a config and a given payload file to generate a payload.
Usage:
uru generate [flags]
Flags:
--class string .Net Class to call (use with donut)
--clearheader Remove peheader of the payload if set (use with srdi)
-c, --config string Config file that definied the modules to use
--donut Process the given payload as an executable using go-donut
--functionname string Methods to call if .Net payload (with donut) or Function name to call after DLL Main (with srdi)
-h, --help help for generate
-o, --output string Output file name
--parameters string Parameters to pass to the payload (use with donut/srdi)
-p, --payload Shellcode/Executable Shellcode/Executable to use in the generated payload
--srdi Convert dll into a position independant code that uses a rdll loader to execute the dll entrypoint.
-c/--config
a config file, a example is provided, see example_go_config.yml. The Config file section will helps you to build your own configuration file.
-p/--payload
the path to the payload you want to execute/inject.
--donut
to process the payload as a binary. It will use go-donut to obtain a shellcode.
--parameters
parameters to pass to the payload (works only if --donut
or --srdi
is passed as an argument).
--srdi
convert the payload into a srdi shellcode.
--clearheader
will remove PE header when using --srdi
.
--functionname
will execute a specific function after dllMain, when using --srdi
.
--class
.Net Class to call (use with donut).
-o/--output
to specify a specific output filename and location.
Example:
./uru generate -c <pathtoconfig.yml> -p <pathtopayload> --donut
Note: uru
supports the following programmation languages:
- c language: see example_c_config.yml
- golang: see example_go_config.yml
The server mode offers the possibility to generate payload through an api call:
Start a server with an api enpoint /generate to generate payload. Listen on 0.0.0.0:8081 by default, can be changed with -a/--addr
Usage:
uru serve [flags]
Flags:
-a, --addr string ip:port to listen on the api server (e.g: 127.0.0.1:3000) (default ":8081")
-h, --help help for serve
-a/--addr
is the ip_addr:port
to listen on. By default it listens on 0.0.0.0:8081
.
The api endpoint /generate
accepts two files in entrypoint:
config
the config filepayload
the payload to process
It also accepts two parameters:
donut
if the payload is an executable, it will use go-donut to obtain a shellcode. Must be equal to "true".srdi
convert the payload into a srdi shellcode. Must be equal to "true".parameters
parameters to pass to the binary (needdonut
orsrdi
)functionname
function name / method to call (needdonut
orsrdi
)class
.Net class to pass to go-donut. (needdonut
)clearheader
remove the peHeader (needsrdi
). Must be equal to "true".
Here is an example to use the api call with the curl command:
curl -F config=@<path to config> -F payload=@<path to payload> http://127.0.0.1:8081/generate?donut=true -o <your payload name>
A dockerfile is provided if you want to run the tool in an isolate environment or in your infrastructure. To build the docker:
docker build -t uru .
Then, you can use the serve command for example:
docker run -p 8081:8081 uru serve --debug
The config file is the core of your generated payload. It contains modules under a yml structure named "artifacts", and global options. It is important to note that these modules will be added and executed in the order in which you defined them. For example, you defined a config.yml like this:
payload:
artifacts:
- name: sleep
type: evasion
args:
- name: delay
value: 10
- name: xor
type: encoder
args:
- name: Key
value: "test"
- name: hex
type: encoder
[...]
The generated payload will perfom the following actions in this order:
- Sleep while 10 seconds
- xor the given payload with the key "test"
- unhex your payload.
More information about these artifacts are detailed in the next sections.
Global options correspond to the required/optional options of the generated payload:
Name | Description | required (yes/no) |
---|---|---|
type | Type of payload to compiled. Possible values: exe, pie, cpl, xll or dll. | yes |
arch | Architecture of the compiled program: x64 or x86. | yes |
debug | Add print and debug functions to the program | no |
obfuscation | Use garble to obfuscate the code | no |
append | Append the provided string at the end of the payload file. Must be hexadecimal ex: 90909090 | no |
prepend | Prepend the payload file with the provided string. Must be hexadecimal ex: 90909090 | no |
You can use uru as a binary service, thanks to this service package. To implement a service, you need to use the following options in your config file, under the payload section:
serviceOptions:
serviceName: YourServiceName
serviceDisplayName: YourServiceDisplayName
serviceDescription: YourServiceDesc
This might be useful to run uru for lateral movement. Note:
- if your try to use a binary which is not a service during a service execution (without cmd /c), you might have the following error:
FAILED 1053: The executable program that this service is configured to run in does not implement the service.
- if you try to use a binary that don't implement the service you are running under, you might have the following error:
FAILED 1083: The executable program that this service is configured to run in does not implement the service.
You need to use at leastserviceName
uru argument to avoir that error.
Encoder are responsible of the payload encoding. Encoding will be process during the payload generation. Then, the opposite actions will be added to generated code.
To encode your payload (and avoid to store it in plaintext), you can use the following "encoders":
Name | Description |
---|---|
aes | Use AES GCM to encrypt given data |
hex | Use hex encoding to encode given data |
rc4 | Use rc4 encoding to encode given data |
xor | Use xor algorithm to encode given data |
zip | Use zip compression on given data |
reverse-order | Reverse the order of the shellcode byte array. |
uuid | [experimental/dev] Transform data into UUID string (only works with ninjauuid injector). |
Name | Description |
---|---|
xor | Use xor algorithm to encode given data |
To use an encoder, specify the name, and the type "encoder".
Note:
- SGN is also a valid encoder but the code is commented (see line with "[SGN] - DECOMMENT TO USE SGN") because of cross compilation problem.
- Each encoder (except hex, zip, sgn) has an argument named "Key". You can modify this arguments like this (by default it is a random string):
[...]
- name: xor
type: encoder
args:
- name: key
value: "mykey"
[...]
Evasions are the modules that help you to evade AV/EDR:
Name | Description | Argument(s) | Comment |
---|---|---|---|
english-words | Add a random number of english words to the binary. | NumberOfWord: define the number of english words to add to the binary between 1 and 1000. | Note that the words will no be obfuscated when using garble |
hideconsole | Prevent windows console to be displayed. | Show: Show the console or not. Default is "false" | |
isdomainjoined | Check if current computer is joined to a domain. | None | |
ntsleep | NtSleep during a fixed amount of time in seconds using NtDelayExecution API call | Delay: the amount of time to sleep, default is 5s | |
patchamsi | Path amsi. (credits: method taken from Merlin, @Ne0nd0g) | UseBanana: if set to "true", use bananaphone to perform syscall | |
patchetw | Path etw. (credits: method taken from Merlin, @Ne0nd0g) | UseBanana: if set to "true", use bananaphone to perform syscall | |
patch | Path a given function. (credits: method taken from Merlin, @Ne0nd0g) | Module: the module where the function is. Example: "ntdll.dll", Proc: the function to patch. Example: "EtwEventWrite", Data: the data to use to patch the function in hex, UseBanana: if set to "true", use bananaphone to perform syscall | By default it patch etw to its "normal state" |
selfdelete | Delete the current binary during runtime | None | |
sleep | Sleep during a fixed amount of time in seconds. | Delay: the amount of time to sleep, default is 5s | |
createmutex | Create a mutex with a specific name. | MutexName: the name of the mutex, default is "UruMutex" | |
refreshdll | Refresh the given dll to remove hook by using the dll on disk. (Inspired by sliver/scarecrow and TimWhitez works). | UseBanana: UseBananaPhone to perform syscall. Default is "false", DllName: Name of the dll to refresh. Default is "C:\\Windows\\System32\\kernel32.dll". | Only work if windows version is "10.0" |
Name | Description | Argument(s) | Comment |
---|---|---|---|
sleep | Sleep during a fixed amount of time in milliseconds. | Delay: the amount of time to sleep, default is 5000ms | |
dllforward | Forward exported functions to correct dll. | File: Local path to file you want to impersonate, ExpectedPath: specify the files typical place on machine (including file name), if is not the same as above |
Injectors are the modules that defined a process injection:
Name | Description |
---|---|
windows/native/local/execute_fp | Executes Shellcode in the current running proccess by making a Syscall on the Shellcode's entry point. |
windows/native/local/createthread | Use native windows api call CreateThread to inject into the current process. |
windows/native/local/ntqueueapcthreadex | Use native windows api call NtQueueApcThreadEx to inject in the current process |
windows/bananaphone/local/execute_fp | Executes Shellcode in the current running proccess by making a Syscall on the Shellcode's entry point. |
windows/bananaphone/local/ntqueueapcthreadex | Use native windows api call NtQueueApcThreadExt to inject in the current process. Call is performed using bananaphone from @C-Sto. |
windows/bananaphone/local/ninja_uuid | [experimental/dev] Module stomping following EnumSystemLocalesA for injection. Injection taken from @boku7 project. uuid encoder must be used as your last encoder. |
Name | Description | Argument(s) | Comment |
---|---|---|---|
windows/native/local/execute_fp | Use windows apis (virtual alloc, virtual protect, memcpy) to inject code | ||
windows/native/local/createthread | Use windows apis (virtual alloc, virtual protect, memcpy, CreateThread) to inject code |
Note:
bananaphone
injections require to install bananaphone from @C-Sto.
Tampering options allow you to alter the paylaod generated. It is inspired from ScareCrow.
You can add metadata to the payload using a json file that contains the information, see this file for an example. To add metadata, you must add this option to the config file:
file_properties_path: ./<path to the json metadata file>.json
You can also signed the binary. It uses LimeLighter.
The options must be defined like this in the config file:
limelighter:
domain: mydomain.com
Note: Limelighter options "password" and "real" are also supported.
This section will go through an artifact addition. The first step is to defined the type of the artifact:
- encoder will go to ./pkg/encoder/
- injector will go to ./pkg/injector/
- evasion will go to ./pkg/evasion/
In this example, we will add an evasion module that just print a message.
First, we will add a module under ./pkg/<module type>/<module name>.go
, for our example we will call it ./pkg/evasion/print_testgo
.
The module must implement the model type named ObjectModel
, see ./pkg/models.go
.
This type requires to implement 3 functions:
- GetImports: Corresponds to an array of golang import needed by our module
- RenderInstanciationCode: Will render our instanciation code, how do we instanciate our module
- RenderFunctionCode: Will render our function(s) code
A completed module will look like this:
package evasion
import (
"embed"
"github.com/guervild/uru/pkg/common"
"github.com/guervild/uru/pkg/models"
)
type PrintTestEvasion struct {
Name string
Description string
PrintString string
}
func NewPrintTestEvasion() models.ObjectModel {
return &PrintTestEvasion{
Name: "print-test",
Description: `Example of how to implement a module in Uru.
Argument(s):
PrintString: The string to print, default is "Hello World."`,
PrintString: "Hello World",
}
}
func (e *PrintTestEvasion) GetImports() []string {
return []string{
`"fmt"`,
}
}
func (e *PrintTestEvasion) RenderInstanciationCode(data embed.FS) (string, error) {
return common.CommonRendering(data, "templates/go/evasions/print-test/instanciation.go.tmpl", e)
}
func (e *PrintTestEvasion) RenderFunctionCode(data embed.FS) (string, error) {
return common.CommonRendering(data, "templates/go/evasions/print-test/functions.go.tmpl", e)
}
Then, will add the new ObjectModel to the corresponding factory:
- Injector factory is located at ./pkg/injectorFactory.go
- Evasion factory is located at ./pkg/evasion.go
- Encoder factory is located at ./pkg/encoder.go
For our example, we will add the new module in ./pkg/evasion.go:
[...]
func GetEvasion(evasionType string) (models.ObjectModel, error) {
[...]
if evasionType == "print-test" {
return NewPrintTestEvasion(), nil
}
[...]
We can now write the code corresponding to our new modules. The code is embedded inside the binary and is defined inside the ./data/templates/
directory. A module directory is defined like this ./data/templates/<module type>/<module name>
.
We create a file under ./data/templates/evasions/print-test/instanciation.go.tmpl
(corresponding to the path inside the RenderInstanciationCode function). This code will be added inside the func main
during payload generation (see ./data/templates/core.go.tmpl
). The final code looks like this:
printTest("{{ .PrintString }}")
PrintString
corresponds to our argument that we want to render. Here it is just a message we want to print.
Then, we create a file under ./data/templates/evasions/print-test/functions.go.tmpl
(corresponding to the path inside the RenderFunctionCode function). This code will corresponds to the function called by instanciation.go.tmpl
, and is also added during payload generation (see ./data/templates/core.go.tmpl
). The function code will look like this:
func printTest(message string) {
fmt.Println(message)
}
Now we can compile our main.go
, and call the module in the config.yml
file:
payload:
artifacts:
- name: print-test
type: evasion
args:
- name: PrintString
value: "It works!"
- name: windows/native/local/go-shellcode-syscall
type: injector
type: exe
arch: x64
Once the payload generated, you should see your message printed before your process injection:
Do not hesitate to contribute with issue/pull request if you find a bug, or if you want to add features. Some possible features that i would like to implement next are in the TODO.md file.