JobToday is a website to find blue collar jobs easily using features like location based search of candidates and in app chat.
We will exploit this location feature to get the exact coordinates of an arbitrary user.
We can query the endpoint https://api.jobtoday.com/v1/seekers
with the following data.
interface GetParams {experienceInMonthsFrom?: string;experienceInMonthsTo?: string;keyword: string;lat: string;lng: string;pageIndex: string;pageSize: string;sortBy?: string;toRadius: string;};
In the intercepted calls from the Website it also sends a few tokens in the header, surprisingly they are not used to authenticate or otherwise and we can query directly the api.
We then get a list of results that match the keyword
param. This can interestingly be the name of the candidate or some field on the profile.
The response has the following format
interface Response {meta: JobSeekersMeta;data: Array<JobSeeker>;};interface JobSeekersMeta {code: number;errors: Array<any>;totalRecords: number;pageSize: number;pageIndex: number;requestSpecificData: any;};interface JobSeeker {distance: number,languagesWithLevels: Array<any>,id: number,status: number,userType: number,firstname: string,lastname: string,avatarUrl640: string;avatarUrl160: string;lastOnline: number;experienceLength: number;previousExperience: Array<any>;address: string;education: string;languages: string;about: string;createDate: number;countryCode: string;hasPhone: boolean;chatUnlocked: boolean;isPhoneVerified: boolean;city: string;employmentTypes: Array<any>;online: string;avatarUrl: string;profileImagesUrls: Array<string>;key: string;isBanned: boolean;}
On the response object we get a list of users that include the field distance
this is the distance in matters the user is from the coordinates sent in the request GetParams.lat, GetParams.lng
so for every user we get a radios of where they are.
To optimise the smallest radios for all users I made a script that would query the API for #50000
users, then it would save this user list to a sqlite db
including the coordinates of the request.
It will then move the request coordinates to a close point and do the same query, with this new response it would match the users and find the ones that got closer and update the coordinates for those users accordingly.
After a running a couple hours in an area of Seville, Spain (~6km^2
) if found ~11,000 users
and estimate an average location of each user to a radius of ~2km
.
This is not particularly useful but we can optimise it much further if we focus on a single user.
If we triangulate the position of a user recursively getting three new coordinates close to the user radius we can reduce the distance to around 10 meters
in ~10 minutes
You can find the code used here:
Over the course of a few hours a make in the range of ~2k requests
coming from the same ip. This could be easily limited to avoid attack like this.
There was no kind of user auth needed to query candidate data. Requiring a token an validate that the user is not banned and that the account type is recruiter will make this harder and easier to identify
On the request GetParams
the parameter pageSize
is a number that tells the server how many results to send back. In the original request it uses a reasonable number of 50
, nonetheless I have been able to query up to 5000
users in a single request, taking around 20 seconds
to complete and 1.4mb
of size
There is no minimum on how close the distance can get and thus you can retrieve the exact location the user used. There is no use for a location closer than 0.5km
and it will give the user much better privacy added with the next recommendation
GPS coordinates are accurate around 20 meters
so if we set a minimum radius of 0.5km
we should create a fuzzed version on the location in a random point inside this radius. And send this location when requested publicly by the API
I found odd that you can search candidates by names, this allowed me to find friends by first name and validate the location the previous steps found, noteless I don’t see any reason why a employer would need that.