gscript

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 17, 2017 License: AGPL-3.0 Imports: 29 Imported by: 0

README

gscript

Genesis Scripting Engine

Genesis Logo

WARNING: This library is under active development. API is NOT stable and will have breaking changes for the foreseeable future.

Description

GENESIS Scripting (gscript for short) is a technology I've developed to allow dynamic runtime execution of malware installation based on parameters determined at runtime.

Inspiration for this comes from my old AutoRune™ days and from the need for malware to basically become self aware without a bunch of duplicate overhead code.

GScript uses a JS V8 Virtual Machine to interpret your genesis script and allow it to hook into the malware initialization.

The Engine itself is referred commonly as "GSE" - Genesis Scripting Engine.

What is GENESIS btw?

GENESIS was created by @vyrus, @gen0cide, @emperorcow, and @ahhh for dynamically bundling multiple payloads into one dropper for faster deployment of implants for the CCDC Red Team.

For more information on this work we do every year, see my blog post outlining our toolbox:

GSE's goal is to allow intelligent deployment of those payloads.

Variables

User Defined (You define/overwrite as needed)
Variable Name Type Default Purpose
source_url string null Location GSE should download implant from.
packed_file string null A packed GSE File (created with the GSE compiler)
source_bytes array Generated At Runtime The contents to be written to file_dest during Deploy()
file_dest string null The location where source_bytes get's written to, and subsequently executed.
exec_args array [] Array of strings that will be passed to the dest_file Exec() call.
timeout int 180000 The global timeout for the entire GSE VM. Default = 3 minutes. Can be overwritten to shorten or lengthen.
Read Only (Defined at Runtime by GSE)
Variable Name Type Example Purpose
user_info object {uid: 0, gid: 0, username: "root", home_dir: "/root"} Information about the User. Will be basically whatever is returned with https://golang.org/pkg/os/user/#User
hostname string example01 The hostname of the machine.
ip_addrs array ["127.0.0.1","192.168.1.5"] The IP addresses of the machine.
os string linux The operating system (basically runtime.GOOS)
arch string amd64 The CPU architecture (basically runtime.GOARCH)

Builtin Functions

These functions are available to you automatically within the GSE scripting context.

Halt()

Terminates the current GSE VM gracefully.

Argument List

None

Return Type

boolean (true = success, false = error)


DeleteFile(path)

Delete the file located at path.

Argument List
  • path (String) - Path to file you wish to delete.
Return Type

boolean (true = success, false = error)


CopyFile(srcPath, dstPath)

Copy file from srcPath to dstPath.

Argument List
  • srcPath (String) - Path to source file.
  • dstFile (String) - Path to destination file.
Return Type

boolean (true = success, false = error)


WriteFile(path, bytes, perms)

Write bytes to path and set perms to perms.

Argument List
  • path (String) - Path to file you wish to write.
  • bytes (Array) - Array of bytes you wish to write to the path location.
  • perms (String) - Octal unix permissions represented as a string. ie: 0777.
Return Type

boolean (true = success, false = error)


ExecuteFile(path, args)

Execute a file located at path with args as arguments.

Argument List
  • path (String) - Path to file you wish to execute.
  • args (Array) - Arguments to pass to during file execution.
Return Type

boolean (true = success, false = error)


AppendFile(path, bytes)

Append bytes to the file located at path.

Argument List
  • path (String) - Path to file you wish to append.
  • bytes (Array) - Array of bytes you wish to append.
Return Type

boolean (true = success, false = error)


ReplaceInFile(path, target, replace)

Replace any instances of target with replace in the file located at path.

Argument List
  • path (String) - Path to file you wish to modify.
  • target (String) - String value you wish to replace in the file.
  • replace (String) - String value you wish to substitute target with.
Return Type

boolean (true = success, false = error)


Signal(pid, signal)

Send a signal to another process.

Argument List
  • pid (String) - Process ID you wish to signal
  • signal (Integer) - Type of signal you wish to send (9, 15, etc.)
Return Type

boolean (true = success, false = error)


RetrieveFileFromURL(url)

Retrieve a file via GET for a given url.

Argument List
  • url (String) - Full URL of location you wish to retrieve.
Return Type

[]bytes - Byte array of body response.


DNSQuery(question, type)

Perform a DNS lookup.

Argument List
  • question (String) - DNS query question (eg: "twitter.com")
  • type (String) - DNS question type (A, CNAME, MX, etc.)
Return Type

Object - Reference VMDNSQueryResponse in response_objects.go for object details.


HTTPRequest(method, url, body, headers)

Perform an HTTP/S request.

Argument List
  • method (String) - HTTP Method (GET, POST, PUT, DELETE, HEAD, etc.)
  • url (String) - Full URL (including https://) you wish to make a request to.
  • body (String) - Any body you wish to include (nil if none).
  • headers (Object) - A key/value object that will be set as HTTP Request Headers.
Return Type

Object - Reference VMHTTPRequestResponse in response_objects.go for object details.


Exec(cmd, args)

Execute the given command and arguments.

Argument List
  • cmd (String) - Base command you wish to run
  • args (Array) - Arguments as an array of strings.
Return Type

Object - Reference VMExecResponse in response_objects.go for object details.


MD5(bytes)

Create a MD5 hash of the given bytes.

Argument List
  • path (Array) - Array of bytes.
Return Type

string - Hex encoded MD5 hash.


SHA1(bytes)

Create a SHA1 hash of the given bytes.

Argument List
  • bytes (Array) - Array of bytes.
Return Type

string - Hex encoded SHA1 hash.


B64Encode(bytes)

Perform a Base64 encode on bytes.

Argument List
  • bytes (Array) - Array of bytes you wish to base64 encode.
Return Type

string - Base64 encoded string representation.


B64Decode(string)

Perform a Base64 decode on string.

Argument List
  • string (String) - Base64 encoded string
Return Type

[]bytes - Byte array of the deserialized b64 string.


Timestamp()

Get current time in Epoch.

Argument List

None

Return Type

integer - Current time in Epoch.


CPUStats()

Retreive specs about the machine's CPU.

Argument List

None

Return Type

Object - Reference VMCPUStatsResponse in response_objects.go for object details.


MemStats()

Retreive specs about the machine's memory.

Argument List

None

Return Type

Object - Reference VMMemStatsResponse in response_objects.go for object details.


SSHExec(host, port, creds, cmds)

Executes SSH commands on the given host.

Argument List
  • host (String) - Host you wish to connect to.
  • port (String) - Port you wish to connect to.
  • creds (Object) - Credential Object: { username: "", password: "", privateKey: "" }
  • cmds (Array) - Commands you wish to run as an array of strings.
Return Type

Object - Reference VMSSHExecResponse in response_objects.go for object details.


Sleep(seconds)

Sleep for seconds number of seconds.

Argument List
  • seconds (Int) - Sleep Duration
Return Type

boolean (true = success, false = error)


GetDirsInPath()

Get a list of all the directories currently in our PATH.

Argument List

None

Return Type

[]string - Array of directories in the current PATH as strings.


EnvVars()

Retrieve an array of all environment variables in the current execution.

Argument List

None

Return Type

Object - Reference VMEnvVarsResponse in response_objects.go for object details.


GetEnv(varname)

Retrieve the value for Environment Variable varname.

Argument List
  • varname (String) - Environment variable name.
Return Type

string - Value, empty if undefined.


FileCreateTime(path)

Lookup the creation time for file located at path.

Argument List
  • path (String) - Path to target file.
Return Type

int - Last modified time in Epoch format.


FileModifyTime(path)

Lookup the last modified time for file located at path.

Argument List
  • path (String) - Path to target file.
Return Type

int - Last modified time in Epoch format.


LoggedInUsers()

Gets an array of unique users currently logged in.

Argument List

None

Return Type

[]string - Array of usernames as strings.


UsersRunningProcs()

Gets an array of unique users currently running processes.

Argument List

None

Return Type

[]string - Array of usernames as strings.


ServeDataOverHTTP(data, port, timeout)

Starts an HTTPServer that will respond to GET / with the data provided on port port.

Argument List
  • data (String) - Data you wish to serve.
  • port (Int) - What port should we listen on?
  • timeout (Int) - How many seconds should we listen? (Cannot be > global timeout variable!)
Return Type

boolean (true = success, false = error)


Notes

This is just my design chicken scratch. I'll slowly migrate this stuff over to more formal documentation as I implement.


// Genesis Hooks (Can be user defined)
function BeforeDeploy() {};
function Deploy()       {};
function AfterDeploy()  {};
function OnError()      {};

// Research Functions (Should *not* be overridden!)
// These functions allow you to get information about
// a given system in order to make decisions based off
// the context the runtime is executing in.
function LocalUserExists(username)      { return boolean; };
function ProcExistsWithName(name)       { return boolean; };
function CanReadFile(path)              { return boolean; };
function CanWriteFile(path)             { return boolean; };
function CanExecFile(path)              { return boolean; };
function FileExists(path)               { return boolean; };
function DirExists(path)                { return boolean; };
function FileContains(path, match)      { return boolean; };
function IsVM()                         { return boolean; };
function IsAWS()                        { return boolean; };
function HasPublicIP()                  { return boolean; };
function CanMakeTCPConn(dst, port)      { return boolean; };
function ExpectedDNS(query, type, resp) { return boolean; };
function CanMakeHTTPConn(url)           { return boolean; };
function DetectSSLMITM(url, cert_fp)    { return boolean; };
function CmdSuccessful(cmd)             { return boolean; };
function CanPing(dst)                   { return boolean; };
function TCPPortInUse(port)             { return boolean; };
function UDPPortInUse(port)             { return boolean; };
function ExistsInPath(progname)         { return boolean; };
function CanSudo()                      { return boolean; };
function Matches(string, match)         { return boolean; };
function CanSSHLogin(ip, port, u, p)    { return boolean; };

TODO

  • Implement All Functions
  • Implement Global Variables
  • Implement Runtime Variable Loading
  • Implement Timeout
  • Implement Hook Callers
  • Implement CLI Framework (cmd/gscript)
  • Implement Compiler / Crypter

Credits

Shoutouts to the homies:

  • vyrus
  • ahhh
  • cmccsec
  • carnal0wnage
  • indi303
  • emperorcow
  • rossja

Documentation

Index

Constants

View Source
const EntryPoint = `genesis_entry_point.gs`

Variables

View Source
var (
	Debugger = true
)
View Source
var TempEnd = "\tgse.LoadScript(gse.Imports[\"genesis_entry_point.gs\"]())\n\tgse.ExecutePlan()\n}\n"
View Source
var TempMain = "func main() {\n\tgse := gscript.New(\"\")\n\tgse.CreateVM()"
View Source
var TempStart = "package %s\n\nimport (\n\t\"bytes\"\n\t\"compress/flate\"\n\t\"io/ioutil\"\n\n\t\"github.com/gen0cide/gscript\"\n)\n"
View Source
var VMPreload = `` /* 1010-byte string literal not displayed */

Functions

func BytesToCompressed

func BytesToCompressed(b []byte) []byte

func CalledBy

func CalledBy() string

func CompressedToBytes

func CompressedToBytes(b []byte) []byte

func DNSQuestion

func DNSQuestion(target, request string) (string, error)

func HTTPGetFile

func HTTPGetFile(url string) ([]byte, error)

HTTPGetFile takes a url and returns a byte slice of the file there

func LocalCopyFile

func LocalCopyFile(src, dst string) error

func LocalDirCreate

func LocalDirCreate(path string) error

func LocalDirRemoveAll

func LocalDirRemoveAll(dir string) error

func LocalFileAppendBytes

func LocalFileAppendBytes(filename string, bytes []byte) error

LocalFileAppendBytes adds bytes to the end of filename's path.

func LocalFileAppendString

func LocalFileAppendString(input, filename string) error

LocalFileAppendString adds input as strings to the end of filename's path.

func LocalFileCreate

func LocalFileCreate(path string, bytes []byte) error

func LocalFileDelete

func LocalFileDelete(path string) error

func LocalFileExists

func LocalFileExists(path string) bool

func LocalFileRead

func LocalFileRead(path string) ([]byte, error)

LocalReadFile takes a file path and returns the byte array of the file there

func LocalFileReplace

func LocalFileReplace(file, match, replacement string) error

Replace will replace all instances of match with replace in file.

func LocalFileReplaceMulti

func LocalFileReplaceMulti(file string, matches []string, replacement string) error

ReplaceMulti will replace all instances of possible matches with replacement in file.

func LocalSystemInfo

func LocalSystemInfo() ([]string, error)

func RandLowerAlphaString

func RandLowerAlphaString(strlen int) string

func RandString

func RandString(strlen int) string

RandString returns a string the length of strlen

func RandStringRunes

func RandStringRunes(n int) string

func RandomInt

func RandomInt(min, max int) int

RandomInt returns an int inbetween min and max.

func StripSpaces

func StripSpaces(str string) string

StripSpaces will remove the spaces from a single string and return the new string

func XorBytes

func XorBytes(a []byte, b []byte) []byte

func XorFiles

func XorFiles(file1 string, file2 string, outPut string) error

Types

type Compiler

type Compiler struct {
	OS          string
	Arch        string
	ScriptFile  string
	PackageName string
	OutputFile  string
	AssetFiles  []string
	Embeds      []EmbeddedFile
	BuildDir    string
	AssetDir    string
	Logger      *l.Logger
}

func NewCompiler

func NewCompiler(script, outfile, os, arch string) *Compiler

func (*Compiler) BuildEntryPoint

func (c *Compiler) BuildEntryPoint()

func (*Compiler) CompileAssets

func (c *Compiler) CompileAssets()

func (*Compiler) CreateBuildDir

func (c *Compiler) CreateBuildDir()

func (*Compiler) Do

func (c *Compiler) Do()

func (*Compiler) GatherAssets

func (c *Compiler) GatherAssets()

func (*Compiler) WriteScript

func (c *Compiler) WriteScript()

type EmbeddedFile

type EmbeddedFile struct {
	SourcePath   string
	SourceURL    string
	Filename     string
	NameHash     string
	VariableDef  string
	Uncompressed []byte
	Compressed   []byte
}

func (*EmbeddedFile) Compress

func (e *EmbeddedFile) Compress()

func (*EmbeddedFile) Embed

func (e *EmbeddedFile) Embed()

func (*EmbeddedFile) GenerateVariableDef

func (e *EmbeddedFile) GenerateVariableDef()

func (*EmbeddedFile) ResolveData

func (e *EmbeddedFile) ResolveData()

func (*EmbeddedFile) ResolveFilename

func (e *EmbeddedFile) ResolveFilename()

func (*EmbeddedFile) ResolveVariableName

func (e *EmbeddedFile) ResolveVariableName()

type Engine

type Engine struct {
	VM      *otto.Otto
	Logger  *l.Logger
	Imports map[string]func() []byte
	Name    string
}

func New

func New(name string) *Engine

func (*Engine) AddImport

func (e *Engine) AddImport(name string, data func() []byte)

func (*Engine) CreateVM

func (e *Engine) CreateVM()

func (*Engine) DebugConsole

func (e *Engine) DebugConsole(call otto.FunctionCall) otto.Value

func (*Engine) EnableLogging

func (e *Engine) EnableLogging()

func (*Engine) ExecutePlan

func (e *Engine) ExecutePlan() error

func (*Engine) InteractiveSession

func (e *Engine) InteractiveSession()

func (*Engine) LoadScript

func (e *Engine) LoadScript(source []byte) error

func (*Engine) LogCrit

func (e *Engine) LogCrit(i ...interface{})

func (*Engine) LogCritf

func (e *Engine) LogCritf(fmtString string, i ...interface{})

func (*Engine) LogDebug

func (e *Engine) LogDebug(i ...interface{})

func (*Engine) LogDebugf

func (e *Engine) LogDebugf(fmtString string, i ...interface{})

func (*Engine) LogError

func (e *Engine) LogError(i ...interface{})

func (*Engine) LogErrorf

func (e *Engine) LogErrorf(fmtString string, i ...interface{})

func (*Engine) LogInfo

func (e *Engine) LogInfo(i ...interface{})

func (*Engine) LogInfof

func (e *Engine) LogInfof(fmtString string, i ...interface{})

func (*Engine) LogWarn

func (e *Engine) LogWarn(i ...interface{})

func (*Engine) LogWarnf

func (e *Engine) LogWarnf(fmtString string, i ...interface{})

func (*Engine) RunAfterDeploy

func (e *Engine) RunAfterDeploy() error

func (*Engine) RunBeforeDeploy

func (e *Engine) RunBeforeDeploy() error

func (*Engine) RunDeploy

func (e *Engine) RunDeploy() error

func (*Engine) RunOnError

func (e *Engine) RunOnError() error

func (*Engine) SessionCompleter

func (e *Engine) SessionCompleter(d prompt.Document) []prompt.Suggest

func (*Engine) SessionExecutor

func (e *Engine) SessionExecutor(in string)

func (*Engine) SetName

func (e *Engine) SetName(name string)

func (*Engine) VMAppendFile

func (e *Engine) VMAppendFile(call otto.FunctionCall) otto.Value

func (*Engine) VMAsset

func (e *Engine) VMAsset(call otto.FunctionCall) otto.Value

func (*Engine) VMB64Decode

func (e *Engine) VMB64Decode(call otto.FunctionCall) otto.Value

func (*Engine) VMB64Encode

func (e *Engine) VMB64Encode(call otto.FunctionCall) otto.Value

func (*Engine) VMCPUStats

func (e *Engine) VMCPUStats(call otto.FunctionCall) otto.Value

func (*Engine) VMCanExecFile

func (e *Engine) VMCanExecFile(call otto.FunctionCall) otto.Value

func (*Engine) VMCanMakeHTTPConn

func (e *Engine) VMCanMakeHTTPConn(call otto.FunctionCall) otto.Value

func (*Engine) VMCanMakeTCPConn

func (e *Engine) VMCanMakeTCPConn(call otto.FunctionCall) otto.Value

func (*Engine) VMCanPing

func (e *Engine) VMCanPing(call otto.FunctionCall) otto.Value

func (*Engine) VMCanReadFile

func (e *Engine) VMCanReadFile(call otto.FunctionCall) otto.Value

func (*Engine) VMCanSSHLogin

func (e *Engine) VMCanSSHLogin(call otto.FunctionCall) otto.Value

func (*Engine) VMCanSudo

func (e *Engine) VMCanSudo(call otto.FunctionCall) otto.Value

func (*Engine) VMCanWriteFile

func (e *Engine) VMCanWriteFile(call otto.FunctionCall) otto.Value

func (*Engine) VMCmdSuccessful

func (e *Engine) VMCmdSuccessful(call otto.FunctionCall) otto.Value

func (*Engine) VMCopyFile

func (e *Engine) VMCopyFile(call otto.FunctionCall) otto.Value

func (*Engine) VMDNSQuery

func (e *Engine) VMDNSQuery(call otto.FunctionCall) otto.Value

Uses the native DNS client (including things like host files and resolution)

func (*Engine) VMDeleteFile

func (e *Engine) VMDeleteFile(call otto.FunctionCall) otto.Value

func (*Engine) VMDetectSSLMITM

func (e *Engine) VMDetectSSLMITM(call otto.FunctionCall) otto.Value

func (*Engine) VMDirExists

func (e *Engine) VMDirExists(call otto.FunctionCall) otto.Value

func (*Engine) VMEnvVars

func (e *Engine) VMEnvVars(call otto.FunctionCall) otto.Value

func (*Engine) VMExec

func (e *Engine) VMExec(call otto.FunctionCall) otto.Value

func (*Engine) VMExecuteFile

func (e *Engine) VMExecuteFile(call otto.FunctionCall) otto.Value

func (*Engine) VMExistsInPath

func (e *Engine) VMExistsInPath(call otto.FunctionCall) otto.Value

func (*Engine) VMExpectedDNS

func (e *Engine) VMExpectedDNS(call otto.FunctionCall) otto.Value

func (*Engine) VMFileContains

func (e *Engine) VMFileContains(call otto.FunctionCall) otto.Value

func (*Engine) VMFileCreateTime

func (e *Engine) VMFileCreateTime(call otto.FunctionCall) otto.Value

func (*Engine) VMFileExists

func (e *Engine) VMFileExists(call otto.FunctionCall) otto.Value

func (*Engine) VMFileModifyTime

func (e *Engine) VMFileModifyTime(call otto.FunctionCall) otto.Value

func (*Engine) VMGetDirsInPath

func (e *Engine) VMGetDirsInPath(call otto.FunctionCall) otto.Value

func (*Engine) VMGetEnv

func (e *Engine) VMGetEnv(call otto.FunctionCall) otto.Value

func (*Engine) VMGetTweet

func (e *Engine) VMGetTweet(call otto.FunctionCall) otto.Value

func (*Engine) VMHTTPRequest

func (e *Engine) VMHTTPRequest(call otto.FunctionCall) otto.Value

func (*Engine) VMHalt

func (e *Engine) VMHalt(call otto.FunctionCall) otto.Value

func (*Engine) VMHasPublicIP

func (e *Engine) VMHasPublicIP(call otto.FunctionCall) otto.Value

func (*Engine) VMImplode

func (e *Engine) VMImplode(call otto.FunctionCall) otto.Value

func (*Engine) VMIsAWS

func (e *Engine) VMIsAWS(call otto.FunctionCall) otto.Value

func (*Engine) VMIsVM

func (e *Engine) VMIsVM(call otto.FunctionCall) otto.Value

func (*Engine) VMLocalUserExists

func (e *Engine) VMLocalUserExists(call otto.FunctionCall) otto.Value

func (*Engine) VMLogCrit

func (e *Engine) VMLogCrit(call otto.FunctionCall) otto.Value

func (*Engine) VMLogDebug

func (e *Engine) VMLogDebug(call otto.FunctionCall) otto.Value

func (*Engine) VMLogError

func (e *Engine) VMLogError(call otto.FunctionCall) otto.Value

func (*Engine) VMLogInfo

func (e *Engine) VMLogInfo(call otto.FunctionCall) otto.Value

func (*Engine) VMLogWarn

func (e *Engine) VMLogWarn(call otto.FunctionCall) otto.Value

func (*Engine) VMLoggedInUsers

func (e *Engine) VMLoggedInUsers(call otto.FunctionCall) otto.Value

func (*Engine) VMMD5

func (e *Engine) VMMD5(call otto.FunctionCall) otto.Value

func (*Engine) VMMatches

func (e *Engine) VMMatches(call otto.FunctionCall) otto.Value

func (*Engine) VMMemStats

func (e *Engine) VMMemStats(call otto.FunctionCall) otto.Value

func (*Engine) VMProcExistsWithName

func (e *Engine) VMProcExistsWithName(call otto.FunctionCall) otto.Value

func (*Engine) VMReplaceInFile

func (e *Engine) VMReplaceInFile(call otto.FunctionCall) otto.Value

func (*Engine) VMRetrieveFileFromURL

func (e *Engine) VMRetrieveFileFromURL(call otto.FunctionCall) otto.Value

func (*Engine) VMSHA1

func (e *Engine) VMSHA1(call otto.FunctionCall) otto.Value

func (*Engine) VMSSHCmd

func (e *Engine) VMSSHCmd(call otto.FunctionCall) otto.Value

func (*Engine) VMServeFileOverHTTP

func (e *Engine) VMServeFileOverHTTP(call otto.FunctionCall) otto.Value

func (*Engine) VMSignal

func (e *Engine) VMSignal(call otto.FunctionCall) otto.Value

func (*Engine) VMSleep

func (e *Engine) VMSleep(call otto.FunctionCall) otto.Value

func (*Engine) VMTCPPortInUse

func (e *Engine) VMTCPPortInUse(call otto.FunctionCall) otto.Value

func (*Engine) VMTimestamp

func (e *Engine) VMTimestamp(call otto.FunctionCall) otto.Value

func (*Engine) VMUDPPortInUse

func (e *Engine) VMUDPPortInUse(call otto.FunctionCall) otto.Value

func (*Engine) VMUsersRunningProcs

func (e *Engine) VMUsersRunningProcs(call otto.FunctionCall) otto.Value

func (*Engine) VMWriteFile

func (e *Engine) VMWriteFile(call otto.FunctionCall) otto.Value

func (*Engine) ValidateAST

func (e *Engine) ValidateAST(source []byte) error

func (*Engine) ValueToByteSlice

func (e *Engine) ValueToByteSlice(v otto.Value) []byte

type VMExecResponse

type VMExecResponse struct {
	Stdout   []string `json:"stdout"`
	Stderr   []string `json:"stderr"`
	Success  bool     `json:"success"`
	PID      int      `json:"pid"`
	ErrorMsg string   `json:"error_message"`
}

func ExecuteCommand

func ExecuteCommand(c string, args ...string) VMExecResponse

ExecuteCommand function

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL