Hacking into Toyota’s global supplier management network

Eaton

Discussion links: Twitter | Reddit

News coverage:

February 13, 2023 update: A previous version of this post indicated SHI might have developed the GSPIMS application based on a license key found in the JavaScript code. SHI has now confirmed they did not develop GSPIMS and simply sold Toyota the license key. Based on this information, GSPIMS is a Toyota-developed application and has no known third party development involvement.

Key Points / Summary:

Over the course of a slow week in late October 2022, I decided to explore the subdomains of various major companies to see if I could find any exploits worth reporting/writing about. I found several interesting Toyota websites. In 7 days, I reported 4 different security issues to Toyota, all of which were classified as “critical”. One of the reports had a remarkably severe impact and is one of the most severe vulnerabilities I have ever found (so far!)

I discovered what was essentially a backdoor login mechanism in the Toyota GSPIMS website/application that allowed me to log in as any corporate Toyota user or supplier just by knowing their email. I eventually uncovered a system administrator email and was able to log in to their account. Once that was done, I had full control over the entire global system. I used the word “staggering” to describe the amount of data I had access to in the Jacuzzi SmartTub hack, but that was relatively minor compared to this. I had full access to internal Toyota projects, documents, and user accounts, including user accounts of Toyota’s external partners/suppliers. External accounts include users from:

GSPIMS stands for “Global Supplier Preparation Information Management System”. It is an Angular single-page-application created and maintained by Toyota. At first, I didn’t know what GSPIMS was. Google showed a few job listings about it, but otherwise seemed obscure. I didn’t think it was that important at first, but I decided to put some time into it to see what might be hiding behind the login screen. It wasn’t until I bypassed the login screen that I saw the “Global Supplier Preparation Information Management System” label. Sounds important!

Bypassing the login

The login screen features corporate Toyota and Supplier login options:

There should be a “Are you a Hacker?” option.🙂

Both options supply the same list of login methods:

If you work for/with Toyota on any continent, it is likely you can log in to the system using one of these options. I do not work for Toyota, so I had to get past this login screen by patching the JavaScript code.

Developers control access to Angular routes/pages by implementing CanActivate and CanActivateChild. Basically, when a user attempts to navigate to a route/page, you would determine if they are allowed to view it, and then return true or false. By patching both to return true, you can usually fully unlock an Angular app:

The logout code also needed to be removed to prevent a redirect back to the login page:

With those patches applied, the app loads and can be browsed:

In the Jacuzzi SmartTub case, patching the JavaScript was all that was needed to achieve full access, since their API was improperly secured. In GSPIMS’ case, no data would load from the API. All the endpoints would return HTTP status 401 – Unauthorized responses due to the missing login cookie. This was the case for every page I browsed. Toyota had seemingly secured their API correctly, and at this point I was about to write this site off as “probably secure”. I don’t bother reporting single-page-application bypasses unless it also exposes a leaky/improperly secured API.

JWTs for everyone!

Before abandoning work on the GSPIMS app, I looked through its code to see if there might have been any API keys, secret API endpoints, or anything else that might be interesting. I came across this function in the user service. Can you spot what is interesting about it?

JWT stands for JSON Web Token. Think of it as a session token that represents your valid authenticated session on a website. You typically get a JWT after logging into a website using your email and password. You then provide the JWT to secured parts of a website or API to prove your identity and what you are allowed to access. More information

It is interesting because it appears to be generating a JWT based on a provided email. No password required. I decided to compose this HTTP request to see if that createJWT endpoint actually worked. Even if it worked, it wouldn’t be enough without a valid email, which could have been difficult to figure out, especially if the underlying userbase was small.

Corporate Toyota emails use a predictable format in North America: firstname.lastname@toyota.com. That made guessing a valid email easier, but I still had to find someone. I Googled for Toyota employees involved in the supply chain, hoping to find someone who may be registered in the GSPIMS system. I found a promising match and formulated their email address based on their name. Then I fired off the createJWT HTTP request, and it returned a valid JWT!

It seems like I had discovered a way to generate a valid JWT for any Toyota employee or supplier registered in GSPIMS, completely bypassing the various corporate login flows, which probably also enforce two-factor authentication options. The reason createJWT exists will be revealed later in this post.

Utilizing the JWT was easy. The GSPIMS API authenticates via cookie, so I just added it through Chrome’s dev tools:

Escalating to System Admin

The user I logged in had the role of “Mgmt – Purchasing” which probably means they are in charge of organizing purchases of things from suppliers through this system. I had access to some data at this point, but I felt there was more waiting to be unlocked. Looking at the HTTP requests and responses, there is a rolePrivileges node in the user/details API response that returns information about the currently logged in user:

I wanted to try and find a user with the System Admin role. I noticed another API endpoint named findByEmail that returned information about a user’s account by just providing a valid email. Conveniently, this also tells you who the user’s managers are:

Imagine having 3 different managers to report to!😱

Checking the managers of the managers, etc. made it easy to find accounts that had elevated access to the system. Eventually I found a North America Regional Admin. That gave me access to the User Administration section. I then poked around more and found users with even higher access, such as Supplier Admin, Global Admin, and finally, System Admin.

In the GSPIMS settings, the tabs that appear are dependent on your role. There’s Regional Settings for Regional Admins, Global Settings for Global Admins, and System Admin Settings for System Admins. System Admins can access all the tabs. I also noticed that System Admins had access to substantially more users in the User Administration section. Regional Admins are probably only able to manage users in their region, whereas System Admins could manage everyone. With a System Admin JWT, I basically had total, global control over the entire system.

A few screenshots of various sets of users. Take note of the user and page counts in the bottom right corner (there’s a lot – 14,063 users across 563 pages!) Click on any image to enlarge it in a new tab.

American users
Asian users
Japanese users
American System Admins
European users
More Japanese users

I could edit any of those users:

Or add a new user using the user import button above the columns. There is also an export users button you can use to download user information.

Exploring the impact

Having full access, I looked around the GSPIMS app to see what was available to me as a System Admin. I was very careful to not modify anything. Here’s the System Admin Settings if you are curious about what is there. Nothing too exciting – it just controls what roles can access certain features.

Now on to more interesting things. The Parts section has a list of parts associated with the various projects. You choose an affiliate at the top and then the project to see the list of parts. Here is the parts list for a project:

It’s worth noting the projects are in codenames/numbers. It didn’t say anything like “2024 Toyota Corolla”. With a lot more time I probably could have figured out the codenames, but that was outside the scope of this investigation. Speaking of projects, I had access to all the active, global, and inactive projects:

I could view the details of each project, including who is involved, the schedule and milestones, and some type of survey feature. Click on any image to enlarge it in a new tab.

Project Parts List
Add/Remove Users
Project Schedule
Surveys
System Management

Can’t forget about the documents! Classified documents are all the rage nowadays.

Reviewing the HTTP trace I captured, I noticed you can also see Toyota’s various comments about their suppliers. It’s probably also visible in the UI somewhere.

The HTTP trace was captured by just having Fiddler open while browsing the app. The API is very generous with the amount of data it returns, in particular with the users list. You could download a lot of user information by increasing the page size and flipping through the pages.

You could also see all the suppliers and how Toyota ranks them regarding risk, delivery, and prep. There are almost 3,000 of them:

Finally, I discovered what the createJWT API was actually used for. There is an “Act As” feature that let me log in as any of those 14k+ global users. I could easily log in as anyone and get an idea of what projects they are working on, their tasks, surveys, etc. Whoever made the Act As system apparently didn’t realize they added a backdoor to the entire system. The Act As feature is only visible to certain users, like the System Admin user I was logged in as.

That is one long drop-down list.

If a threat actor had discovered this issue, the consequences could have been severe. Here’s a few bad things they could have done. Please note that these are just ideas and none of these were carried out.

Reporting to Toyota

The issue was reported to Toyota on November 3, 2022, and they responded later that same day confirming they received the report. On November 23, 2022, they confirmed the issue was remediated, although I noticed it was fixed before that when I randomly tested. I then informed them I would publish my writeup after the industry standard 90-day period has passed.

Toyota fixed the issue by making the createJWT and findByEmail endpoints return HTTP status 400 – Bad Request in all cases.

Out of all the security issues I have reported so far to various vendors, Toyota’s response was the fastest and most effective. I was very impressed with how quickly they responded and fixed the issue. Some companies can be slow to respond or fail to respond at all, so this experience was refreshing.

Thanks to this responsible disclosure, Toyota avoided what could have been a catastrophic leak of not only their own employees’ data, but the data of their partners/suppliers. Embarrassing internal comments and supplier rankings could have been published for the world to see. Toyota and their suppliers have been hit by cyberattacks before and it could have easily happened again.

Unfortunately, the reward for reporting this critical issue was $0. While it’s fun to find significant vulnerabilities like these, I will probably start shifting my efforts to companies offering monetary rewards help to sustain these often-lengthy investigations and writeups. PS: If you/your company’s security team are currently hiring, feel free to say hello🙂.

Subscribe to new posts

Get an email notification every time something new is published.
📧