Goin’ Connect Security Breach
Goin Connect Security Vulnerability
The Story:
Before our exams, Eren and I were in my room, studying and goofing around. Around midnight to 1 AM, the studying had completely devolved into goofing, and Eren was getting ready to head back to his room. That’s when we decided to check out the new people at our university on the Goin Connect app, sending friend requests to everyone, and so on.
While we were doing this, I came across someone and thought, "Even if I send a request, this person won't accept it." So I decided to just add them on Instagram directly. But the app didn't even show a last name; all I had was a first name, age, and a profile picture. This gave me an idea: "What if I connect this iOS app to a debugger on my Mac, export the profile picture, and do a reverse search to find their Instagram?" It was a solid plan. I connected the app to a debugger, and from the artifacts that appeared when I clicked on the profile, I was able to grab the URL of the profile picture. But something else caught my eye. That's where everything started.
While looking at her profile, I could see her FCM token. I was incredibly confused. Why would her FCM token be on my phone?
For those who don't know, **FCM**, or **Firebase Cloud Messaging**, is used to send what we call Push Notifications. When you open your phone or log into your account, it creates a unique code for your device. You're supposed to keep this code private, because even though it's encrypted and untraceable, it's still a GDPR security breach. The problem is, the server is usually the one that handles sending a notification using an FCM token. So the client (my phone) should never know what that token is.
api.cidqu.net/bildirim.php?kullanici=YAVUZ
Typically, a server request is sent in this format, and the server finds Yavuz's FCM token stored on its end and sends the necessary notification to that token via Google Cloud's API.
What confused me was this: "Are they sending FCM notifications from the device instead of the server?" If that was the case, it meant that the **MESSAGING KEY** was stored inside the phone's .ipa/.apk file. This is a very scary situation. Because if I could get my hands on that key, it meant I could send notifications to anyone whose FCM token I found on the system.
I quickly sent Eren back to his room, giving him a brief explanation of the situation. Then I put on my headphones and said to myself, "Let's dig into this app a little bit." First, I extracted the .ipa file. The Info.plist was clean, and google-services.json was in a binary format, not text. Everything looked fine. Next, I downloaded the APK. It was written with React Native, and since they were using the Hermes framework, I couldn't see the original code. There are plenty of decompilers for Hermes, but unfortunately, none give a 100% accurate result. Finally, I thought, "Maybe they didn't mess up that badly. The reason I can see the Firebase token is probably an API flaw." As a last resort, I decided to check the values/strings.xml file. For those who don't know, this is where all the text variables are stored in Gradle projects.
The strings.xml file looked as it should, though it seemed like they tried to implement Facebook login at some point and gave up. Or maybe the app was purchased directly from a website like CodeCanyon. I can't say for sure. But something else caught my attention: there was no FCM key, but there was something else. **STREAMIO_SECRET**. Now, I know Streamio is a video calling tool, but Goin doesn't have a feature like that. I took a quick look at Streamio's website and immediately saw "Chat Function." That's when I understood—the chat part of the app was running on Streamio. Wait a minute... STREAMIO_SECRET in the strings file? What? Are they generating **JWT** client-side?
What is JWT?
For those who don't know, **JWT** (Json Web Token) is a token that users use to authenticate themselves to create an API request. Instead of sending their username and password directly to the system, they authenticate, and authenticated users are given a JWT. With this JWT, users can access the information they need on every request without having to log in again. Let me put it this way: When you check into a hotel, you go to the reception, show your passport, and show your reservation. Your passport is your password, and your reservation is your username. The reception then authenticates you and gives you a room key card. This key card is the JWT. Thanks to it, every time you enter and exit your room, you don't have to go through the passport and reservation check again—you just need to swipe your card. That's what JWT is for.
Generating a JWT is a very risky thing, because if others learn how your system creates these tokens, they can create JWTs as if they were the app itself. (Imagine stealing the card machine from the hotel and printing your own fake cards.)
In Streamio's API documentation, it was written in all caps: **“DO NOT GENERATE JWT CLIENT-SIDE YOUR SECRET KEY MAY BE EXPOSED”**
I took this secret key and downloaded the Streamio admin tools for Python. Just as I suspected, the secret key worked. My first order of business was to read Eren's messages. (I didn't look at anyone else's information, it's not legal, and I wouldn't do it.) I could read all of Eren's messages. Not just that, I could ban Eren from the system, make myself an admin, send notifications to everyone, and even set up templates. This meant I could send a link-opening notification to every Goin user. I could send a notification to 350k+ people that would open a link when they clicked it. Do you have any idea how horrifying that is? I could send a notification to everyone that looked like it was from a girl or a friend and send them to a phishing website. I could read everyone's messages, edit them, create new ones, and so on. As you can imagine, I had the entire system at my fingertips. I could even delete the entire chat feature in the app, and nothing would be left.
When I found all this, it was around 4 AM. I immediately wrote a nice email to Goin Connect. Around 6 AM that Monday, I wrote a long email detailing everything I found, how to fix it, that they needed to shut down the system immediately, and that I was serious and not joking around.
I received this automatic email:
“I'm not in the office until Monday, I'll get back to your email once I'm there”
Okay.
I said I'd wait for him to wake up and went to sleep. When I woke up around noon, the CTO of the app had sent me a connection request on LinkedIn; they had noticed my email and had taken immediate action.
In the end, they gave me a XXX € bounty reward.
Currently, there is no such issue with the Goin Connect app. All JWTs are now generated on a separate server and sent to the device from there. (The hotel card machines are now behind a safe and secure.)
I would like to thank the COO and CTO of Goin Connect for their cooperation and bounty.
⁃ CidQu