---
# System prepended metadata

title: MobSec report Lovoo dating app

---

# MobSec report Lovoo dating app

## Description
Lovoo is a popular dating app especially in Austria and Germany. It is geo location based and has the typical matching functions, people near you and also a streaming function. 

Source: https://about.lovoo.com/

## Structure of report
1. Description
2. Registration process
3. Login process
6. Obtaining other users data
7. Direct profile access
8. Investigating premium functions
9. Liking Process
10. Tracking
11. Conclusion

## Registration process
In the registration process of this app couldn't anything interesting be found.

## Login process
The first thing the app does after starting up is to load the users data with a initial get request
```
GET /init HTTP/2
Host: api.lovoo.com
Cache-Control: no-cache
Accept: application/json
Kissapi-App-Version: 12300
Kissapi-Version: 1.36.0
Kissapi-Device-Os: 28
Kissapi-Device-Model: AOSP on IA Emulator
Tz: Europe/Vienna
Kissapi-Device: android
Kissapi-App-Package-Id: net.lovoo.android
Kissapi-Android-Fingerprint: google/sdk_gphone_x86_arm/generic_x86_arm:9/PSR1.180720.117/5875966:user/release-keys
Kissapi-App: lovoo
Kissapi-Apptype: google
User-Agent: LOVOO/12300 Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117) 4.9.1
Kissapi-Android-Id: 7eaa55c7260aeb8f
Kissapi-App-Id: 682f302f-e535-4ca9-889c-d462ffe2b58b?identifier=lovoo.prod.firebase
Kissapi-Gpsa-Id: 682f302f-e535-4ca9-889c-d462ffe2b58b?identifier=lovoo.prod.firebase
Kissapi-Gpsa-On: true
Accept-Language: en-US
Wifi: 1
Kissapi-Mac: e3f5536a141811db40efd6400f1d0a4e
Kissapi-Ad-Id: f8738acc8d7372f0dd5af125bfae4c9e
Kissapi-Ad-Name: Organic
Authorization: Bearer {Bearer Token}
Accept-Encoding: gzip, deflate
```
The response has a lot of data, but the first part looks pretty interesting
```=json
{
    "response":{
        "user":{
            "_type":"user",
            "id":"6266cd58b5e1341bb476f697",
            "name":"JeffBayzoss",
            "gender":1,
            "age":23,
            "lastOnlineTime":1654364115,
            "whazzup":"",
            "freetext":"I enjoy pina coladas, getting caught in the rain, and making love at midnight.",
            "subscriptions":[
            ],
            "isVip":0,
            "flirtInterests":[
                "frie",
                "chat"
            ],
            "options":{
                "profileShareable":1
            },
            "compatibilityQuestionsAnswered":0,
            "counts":{
                "v":45,
                "l":1,
                "pp":83,
                "p":1,
                "m":1,
                "ib":0
            },
            "credits":6,
            "locations":{
                "home":{
                    "city":"Vienna",
                    "country":"AT",
                    "lat":48.18633,
                    "lon":16.323064
                },
                "current":{
                    "city":"Graz",
                    "country":"AT",
                    "lat":47.0707133,
                    "lon":15.4395033
                },
                "billing":{
                    "country":"US"
                }
            },
            "isNew":0,
            "isOnline":1,
            "isMobile":1,
            "isHighlighted":0,
            "picture":"62767fb02a175046ef7c7350",
            "images":[
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/image.jpg",
                    "width":365,
                    "height":481
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/image_l.jpg",
                    "width":365,
                    "height":481
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/image_m.jpg",
                    "width":365,
                    "height":481
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/image_s.jpg",
                    "width":243,
                    "height":320
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/thumb_xl.jpg",
                    "width":320,
                    "height":320
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/thumb_l.jpg",
                    "width":160,
                    "height":160
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/thumb_m.jpg",
                    "width":120,
                    "height":120
                },
                {
                    "url":"https:\/\/img.lovoo.com\/users\/pictures\/62767fb02a175046ef7c7350\/thumb_s.jpg",
                    "width":80,
                    "height":80
                }
            ],
            "email":"jeff.bayzoss@gmail.com",
            "birthday":"30.08.1998",
            "isConfirmed":0,
            "registerDate":1650904408,
            "apps":[
                "lovoo"
            ],
            "interests":[
                "frie",
                "chat"
            ],
            "genderLooking":2,
            "languages":[
                "en",
                "de"
            ],
            "promoconsent":0,
            "settings":{
                "screensaver":0,
                "share_profile_disabled":0,
                "radar_disabled":0,
                "match_disabled":0,
                "ads_gps_disabled":0,
                "ads_personalized_disabled":0,
                "push_intensity":"all",
                "gps":1,
                "sounds":1,
                "push":1,
                "push_msg":1,
                "push_visit":1,
                "push_match":1,
                "push_info":1,
                "push_comments":1,
                "push_likes":1,
                "push_follow":1,
                "push_match_vote":1,
                "push_reply":1,
                "message_preview":1,
                "push_video_nearby":1,
                "push_video_favorite":1,
                "push_video_favorite_blast":1,
                "push_best_picks":1
            },
            "searchSettings":{
                "ageFrom":18,
                "ageTo":25,
                "radius":500,
                "matchRadius":0,
                "gender":2
            },
            "app":"12601",
            "verifications":{
                "facebook":0,
                "verified":0,
                "confirmed":0
            },
            "verificationStatus":0,
            "resources":{
                "icebreaker":{
                    "slotsLeft":3,
                    "slotLimit":3,
                    "refillInterval":86400,
                    "refillTime":0,
                    "slotsExtra":0,
                    "slotsNotify":0
                },
                "boost":0,
                "boost_refill_time":0
            }
        }
    }
}
```
It looks like the app has saved the "home" location and tracks the current location. In the last part of the response is a tag "homeLocation":1.

## Obtaining other users data

When going to the match section the app sends a request to load in user charts for voting. The request contains a filter like gender, age, but also the current location.
```
GET /matches?gender=2&ageFrom=18&ageTo=25&lat=47.0707133&long=15.4395033&limit=30 HTTP/2
Host: api.lovoo.com
```

The API then responds with users, who match the filter and sends their data.
```=json
{
    "response":{
        "result":[
            {
                "_type":"user",
                "id":"514bf0cb150ba0e941000331",
                "name":"Victoria",
                "gender":2,
                "age":24,
                "lastOnlineTime":1654224749,
                "whazzup":"Bundesheer? Bitte, danke",
                "subscriptions":[
                ],
                "isVip":0,
                "flirtInterests":[
                    "date",
                    "frie"
                ],
                "options":{
                    "profileShareable":1
                },
                "counts":{
                    "p":15,
                    "m":10
                },
                "possibleMatch":0,
                "locations":{
                    "home":{
                        "city":"Graz",
                        "country":"AT",
                        "distance":2.7
                    },
                    "current":{
                        "city":"Graz",
                        "country":"AT",
                        "distance":1.2
                    },
                    "billing":{
                        "country":"AT"
                    }
                },
                "isNew":0,
                "isOnline":0,
                "isMobile":1,
                "isHighlighted":0,
                "picture":"62250c4c515cd339521aa842",
                "images":[
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/image.jpg",
                        "width":960,
                        "height":1280
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/image_l.jpg",
                        "width":720,
                        "height":960
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/image_m.jpg",
                        "width":480,
                        "height":640
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/image_s.jpg",
                        "width":240,
                        "height":320
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/thumb_xl.jpg",
                        "width":320,
                        "height":320
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/thumb_l.jpg",
                        "width":160,
                        "height":160
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/thumb_m.jpg",
                        "width":120,
                        "height":120
                    },
                    {
                        "url":"https:\/\/img.lovoo.com\/users\/pictures\/62250c4c515cd339521aa842\/thumb_s.jpg",
                        "width":80,
                        "height":80
                    }
                ],
                "verifications":{
                    "facebook":1,
                    "verified":1,
                    "confirmed":1
                },
                "verificationStatus":1
            }
...
```
This response contains some interesting data that could be used malicously.
Besides the userid and a possibleMatch prediction we get the last online time "1654224749". Converting that from UNIX Timestamp with units in seconds, we get "Fri 3 June 2022 02:52:29 UTC".
The API sends the home and current location with the actual distance to the suggested user:
```=json
"locations":{
            "home":{
                    "city":"Graz",
                    "country":"AT",
                    "distance":2.7
                   },
           "current":{
                      "city":"Graz",
                      "country":"AT",
                      "distance":1.2
                     },
          "billing":{
                      "country":"AT"
                    }
            }
```
Additionally the response contains tags like isNew, isOnline, isMobile, verification with facebook and the link to the picture.
At the end of the response it shows us the remaining votes count
```
"allCount":30,"votesLeft":30,"timeToNewVotes":1
```
Thereafter the app sends a POST request containing all the user ids it has loaded with the request before. 
```
POST /users/6266cd58b5e1341bb476f697/connections_multi HTTP/2
Host: api.lovoo.com
```
The response shows the relationship to these users
```=json
"5f1ba612d1e9903275732b32":{
    "isMatch":0,
    "hasLiked":0,
    "icebreakerState":0,
    "hasContact":0
    }
```

## Direct profile access

When clicking on a profile of a other user the app makes a few requests:
It makes a POST request with the id it wants to load
```
POST /users/6266cd58b5e1341bb476f697/connections_multi HTTP/2
Host: api.lovoo.com
...
ids=5ffdfaedd31a33034b422abd
```
Then loads some facts about the user where the app gets some data.
```
GET /users/5ffdfaedd31a33034b422abd/facts HTTP/2
Host: api.lovoo.com
```
In this data is the actual search setting age range of the user looking at, interests and data like height, home location, ...
```=json
{
    "response":{
        "gender_looking":"1",
        "searchsettings_age_from":19,
        "searchsettings_age_to":25,
        "locationhome_city":"Sankt P\u00f6lten",
        "locationhome_country":"AT",
        "locationhome_lat":0,
        "locationhome_long":0,
        "interests":[
            "chat"
        ],
        "whazzup":"",
        "languages":[
            "de",
            "en"
        ],
        "match_disabled":0,
        "interview_size":"s23",
        "interview_size_cm":162
    },
...
}
...
```
It also loads in more data with the request
```
GET /users/5ffdfaedd31a33034b422abd/feed?resultLimit=40&sort=distance HTTP/2
Host: api.lovoo.com
...
```
But the data in the response looks similar to that returned in the match request. With this request the app gets again the pictures and other basic facts (somehow it's an array with 7 times the same struct in it).

The app again loads data with the following two requests:

```
GET /users/5ffdfaedd31a33034b422abd/freetext HTTP/2
Host: api.lovoo.com
...
```
```
GET /prompts/5ffdfaedd31a33034b422abd HTTP/2
Host: api.lovoo.com
...
```
These just load the texts and question - answer prompts of the user.

Finally the app makes a POST request to the visit endpoint, to show the other user, that I clicked on the profile and where I did that.
```
POST /users/5ffdfaedd31a33034b422abd/visits HTTP/2
Host: api.lovoo.com
...
ref=match
```

This app links photos of the user staticly, but has uses instead of the user id a unrelated picture id
```=json
"id":"628e8f3ce42e4d62eb00ad6e"
"url":"https:\/\/img.lovoo.com\/users\/pictures\/628e8f40a5457d5e181ff973\/image.jpg"
```

## Investigating premium functions

On the app this can be watched in the "News" section. The feature only shows the profiles unblurred with an upgraded account. Looking at the response of the load request in this section showed that without an upgraded account, the app gets encrypted user ids, blurred images but still the home and current location (without the exact distance).
```=json
{
...
    "_type":"visit",
    "id":"CRYPKDc0t9vW+EPsWHs70PSaNhcLpSTPJbUhBeAFBIro4w=",
    "user":{
        "_type":"user",
        "id":"CRYPKDc0t9vW+EPsWHs70PSaNhcLpSTPJbUhBeAFBIro4w=",
        "name":"Ceci",
        "gender":2,
        "age":20,
        "lastOnlineTime":1654511814,
        "whazzup":"",
        "freetext":"Ich mag Pizza, Sushi und Harry Potter. \n\nWenn du ein Hund oder eine Katze hast, oder T\u00e4towierer bist, hast du schon gewonnen!",
        "subscriptions":[
        ],
        "isVip":0,
        "flirtInterests":[
            "frie"
        ],
        "options":{
            "profileShareable":1
        },
        "counts":{
            "p":1,
            "m":4
        },
        "isLocked":1,
        "locations":{
            "home":{
                "city":"Kapfenberg",
                "country":"AT"
            },
            "current":{
                "city":"Deuchendorf",
                "country":"AT"
            },
            "billing":{
                "country":"AT"
            }
        },
        "isNew":0,
        "isOnline":0,
        "isMobile":1,
        "isHighlighted":0,
        "picture":"28912190f25ee452c9baa29819d8be3d",
        "images":[
            {
                "url":"https:\/\/img.lovoo.com\/users\/pictures\/28912190f25ee452c9baa29819d8be3d\/thumb_xl.jpg",
                "width":320,
                "height":320
            },
            {
                "url":"https:\/\/img.lovoo.com\/users\/pictures\/28912190f25ee452c9baa29819d8be3d\/thumb_l.jpg",
                "width":160,
                "height":160
            },
            {
                "url":"https:\/\/img.lovoo.com\/users\/pictures\/28912190f25ee452c9baa29819d8be3d\/thumb_m.jpg",
                "width":120,
                "height":120
            },
            {
                "url":"https:\/\/img.lovoo.com\/users\/pictures\/28912190f25ee452c9baa29819d8be3d\/thumb_s.jpg",
                "width":80,
                "height":80
            }
        ],
        "verifications":{
            "facebook":0,
            "verified":0,
            "confirmed":1
        },
        "verificationStatus":0
    },
    "unlocked":0,
    "time":1654501242

},
...}
```
In this data are "isLocked" and "unlocked" tags. Intercepting the response and changing these lets skipping the upgrading and tries to load the profile. But the user ids are still encrypted and therefore the load requests fail with a unknown user error message. Investigating the "likes you" section reveals the same issue.

## Liking process
The match request to vote contains the target user id. Trying to change the values to get some interesting results didn't work.
```
POST /matches/629cedf7e6166b0f82556935 HTTP/2
Host: api.lovoo.com
...
vote=1&ref=direct%2Cmatch&dwell_time=1959
```
So the liking process on this app seems to be straight forward.

## Tracking

To track user activity for reasons like marketing, the app sends following requets with encrypted data to the so called "AppLovin" service.
```
POST /1.0/event/load?id=99ed804848a284ccc2c5ed1c1036cc7c5f90432c&load_time_ms=223&postback_ts=1654701969116 HTTP/2
Host: prod-ms.applovin.com
```
```
POST /1.0/event/load?id=c0f8b73a85d500b3ee038b0e5884f24eb30d7d71&load_time_ms=60&postback_ts=1654701992235 HTTP/2
Host: stage-ms.applovin.com
```
```
POST /1.0/mediate?p=1%3A96943fdea087a1d759e08734fce0068af23ef96e%3AWGUTRJff5vD55Paf-9cSLJ84TV6pnUas3JefM3thUQJ6QpVbKwF4Ly%3AMMPK3XoTuUjbcwDnQ9MFGeYLFPqCWyE7FrXUHjD-J4KOB8wezjR0WZIIrA-B6LRMSaskbRjPWiDXrtaGbCyT_g** HTTP/2
Host: ms4.applovin.com
```
AppLovin is a marketing and advertising service which helps to analyze user behaviour.

Source: https://en.wikipedia.org/wiki/AppLovin

## Conclusion
Lovoo gives away some information about their users. Looking at the data the app loads from a user, we know their location, if the user is online at the moment, the last time online, their search settings and so on. 
Around 6 years ago a user created a tool to calculate users location with requests to the API. This could easily be adapted and used for malicious reasons with state of the communication in this point of time.

Source: https://github.com/posixpascal/lovoo-data-breach
# MobSec report OKCupid dating app

## Description
OK Cupid is also a very popular dating app. It has the typical matches, where you can like or dislike suggested users. But also tabs where the app suggests you users with the most "match percentages", recommendations, users who like you and "Cupid's picks". But like all dating apps, a lot of functions are behind a paywall: daily like cap, the users who like you are made unrecognizable by the app until you pay, popularity boost if premium user.

Source: https://en.wikipedia.org/wiki/OkCupid

**Device used:** Android emulator Pixel 4 Android 9 API level 28 

## Structure of report
1. Description
2. Registration process
3. Login process
6. Direct profile access and obtaining other users data
9. Liking Process
10. Tracking
11. Conclusion
12. Future work

## Registration process
This app requires for a full registration a phone number. But we discovered that the app doesn't relly check if the number is valid. It tries to send a verification code to the entered number. After that the user needs to enter the code and if vaild, can log in. 
However, if we enter a invalid phone number, invalid code, intercept and modify the responses to look like a valid verfication, we can log in without any number. 
We can enter just any number:
```
POST /graphql/SendPhoneNumber HTTP/2
Host: okcupid.com
Cookie: override_session=0; secure_login=1; secure_check=1; guest=7499627801677657669; session=7499627801677657669%3A16707096719338247505; authlink=158b116f; _fbp=fb.1.1652979626229.695918336; nano=k%3Diframe_prefix_lock_3%2Ce%3D1653930142760%2Cv%3D1%7Ck%3Diframe_prefix_lock_2%2Ce%3D1653650365181%2Cv%3D1%7Ck%3Diframe_prefix_lock_1%2Ce%3D1652979653689%2Cv%3D1%7Ck%3Diframe_prefix_lock_-1%2Ce%3D1653930218437%2Cv%3D1
Accept: application/json
X-Apollo-Operation-Id: e8d883f7395f6212398be1a07497ce5f9d72bf05ffda5f90bf523214fb5e6065
X-Apollo-Operation-Name: SendPhoneNumber
X-Okcupid-Device-Id: Android; AOSP on IA Emulator; 9; PSR1.180720.117; Unavailable; fwciLTsdTECsUopudsdVN0;
X-Okcupid-App: OkCupid Android App 64.1.0
X-Okcupid-Platform: Android
X-Okcupid-Version: 64.1.0
X-Okcupid-Locale: en
X-Okcupid-Emulator: true
User-Agent: Android 64.1.0
X-Emb-Path: /graphql/SendPhoneNumber
Content-Type: application/json; charset=utf-8
Content-Length: 442
Accept-Encoding: gzip, deflate

{
    "operationName":"SendPhoneNumber",
    "variables":{
        "input":{
            "tspAccessToken":"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6Ijc2Njg0NTc3MyIsImV4cCI6MTY1NDMzNjEwMSwidW5pcXVlbmVzc19pZCI6IkdIR01qY1NRc05SeCJ9.qe-khFh1iLNxKyeaP_lumP0lER2ro8FEN6_Pn28-G1w",
            "phoneNumber":"+4386637206742",
            "platform":"android",
            "smsFlow":"PHONE_NUMBER_LINKING"
        }
    },
    "query":"mutation SendPhoneNumber($input:AuthOTPSendInput!) { authOTPSend(input: $input) { __typename success statusCode } }"
}
```
And get following response, where we need to set success to true and statuscode to 0.
```
HTTP/2 200 OK
Date: Sat, 04 Jun 2022 09:24:32 GMT
Content-Type: application/json; charset=utf-8
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Vary: Accept-Encoding
Access-Control-Allow-Credentials: true
Etag: W/"6c-aYySrsNvT+jV1x6eiVIsKYgWWMU"
Cf-Cache-Status: DYNAMIC
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Server: cloudflare
Cf-Ray: 715f9db0f88038c0-VIE

{
    "data":{
        "authOTPSend":{
            "__typename":"AuthOTPSendPayload",
            "success":true,
            "statusCode":0
        }
    },
    "extensions":{
    }
}
```
Now we are able to enter the verification code, where the app sends the code via a request.
```
POST /graphql/SendVerificationCode HTTP/2
Host: okcupid.com
Cookie: override_session=0; secure_login=1; secure_check=1; guest=7499627801677657669; session=7499627801677657669%3A16707096719338247505; authlink=158b116f; _fbp=fb.1.1652979626229.695918336; nano=k%3Diframe_prefix_lock_3%2Ce%3D1653930142760%2Cv%3D1%7Ck%3Diframe_prefix_lock_2%2Ce%3D1653650365181%2Cv%3D1%7Ck%3Diframe_prefix_lock_1%2Ce%3D1652979653689%2Cv%3D1%7Ck%3Diframe_prefix_lock_-1%2Ce%3D1653930218437%2Cv%3D1
Accept: application/json
X-Apollo-Operation-Id: b83a704ae9c70899f8363708cff63a3fd2ee8fe6e1472ae1dadfaf822719914f
X-Apollo-Operation-Name: SendVerificationCode
X-Okcupid-Device-Id: Android; AOSP on IA Emulator; 9; PSR1.180720.117; Unavailable; fwciLTsdTECsUopudsdVN0;
X-Okcupid-App: OkCupid Android App 64.1.0
X-Okcupid-Platform: Android
X-Okcupid-Version: 64.1.0
X-Okcupid-Locale: en
X-Okcupid-Emulator: true
User-Agent: Android 64.1.0
X-Emb-Path: /graphql/SendVerificationCode
Content-Type: application/json; charset=utf-8
Content-Length: 439
Accept-Encoding: gzip, deflate

{
    "operationName":"SendVerificationCode",
    "variables":{
        "input":{
            "tspAccessToken":"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6Ijc2Njg0NTc3MyIsImV4cCI6MTY1NDMzNjU5OSwidW5pcXVlbmVzc19pZCI6Ik9aTkVUR0VqQTR3biJ9.lBdwohptxBNDtiwMOEMITHIKZzx_p7knj3KeIAXi3Sk",
            "phoneNumber":"+4386637206742",
            "otp":"111111"
        }
    },
    "query":"mutation SendVerificationCode($input:AuthTSPRefreshTokenCreateInput!) { authTSPRefreshTokenCreate(input: $input) { __typename tspRefreshToken } }"
}
```
The response we get shows that the code entered was invalid.
```
HTTP/2 200 OK
Date: Sat, 04 Jun 2022 09:32:25 GMT
Content-Type: application/json; charset=utf-8
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Vary: Accept-Encoding
Access-Control-Allow-Credentials: true
Etag: W/"20b-apZNTJoeVwK6QYZENYGytWrrtD8"
Cf-Cache-Status: DYNAMIC
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Server: cloudflare
Cf-Ray: 715fa9411de6cbc8-VIE

{
    "errors":[
        {
            "message":"16 UNAUTHENTICATED: Tinder Status Code 992401 Invalid OTP attempt",
            "locations":[
                {
                    "line":1,
                    "column":73
                }
            ],
            "path":[
                "authTSPRefreshTokenCreate"
            ],
            "extensions":{
                "code":16,
                "exception":{
                    "stacktrace":[
                        "Error: 16 UNAUTHENTICATED: Tinder Status Code 992401 Invalid OTP attempt",
                        " at /usr/src/app/dist/auth/dataSources/sms.js:105:13",
                        " at runMicrotasks (<anonymous>)",
                        " at processTicksAndRejections (internal/process/task_queues.js:95:5)"
                    ]
                }
            }
        }
    ],
    "data":{
        "authTSPRefreshTokenCreate":null
    },
    "extensions":{
    }
}

```
So we need to change the response, that it looks like the verification was successful. Changing it to following response should do the job:
```
HTTP/2 200 OK
Date: Sat, 04 Jun 2022 09:28:42 GMT
Content-Type: application/json; charset=utf-8
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Vary: Accept-Encoding
Access-Control-Allow-Credentials: true
Etag: W/"20b-apZNTJoeVwK6QYZENYGytWrrtD8"
Cf-Cache-Status: DYNAMIC
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Server: cloudflare
Cf-Ray: 715fa3ce6f8f38b0-VIE

{
    "errors":[
        {
            "message":"0 AUTHENTICATED",
            "locations":[
                {
                    "line":1,
                    "column":73
                }
            ],
            "path":[
                "authTSPRefreshTokenCreate"
            ],
            "extensions":{
            }
        }
    ],
    "data":{
        "authTSPRefreshTokenCreate":null
    },
    "extensions":{
    }
}
```
Now the app wants to update the phone number and checks, if it was successful
```
POST /graphql/UpdatePhoneNumber HTTP/2
Host: okcupid.com
Cookie: override_session=0; secure_login=1; secure_check=1; guest=7499627801677657669; session=7499627801677657669%3A16707096719338247505; authlink=158b116f; _fbp=fb.1.1652979626229.695918336; nano=k%3Diframe_prefix_lock_3%2Ce%3D1653930142760%2Cv%3D1%7Ck%3Diframe_prefix_lock_2%2Ce%3D1653650365181%2Cv%3D1%7Ck%3Diframe_prefix_lock_1%2Ce%3D1652979653689%2Cv%3D1%7Ck%3Diframe_prefix_lock_-1%2Ce%3D1653930218437%2Cv%3D1
Accept: application/json
X-Apollo-Operation-Id: 706c24c6f60c764605db4e932c1ccdc80020cc928b1111bcc0a14531d265811e
X-Apollo-Operation-Name: UpdatePhoneNumber
X-Okcupid-Device-Id: Android; AOSP on IA Emulator; 9; PSR1.180720.117; Unavailable; fwciLTsdTECsUopudsdVN0;
X-Okcupid-App: OkCupid Android App 64.1.0
X-Okcupid-Platform: Android
X-Okcupid-Version: 64.1.0
X-Okcupid-Locale: en
X-Okcupid-Emulator: true
User-Agent: Android 64.1.0
X-Emb-Path: /graphql/UpdatePhoneNumber
Content-Type: application/json; charset=utf-8
Content-Length: 230
Accept-Encoding: gzip, deflate

{
    "operationName":"UpdatePhoneNumber",
    "variables":{
        "input":{
            "tspRefreshToken":""
        }
    },
    "query":"mutation UpdatePhoneNumber($input:UserUpdatePhoneNumberInput!) { userUpdatePhoneNumber(input: $input) { __typename success statusCode } }"
}
```
We need to change the response to this request by setting "success" to true and the statuscode to 0.
```
HTTP/2 200 OK
Date: Sat, 04 Jun 2022 09:43:55 GMT
Content-Type: application/json; charset=utf-8
X-Powered-By: Express
Vary: Origin, Accept-Encoding
Vary: Accept-Encoding
Access-Control-Allow-Credentials: true
Etag: W/"81-hrlnHKfISioHEIcL0gOlcbam6Zg"
Cf-Cache-Status: DYNAMIC
Expect-Ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Server: cloudflare
Cf-Ray: 715fba185fb577fb-VIE

{
    "data":{
        "userUpdatePhoneNumber":{
            "__typename":"UserUpdatePhoneNumberResponse",
            "success":true,
            "statusCode":0
        }
    },
    "extensions":{
    }
}
```
Forwarding this modified response logs us in without a valid phone number. This whole process needs to be done really quick, because otherwise it shows a time-out error message.
Besides that, this needs to be done everytime we restart the app, because it's only valid on the client side and no number is saved in the account.
With automation of this process, a lot spam accounts could be made. Additionally the app also doesn't care about real email addresses.

## Direct profile access and obtaining other users data
At first we wanted to investigate the data of users the app gives us. Luckily after first login, a staff sent us a "welcome" message. After clicking on the profile, the app sends a few request like:
```
GET /1/profile/4170169657126759250 HTTP/2
Host: api.okcupid.com
Cookie: override_session=0; secure_login=1; secure_check=1; guest=7499627801677657669; session=7499627801677657669%3A16707096719338247505; authlink=158b116f; _fbp=fb.1.1652979626229.695918336; siftsession=710935292553977095; nano=k%3Diframe_prefix_lock_-1%2Ce%3D1653930218437%2Cv%3D1%7Ck%3Diframe_prefix_lock_3%2Ce%3D1653930142760%2Cv%3D1%7Ck%3Diframe_prefix_lock_2%2Ce%3D1653650365181%2Cv%3D1%7Ck%3Diframe_prefix_lock_1%2Ce%3D1652979653689%2Cv%3D1%7Ck%3Diframe_prefix_lock_4%2Ce%3D1654341185691%2Cv%3D1
Endpoint_version: 1
X-Okcupid-Device-Id: Android; AOSP on IA Emulator; 9; PSR1.180720.117; Unavailable; fwciLTsdTECsUopudsdVN0;
X-Okcupid-App: OkCupid Android App 64.1.0
X-Okcupid-Platform: Android
X-Okcupid-Version: 64.1.0
X-Okcupid-Locale: en
X-Okcupid-Emulator: true
User-Agent: Android 64.1.0
Accept-Encoding: gzip, deflate
```
Also to this endpoints:
```
GET /1/profile/4170169657126759250/fields?fields=userinfo%2Cphotos.limit%281%29%2Clikes%2Cpercentages
```
```
GET /1/profile/4170169657126759250/answer_filters 
```
```
GET /1/profile/4170169657126759250/answers?filter=9&after=&limit=60&sort=0 HTTP/2
```
These are to load the user data and present it on the user chart.
But it also sends a request with my user id:
```
GET /1/profile/7499627801677657669/fields?fields=userinfo%2Cphotos.limit%281%29%2Clikes%2Cpercentages HTTP/2
```
The response to this give a lot of my information:
```=json
{
    "userinfo":{
        "realname":"OhBoy",
        "gender_letter":"M",
        "gender":"Man",
        "age":22,
        "join_date":1652957903,
        "email_address":"oh.boy@gmail.com",
        "rel_status":"Single",
        "location":"Graz",
        "orientation":"Straight",
        "displayname":"OhBoy",
        "staff_badge":null
    },
    "photos":[
        {
            "full":"https://cdn.okccdn.com/php/load_okc_image.php/images/150x150/558x800/52x197/152x297/0/4918138661936607854.webp?v=3",
            "original":"https://cdn.okccdn.com/php/load_okc_image.php/images/52x197/152x297/0/4918138661936607854.webp?v=3",
            "info":{
                "full":{
                    "thumbnail":{
                        "height":100,
                        "y":197,
                        "x":52,
                        "width":100
                    },
                    "size":{
                        "height":481,
                        "y":0,
                        "x":0,
                        "width":365
                    },
                    "center":{
                        "y":247,
                        "x":102,
                        "y_percent":51,
                        "x_percent":27
                    }
                },
                "caption":"",
                "id":"4918138661936607854",
                "original":{
                    "size":{
                        "width":365,
                        "height":481
                    },
                    "thumbnail":{
                        "height":100,
                        "y":197,
                        "x":52,
                        "width":100
                    }
                }
            },
            "full_small":"https://cdn.okccdn.com/php/load_okc_image.php/images/150x150/279x400/52x197/152x297/0/4918138661936607854.webp?v=3"
        }
    ],
    "likes":{
        "via_superboost":null,
        "mutual_like_vote":0,
        "recycled":0,
        "they_superlike":null,
        "passed_on":0,
        "they_like":null,
        "you_superlike":0,
        "you_like":null,
        "via_spotlight":null,
        "mutual_like":0,
        "vote":{
        }
    },
    "percentages":{
        "match":96,
        "enemy":99
    },
    "inactive":false,
    "userid":"7499627801677657669",
    "staff":false,
    "username":"7499627801677657669",
    "isAdmin":false
}
```
The staff and isAdmin tag was interesting on first sight. I tried to alter it in some other requests and response, but nothing interesting happend. 

However, investigating the reponse of the first request to load the staff profile showed some interesting data. It is a lot of JSON data with a lot of tags. The tag "staff" obviously is true, but also isAdmin is set to true on this profile. While reading through the data and comparing it with the profile chart on the app, we found a tag with data that was not visible on the app. Besides the location (New York), which is also visible, there are tags "neighbourhood" and "home_neighbourhood" which are set to "Chelsea" on this profile. The tag "distance" show the distance between both users. With more accounts and the actual distance of these that could be exploited to triangluate the target users exact location!
There is also hidden information that could help scammers or people with bad intentions. Alice is her username for some reason. If you look at my JSON data, my username is my user id.
```=json
    "nudge":{
        "text":"Alice is looking for people near Manhattan, NY",
        "type":"wiw",
        "id":"distance",
        "negative":true,
        "hidden":true
    }
```

The app uses staticly linked photos like 
```
https://cdn.okccdn.com/php/load_okc_image.php/images/0x375/1125x1500/0/10209088093963045520.jpeg
```
where the user id is 10209088093963045520.

The whole JSON data looks like this:
```=json
{
    "user":{
        "connection_compatibilities":[
        ],
        "inactive":false,
        "likes":{
            "via_superboost":null,
            "mutual_like_vote":0,
            "recycled":0,
            "they_superlike":null,
            "passed_on":0,
            "they_like":null,
            "you_superlike":0,
            "you_like":null,
            "via_spotlight":null,
            "mutual_like":0,
            "vote":{
            }
        },
        "details":[
            {
                "text":{
                    "text":"Straight, Woman, Cis Woman, Single, Monogamous",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"basics"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_basics.png"
                }
            },
            {
                "text":{
                    "text":"Uses she/her pronouns",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"pronouns"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_pronouns.png"
                }
            },
            {
                "text":{
                    "text":"5\u2019 10\u201d, Average build",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"looks"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_looks.png"
                }
            },
            {
                "text":{
                    "text":"White, Speaks English",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"background"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_background.png"
                }
            },
            {
                "text":{
                    "text":"Doesn't smoke cigarettes, Drinks sometimes, Omnivore",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"lifestyle"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_lifestyle.png"
                }
            },
            {
                "text":{
                    "text":"Has cat(s), Has dog(s)",
                    "text_color":{
                        "blue":53,
                        "green":47,
                        "red":42,
                        "alpha":1
                    }
                },
                "info":{
                    "name":"family"
                },
                "icon":{
                    "icon_color":null,
                    "url":"https://cdn.okccdn.com/media/img/icons/details_family.png"
                }
            }
        ],
        "last_login":1653922874,
        "essays":[],
        "bookmarked":false,
        "location":{
            "country_code":"US",
            "formatted":{
                "distance_unit":"mi",
                "short":"Manhattan, US",
                "standard":"Manhattan, United States",
                "home_neighborhood":"Chelsea",
                "distance":4228.04917,
                "neighborhood":"Chelsea"
            },
            "state_code":"NY",
            "locid":4335338
        },
        "userid":"4170169657126759250",
        "staff":true,
        "isAdmin":true,
        "photos":[],
        "match_genres":{
            "sex":{
                "insufficient":true,
                "you_answered":0,
                "they_answered":0,
                "match":50,
                "mutual_answered":0,
                "label":"Intimacy",
                "rel":"/profile/Alice/questions?Sex=1",
                "enemy":0
            },
            "religion":{
                "insufficient":true,
                "you_answered":2,
                "they_answered":0,
                "match":50,
                "mutual_answered":0,
                "label":"Religion",
                "rel":"/profile/Alice/questions?Religion=1",
                "enemy":0
            },
            "ethics":{
                "insufficient":true,
                "you_answered":0,
                "they_answered":1,
                "match":50,
                "mutual_answered":0,
                "label":"Ethics",
                "rel":"/profile/Alice/questions?Ethics=1",
                "enemy":0
            },
            "other":{
                "insufficient":true,
                "you_answered":5,
                "they_answered":1,
                "match":50,
                "mutual_answered":0,
                "label":"Other",
                "rel":"/profile/Alice/questions?Other=1",
                "enemy":0
            },
            "lifestyle":{
                "insufficient":true,
                "you_answered":5,
                "they_answered":0,
                "match":50,
                "mutual_answered":0,
                "label":"Lifestyle",
                "rel":"/profile/Alice/questions?Lifestyle=1",
                "enemy":0
            },
            "dating":{
                "insufficient":true,
                "you_answered":3,
                "they_answered":0,
                "match":50,
                "mutual_answered":0,
                "label":"Dating",
                "rel":"/profile/Alice/questions?Dating=1",
                "enemy":0
            }
        },
        "last_contacts":{
            "forward":0,
            "reverse":0
        },
        "online":false,
        "userinfo":{
            "realname":"Sarah",
            "gender_letter":"F",
            "gender":"Woman",
            "age":38,
            "join_date":1296597315,
            "rel_status":"Single",
            "location":"Manhattan, United States",
            "orientation":"Straight",
            "staff_badge":"OkStaff",
            "displayname":"Sarah"
        },
        "linked_account":null,
        "username":"Alice",
        "first_message":null,
        "hidden":false,
        "nudge":{
            "text":"Alice is looking for people near Manhattan, NY",
            "type":"wiw",
            "id":"distance",
            "negative":true,
            "hidden":true
        },
        "blocked":false,
        "percentages":{
            "match":100,
            "enemy":0
        }
    },
    "extras":{
        "shadowbox":{
            "likesCap":{
                "name":"likesCapAlist",
                "title":"You\u2019ve sent the max daily likes",
                "actions":[
                    {
                        "target_url":"/upgrade?clear_history=1&cf=likescappaywall",
                        "text":"GET A-LIST"
                    },
                    {
                        "cancel":1,
                        "text":"Cancel",
                        "action":"cancel"
                    }
                ],
                "desc":"Send unlimited likes & find more matches with A-List",
                "icon":"likesCapError",
                "icon_src":"http://cdn.okccdn.com/media/img/mobile_app/illustrations/morelikes.png"
            }
        },
        "hasHeader":true,
        "hasAds":true,
        "draft":null,
        "promo":{
            "feature":"browse",
            "text":"<strong>Browse invisibly</strong>"
        },
        "isVisibleThroughIncognito":false,
        "userInstructions":{},
        "questionPlaceholder":{
            "cta":"Answer",
            "text":"You haven\u2019t answered enough of the same questions.",
            "rel":"/profile/Alice/questions?unanswered=1",
            "strong":"Not enough data",
            "icon":"lock-closed"
        },
        "selfView":false,
        "hasIncognito":false,
        "userGuides":{},
        "isBoosting":false,
        "hasProfilePassing":true,
        "numQuestions":15,
        "isIncognito":false,
        "boostTokenCount":0,
        "canBribe":false,
        "reduxParams":{
            "profile":{
                "percentages":{
                    "match":100,
                    "enemy":0
                },
                "details":[],
                "likes":{
                    "via_superboost":null,
                    "mutual_like_vote":0,
                    "recycled":0,
                    "they_superlike":null,
                    "passed_on":0,
                    "they_like":null,
                    "you_superlike":0,
                    "you_like":null,
                    "via_spotlight":null,
                    "mutual_like":0,
                    "vote":{
                    }
                },
                "bookmarked":false,
                "isVisibleThroughIncognito":false,
                "userInstructions":{},
                "photos":[],
                "viewerUnderstandsOcsLike":true,
                "online":false,
                "currentUserThumb":"https://cdn.okccdn.com/php/load_okc_image.php/images/160x160/160x160/52x197/152x297/2/4918138661936607854.webp?v=3",
                "likesCapRateCardViewCount":0,
                "isBoosting":false,
                "userinfo":{
                    "realname":"Sarah",
                    "gender_letter":"F",
                    "gender":"Woman",
                    "age":38,
                    "join_date":1296597315,
                    "rel_status":"Single",
                    "location":"Manhattan, United States",
                    "orientation":"Straight",
                    "staff_badge":"OkStaff",
                    "displayname":"Sarah"
                },
                "numQuestions":15,
                "location":{
                    "country_code":"US",
                    "formatted":{
                        "distance_unit":"mi",
                        "short":"Manhattan, US",
                        "standard":"Manhattan, United States",
                        "home_neighborhood":"Chelsea",
                        "distance":4228.04917,
                        "neighborhood":"Chelsea"
                    },
                    "state_code":"NY",
                    "locid":4335338
                },
                "isIncognito":false,
                "isLoggedIn":true,
                "boostTokenCount":0,
                "userid":"4170169657126759250",
                "displayname":"Sarah",
                "showNoPhotosBlock":false,
                "hasInstagram":false,
                "viewerUnderstandsOcsPass":true,
                "isSelfView":false,
                "linkedAccount":null,
                "likesCapResetTime":1654394400,
                "nudge":{
                    "text":"Alice is looking for people near Manhattan, NY",
                    "type":"wiw",
                    "id":"distance",
                    "negative":true,
                    "hidden":true
                },
                "essays":[],
                "mobileMessageLink":"/messages?compose=1&userid=4170169657126759250",
                "staffBadge":"OkStaff",
                "blocked":false,
                "username":"Alice",
                "age":38,
                "firstMessage":null
            }
        },
        "hasInstagram":false,
        "lastThreadId":null,
        "canVisit":false,
        "hasLikesRemaining":true,
        "current_user_has_seen_likes_cap":false,
        "notice":{
            "display":{
            },
            "state":null
        },
        "activeAlbumCount":1,
        "shareURL":"/1/apitun/profile/Alice/share",
        "icebreakers":{
            "geo":{
                "neighborhood":"Chelsea",
                "type":"geo"
            }
        },
        "matchAnalysis":{
            "match_pct":50,
            "text":"They need to answer more questions.",
            "id":"insufficient_them",
            "enemy_pct":0,
            "accurate":false,
            "title":"Not enough info"
        },
        "morePhotos":[
        ],
        "isBrowsingAnon":false,
        "hasAgeBlock":false,
        "staffBadge":"OkStaff",
        "blocked":false,
        "canBigMailbox":false,
        "morePhotosAlbumId":"14300816382384942176",
        "lastContact":0,
        "canMessage":false,
        "genreOrder":[
            "sex",
            "religion",
            "ethics",
            "other",
            "lifestyle",
            "dating"
        ],
        "reportingIntro":{
            "blocking":{
                "userGuide":"",
                "shadowbox":{},
                "understands":false
            }
        },
        "profileCommenting":{
            "userGuide":"profile_commenting_photo",
            "understands":true
        },
        "lastOnlineString":"Last online Monday - 5:01pm",
        "showPhotoMessageBlock":false
    }
}
```
More tags are "isOnline", "lastContact", "lastOnlineString" that shows last time online e.g. "Last online Wednesday - 12:11am", "isLoggedIn" and hasInstagram. There is also a section where you can see the "match_genres" what the user answered and what the match value is.

Looking at the match analysis in this data we can see in advance, what the app "thinks" about the match without interacting with the user. 

```=json
    "matchAnalysis":{
        "disposition":"negative",
        "enemy_pct":60,
        "match_pct":78,
        "id":"high_enemy",
        "title":"Y\u2019all got issues",
        "accurate":true,
        "text":"Compatibility looks hazy."
    }
```
This information could be used to change the profile in a way, to match the target user and get a perfect match.

Looking further in the location part shows that the neighbourhood tag is not automatically set. Maybe it's an option when living in a big city. But it shows still the distance and also the distance unit
```=json
"location":{
    "country_code":"AU",
    "formatted":{
        "distance_unit":"mi",
        "short":"Lassnitzh\u00f6he",
        "standard":"Lassnitzh\u00f6he",
        "neighborhood":null,
        "distance":6.281863434
    },
```

## Investigating premium functions

The app offers a tab where the user can see who likes them. As usual, this is behind a pay wall. But here we can also modify the response, when data is loaded in with a request, to remove the locking function.
First the app sends a request to 
```
POST /graphql/SeeWhoLikesYouFeatureStatus HTTP/2
```
Changing the values of "isActive" and "wasEverActive" the response to true, removes the scrolling lock. Sadly the profile are still defaced and still behind a paywall. 
```
{
    "data":{
        "me":{
            "__typename":"User",
            "featureSubscription":{
                "__typename":"FeatureSubscription",
                "id":"SEE_WHO_LIKES_YOU",
                "isActive":true,
                "wasEverActive":true,
                "timeOfMarkedLoss":null,
                "timeOfActualLoss":null
            }
        }
    },
    "extensions":{
    }
}
```

## Liking process
Using the discover tab (where users are able to like/dislike suggested users) the app first loads in a few profiles with pictures. If the user likes or dislikes the presented other user, the app doesn't instantly send a POST request to the API. It seems that the app collects the votes and sends them after a few minutes. Looking at the POST request
```
POST /1/locals/vote HTTP/2
Host: api.okcupid.com
Cookie: override_session=0; secure_login=1; secure_check=1; guest=7499627801677657669; session=7499627801677657669%3A16707096719338247505; authlink=158b116f; _fbp=fb.1.1652979626229.695918336; siftsession=710935292553977095; nano=k%3Diframe_prefix_lock_5%2Ce%3D1654344199164%2Cv%3D1%7Ck%3Diframe_prefix_lock_4%2Ce%3D1654341185691%2Cv%3D1%7Ck%3Diframe_prefix_lock_3%2Ce%3D1653930142760%2Cv%3D1%7Ck%3Diframe_prefix_lock_2%2Ce%3D1653650365181%2Cv%3D1%7Ck%3Diframe_prefix_lock_1%2Ce%3D1652979653689%2Cv%3D1%7Ck%3Diframe_prefix_lock_-1%2Ce%3D1654344250986%2Cv%3D1
Endpoint_version: 1
X-Okcupid-Device-Id: Android; AOSP on IA Emulator; 9; PSR1.180720.117; Unavailable; fwciLTsdTECsUopudsdVN0;
X-Okcupid-App: OkCupid Android App 64.1.0
X-Okcupid-Platform: Android
X-Okcupid-Version: 64.1.0
X-Okcupid-Locale: en
X-Okcupid-Emulator: true
User-Agent: Android 64.1.0
Content-Type: application/json; charset=UTF-8
Content-Length: 1742
Accept-Encoding: gzip, deflate

{
    "votes":[
        {
            "cardType":"user",
            "id":"10148139789107709024",
            "user_data":"CmaiYuuFatwY49Nv5Ci6chBljp77aOI3EtFZ28mKGhNNYe/m9oGA4anxsx2abwO9v+YT6yUY6ZVgtT2sGegwUrU7DvxuDf+vgudjxf11uiJnpK0pm3n4M8OVMXkdHVRJLhrQ424i1FcAv4n0+DM6h75HMCr/GpMusdJNNdtXV9QgBbZq2CZxfnBs3htDUDKA5H8icmnTHAX6tc2I3SKk5b2yflfgVPowZ7Qh+BSVCCSCu/ZZ5BlItm7+HM72zf3tEq3Z2BC/BZcJCR44UB+P6g==",
            "voteValue":"yes"
        },
        {
            "cardType":"user",
            "id":"17006486363134290132",
            "user_data":"YOIEjbS5lKIFSXM48SLq7pa5a/Tj0TMtL/d/fHy390VPqbDxZ23OFbkwM/qMrARsODBCWbMxNKe58w7dE2rtWzQQ5jTSGxRrzvA66y6BYCZILGtzyfcIohYoSGdcldxtr9CCVluvDllIfrcl9pF4dIr3B1DTJkXz/ScjOfHdmmgnZGYqzQGsHsYIAdo20rbQr3xVUkqg+aYr5W80VRHZ18wzGOumIhDmewxXPb3w2S0Uk4JG+uua+FBxtj+UMt2K",
            "voteValue":"yes"
        },
        {},
        {},
        {}
    ]
}
```
we can see the collected votes. Besides the voteValue the app sends encrypted user_data. At the top, we can see the user_id of my account. In the session tag, we can see that the first part is also my user_id.
Playing around and altering these values to like my account with their user_id or liking myself didn't do anything. Decrypting the user data also didn't show anything.

Liking a lot of users, i finally hit the cap. The like remaining till the cap, are saved and updated on the API.
```=json
{
    "likesRemaining":0,
    "likesCapBlankState":{
        "id":"likesCapAList",
        "title":"You\u2019ve sent the max daily likes",
        "subtitle":"Send unlimited likes & find more matches with A-List",
        "buttons":[
            {
                "intent":{
                    "url":"/upgrade?clear_history=1&cf=likescappaywall&source=doubletake"
                },
                "text":"GET A-LIST"
            }
        ],
        "icon_src":"http://cdn.okccdn.com/media/img/mobile_app/illustrations/morelikes.png"
    },
    "user":[
        {
            "via_spotlight":false,
            "status":1,
            "mutual_like":false,
            "notify_good_vote":true
        },
        {
            "via_spotlight":false,
            "status":1,
            "mutual_like":false,
            "notify_good_vote":true
        },
        {
            "via_spotlight":false,
            "status":1,
            "mutual_like":false,
            "notify_good_vote":true
        },
        {
            "via_spotlight":false,
            "status":1,
            "mutual_like":false,
            "notify_good_vote":true
        },
        {
            "via_spotlight":false,
            "status":1,
            "mutual_like":false,
            "notify_good_vote":true
        }
    ],
    "likesCapResetTime":1654394400,
    "likesCapRateCardViewCount":0,
    "shouldTrackHitLikesCap":true
}
```
That is the response from the API after liking the last user. From there liking a user doesn't do anything. The number of times to try to like is tracked and sent with a request: 
```
POST /1/rate_card/view HTTP/2
Host: api.okcupid.com
...
{"promo_id":"alist.likescappaywall"}
```
## Tracking

To log user events this app regularly sends this request with encrypted data to https://data.emb-api.com.

```
POST /v1/log/logging HTTP/1.1
```

This can track events on the app and begins to track even in the register/login process. Thats how they can track user behaviour.

## Conclusion
Looking at the data we got from other users, it can be said that this app leaks a lot of user data. Just by looking at a profile you get information about last login, join date, is boosting, is online and is login, has likes remaining, match analysis without any interaction, sometimes home neighbourhood and a pretty precise distance. Also tags about is staff or is admin are shown. So in theory staff and admins can be avoided. 
It's also interesting that the whole verification with a phone number can be skipped. So in theory if this can be automated, spam accounts could be made with ease, because they also don't care about the email entered. 

## Future work
The app could be modified in a way that the app can present all leaked data, when going to a user profile. And the number skip could possibly be automated.
# MobSec report Plenty Of Fish dating app

## Description
Plenty of Fish is a canadian dating app with the typical like/dislike charts, but also has a area, where user can watch a live stream of other users. It has a lot of recommentation of users in nearly every tab. Without upgrading (paying them some money) the user can like/dislike the charts, write a message to other users and watch a stream. 

Source: https://en.wikipedia.org/wiki/POF_(dating_website)

## Structure of report
1. Description
2. Registration process
3. Login process
6. Obtaining other users data
7. Direct profile access
9. Liking Process
10. Tracking
11. Conclusion

## Registration process
Inspecting the registration process, showed nothing suspicious. A number is required to register. Skipping that didn't work.

## Login process
Logged in we see that most of the communication of the app is encrypted. But some request and responses are still in clear text.

At some early point in the research of the communication, burpsuit captured following request. The api seems to send keys and also an encryption key.
Request:
```
POST /V2/Instance HTTP/1.1
Accept: text/json
Content-Type: application/json; charset=utf-8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 9; AOSP on IA Emulator Build/PSR1.180720.117)
Host: pofapi.sinch.com
Connection: close
Accept-Encoding: gzip, deflate
Content-Length: 325

{
    "ApplicationKey":"d61c01ea-111d-4805-b662-d47cc14320a1",
    "Capabilities":[
        "ice-restart.1",
        "p2p",
        "push",
        "push.2",
        "push.3",
        "push.4",
        "srtp.1",
        "voip.1"
    ],
    "DeviceId":"0f10eeb6-01e6-40ad-8576-f3f391a704d2",
    "DeviceType":2,
    "PushData":"",
    "SdkVersion":"3.17.4",
    "Seed":"0",
    "Signature":"D5K4JWZbFNZxAKFZxmPPWsdcbcM=",
    "UserId":"316992311"
}
```
The Response contains this data
```=json
{
    "Data":{
        "Instance":{
            "UserId":"316992311",
            "ApplicationKey":"d61c01ea-111d-4805-b662-d47cc14320a1",
            "InstanceId":"d26ecbbd-f106-420b-9f4e-9cdeca242e84"
        },
        "Config":{
            "BroadcastChannel":"3c57058e-b8cd-45fb-85ec-3e8654e71f23B",
            "SignalChannel":"3c57058e-b8cd-45fb-85ec-3e8654e71f23S",
            "EncryptionKey":"jzIfVbDioSzztQpun9hqmQ==",
            "BroadcastSubscribeKey":"sub-c-c5e52f20-d446-11e3-b488-02ee2ddab7fe",
            "BroadcastPublishKey":"pub-c-491d2c47-7cf0-4cde-bc39-f282dcec788e",
            "SignalSubscribeKey":"sub-c-56224c36-d446-11e3-a531-02ee2ddab7fe",
            "SignalPublishKey":"pub-c-1ba05dee-92cb-49b5-83e2-96bec2103751",
            "PubSubUri":"rebtelsdk.pubnub.com",
            "Stun":"stun.l.google.com:19302",
            "Turn":"",
            "GcmSenderId":"1017500357356",
            "PubSubSsl":true
        }
    },
    "Result":200,
    "Message":"InstanceCreated"
}
```
Repeating the request gave the same result.

## Obtaining other users data

When clicking on the live stream tab, the app requests the streaming users with:
```
POST /1/functions/sns-video:getBroadcastsByNearbyMarquee HTTP/2
Host: video-api.gateway.pof.com
X-Parse-Session-Token: r:106d3f67bf0096b7b42aed87d7ef8774
X-Parse-Application-Id: sns-video
X-Parse-App-Build-Version: 1501955
X-Parse-App-Display-Version: 4.66.0.1501955
X-Parse-Os-Version: 9
X-Parse-Installation-Id: cf64fa12-ddc9-45ca-89d2-132a1e2b6b21
X-Parse-Client-Key: com.pof.android
User-Agent: Parse Android SDK 1.19.0 (com.pof.android/1501955) API Level 28
Content-Type: application/json
Content-Length: 64
Accept-Encoding: gzip, deflate

{"score":"0","nearMyAge":false,"gender":"female","pageSize":100}
```
The parameters here can be changed an effects the loaded in users. The score is the position in the loaded list and the pageSize is the number of users loaded in. So it does not really change the result. 
The response shows some interesting infos about the users loaded in:
```=json
HTTP/2 200 OK
Date: Wed, 01 Jun 2022 06:49:42 GMT
Content-Type: application/json; charset=utf-8
Set-Cookie: AWSALB=I8EWtteIbhi22vDe81k4AZy6u3YzSGsHF35QcmuUfUFA2SH9x5OW6hut8z6CQZMd+Cka0Wh9VJPrmDVixdgKYslUsLjdTbPxN8o2EU/0cCCTdbKeuEToLoIwJPmL; Expires=Wed, 08 Jun 2022 06:49:42 GMT; Path=/
Set-Cookie: AWSALBCORS=I8EWtteIbhi22vDe81k4AZy6u3YzSGsHF35QcmuUfUFA2SH9x5OW6hut8z6CQZMd+Cka0Wh9VJPrmDVixdgKYslUsLjdTbPxN8o2EU/0cCCTdbKeuEToLoIwJPmL; Expires=Wed, 08 Jun 2022 06:49:42 GMT; Path=/; SameSite=None; Secure
Server: nginx
Vary: Accept-Encoding
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, Content-Type
Etag: W/"2de2-5VUYajM9MfBbl0gefcDp0VXrTKo"

{

    "result":{
        "more":false,
        "score":"6",
        "broadcasts":[
            {
                "createdAt":"2022-06-01T04:48:12.484Z",
                "updatedAt":"2022-06-01T06:49:12.721Z",
                "activeUntil":{
                    "__type":"Date",
                    "iso":"2022-06-01T06:50:12.716Z"
                },
                "reports":[
                    {
                        "userId":"wSDYGOqhEj",
                        "reportedAt":1654063055515
                    }
                ],
                "socialNetwork":{
                    "__type":"Pointer",
                    "className":"SNSSocialNetwork",
                    "objectId":"gBkJOk6EBS"
                },
                "language":"en",
                "broadcasterAge":32,
                "userDetails":{
                    "createdAt":"2001-01-01T00:00:00.0Z",
                    "updatedAt":"2001-01-02T00:00:00.0Z",
                    "profilePic":{
                        "large":"https://pics.pof.com/thumbnails/size480/1186/75/59/4633dd5ee-4bc9-4972-9ccd-ea5071f66b3b.jpg",
                        "square":"https://pics.pof.com/thumbnails/size220/1186/75/59/4633dd5ee-4bc9-4972-9ccd-ea5071f66b3b.jpg"
                    },
                    "birthDate":
...
                    {"id":"fgisiSujdL",
                    "distanceInKm":0,
                    "isBattle":false,
                    "isPoll":false,
                    "isBlindModeActivated":false,
                    "isNextGuest":false,
                    "isNextDateGame":false,
                    "isDateNightModeActivated":false
                },
                {"id":"WBPFgGdBD2",
                "distanceInKm":0,
                "isBattle":false,
                "isPoll":false,
                "isBlindModeActivated":false,
                "isNextGuest":false,
                "isNextDateGame":false,
                "isDateNightModeActivated":false
            },
            {
                "id":"FU92vqVftf",
                "distanceInKm":0,
                "isBattle":false,
                "isPoll":false,
                "isBlindModeActivated":false,
                "isNextGuest":false,
                "isNextDateGame":false,
                "isDateNightModeActivated":false
            },
            {
                "id":"mLqTdSzYxL",
                "distanceInKm":0,
                "isBattle":false,
                "isPoll":false,
                "isBlindModeActivated":false,
                "isNextGuest":false,
                "isNextDateGame":false,
                "isDateNightModeActivated":false
            },
            {
                "id":"0N6EmEjgCP",
                "distanceInKm":0,
                "isBattle":false,
                "isPoll":false,
                "isBlindModeActivated":false,
                "isNextGuest":false,
                "isNextDateGame":false,
                "isDateNightModeActivated":false
            },
            {
                "id":"I0BAAnisdJ",
                "distanceInKm":0,
                "isBattle":false,
                "isPoll":false,
                "isBlindModeActivated":false,
                "isNextGuest":false,
                "isNextDateGame":false,
                "isDateNightModeActivated":false
            }
        ]
    },
    "correlation":{
        "id":"getNearByMarqueeVideos-pof:user:guest-1654066182368",
        "source":"search"
    }
}
```
There are also static links to the profile pictures, but in this case it seems that the link is not directly related to the user id.

In the livestream section several tabs. In one of them the app made a request and the reponse had lat. and longt. of my device in it: 
```=json
HTTP/2 200 OK
Date: Wed, 01 Jun 2022 08:25:05 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 441
Set-Cookie: AWSALB=dYbhTsmIKnBG+GGjJjbBAQie4Q1q4Mon7qnBZTV9jvRzgs5QMY0p+uFNRh3gM/Di1pU7aHSoQ5aqlA3JisQEt5FYyifTrdSLbrn6EWO9t7gKB6EN6S4XRa0ZyD05; Expires=Wed, 08 Jun 2022 08:25:05 GMT; Path=/
Set-Cookie: AWSALBCORS=dYbhTsmIKnBG+GGjJjbBAQie4Q1q4Mon7qnBZTV9jvRzgs5QMY0p+uFNRh3gM/Di1pU7aHSoQ5aqlA3JisQEt5FYyifTrdSLbrn6EWO9t7gKB6EN6S4XRa0ZyD05; Expires=Wed, 08 Jun 2022 08:25:05 GMT; Path=/; SameSite=None; Secure
Server: nginx
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, Content-Type
Etag: W/"1b9-vggWPuhsLr+dI/M0yBAA2g4i//s"

{
    "objectId":"ZLYetyGZzl",
    "createdAt":"2022-06-01T08:25:00.241Z",
    "updatedAt":"2022-06-01T08:25:00.241Z",
    "username":"user_351791324@pof.net",
    "email":"user_351791324@pof.net",
    "birthDate":{
        "__type":"Date",
        "iso":"2001-01-01T08:00:00.000Z"
    },
    "locale":"en-US",
    "latitude":47.1,
    "longitude":15.4,
    "ACL":{
        "*":{
            "read":true
        },
        "ZLYetyGZzl":{
            "read":true,
            "write":true
        }
    },
    "__type":"Object",
    "className":"_User",
    "sessionToken":"r:0d0afaccdf02a64ba34189703ec72ce1"
}
```
But e.g. in the "nearby" tab the lat. and longt. were in the request.

## Direct profile access

To get users data the app sends following request:
```
GET /profile/users/pof:user:351791324 HTTP/2
Host: api.gateway.pof.com
Authorization: Bearer {Bearer Token}
User-Agent: pof/1501955 (id=com.pof.android) android/28 (9) sns/4.24.7 (release) okhttp/???
Accept-Language: en-US
Accept-Encoding: gzip, deflate
```
351791324 is my userid in this case.
The Response shows following:
```=json
HTTP/2 200 OK
Date: Wed, 01 Jun 2022 08:25:09 GMT
Content-Type: application/json
Content-Length: 1215
Set-Cookie: AWSALB=vpTA+JCypYbSVnJgVQqDtc+ks6yPbxuO5EOjL33ih2YRiaJiMfDPxEcUGrWwJ7+xPn3fLgA7w6go6Csidp09wqfvuM336aRwCjlqANzggahGVmXIHEST8f6Y3WJI; Expires=Wed, 08 Jun 2022 08:25:09 GMT; Path=/
Set-Cookie: AWSALBCORS=vpTA+JCypYbSVnJgVQqDtc+ks6yPbxuO5EOjL33ih2YRiaJiMfDPxEcUGrWwJ7+xPn3fLgA7w6go6Csidp09wqfvuM336aRwCjlqANzggahGVmXIHEST8f6Y3WJI; Expires=Wed, 08 Jun 2022 08:25:09 GMT; Path=/; SameSite=None; Secure
Set-Cookie: AWSALB=Z58VQloOFLlbskqgwbpOALVl9lS3Ts2cLuPahp4x1TAtcn2ri47xcFn7B0eNKJZwSBmM3rz1UsvI3tEDke6lIW1H+wssFrJtTdbEAPxC2nKd0J8sUatv1m95sKm4; Expires=Wed, 08 Jun 2022 08:25:09 GMT; Path=/
Set-Cookie: AWSALBCORS=Z58VQloOFLlbskqgwbpOALVl9lS3Ts2cLuPahp4x1TAtcn2ri47xcFn7B0eNKJZwSBmM3rz1UsvI3tEDke6lIW1H+wssFrJtTdbEAPxC2nKd0J8sUatv1m95sKm4; Expires=Wed, 08 Jun 2022 08:25:09 GMT; Path=/; SameSite=None; Secure
Vary: Accept-Encoding
Server: TMG-Gateway/2.0.21

{
    "about":"What is love?\n\nSurfing, traveling, films, good food",
    "bodyType":[
        "athletic"
    ],
    "covidVaccineStatus":null,
    "displayName":"JeffBayzoss1",
    "education":"some_college",
    "ethnicity":[
    ],
    "firstName":"JeffBayzoss1",
    "gender":"male",
    "hasChildren":"not_specified",
    "height":1830,
    "id":"pof:user:351791324",
    "images":[
        {
            "id":null,
            "square":"https://pics.pof.com/thumbnails/size220/1187/70/38/209b67a05-e67c-4e19-bf79-22bee43bb467.jpg",
            "large":"https://pics.pof.com/thumbnails/size480/1187/70/38/209b67a05-e67c-4e19-bf79-22bee43bb467.jpg"
        }
    ],
    "interestedIn":"women",
    "interests":[
    ],
    "languages":null,
    "lastName":null,
    "lastSeen":1654071516963,
    "liveAbout":"What is love?\n\nSurfing, traveling, films, good food",
    "locale":"en-US",
    "location":{
        "city":"Graz",
        "state":"Styria",
        "country":"AT"
    },
    "lookingFor":[
        "dating",
        "friendship"
    ],
    "network":"pof",
    "privacySettings":null,
    "registrationTs":1654065715680,
    "relationshipStatus":null,
    "religion":"not_specified",
    "searchGender":"male",
    "settings":null,
    "smoker":"not_specified",
    "verificationBadges":[
    ],
    "age":21,
    "relations":{
        "id":"pof:user:351791324",
        "following":false,
        "blocked":false,
        "$id":"/users/pof:user:351791324/relations",
        "$type":"resource"
    },
    "$type":"resource",
    "$id":"/users/pof:user:351791324"
}
```
It works also with other user ids, but the Bearer Token, which changes from time to time, needs to be valid. And it also shows nothing to revealing ...
There is also a similar request to the video api:
```
GET /1/users/313614240 HTTP/2
Host: video-api.gateway.pof.com
```
It shows similar response but in this case, making a request to a other user gives an error:
```
HTTP/2 403 Forbidden
```
## Liking process
The liking process in this app is also encrypted. 

## Tracking
Inspecting the communication further showed, that the app logs what tab the user visits and what tab was previous visited:
```
POST /event-ingest/log HTTP/2
Host: api.gateway.pof.com
Authorization: Bearer {Bearer Token}
User-Agent: pof/1501955 (id=com.pof.android) android/28 (9) sns/4.24.7 (release) okhttp/???
Accept-Language: en-US
Content-Type: application/json; charset=UTF-8
Content-Length: 196
Accept-Encoding: gzip, deflate

{
    "events":[
        {
            "body":{
                "selected_tab":"new",
                "previous_tab":"for_you"
            },
            "event":"s_mobile_tmglive_ui_tab_selected",
            "version":2,
            "timestamp":1654096835661,
            "uuid":"d53bf710-aaef-431e-aeed-4c95aa095ea6"
        }
    ]
}
```
It also has some log requests to the API, but these are as well encrpyted.

## Conclusion
For this app it can be said that it does a good job in preventing user data leaked. Besides encrypting a lot of the traffic sent and received, it makes e.g. exposure of the distance harmless, because it's rounded presented in km (which would be a big area to find a person). 
# MobSec report Tinder dating app

## Description
Tinder is the most popular or better most common dating app nowadays. The name Tinder comes from an easily combustible material used to start a fire. It advertises with the simple method of swiping left or right on a users profile if they like them or dislike them. If both users like each other a so called match is made and they can start a conversation.
It's very well made and also very simple in use. Because it has grown so big we didn't expect to find much on this app. But for completeness we have investigated this app as well.

Source: https://tinder.com/about-tinder

## Structure of report
1. Description
2. Registration process
3. Login process
6. Obtaining other users data
7. Direct profile access
8. Investigating premium functions
9. Liking Process
10. Tracking
11. Conclusion
12. Future work

## Registration process
In the registration process was nothing noticeable to be found besides that russian phone number don't work anymore.

## Login process
Starting up the app makes some init requests with collecting some data about the device. One of them has a lot of interesting tags in it:
```=json
POST /v1/open HTTP/2
Host: api2.branch.io
...
{
    "device_fingerprint_id":"1062755633515839194",
    "identity_id":"1062755634115628367",
    "hardware_id":"bca0d57693dc1b8b",
    "is_hardware_id_real":true,
    "brand":"Google",
    "model":"Android SDK built for x86",
    "screen_dpi":440,
    "screen_height":1977,
    "screen_width":1080,
    "wifi":true,
    "ui_mode":"UI_MODE_TYPE_NORMAL",
    "os":"Android",
    "os_version":29,
    "country":"US",
    "language":"en",
    "local_ip":"10.0.2.15",
    "app_version":"13.6.1",
    "facebook_app_link_checked":false,
    "is_referrable":1,
    "debug":false,
    "update":1,
    "latest_install_time":1654617540261,
    "latest_update_time":1654617540261,
    "first_install_time":1654617540261,
    "previous_update_time":1654617540261,
    "environment":"FULL_APP",
    "cd":{
        "mv":"-1",
        "pn":"com.tinder"
    },
    "metadata":{
    },
    "google_advertising_id":"d104807b-7db9-4aa8-b9af-47d3efcde938",
    "lat_val":0,
    "instrumentation":{
        "v1\/open-qwt":"0"
    },
    "sdk":"android4.0.0",
    "branch_key":"key_live_mhkKfK7Br0UHcrNnmV6CVhbosDoGS8JH",
    "retryNumber":0
}
```
Besides the fingerprints of the device it tracks "is_hardware_id_real", if it's connected to the wifi, "local_ip", "latest_install_time", "first_install_time", "latest_update_time" and "previous_update_time".

After that the app sends the current location to the API.
```
POST /v2/meta HTTP/2
Host: api.gotinder.com
...
{"lat":47.0707133,"lon":15.4395033,"force_fetch_resources":true}
```
## Obtaining other users data
Clicking on the "main" tab, where the user can vote sends following request to load the user cards in.
```
GET /v2/recs/core?locale=en HTTP/2
Host: api.gotinder.com
```
The response to this gives us some data about the user:
```=json
{
    "type":"user",
    "user":{
        "_id":"62923183cc00110100309cee",
        "badges":[
        ],
        "bio":"running & \uD83C\uDF55",
        "birth_date":"1994-06-10T16:21:50.584Z",
        "name":"Anita",
        "photos":[
        ],
        "gender":1,
        "jobs":[
        ],
        "schools":[
            {
                "name":"Karl-Franzens-Universität Graz"
            }
        ],
        "city":{
            "name":"Graz"
        },
        "is_traveling":false,
        "show_gender_on_profile":true,
        "recently_active":true,
        "online_now":false,
        "selected_descriptors":[
        ]
    },
    "facebook":{
        "common_connections":[
        ],
        "connection_count":0,
        "common_interests":[
        ]
    },
    "spotify":{
    },
    "distance_mi":1,
    "content_hash":"6P8UoMck7HLF0pIa5f3LHbJI7bcl2CLMTaIPzcLhm9hp1",
    "s_number":1883739694839039,
    "teaser":{
        "type":"school",
        "string":"Karl-Franzens-Universität Graz"
    },
    "teasers":[
        {
            "type":"school",
            "string":"Karl-Franzens-Universität Graz"
        }
    ],
    "experiment_info":{
    },
    "is_superlike_upsell":true,
    "tappy_content":[
    ]
}
```
In this data is the name, birthdate, school but also the distance in miles. More intersting are the tags "is_traveling", "recently_active" and "online_now". But besides that, it doesn't show much. 

## Direct profile access

Looking at the communication when clicking on a user shows a response with almost equal data, with not much revealing data of the user.
```=json
"status":200,
"results":{
    "common_friends":[
    ],
    "common_friend_count":0,
    "spotify_top_artists":[
    ],
    "distance_mi":1,
    "connection_count":0,
    "common_connections":[
    ],
    "bio":"\uD83D\uDC83\uD83C\uDFFC\uD83C\uDFC3\uD83C\uDFFC‍",
    "birth_date":"1994-06-10T16:53:44.966Z",
    "name":"Melanie",
    "jobs":[
    ],
    "schools":[
        {
            "name":"University of Graz",
            "displayed":false
        }
    ],
    "teasers":[
        {
            "type":"sameSchool",
            "string":"University of Graz"
        }
    ],
    "gender":-1,
    "show_gender_on_profile":false,
    "birth_date_info":"fuzzy birth date active, not displaying real birth_date",
    "ping_time":"2014-12-09T00:00:00.000Z",
    "badges":[
    ],
    "photos":[],
    "user_interests":{
        "selected_interests":[
            {
                "id":"it_4",
                "name":"Running"
            },
            {
                "id":"it_28",
                "name":"Reading"
            },
            {
                "id":"it_2016",
                "name":"Dancing"
            },
            {
                "id":"it_10",
                "name":"Brunch"
            },
            {
                "id":"it_7",
                "name":"Travel"
            }
        ]
    },
    "common_likes":[
    ],
    "common_like_count":0,
    "common_interests":[
    ],
    "s_number":3805960890940599,
    "_id":"62817714cfd03201005c2335",
    "is_tinder_u":false
}
```

## Investigating premium functions

The app regularly makes following request where it checks if the user is "boosting" (becomes more suggested to other users).
```
POST /updates?is_boosting=false&boost_cursor=0 HTTP/2
Host: api.gotinder.com
...
{"last_activity_date":"2022-06-07T16:20:00.046Z"}
```
The response has some interesting fields in it with matches, harassing_messages, blocks, etc. ...
```=json
{
    "matches":[
    ],
    "blocks":[
    ],
    "inbox":[
    ],
    "liked_messages":[
    ],
    "harassing_messages":[
    ],
    "lists":[
    ],
    "goingout":[
    ],
    "deleted_lists":[
    ],
    "matchmaker":[
    ],
    "squads":[
    ],
    "last_activity_date":"2022-06-07T16:20:00.046Z",
    "poll_interval":{
        "standard":2000,
        "persistent":120000
    }
}
```
In combination with the request when clicking on the boost icon:
```
POST /boost HTTP/2
Host: api.gotinder.com
```
I thought that could be manipulated to get an effect.
So I tried to change the request before to
```
POST /updates?is_boosting=true&boost_cursor=1 HTTP/2
```
everytime the app would send it and intercepting and changing the response from the boost icon like this.
```=json
{
    "duration":1800000,
    "allotment":1,
    "allotment_used":0,
    "allotment_remaining":1,
    "internal_remaining":1,
    "purchased_remaining":1,
    "remaining":1,
    "boost_refresh_amount":1,
    "boost_refresh_interval":1,
    "boost_refresh_interval_unit":"m",
    "out_of_boost":false
}
```
Repeating this some time trying to trick the app into thinking we have purchased a boost, gave on time a successful popup!
After that the app made some of these requests over some time:
```
GET /v2/profile?include=boost HTTP/2
Host: api.gotinder.com
```
But besides that we didn't have an indicator to see if it really worked.
Some time later Tinder locked the account because of supicious activity until we verify our profile.

## Liking process
The liking process on this app is really straight forward. With a request to this endpoint, the app sends how the user liked the other user.
```=json
POST /like/6289d98ccfd03201005ee2e4 HTTP/2
Host: api.gotinder.com
...
{
    "photoId":"30e94e1c-463d-4c80-90b9-389574ecf2a7",
    "content_hash":"RX8iwbIl2HZhMbhx2SZ5i5qHE3c93cDbheMUZQhXzi5rtmN",
    "super":0,
    "fast_match":0,
    "top_picks":0,
    "undo":0,
    "s_number":7248806679023904
}
```
The app gets a response with a status, a tag if a match happend and the remaining likes.
```
"status":200,"match":false,"likes_remaining":100,"X-Padding":...
```
The response on the other hand is to a other endpoint but with the info in the parameters. 
```
GET /pass/5e2c8af6f057db0100f5f9b9?photoId=4a09f78d-6b36-4e1f-9621-8b0ef2890cb4&content_hash=RX3CGUrjIaEt2pcxOfZlHVFEwFGS8VTghdeCrXuRf55&s_number=5848852989094555 HTTP/2
Host: api.gotinder.com
```
Other then that here isn't really anything interesting to be found within the liking process.

## Tracking
The app regularly checks if it's in background.
```
GET /v2/device-check/android?background=0 HTTP/2
Host: api.gotinder.com
```
It also makes POST requests with users coordinates to update them.
```
POST /v2/meta HTTP/2
Host: api.gotinder.com
...
{"lat":47.0707133,"lon":15.4395033,"force_fetch_resources":true}
```
This request, described above, is really regularly made. Maybe related to the boosting function, which is time based (half an hour once activated).
```
POST /updates?is_boosting=false&boost_cursor=0 HTTP/2
```
This request is made to a marketing service. I think for tracking purposes.
```
POST /api/v6.3/androidevent?app_id=com.tinder&buildnumber=6.3.1 HTTP/2
Host: inapps.appsflyer.com
```
## Conclusion
The app is really well made and we didn't find that much on this app. Not much of user data is leaked to other users especially in comparison to other dating apps. There is still some strange tracking with coordinates, "is_traveling" tag and the data pulled out of the device after login.

## Future work
A deeper investigation of the boost activation with request could be interesting. A comparison how the app acts, when a boost in purchased and how it's visible for other user is necessary.