The initial exploitation to web interface was exploited with a login bypass due to PHP Type Juggling Vulnerability.
The second stage exploitation was gained through cracking a zipped file via ZipCrypto decode method that gave us the ssh key to the machine.
The privilege escalation was possible because of clear-text root password in the machine that gave access to root user.
Initial Enumeration
Starting the enumeration with a nmap service scan.
root@kali ~/h/ransom# nmap -sC -sV -O 10.10.11.153
Starting Nmap 7.92 ( https://nmap.org ) at 2022-03-29 01:53 EDT
Nmap scan report for 10.10.11.153
Host is up (0.13s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-title: Admin - HTML5 Admin Template
|_Requested resource was http://10.10.11.153/login
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 41.94 seconds
The machines has two ports open - 22 and 80. Visiting port 80 webpage.
The web-page hosts a normal login page that requires password to login. Default passwords combination didn't worked. Some basic SQLI parameters also didn't worked.
Type random password and intercept the requests in the background using burp.
In the above request there is a cookie called laravel_session which indicates that the application running on Laravel Framework. Send this request to repeater.
Now tampering the request body to see how the application reacts. First remove the 'password' value.
This resulted an output in JSON format.
Now right click and select 'Change request method', this will change GET request to POST and vice versa. Then send the request.
From the response gave 405 method not allowed but if we change the remove the POST and replace it with GET.
The response gave a weird error. Now this comes down to trail and error method where we try to tamper the request body to check the behavior of the response that an application gives. After some trial and error session if we remove this header
X-Requested-With: XMLHttpRequest
This gave a unique re-directing error back to the login page. Again if we change the request method to JSON format.
Got a 200 Ok Response that gave normal 'Invalid Password'. Now we can move towards exploitation phase.
Initial Exploitation
Looking at the parameters and requests that we tampered and experimented with indicates the possibility of PHP Type Juggling vulnerability. This means that variable types are checked while the program is executing. Dynamic typing allows developers to be more flexible when using PHP. But this kind of flexibility sometimes causes unexpected errors in the program flow and can even introduce critical vulnerabilities into the application. In short, if PHP has loose comparison i.e '==' then it get confused and treats different elements as same. For example, it can treat integer as string.
Note - In the end of the blog I've explained PHP Type Juggling in detail and also showed the vulnerable code responsible for it. If you want to read more please refer the Source Code Analysis section
The basic login bypass of PHP Type Juggling is to present the value as boolean.
Changing the password parameter value to boolean true successfully bypassed the login. Now to bypass login on the fly, first type random password on the login field and intercept the request in burp.
Now we can do two things, either make changes as we did in the repeater tab or copy-paste the entire request body. We can prefer the second method.
Now right click then Do intercept -> Response to this request. Then click on forward.
The login is bypassed successfully. Forward the request. Check the browser.
After successful bypass of login. We are presented with two files. The second one is the user.txt
Now download the second folder.
While unzipping the file. We are presented with a password prompt.
There is a ssh id_rsa key that can give us ssh access to the machine. Listing the metadata of the zip file.
root@kali ~/h/ransom# 7z l -slt uploaded-file-3422.zip
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Pentium(R) CPU G4560 @ 3.50GHz (906E9),ASM,AES-NI)
Scanning the drive for archives:
1 file, 7735 bytes (8 KiB)
Listing archive: uploaded-file-3422.zip
--
Path = uploaded-file-3422.zip
Type = zip
Physical Size = 7735
----------
Path = .bash_logout
Folder = -
Size = 220
Packed Size = 170
Modified = 2020-02-25 08:03:22
Created =
Accessed =
Attributes = _ -rw-r--r--
Encrypted = +
Comment =
CRC = 6CE3189B
Method = ZipCrypto Deflate
Host OS = Unix
Version = 20
Volume Index = 0
The method that was used to encrypt the file → ZipCrypto Deflate that is vulnerable to plain text attack.
The Method is ZipCrypto, which is the less secure algorithm. The CRC32 from the Zip output above is 6CE3189B. That is a CRC of the decrypted file, used after decryption to verify the correct file resulted.
Second Phase Exploitation
Refer the article below to understand the steps in details.
Of the files in the archive, .bash_logout seems like a good candidate. user.txt could work, but with HTB flag rotation, it actually won’t. .bash_logout is a file that is not commonly changed. The one in the zip is 220 bytes. If you do not have a .bash_logout file or is not the proper size of 220 bytes, then download it from the github repo link.
htb@ransom:/srv/prod$ ls -al
total 336
drwxr-xr-x 1 www-data www-data 446 Feb 17 22:54 .
drwxr-xr-x 1 root root 8 Mar 7 12:15 ..
-rw-r--r-- 1 www-data www-data 258 Feb 17 22:54 .editorconfig
-rw-r--r-- 1 www-data www-data 955 Feb 17 22:54 .env
-rw-r--r-- 1 www-data www-data 899 Feb 17 22:54 .env.example
drwxr-xr-x 1 www-data www-data 144 Feb 17 22:54 .git
-rw-r--r-- 1 www-data www-data 152 Feb 17 22:54 .gitattributes
-rw-r--r-- 1 www-data www-data 207 Feb 17 22:54 .gitignore
-rw-r--r-- 1 www-data www-data 194 Feb 17 22:54 .styleci.yml
-rw-r--r-- 1 www-data www-data 3958 Feb 17 22:54 README.md
drwxr-xr-x 1 www-data www-data 72 Feb 17 22:54 app
-rwxr-xr-x 1 www-data www-data 1686 Feb 17 22:54 artisan
drwxr-xr-x 1 www-data www-data 24 Feb 17 22:54 bootstrap
-rw-r--r-- 1 www-data www-data 1745 Feb 17 22:54 composer.json
-rw-r--r-- 1 www-data www-data 289854 Feb 17 22:54 composer.lock
drwxr-xr-x 1 www-data www-data 312 Feb 17 22:54 config
drwxr-xr-x 1 www-data www-data 72 Feb 17 22:54 database
-rw-r--r-- 1 www-data www-data 473 Feb 17 22:54 package.json
-rw-r--r-- 1 www-data www-data 1202 Feb 17 22:54 phpunit.xml
drwxr-xr-x 1 www-data www-data 166 Mar 15 19:16 public
drwxr-xr-x 1 www-data www-data 28 Feb 17 22:54 resources
drwxr-xr-x 1 www-data www-data 74 Mar 7 12:25 routes
-rw-r--r-- 1 www-data www-data 563 Feb 17 22:54 server.php
drwxr-xr-x 1 www-data www-data 32 Feb 17 22:54 storage
drwxr-xr-x 1 www-data www-data 90 Feb 17 22:54 tests
drwxr-xr-x 1 www-data www-data 642 Feb 17 22:54 vendor
-rw-r--r-- 1 www-data www-data 559 Feb 17 22:54 webpack.mix.js
This is the laravel webserver files. We noted during enumeration that there was a laravel_sessioncookie set on visiting the page. There are further clues to show that is the framework in use on this site.
We can find the hardcoded credentials for root user inside one of these files. The simple trick we can use is to utilize the grep command utility.
The above command grep the string "Invalid Password" because if we remember whenever we put wrong password on the login page it displays this string. The location of the credentials might be inside AuthControllers.php file. Checking the php file.
To better understand how we exploited PHP Type Juggling to bypass the login. First we need to understand comparison inside PHP. The PHP language consist of two types of comparisions.
Loose Comparison: "=="
Strict Comparison: "==="
In Loose Comparison if the PHP tries to compare two objects of different types such as one is string and another is integer while having the same value, the it will result in a 'True'.But in the Strict Comparison, both type and value of two objects must be equal otherwise it will result in a 'False'.Let's take a deeper look with and example. On the Linux type -> php -a to spawn a interactive PHP prompt.
First we will take a look at how Loose Comparison works.
In the above statement there are three examples of how loose comparison '==' works. The above code snippet compares two values and print "Success" if the conditions are met.
In the first example:
if ("1"==1) echo"Success";
The php compares uses loose comparison '=='. In which it compares the string "1" and the integer 1 that resulting in Success because it didn't checked if both objects has same values.
In the second example:
if ("1"==2) echo"Success";
The output didn't print anything(that means false) because even though it uses '==' the value of both the objects must be same.
In the third example:
if ("1"==true) echo"Success";
The PHP also gets confused on comparing anything with boolean as 'True'. The PHP Type Juggling we exploited for initial foothold followed this logic.
Now we take at how Strict Comparison works.
Now the code above is using strict comparison '==='.
In the first example:
if (1===1) echo"Success";if ("1"==="1") echo"Success"
It compared the type and value of the objects. Both were same so we got a "Success".
In the second example:
if ("Hi"==="Hey") echo"Success";if (1===2) echo"Success";
Even though both the objects were string. It did compare the values.
In the third example:
if (1===true) echo"Success";if ("1"===true) echo"Success";
By using the strict comparison. It doesn't get confused when we compare an object with a boolean value.
As we've seen that the loose comparison '==' is very dangerous that can lead to authentication bypass or RCE vulnerability. It is highly advised that we should always use '===' instead of '=='.
In the below is the PHP code snippet from the vulnerable machine that gave us the authentication bypass via Type Juggling.
In the if statement we can see the comparison between the hard-coded credentials that is a string and the value of the 'password' parameter using the loose '==' comparison. In the burp request we've use boolean to bypass this comparison.
"password":true
The comparison result will be true and we will get access. We can see the behind the scenes of the functions using the php -a on terminal.
This could've not worked if try to use random integer or string.
In this we've used random integer and strings to test. This doesn't result in success because at least values of the two objects must be same. So in this case, random password bruteforce could not have worked. So when there is a login functionality we can try to bypass it with boolean based Type Juggling.
We got the clear image of what happened on back-end when we bypassed it.