-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathshell.py
917 lines (794 loc) · 41.5 KB
/
shell.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
import re
from burp import IBurpExtender
from burp import IContextMenuFactory
from burp import IMessageEditorController
from burp import ITab
from java.awt import BorderLayout
from java.awt import Color
from java.awt import GridLayout
from java.awt import KeyboardFocusManager
from java.awt.event import ActionListener
from java.awt.event import ItemEvent
from java.awt.event import ItemListener
from java.awt.event import KeyListener
from java.io import PrintWriter
from java.lang import Runnable
from java.lang import Thread
from java.util import Collections
from javax.swing.border import EmptyBorder
from javax.swing import BorderFactory
from javax.swing import BoxLayout
from javax.swing import JButton
from javax.swing import JCheckBox
from javax.swing import JComboBox
from javax.swing import JLabel
from javax.swing import JMenuItem
from javax.swing import JPanel
from javax.swing import JScrollPane
from javax.swing import JTabbedPane
from javax.swing import JTextArea
from javax.swing import JTextField
COLOR_BURP_ORANGE = Color(0xFF, 0xC5, 0x99)
COLOR_BURP_TITLE_ORANGE = Color(0xFF, 0x66, 0x33)
class BurpExtender(IBurpExtender, ITab):
#
# implement IBurpExtender
#
def registerExtenderCallbacks(self, callbacks):
# set our extension name
callbacks.setExtensionName("Shell - FWD/LFI")
#Keep references to:
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
# obtain our output stream
self._stdout = PrintWriter(callbacks.getStdout(), True)
#get ShellController instance
self._shellController = ShellController(self)
#Give ourself a class to pass important hooks around
Utils().setParent(self)
#Register main component
callbacks.customizeUiComponent(self._shellController.getMainComponent())
# register SendTo option
callbacks.registerContextMenuFactory(OfferShell())
# add the custom tab to Burp's UI
callbacks.addSuiteTab(self)
# add common used GUI interfaces to our utils
Utils().registerComponents()
return
def getTabCaption(self):
return "Shell - FWD/LFI"
def getUiComponent(self):
return self._shellController.getMainComponent()
class ShellController:
def __init__(self, parent):
self._sessions = {}
self._currentSessionId = None
self._requests = {}
self._currentRequestId = None
self._consoleController = self.ConsoleController(self)
self._positionsController = self.PositionsController(self)
self._configurationController = self.ConfigurationController(self)
self._outputCompleteResponse = True
self._outputIsolator = None
self._removeSpaces = False
self._removeHtmlTags = False
self._virtualPersistence = False
self._tabCompletion = False
self._urlEncode = True
self._waf = False
def getMainComponent(self):
# tabsOriginal with request/response viewers
self._tabsMain = JTabbedPane()
# Add to the main tabs
self._tabsMain.addTab("Positions", self._positionsController.getMainComponent())
self._tabsMain.addTab("Sessions", self._consoleController.getMainComponent())
self._tabsMain.addTab("Configuration", self._configurationController.getMainComponent())
return self._tabsMain
def addRequest(self, iHttpRequestResponse):
#TODO Note sure yet if I want to store more than one request... It's just used to start a session
requestId = Utils.urlFrom(iHttpRequestResponse)
requestHttpService = Utils.httpServiceFrom(iHttpRequestResponse)
request = Utils.requestFrom(iHttpRequestResponse)
self._currentRequestId = requestId
self._requests[requestId] = {'data': request, 'httpService': requestHttpService,
'positionStart': None, 'positionEnd': None}
# TODO I'll need to delegate this a combobox or table, but for now, I'll select the first one
self._positionsController._buttonAdd.setEnabled(True)
self._consoleController.resetOutput()
self._positionsController.setEditor(request)
def sessionIds(self):
return self._sessions.keys()
def sessions(self):
return self._sessions
def requestIds(self):
return self._requests.keys()
def request(self, requestId):
return self.requests()[requestId]
def requests(self):
return self._requests
def currentRequest(self):
return self._requests[self._currentRequestId]
def currentRequestRaw(self):
"""Return the raw (bytes) of the request"""
return self._requests[self._currentRequestId]['data']
def setCurrentRequestBounds(self, positionStart, positionEnd):
self._requests[self._currentRequestId]['positionStart'] = positionStart
self._requests[self._currentRequestId]['positionEnd'] = positionEnd
def currentRequestId(self):
return self._currentRequestId
def setCurrentRequestId(self, requestId):
self._currentRequestId = requestId
def getRequestWithCommand(self, requestId, cmd):
Utils.out("getRequestWithCommand > self._outputIsolator")
Utils.out(self._outputIsolator)
if self._outputIsolator:
Utils.out("getRequestWithCommand > outputIsolator wanted > cmd before")
Utils.out(cmd)
cmd = "echo " + Utils.outputIsolator[0] + "; " + cmd + "; echo " + Utils.outputIsolator[1]
Utils.out("getRequestWithCommand > outputIsolator wanted > final command:")
Utils.out(cmd)
if self._urlEncode:
Utils.out("getRequestWithCommand > urlEncode wanted")
cmd = Utils.urlEncode(cmd)
Utils.out("getRequestWithCommand > self._waf")
Utils.out(self._waf)
if self._waf:
Utils.out("getRequestWithCommand > waf wanted > cmd before waf")
Utils.out(cmd)
cmd_waf = ''
for character in cmd:
if len(re.findall("\S", character)) > 0:
cmd_waf = cmd_waf + '\\' + character
else:
cmd_waf = cmd_waf + character
cmd = cmd_waf
Utils.out("getRequestWithCommand > waf wanted > final command:")
Utils.out(cmd)
Utils.out("ShellController > getRequestWithCommand")
original_request = self.requests()[requestId]
original_request_data = original_request['data']
original_request_service = original_request['httpService']
Utils.out("Utils.bytesToString(original_request_data)")
Utils.out(Utils.bytesToString(original_request_data))
modified_request = Utils.changeRawData(original_request_data, original_request['positionStart'], original_request['positionEnd'], cmd, original_request_service)
Utils.out("Utils.bytesToString(modified_request) ------")
Utils.out(Utils.bytesToString(modified_request))
Utils.out("Utils.bytesToString(modified_request) ---END---")
return modified_request
def getRequestHttpService(self, requestId):
return self.request(requestId)['httpService']
class ConsoleController:
def __init__(self, parent):
self._parent = parent
self._sessions = self._parent.sessions()
self._request = None #TODO I'll need a request in order to connect to something
self._position = None #TODO I'll need a position, something to change in the header to insert the command
self._pwd = None
self._commandHistory = []
self._historyIndex = 0
self._tabComplete = []
def getMainComponent(self):
self._mainPanel = JPanel(BorderLayout())
# input
self._consolePwd = JTextField()
self._consolePwd.setEditable(False)
self._consolePwd.setText("Not initialized")
self._consoleInput = JTextField()
#Remove 'tab' low-level tab-function of jumping to other component, so I can use it
self._consoleInput.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.EMPTY_SET)
self._consoleInput.addActionListener(self.EnterPress())
self._consoleInput.addKeyListener(self.KeyPress())
self._inputPanel = JPanel(BorderLayout())
self._inputPanel.add(self._consolePwd, BorderLayout.WEST)
self._inputPanel.add(self._consoleInput, BorderLayout.CENTER)
# output
self._consoleOutput = JTextArea()
self._consoleOutput.setEditable(False)
self._consoleOutput.setForeground(Color.WHITE)
self._consoleOutput.setBackground(Color.BLACK)
self._consoleOutput.setFont(self._consoleOutput.getFont().deriveFont(12.0))
self._scrollPaneConsoleOutput = JScrollPane(self._consoleOutput)
# Add to main panel and return the main panel
self._mainPanel.add(self._scrollPaneConsoleOutput, BorderLayout.CENTER)
self._mainPanel.add(self._inputPanel, BorderLayout.SOUTH)
return self._mainPanel
def sendCommand(self, requestId, cmd, directTo):
Utils.out("ConsoleController > sendCommand > 'cmd'")
Utils.out(cmd)
if cmd == 'clear':
self.resetOutput()
self._commandHistory.append(cmd)
self.resetHistoryIndex()
self.clearCmd()
return
cmdModified = cmd
requestHttpMethod = self._parent.getRequestHttpService(requestId)
#If I use virtual persistence and there's already a pwd set
if Utils.shellController._virtualPersistence and self.pwd():
#Then always prepend 'cd <pwd>' to any command executed. In reality we
# always enter in the same directory, but because this shell keeps track
# of where the user thinks he is, and always goes to that directory first
# the illusion of a persistence is created
cmdVirtual = "cd " + self.pwd()
cmdModified = cmdVirtual + "; " + cmd
requestWithCommand = self._parent.getRequestWithCommand(requestId, cmdModified)
Thread(GetThreadForRequest(requestHttpMethod, requestWithCommand, directTo)).start()
self._commandHistory.append(cmd)
self.resetHistoryIndex()
self.clearCmd()
if Utils.shellController._virtualPersistence:
if cmd.startswith('cd '):
Utils.out("ConsoleController > sendCommand: detected 'cd '")
#ask for pwd
cmdPwd = cmdModified + "; " + Commands.pwd(Commands.OS_LINUX)
requestWithCommand = self._parent.getRequestWithCommand(requestId, cmdPwd)
Thread(GetThreadForRequest(requestHttpMethod, requestWithCommand, 'pwd')).start()
if Utils.shellController._tabCompletion:
#ask 'ls -1a' for tab-completion
# The first command, pwd is set here, but cmdVirtual ain't. But this
# also means we are at the entry directory anyway, so we can just ask ls
# and get the correct tab completion anyway
try:
cmdTabComplete = cmdVirtual + "; " + Commands.ls(Commands.OS_LINUX)
except:
cmdTabComplete = Commands.ls(Commands.OS_LINUX)
requestWithCommand = self._parent.getRequestWithCommand(requestId, cmdTabComplete)
Thread(GetThreadForRequest(requestHttpMethod, requestWithCommand, 'tabComplete')).start()
else:
if Utils.shellController._tabCompletion:
cmdTabComplete = Commands.ls(Commands.OS_LINUX)
requestWithCommand = self._parent.getRequestWithCommand(requestId, cmdTabComplete)
Thread(GetThreadForRequest(requestHttpMethod, requestWithCommand, 'tabComplete')).start()
#either way execute the requested command
def startSession(self):
#TODO when starting a session I want to test for a number of things:
# if I can reform the request to a post request and still have it work
# if base 64 is available
# if bash is available
self.setPwd(None)
if Utils.shellController._virtualPersistence and Utils.shellController._outputIsolator:
Utils.out("startSession > virtualPersistence enabled > Requesting pwd")
self.sendCommand(self._parent.currentRequestId(), Commands.pwd(Commands.OS_LINUX), 'pwd')
def appendOutput(self, text, printCommand=True):
try:
if printCommand:
self.printCommand(self._commandHistory[-1])
except:
pass
self._consoleOutput.append("\n" + text)
#auto scroll down if needed
self._consoleOutput.setCaretPosition(self._consoleOutput.getDocument().getLength())
def resetOutput(self):
Utils.setConsole('')
def printCommand(self, cmd):
self._consoleOutput.append("\n" + self._pwd + "# " + cmd)
def printCurrentCommand(self):
self.printCommand(self.cmd())
def setPwd(self, pwd):
self._pwd = pwd
if pwd is None:
self._consolePwd.setText('')
else:
self._consolePwd.setText(pwd)
Utils.consoleController._mainPanel.revalidate()
def pwd(self):
return self._pwd
def cmdHistoryCount(self):
return len(self._commandHistory) #TODO - 1
def setCmd(self, cmd):
self._consoleInput.setText(cmd)
def cmd (self):
return self._consoleInput.getText()
def clearCmd(self):
self._consoleInput.setText('')
def resetHistoryIndex(self):
self._historyIndex = self.cmdHistoryCount()
def previousCommand(self):
if self._historyIndex > 0:
self._historyIndex -= 1
self.setCmd(self._commandHistory[self._historyIndex])
def nextCommand(self):
if self._historyIndex < self.cmdHistoryCount():
self._historyIndex += 1
self.setCmd(self._commandHistory[self._historyIndex])
else:
self.clearCmd()
self.resetHistoryIndex()
def setTabComplete(self, text):
self._tabComplete = text.splitlines()
def findTabComplete(self, beginCharacters=''):
suggestions = []
if beginCharacters:
for suggestion in self._tabComplete:
Utils.debug("suggestion", suggestion)
Utils.debug("text", beginCharacters)
if suggestion[0:len(beginCharacters)] == beginCharacters:
suggestions.append(suggestion)
else:
suggestions = self._tabComplete
return suggestions
def tabComplete(self):
currentCommand = self.cmd()
Utils.debug("currentCommand", currentCommand)
if currentCommand:
commandArray = currentCommand.split(' ')
lastword = commandArray.pop()
Utils.debug("lastword", lastword)
suggestions = self.findTabComplete(lastword)
if suggestions:
if len(suggestions) > 1:
self.printCurrentCommand()
for suggestion in suggestions:
self.appendOutput(suggestion, False)
if len(suggestions) == 1:
self.setCmd(' '.join(commandArray) + ' ' + suggestions.pop())
else:
suggestions = self.findTabComplete()
if len(suggestions) > 1:
self.printCurrentCommand()
for suggestion in suggestions:
self.appendOutput(suggestion, False)
class EnterPress(ActionListener): #TODO remove: AbstractAction
def actionPerformed(self, e):
Utils.consoleController.sendCommand(Utils.shellController.currentRequestId(), Utils.consoleInput.getText(), 'console')
def keyPressed(self, e):
Utils.out("key pressed")
class KeyPress(KeyListener):
def keyTyped(self, e):
pass
def keyReleased(self, e):
if e.getKeyCode() == e.VK_DOWN:
Utils.consoleController.nextCommand()
Utils.out("released down")
if e.getKeyCode() == e.VK_UP:
Utils.consoleController.previousCommand()
Utils.out("released up")
if e.getKeyCode() == e.VK_TAB:
Utils.out("pressed tab")
Utils.consoleController.tabComplete()
def keyPressed(self, e):
pass
class PositionsController(IMessageEditorController):
def __init__(self, parent):
self._parent = parent
def getMainComponent(self):
self._mainPanel = JPanel(BorderLayout())
#Left panel
self._leftPanel = JPanel(BorderLayout())
self._leftPanel.setBorder(EmptyBorder(10, 10, 10, 10))
#Left subpanel - Positions editor
self._positionsEditor = Utils.callbacks.createTextEditor()
#TODO Remove a normal editor? self._positionsEditor = Utils.callbacks.createMessageEditor(self, True)
self._positionsEditor.getComponent().setBorder(BorderFactory.createLineBorder(Color.BLACK))
#Left subpanel - Title pane
self._leftTitlePanel = JPanel(GridLayout(0, 1))
self._leftTitlePanel.setBorder(EmptyBorder(0, 10, 10, 10))
self._titleText = JLabel("Commands Position")
self._titleText.setForeground(COLOR_BURP_TITLE_ORANGE)
self._titleText.setFont(self._titleText.getFont().deriveFont(16.0))
self._titleSubtitleText = JTextArea("Configure the position where commands will be inserted into the base request. Select the requests that were send Shell in the dropdown, then select the part of the request where commands need to be inserted and click the 'Add $' button.")
self._titleSubtitleText.setEditable(False)
self._titleSubtitleText.setLineWrap(True)
self._titleSubtitleText.setWrapStyleWord(True)
self._titleSubtitleText.setHighlighter(None)
self._titleSubtitleText.setBorder(None)
self._leftTitlePanel.add(self._titleText)
self._leftTitlePanel.add(self._titleSubtitleText)
#Left subpanel - Add positions editor and title
self._leftPanel.add(self._leftTitlePanel, BorderLayout.NORTH)
self._leftPanel.add(self._positionsEditor.getComponent(), BorderLayout.CENTER)
#Right panel
#self._rightPanel = JPanel(GridLayout(20, 1))
self._rightPanel = JPanel()
self._rightPanel.setLayout(BoxLayout(self._rightPanel, BoxLayout.Y_AXIS))
#self._rightPanel.setPreferredSize(Dimension(150, 30))
self._rightPanel.setBorder(EmptyBorder(10, 10, 10, 10))
#Right panel - buttons
self._buttonAdd = JButton(" Add $ ", actionPerformed=self.buttonAddClick)
self._buttonClear = JButton(" Clear $ ", actionPerformed=self.buttonClearClick) #, actionPerformed=None
# Right panel - add components
self._rightPanel.add(self._buttonAdd)
self._rightPanel.add(self._buttonClear)
self._mainPanel.add(self._rightPanel, BorderLayout.EAST)
self._mainPanel.add(self._leftPanel, BorderLayout.CENTER)
return self._mainPanel
def buttonAddClick(self, e):
Utils.out("Button Add click")
if self._positionsEditor.getSelectedText():
#TODO: For if it's a messageeditor in stead of texteditor Utils.out(self._positionsEditor.getSelectedData())
self.addPosition(self._positionsEditor.getSelectionBounds())
self._parent._consoleController.startSession()
self._buttonAdd.setEnabled(False)
def buttonClearClick(self, e):
Utils.out("Button Clear click")
self.setEditor(self._parent.currentRequestRaw())
self._buttonAdd.setEnabled(True)
def setEditor(self, request_in_bytes):
self._positionsEditor.setText(request_in_bytes)
#TODO: for if I make the positions a messageExitor: self._positionsEditor.setMessage(iHttpRequestResponse.getRequest(), True)
def addPosition(self, boundsArray):
if len(boundsArray) == 2:
self._parent.setCurrentRequestBounds(boundsArray[0], boundsArray[1])
modified_request = Utils.wrapRawData(self._parent.currentRequestRaw(), boundsArray[0], boundsArray[1], "$", "$")
self.setEditor(modified_request)
class ConfigurationController(IMessageEditorController):
def __init__(self, parent):
self._parent = parent
def getMainComponent(self):
self._mainPanel = JPanel()
#self._mainPanel.setLayout(BoxLayout(self._mainPanel, BoxLayout.Y_AXIS))
self._mainPanel.setLayout(BorderLayout())
self._titlePanel = JPanel(GridLayout(0, 1))
self._titlePanel.setBorder(EmptyBorder(10, 20, 10, 10))
self._titleText = JLabel("Mode configuration")
self._titleText.setForeground(COLOR_BURP_TITLE_ORANGE)
self._titleText.setFont(self._titleText.getFont().deriveFont(16.0))
self._titleSubtitleText = JTextArea("Select a mode that works best for the type of exploit you are dealing with.")
self._titleSubtitleText.setEditable(False)
self._titleSubtitleText.setLineWrap(True)
self._titleSubtitleText.setWrapStyleWord(True)
self._titleSubtitleText.setHighlighter(None)
self._titleSubtitleText.setBorder(None)
#Local File Inclusion (LFI): Explore files
#Command injection (visual): Shell
#Command injection (blind): Shell
modes = ['Select mode...', 'Local File Inclusion (LFI) - Discover files', 'OS Command injection (visual) - Shell']
self._cboModes = JComboBox(modes)
self._cboModes.addActionListener(self.cboModesChanged())
self._titlePanel.add(self._titleText)
self._titlePanel.add(self._titleSubtitleText)
self._titlePanel.add(self._cboModes)
self._mainPanel.add(self._titlePanel, BorderLayout.NORTH)
self._switchPanel = JPanel()
self._switchPanel.setLayout(BoxLayout(self._switchPanel, BoxLayout.Y_AXIS))
self._switchPanel.setBorder(EmptyBorder(5, 30, 10, 10))
self._outputCompleteResponseSwitch = JCheckBox("Use full response (not only the response body)")
self._outputCompleteResponseSwitch.setSelected(True)
self._outputCompleteResponseSwitch.addItemListener(self.OutputCompleteResponseSwitchListener())
self._switchPanel.add(self._outputCompleteResponseSwitch)
self._urlEncodeSwitch = JCheckBox("Use url encode")
self._urlEncodeSwitch.setSelected(True)
self._urlEncodeSwitch.addItemListener(self.UrlEncodeSwitchListener())
self._switchPanel.add(self._urlEncodeSwitch)
self._outputIsolatorSwitch = JCheckBox("Use output isolator")
self._outputIsolatorSwitch.setSelected(False)
self._outputIsolatorSwitch.addItemListener(self.OutputIsolatorSwitchListener())
self._switchPanel.add(self._outputIsolatorSwitch)
self._removeSpacesSwitch = JCheckBox("Remove leading and trailing spaces")
self._removeSpacesSwitch.setSelected(False)
self._removeSpacesSwitch.addItemListener(self.SpacesSwitch())
self._switchPanel.add(self._removeSpacesSwitch)
self._removeHtmlTagsSwitch = JCheckBox("Remove html-tags (regex '<.*?>')")
self._removeHtmlTagsSwitch.setSelected(False)
self._removeHtmlTagsSwitch.addItemListener(self.HtmlTagsSwitch())
self._switchPanel.add(self._removeHtmlTagsSwitch)
self._tabCompletionSwitch = JCheckBox("Use tab-completion")
self._tabCompletionSwitch.setSelected(False)
self._tabCompletionSwitch.addItemListener(self.TabCompletionSwitchListener())
self._switchPanel.add(self._tabCompletionSwitch)
self._virtualPersistenceSwitch = JCheckBox("Use virtual persistence")
self._virtualPersistenceSwitch.setSelected(False)
self._virtualPersistenceSwitch.addItemListener(self.VirtualPersistenceSwitchListener())
self._switchPanel.add(self._virtualPersistenceSwitch)
self._wafSwitch = JCheckBox("WAF: Prepend each non-whitespace character (regex '\\w') with '\\'")
self._wafSwitch.setSelected(False)
self._wafSwitch.addItemListener(self.WafSwitch())
self._switchPanel.add(self._wafSwitch)
self._mainPanel.add(self._switchPanel, BorderLayout.CENTER)
return self._mainPanel
class cboModesChanged(ActionListener):
def actionPerformed(self, e):
cboModes = e.getSource()
mode = cboModes.getSelectedItem()
if mode == 'Local File Inclusion (LFI) - Discover files':
#Disable output isolator
Utils.shellController._outputIsolator = None
Utils.outputIsolator = None
Utils._outputIsolator = None
Utils.configurationController._outputIsolatorSwitch.setSelected(False)
Utils.configurationController._outputIsolatorSwitch.setEnabled(False)
#Disable remove leading and trailing spaces
Utils.shellController._removeSpaces = False
Utils.configurationController._removeSpacesSwitch.setSelected(False)
Utils.configurationController._removeSpacesSwitch.setEnabled(False)
#Disable remove html tags
Utils.shellController._removeHtmlTags = False
Utils.configurationController._removeHtmlTagsSwitch.setSelected(False)
Utils.configurationController._removeHtmlTagsSwitch.setEnabled(False)
#Disable tab-completion
Utils.shellController._tabCompletion = False
Utils.configurationController._tabCompletionSwitch.setSelected(False)
Utils.configurationController._tabCompletionSwitch.setEnabled(False)
#Disable virtual peristence
Utils.shellController._virtualPersistence = False
Utils.configurationController._virtualPersistenceSwitch.setSelected(False)
Utils.configurationController._virtualPersistenceSwitch.setEnabled(False)
#Disable WAF
Utils.shellController._waf = False
Utils.configurationController._wafSwitch.setSelected(False)
Utils.configurationController._wafSwitch.setEnabled(False)
if mode == 'OS Command injection (visual) - Shell' or mode == 'Select mode...':
Utils.configurationController._outputIsolatorSwitch.setEnabled(True)
Utils.configurationController._removeSpacesSwitch.setEnabled(True)
Utils.configurationController._removeHtmlTagsSwitch.setEnabled(True)
Utils.configurationController._tabCompletionSwitch.setEnabled(True)
Utils.configurationController._virtualPersistenceSwitch.setEnabled(True)
Utils.configurationController._wafSwitch.setEnabled(True)
class OutputIsolatorSwitchListener(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.out("selected")
isolator = ["3535start3535", "3535end3535"]
Utils.shellController._outputIsolator = isolator
Utils.outputIsolator = isolator
Utils._outputIsolator = isolator
elif e.getStateChange() == ItemEvent.DESELECTED:
#TODO If deselected, Virtual persistence should be disabled: unless the body only returns
# the command and nothing else, it's near impossible to warrant virtual persistence.
# One possibility I have, is to also allow to define positions in the response tab and filter
# out the command response like that (without using the outputisolators)
Utils.out("deselected")
Utils.shellController._outputIsolator = None
Utils.outputIsolator = None
Utils._outputIsolator = None
class SpacesSwitch(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._removeSpaces = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._removeSpaces = False
class HtmlTagsSwitch(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._removeHtmlTags = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._removeHtmlTags = False
class TabCompletionSwitchListener(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._tabCompletion = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._tabCompletion = False
class VirtualPersistenceSwitchListener(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._virtualPersistence = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.consoleController.setPwd(None)
Utils.shellController._virtualPersistence = False
class UrlEncodeSwitchListener(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._urlEncode = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._urlEncode = False
class OutputCompleteResponseSwitchListener(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._outputCompleteResponse = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._outputCompleteResponse = False
class WafSwitch(ItemListener):
def itemStateChanged(self, e):
if e.getStateChange() == ItemEvent.SELECTED:
Utils.shellController._waf = True
elif e.getStateChange() == ItemEvent.DESELECTED:
Utils.shellController._waf = False
class GetThreadForRequest(Runnable):
def __init__(self, requestHttpMethod, requestWithCommand, directTo):
self._requestHttpMethod = requestHttpMethod
self._requestWithCommand = requestWithCommand
self._directTo = directTo
def run(self):
iHttpRequestResponse = Utils.callbacks.makeHttpRequest(self._requestHttpMethod, self._requestWithCommand)
Utils.out("GetThreadForRequest > iHttpRequestResponse")
Utils.out(iHttpRequestResponse)
if Utils.shellController._outputCompleteResponse:
output = Utils.bytesToString(Utils.responseFrom(iHttpRequestResponse))
else:
output = Utils.responseBodyFrom(iHttpRequestResponse)
Utils.out(output)
Utils.out("done")
Utils.out("Complete response:")
Utils.out(Utils.bytesToString(Utils.responseFrom(iHttpRequestResponse)))
if Utils.outputIsolator:
Utils.out("Body:")
Utils.out(Utils.responseBodyFrom(iHttpRequestResponse))
Utils.out("outputIsolator detected...")
output = Utils.searchBetweenAndExtract(output, Utils.outputIsolator[0], Utils.outputIsolator[1])
Utils.out("Body after outputIsolator strip:")
Utils.out(output)
if isinstance(self._directTo, list):
for output in self._directTo:
self.sendTo(output, output)
if isinstance(self._directTo, basestring):
self.sendTo(self._directTo, output)
def sendTo(self, output, text):
if Utils.shellController._removeHtmlTags:
text_modified = re.sub('<.*?>', '', text)
else:
text_modified = text
if Utils.shellController._removeSpaces:
text_modified.strip()
text_modified = re.sub('^\s*', '', text_modified, flags=re.MULTILINE)
text_modified = re.sub('\s*$', '', text_modified, flags=re.MULTILINE)
if output == 'pwd':
Utils.setPwd(text_modified)
if output == 'tabComplete':
Utils.setTabComplete(text_modified)
if output == 'console':
Utils.appendOutput(text_modified)
class Utils:
@classmethod
def setParent(cls, parent):
cls._parent = parent
cls.parent = cls._parent
cls._helpers = parent._helpers
cls.helpers = cls._helpers
cls._callbacks = cls._parent._callbacks
cls.callbacks = cls._callbacks
cls._stdout = PrintWriter(cls._callbacks.getStdout(), True)
cls._stderr = PrintWriter(cls._callbacks.getStderr(), True)
cls._shellController = cls._parent._shellController
cls.shellController = cls._shellController
cls._configurationController = cls.shellController._configurationController
cls.configurationController = cls._configurationController
cls._outputIsolator = cls._shellController._outputIsolator
cls.outputIsolator = cls._outputIsolator
cls._consoleController = cls._shellController._consoleController
cls.consoleController = cls._consoleController
@classmethod
def registerComponents(cls):
cls._consoleInput = cls._consoleController._consoleInput
cls.consoleInput = cls._consoleInput
cls._consoleOutput = cls._consoleController._consoleOutput
cls.consoleOutput = cls._consoleOutput
cls._outputIsolatorSwitch = cls.configurationController._outputIsolatorSwitch
cls.outputIsolatorSwitch = cls._outputIsolatorSwitch
@classmethod
def setPwd(cls, text):
"""In the console, set the 'pwd' field to this text"""
cls.consoleController.setPwd(text)
@classmethod
def pwd(cls):
"""In the console, set the 'pwd' field to this text"""
return cls.consoleController.pwd()
@classmethod
def setConsole(cls, text):
"""In the console, set the 'output' pane to this text"""
cls._consoleOutput.setText(text)
@classmethod
def setTabComplete(cls, text):
"""In the console, set the 'output' pane to this text"""
cls.consoleController.setTabComplete(text)
@classmethod
def appendOutput(cls, text):
"""In the console, append this text to the 'output' pane"""
cls.consoleController.appendOutput(text)
"""Everything specific to this program above this separator"""
"""Everything general below this"""
@classmethod
def out(cls, message):
cls._stdout.println(message)
@classmethod
def debug(cls, title, variable):
cls.out(title)
cls.out("value:")
cls.out(variable)
cls.out(type(variable))
cls.out("methods:")
cls.out(dir(variable))
@classmethod
def err(cls, message):
cls._stderr.println(message)
@classmethod
def bytesToString(cls, bytes):
return cls._helpers.bytesToString(bytes)
@classmethod
def stringToBytes(cls, string):
return cls._helpers.stringToBytes(string)
@classmethod
def urlEncode(cls, text):
return cls._helpers.urlEncode(text)
@classmethod
def urlDecode(cls, data):
return cls._helpers.urlDecode(data)
@classmethod
def urlFrom(cls, iHttpRequestResponse):
return cls._helpers.analyzeRequest(iHttpRequestResponse).getUrl()
@classmethod
def methodFrom(cls, iHttpRequestResponse):
return cls._helpers.analyzeRequest(iHttpRequestResponse).getMethod()
@classmethod
def requestBodyFrom(cls, iHttpRequestResponse):
body_offset = cls._helpers.analyzeRequest(iHttpRequestResponse).getBodyOffset()
return cls._helpers.bytesToString(iHttpRequestResponse.getRequest()[body_offset:])
@classmethod
def requestBodyRawFrom(cls, iHttpRequestResponse):
body_offset = cls._helpers.analyzeRequest(iHttpRequestResponse).getBodyOffset()
return iHttpRequestResponse.getRequest()[body_offset:]
@classmethod
def responseBodyFrom(cls, iHttpRequestResponse):
body_offset = cls._helpers.analyzeResponse(iHttpRequestResponse.getResponse()).getBodyOffset()
return cls._helpers.bytesToString(iHttpRequestResponse.getResponse()[body_offset:])
@classmethod
def responseBodyRawFrom(cls, iHttpRequestResponse):
body_offset = cls._helpers.analyzeResponse(iHttpRequestResponse.getResponse()).getBodyOffset()
return iHttpRequestResponse.getResponse()[body_offset:]
@classmethod
def httpServiceFrom(cls, iHttpRequestResponse):
return iHttpRequestResponse.getHttpService()
@classmethod
def requestFrom(cls, iHttpRequestResponse):
return iHttpRequestResponse.getRequest()
@classmethod
def responseFrom(cls, iHttpRequestResponse):
return iHttpRequestResponse.getResponse()
@classmethod
def changeRawData(cls, rawData, start, end, newContent, service=None):
"""In raw bytes: replace everything that's between the 'start' and 'end' position
with the new content"""
modified_raw_data = rawData[0:start]
modified_raw_data = modified_raw_data + Utils.stringToBytes(newContent)
modified_raw_data = modified_raw_data + rawData[end:]
info = Utils.helpers.analyzeRequest(service, modified_raw_data)
body_offset = info.getBodyOffset()
body = Utils.bytesToString(modified_raw_data[body_offset:])
body_length = len(body)
headers = info.getHeaders()
headers_modified = []
for header in headers:
if "content-length:" in header.lower():
header = "Content-Length: " + str(body_length)
headers_modified.append(header)
modified_raw_data = Utils.helpers.buildHttpMessage(headers_modified, modified_raw_data)
return modified_raw_data
@classmethod
def wrapRawData(cls, rawData, start, end, beforeContent, afterContent):
"""In raw bytes: the content between the 'start' and 'end' markers: content is
inserted before and after it. The rest of the message stays in tact"""
modified_raw_data = rawData[0:start]
modified_raw_data = modified_raw_data + Utils.stringToBytes(beforeContent)
modified_raw_data = modified_raw_data + rawData[start:end]
modified_raw_data = modified_raw_data + Utils.stringToBytes(afterContent)
modified_raw_data = modified_raw_data + rawData[end:]
return modified_raw_data
@classmethod
def searchBetweenAndExtract(cls, data, startDelimiter, endDelimiter):
"""In raw bytes: search for the position of the start- and end delimiter
and extract everything that's between that"""
#if input is string (basestring = types 'str' and 'unicode')
if isinstance(data, basestring):
startOffset = data.find(startDelimiter)
endOffset = data.find(endDelimiter)
if startOffset == -1 or endOffset == -1:
Utils.err("Utils > searchBetweenAndExtract - Something went wrong: I expected to find " + startDelimiter + " and " + endDelimiter + ". But at least one of them was missing")
else:
startOffset = startOffset + len(startDelimiter)
return data[startOffset:endOffset].strip()
##
## Creates the sendto tab in other areas of Burp
class OfferShell(IContextMenuFactory):
def createMenuItems(self, invocation):
options = []
if invocation != None and invocation.selectedMessages[0] != None:
menuItem = JMenuItem("Send to Shell - FWD/LFI")
menuItem.addActionListener(self.addRequest(invocation))
options.append(menuItem)
return options
def addRequest(self, invocation):
for iHttpRequestResponse in invocation.getSelectedMessages():
Utils.shellController.addRequest(iHttpRequestResponse)
#self.highlightTab()
class Commands:
OS_LINUX = 'Linux'
@classmethod
def pwd(cls, os):
"""@Return: The command that prints the current directory"""
if os == Commands.OS_LINUX:
return 'pwd'
@classmethod
def ls(cls, os):
"""@Return: The command that lists all directories and files (including hidden) in one continuous list (without any extra information)"""
if os == Commands.OS_LINUX:
return 'ls -1a'