> For the complete documentation index, see [llms.txt](https://writeups.adityadindi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://writeups.adityadindi.com/crackmes.one/1-difficulty-rating/random.md).

# Random

## Challenge

* So first thing to do is to check how the login is working on the web browser

<figure><img src="/files/cT0CqkHhhaP2ieQVzSfu" alt=""><figcaption></figcaption></figure>

* We are sending a HTTP request with JSON data of username and password, now lets look at the code base

<figure><img src="/files/AOn1FYkmrhuLDyQYQdyU" alt=""><figcaption></figcaption></figure>

* In red we are checking if the username and password fields are set, then we pass the username into the `get_user` function, which we check out soon
* In blue, we see a type juggling vulnerability, with the `==`, we can try and set both values equal to each other, for the password, this can be achieved by setting it as an array which will be `null`, so now we have to set the `$user` variable to `null` as well. Lets look at the `get_user` function.

<figure><img src="/files/vbZtJRce5vGNLvaFuIqc" alt=""><figcaption></figcaption></figure>

* So we're basically checking if the username we provided is within in the database, if not, it will return `null`, that is good to know as we want to be able to return the username value as `null` . Next lets check how the username is being checked in `profile.php`

<figure><img src="/files/APdSyO3Q4w1fWOr8gZFL" alt=""><figcaption></figcaption></figure>

* So it looks like we are using the `strpos` function to check if admin is in the username, and if not, return false, if yes, then run the function to get the flag

<figure><img src="/files/K2O0AYRar41r1j8LDdqJ" alt=""><figcaption></figcaption></figure>

* Based on this, it is looking for the string `admin` within the username we provide. This is flawed as we can pass in something like `1234admin` and that would work.&#x20;
* So we know that we have to set the value of username to have admin in the string and also pass in a username that is not valid so that we can set the value of username to `null`&#x20;
* We can achieve this by setting the username to `1234admin` and also set the password to `null` by making it an array

<figure><img src="/files/wMjrIMndFxj51qnJu06m" alt=""><figcaption></figcaption></figure>

* And this should get you admin access and also the flag!

## Notes

Now that we have discussed what type juggling is and under which conditions it is performed, let us explore how it can lead to an unexpected outcome of a comparison that can result in an authentication bypass.

***

### Background

Before analyzing our sample web application, let us establish how type juggling can lead to an authentication bypass in PHP.

**Strcmp Bypass**

As a first simple example, let us consider the following code snippet:

Code: php

```php
$admin_pw = "P@ssw0rd!";

if(isset($_POST['pw'])){
    if(strcmp($_POST['pw'], $admin_pw) == 0){
        // successfully authenticated
        <SNIP>
    } else {
        // invalid credentials
        <SNIP>
    }
}
```

The function [strcmp](https://www.php.net/manual/en/function.strcmp.php) returns `0` if the two compared strings are equal. How can we bypass this authentication check without knowing the admin password? If we supply a variable of the data type `array`, the function `strcmp` returns `null`, resulting in the comparison `null == 0`, which is `true` after type juggling. We can send an array as a POST variable by sending a request like this:

Code: http

```http
POST / HTTP/1.1
Host: typejuggling.htb
Content-Type: application/x-www-form-urlencoded
Content-Length: 8

pw[]=pwn
```

Note: The behavior of `strcmp` was changed in PHP 8.0.0 to throw an error if any argument is not a string. Thus, the bypass only works in PHP versions prior to 8.0.0.

**Magic Hashes**

In a more realistic scenario, the password is hashed before the comparison, resulting in the comparison of two variables of the data type `string`. Consider the following code snippet:

Code: php

```php
$hashed_password = '0e66298694359207596086558843543959518835691168370379069085301337';

if(isset($_POST['pw']) and is_string($_POST['pw'])){ 
    if(hash('sha256', $_POST['pw']) == $hashed_password){
        // successfully authenticated
        <SNIP>
    } else {
        // invalid credentials
        <SNIP>
    }
}
```

Our provided password is hashed using SHA-256 and loosely compared to the hashed admin password. If we look at the correct password hash, we can see that it starts with a `0e` followed by only numbers. As discussed in the previous section, PHP will compare two strings numerically if both can be treated as numbers. Since the hashed password is of a valid number format (in this case, the scientific float notation is equal to `0`), we simply need to provide a password for which the hash follows the same format. These hash values are called `magic hashes`. Luckily for us, there are pre-compiled lists of values that result in such magic hashes, for example, [here](https://github.com/spaze/hashes) is a collection on GitHub. Selecting SHA-256, we can see that the password `34250003024812` results in the hash `0e46289032038065916139621039085883773413820991920706299695051332`, which follows the correct format for our bypass. PHP then compares the two hashes numerically, converting both strings to the number `0` and thus evaluating the comparison to `true` such that we successfully bypass authentication.

***

### Code Review - Identifying the Vulnerability

Now that we discussed how type juggling could lead to an authentication bypass, let us jump into our sample web application:

&#x20;  ![](https://academy.hackthebox.com/storage/modules/205/juggling/typejuggling_authbypass_1.png)

Logging in with the provided credentials for the user `htb-stdnt`, we can see that we are unauthorized to access the post-login page:

&#x20;  ![](https://academy.hackthebox.com/storage/modules/205/juggling/typejuggling_authbypass_2.png)

Looking at the network traffic, we can see that the web application sends our login data in JSON format, which is interesting for a PHP web application:

![image](https://academy.hackthebox.com/storage/modules/205/juggling/typejuggling_authbypass_3.png)

Let us investigate the login logic in the provided PHP code in `index.php`:

Code: php

```php
<?php
   require_once ('config.php');
   session_start();

   // parse json body
   $json = file_get_contents('php://input');
   $data = json_decode($json, true);

   // check login
   if(isset($data['username']) and isset($data['password'])){
    $user = get_user($data['username']);

    if($user) {
        // check password
        if ($data['password'] == $user['password']){
            $_SESSION['username'] = $data['username'];
            $_SESSION['loggedin'] = True;
            echo "Success";
            exit;
        }
    }
    echo "Fail";
    exit;
}

?>
```

Furthermore, we have the following PHP code in `profile.php`:

Code: php

```php
<?php
   require_once ('config.php');
   session_start();

   if (!$_SESSION['loggedin']) {
    header('Location: login.php');
    exit;
   }

   $content = "Unauthorized!";
   // allow access to all our admin users
   if(strpos($_SESSION['username'], 'admin') != false) {
    $content = get_admin_info();
   }
?>
```

Analyzing the source code, we see two loose comparisons leading to potentially unexpected cases of type juggling. The first is in the password check in `index.php`, and the second is in the username check in `profile.php`. The username check grants access to all users containing the string `admin` in the username. Since we cannot change our username, there is no way to bypass this check easily.

***

### Debugging the Application Locally

To debug the web application locally, we need to install the [PHP Debug](https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug) VS Code extension. Afterward, we can open the file `index.php` in VS Code, click `Debug and Run`, and select the PHP Debugger. However, doing so results in an error message. In the debug console, we can see the following error:

&#x20; Authentication Bypass

```shell-session
PHP Fatal error: Uncaught mysqli_sql_exception: Connection refused in src/config.php:8 Stack trace: #0 src/config.php(8): mysqli_connect('127.0.0.1', 'db', Object(SensitiveParameterValue), 'db') #1 src/index.php(2): require_once('...') #2 {main} thrown in src/config.php on line 8

Fatal error: Uncaught mysqli_sql_exception: Connection refused in src/config.php:8 Stack trace: #0 src/config.php(8): mysqli_connect('127.0.0.1', 'db', Object(SensitiveParameterValue), 'db') #1 src/index.php(2): require_once('...') #2 {main} thrown in src/config.php on line 8
```

Looking at the file `config.php` referenced in the error message it contains the following code:

Code: php

```php
<?php

$servername="127.0.0.1";
$dbusername="db";
$password="db-password";
$dBName="db";

$conn = mysqli_connect($servername, $dbusername, $password, $dBName);
```

The web application attempts to connect to a MySQL instance on localhost, which is currently not running. Instead of installing a MySQL server on our local machine, we can use a [MySQL Docker](https://hub.docker.com/_/mysql) container. To match the parameters provided in `config.php`, we can start the docker container using the following parameters:

&#x20; Authentication Bypass

```shell-session
PyrusHacks@htb[/htb]$ docker run -p 3306:3306 -e MYSQL_USER='db' -e MYSQL_PASSWORD='db-password' -e MYSQL_DATABASE='db' -e MYSQL_ROOT_PASSWORD='db' mysql
```

This creates a new MySQL server with the credentials given in `config.php`. However, the database is empty. So, let us create a `users` table with a dummy user. To do so, we need to create a file called `db.sql` with the following contents:

Code: sql

```sql
CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `username` varchar(256) NOT NULL,
  `password` varchar(256) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

#htb-stdnt:Academy_student!
INSERT INTO `users` (`id`, `username`, `password`) VALUES
(1, "htb-stdnt", "44891a5fc2dad49eab817badff4cb98adec418e43e6c6cb39984f8d090c6b0c4");
```

Afterward, kill the docker container we started previously and start a new one with the following command from the directory containing the `db.sql` file:

&#x20; Authentication Bypass

```shell-session
PyrusHacks@htb[/htb]$ docker run -p 3306:3306 -e MYSQL_USER='db' -e MYSQL_PASSWORD='db-password' -e MYSQL_DATABASE='db' -e MYSQL_ROOT_PASSWORD='db' --mount type=bind,source="$(pwd)/db.sql",target=/docker-entrypoint-initdb.d/db.sql mysql
```

Afterward, we can run the web application using PHP's built-in web server by clicking on `Create new launch.json` and selecting the `Launch Built-in web server` debugger in the drop-down menu on the left. Then, we can access the web application at the URL printed in the debug console.

Note: Keep in mind that the behavior of type juggling differs depending on the PHP version. Thus, we need to ensure that our local PHP version matches the PHP version used by the target web server.

***

### Exploitation

Since the web application supports JSON parameters, we are not limited to the data type `string`, enabling us to bypass the authentication check due to type juggling. The password is not hashed, thus, we can provide any data type to the comparison. Looking back at the table in the previous section, we can see that the comparison `0 == "php"` evaluates to `true` in PHP versions before 8.0.0. Thus, if we provide the number `0` as the password and it is compared to the admin user's password, we can bypass the authentication check due to type juggling:

![image](https://academy.hackthebox.com/storage/modules/205/juggling/typejuggling_authbypass_4.png)

Since we are now logged in as the `admin` user, we can access the post-login page.

***

### Prevention & Patching

The prevention of vulnerabilities resulting from type juggling is simple - use the strict comparison operators `===` and `!==` instead of the loose ones `==` and `!=`. In most cases, the result of a loose comparison is unexpected and undesired. In particular, strict comparisons should always be used for sensitive operations such as authentication.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://writeups.adityadindi.com/crackmes.one/1-difficulty-rating/random.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
