TikTok Targeted OSINT Investigations (P1 - User)
You get a new case to investigate and it's a young social media user with no Facebook, no Twitter, just TikTok.
Quite often I get a new case to investigate and it's a young social media user with no Facebook, no Twitter, just TikTok, with that in mind I thought I would outline the steps I take to run targeted OSINT investigations on TikTok.
In Part one we focus on the USER.
TikTok was the second most downloaded app in 2019. 800 millions of people worldwide use the “[…] Chinese video-sharing social networking service owned by ByteDance, a Beijing-based company founded in 2012 by Zhang Yiming”. I don’t get why and that’s one of the many reasons I realize I am getting old. Whatever.
Setup
TikTok was designed as a mobile app so forget about going browser only. In addition to your favorite browser, you will need a way to run the app, be it on your investigative smartphone or using an android emulator.
For this setup, I would add a way for you to easily grab screenshots on your smartphone/emulator. For me, it’s scrcpy and an old Samsung A3.
Then, you should create a proxy on a computer connected to the same network as your smartphone. I will use Fiddler as a proxy and I described my setup in a previous article.
I will not show you any trivial tips like installing TikTok or searching for your target using the magnifying glass.
OPSEC
Just before diving in, I want to touch on operations security. The requests made by the app to the server have the form of a looong URL:
https://api2-16-h2-eagle-useast2a.musical.ly/aweme/v1/user/profile/self/?address_book_access=1&from=0&os_api=23&device_type=SM-A300FU&ssmix=a&manifest_version_code=2021507050&dpi=240&uoo=0&carrier_region=REDACTED_COUNTRY_CODE®ion=REDACTED_COUNTRY_CODE&carrier_region_v2=REDACTED_MCC&app_skin=white&app_name=musical_ly&version_name=15.7.5&timezone_offset=3600&ts=1587234459&ab_version=15.7.5&residence=REDACTED_COUNTRY_CODE&pass-route=1&pass-region=1&is_my_cn=1¤t_region=REDACTED_COUNTRY_CODE&ac2=wifi&app_type=normal&ac=wifi&host_abi=armeabi-v7a&update_version_code=2021507050&channel=googleplay&_rticket=1587234461947&device_platform=android&iid=REDACTED_ID&build_number=15.7.5&locale=en&op_region=REDACTED_COUNTRY_CODE&version_code=150705&timezone_name=REDACTED_REGION%2FREDACTED_CITY&account_region=REDACTED_COUNTRY_CODE&openudid=b2082e20fd0e7460&sys_region=REDACTED_COUNTRY_CODE&device_id=REDACTED_ID&app_language=en&resolution=540*960&os_version=6.0.1&language=en&device_brand=samsung&aid=1233&mcc_mnc=REDACTED_MCC_MNC
As you can see in the parameters sent, the app gives away a lot of information on the user, its location, every detail on the device being used, even the MCC (Mobile Country Code) and the MNC (Mobile Network Code) which are correct, you can trust me.
This should not really come as a surprise but be aware that apps in general are real OPSEC nightmares. You have to protect a lot more than your IP address when you are using a device that also uses GPS and Cellular towers to get a position, especially if giving away these information may compromise your security or your investigation.
Target acquired
My target’s name is “dr cannabis”. My only task in this investigation is to search for this user on TikTok so I will not show you how I would also sherlock it.
Fiddler and scrcpy are on. Let’s start. Launch the app, hit discover and enter the username or the full name or whatever information you are looking for. The search returns a lot of hits but I’m interested in the first one, @user3k25jojtkg. A banner briefly appears, telling me the account was banned. On your proxy, look for an URL containing /aweme/v1/user/profile/other/?
Launching a browser and pointing it to https://www.tiktok.com/@user3k25jojtkg gives you a web overview of the profile. Notice that you don’t get any banner or alert about this profile being banned on the web version.
Identification
First I want to gather unique identifiers for this specific profile. From what I see on the web overview, its source and from the app screen and the intercepted JSON file, a TikTok user has 3 identifiers:
- source:
uniqueID
/ JSON:unique_id
= user3k25jojtkg - source:
nickName
/ JSON:nickname
= Dr. Cannabis - source:
userId
/ JSON:uid
= 6812758758319391749
There are five more identifiers that you will only find in the JSON response when you open the profile in the app:
- JSON :
youtube_channel_id
&youtube_channel_title
= blank - JSON :
twitter_id
&twitter_name
= blank - JSON:
ins_id=justgrowcannabis
Tests show that this identifier is the Instagram account linked with the TikTok account (https://www.instagram.com/justgrowcannabis/)
Profile images
You cannot view a large version of the profile picture on the web version or the app. But again, turning to the source of the page will reveal the URLs for the actual picture. A quick and dirty way to do it is to copy the whole source text and run it through CyberChef and bake it with the “Extract URLs” recipe.
From the “Dr. Cannabis” web page source, I get 77 URLs back from which I extract 2 uniquely identified addresses to JPEG files:
https://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_100x100.jpeg
https://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_720x720.jpeg
Using the same technique on the JSON response gives less URLs more images:
http://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_720x720.jpeg
http://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_100x100.jpeg
http://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_300x300.webp
http://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_168x168.webp
http://p16.muscdn.com/img/musically-maliva-obj/1664062752513030~c5_1080x1080.jpeg
Notice that there’s a *_1080x1080.jpeg image available, a better resolution than the one available in the source page. Actually, you can just grab the 720x720 URL and change the last part from 720x720.jpeg
to 1080x1080.jpeg
for the same result.
But there might be another big difference in the profile picture. In my last article, I showed the difference between the web page and the app profile for “lorengray”:
The app profile has a “video icon” and you can find the URL in the JSON response:
"video_icon": {
"uri": "musically-maliva-obj/1660344574058502",
"url_list": [
"http://p16.muscdn.com/img/musically-maliva-obj/1660344574058502~tpl-1_q65.webp",
"https://p16.muscdn.com/img/musically-maliva-obj/1660344574058502~tpl-1_q65.webp"
]
},
I tried to find a way to deduce the URL for the video icon from the image URL but I still haven’t found it.
Information
Extended information
Signature
The profile includes a field that is called signature
in the source or the JSON response. The value for this field on the “Dr. Cannabis” account is: “No bio yet”. From what I saw on other profiles this is not the default value for someone who doesn’t define his bio.
The JSON adds a signature_language
that could be interesting. For the target account we get the following value: signature_language=en
Birthdate
The JSON response contains two objects that seem related to a birthdate:
birthday
birthday_hide_level
For many profiles I visited (target’s included), the birthday object equals to 1900–01–01.
Gender
The gender
object is present in the JSON response only.
Location
There are several objects in the JSON response that may indicate a location:
country
province
district
location
city
iso_country_code
I have scrolled through dozens of accounts and I am yet to find any data in these objects.
“Toast” status
When I opened the target’s profile on the app, I briefly saw a banner stating that this account was banned. This parameters appears in the general_permission
object in the profile JSON response.
For “Dr. Cannabis”, the values are the following:
"general_permission": {
"follow_toast_type": 2,
"profile_toast": "This account was banned due to multiple Community Guidelines violations.",
"share_profile_toast": "This account was banned and can't be shared.",
"follow_toast": 2,
"shop_toast": 1,
"share_toast": 1
This object is not present when the profile is not banned.
Following/Followers/Likes
The profile shows some basic information like the number of accounts the target follows and the number of followers he has . If you need the exact number of followers/following, you will have to dig it up from the source of the page or the JSON response. For the JSON, look for /aweme/v1/user/following/list/? and/or /aweme/v1/user/follower/list/?
Following: source followingCount
/ JSON following_count
Followers: source followerCount
/ JSON follower_count
You will also get the number of likes but again, the exact number has to be extracted from source or JSON.
source: heartCount
/ JSON: total_favorited
Note that if you use the web view, you cannot access the list of followers/following, you have to use the app. Exporting the list of users following or being followed will require you to get the JSON responses. The number of users returned in one JSON response is 20:
https://api2-16-h2-useast2a.musical.ly/aweme/v1/user/follower/list/?user_id=REDACTED&sec_user_id=REDACTED_&max_time=0&count=20&offset=0&source_type=2&address_book_access=1&gps_access=2&vcd_count=0&carrier_region_v2=228&app_skin=white&ts=1587235310&host_abi=armeabi-v7a&_rticket=1587235310939&op_region=REDACTED&mcc_mnc=REDACTED&
No, I am not good enough to get more users in one response by manipulating the URL!
Exporting the users comes with a price: you will have to scroll through multiple screens (23661/20=1183.05 !!!!!!) and export all the JSON responses. Then you just have to parse them for the users information.
The good thing is that the JSON format for each user is a very good summary of every information available. And actually, you get more information than when you open the profile of a user!
You will easily find the relevant identifiers (uid
, nickname
,unique_id
and signature
) in this new JSON response. Exporting the profiles to a more readable format (*.csv, *.xlsx) is easy, you will find plenty of tools to do that. It’s a more or less easy way of exporting the following/followers of an account.
The extra information contained in the follower/following description is worth its own category:
Extended information
There are 110 objects in the JSON response for the following request which is 15 more than in the profile JSON Response. Combined we get a total of a 165 unique objects we can use to collect information about our target. So I strongly suggest that after checking the profile of your target, you also find a user he is following. Open this profile and find its followers. Scroll through the followers until you find your target to get his profile as a follower.
Here are some extended information about “Dr. Cannabis” account:
current_region=CA
language=en
unique_id_modify_time=1550569152
region=CA
region_of_residence=CA
The language is obviously english. From what I saw in other profiles, the region’s value is the ISO 3166–1 alpha-2 country code. Here it is Canada. I had a lot of hope when I saw the unique_id_modify_time
object. Sadly, the value was the same for all the accounts I checked.
The JSON response contains more promising objects that had negative values (or no values) for “Dr. Cannabis”:
is_binded_weibo=false
weibo_name=is_phone_binded=false
bind_phone=has_email=false
google_account=school_name=
hide_location=false
create_time=0
is_verified=false
There’s a another object in this response that was equal to zero in “Dr Cannabis” account but that contained a UNIX Timestamp in other accounts:download_prompt_ts
This UNIX Timestamp (converted with CyberChef or your favorite converter) returned dates and times that made sense but I could not find which one!
To summarize, to create a full profile for your target, you have to obtain the web page and its source, the app account and its JSON and the follower/following profile JSON. An alternative technique is to get information about the videos your target posted. In the JSON files describing the posts, you will also get the extended information you want to collect (see my articles on videos and on a script to facilitate the collection)
This article is a work in progress and part one in my series, but I will stop attacking the same JSON files here. In the second part, I try to analyze the posts and related elements.
Stay safe and happy proxying!
List of Objects collected in JSON responses
GET /aweme/v1/user/profile/other/?
apple_account
avatar_168x168/uri
avatar_168x168/url_list/0
avatar_300x300/uri
avatar_300x300/url_list/0
avatar_larger/uri
avatar_larger/url_list/0
avatar_medium/uri
avatar_medium/url_list/0
avatar_thumb/uri
avatar_thumb/url_list/0
aweme_count
birthday
birthday_hide_level
city
commerce_user_info
commerce_user_level
country
cover_url/0/uri
cover_url/0/url_list/0
custom_verify
district
dongtai_count
enterprise_verify_reason
extra/now
favoriting_count
follow_status
follower_count
follower_status
following_count
gender
general_permission/follow_toast
general_permission/follow_toast_type
general_permission/profile_toast
general_permission/share_profile_toast
general_permission/share_toast
general_permission/shop_toast
ins_id
is_activity_user
is_block
is_blocked
is_effect_artist
is_gov_media_vip
is_star
iso_country_code
live_commerce
location
log_pb/impr_id
message_chat_entry
mplatform_followers_count
nickname
original_musician/digg_count
original_musician/music_count
original_musician/music_used_count
profile_tab_type
province
recommend_reason_relation
recommend_user_reason_source
room_id
sec_uid
secret
share_info/bool_persist
share_info/share_desc
share_info/share_image_url/uri
share_info/share_image_url/url_list/0
share_info/share_qrcode_url/uri
share_info/share_qrcode_url/url_list
share_info/share_title
share_info/share_title_myself
share_info/share_title_other
share_info/share_url
share_info/share_weibo_desc
short_id
show_favorite_list
signature
signature_language
sync_to_toutiao
tab_settings/private_tab/private_tab_style
tab_settings/private_tab/show_private_tab
total_favorited
twitter_id
twitter_name
uid
unique_id
verification_type
video_cover
video_icon/uri
video_icon/url_list
watch_status
with_commerce_enterprise_tab_entry
with_commerce_entry
with_fusion_shop_entry
with_new_goods
youtube_channel_id
youtube_channel_title
GET /aweme/v1/user/following/list/?
GET /aweme/v1/user/follower/list/?
accept_private_policy
account_region
allow_be_located
apple_account
authority_status
avatar_168x168/uri
avatar_168x168/url_list/0
avatar_300x300/uri
avatar_300x300/url_list/0
avatar_larger/uri
avatar_larger/url_list/0
avatar_medium/uri
avatar_medium/url_list/0
avatar_thumb/uri
avatar_thumb/url_list/0
avatar_uri
aweme_hotsoon_auth
aweme_hotsoon_auth_relation
bind_phone
bio_info_permission
birthday
birthday_hide_level
comment_filter_status
comment_restrict
comment_setting
commerce_user_level
constellation
cover_url/0/uri
cover_url/0/url_list/0
cover_url/0/url_list/1
cover_url/0/url_list/2
create_time
current_region
custom_verify
cv_level
default_download_setting
download_prompt_ts
download_setting
duet_setting
enterprise_verify_reason
follow_status
follower_status
gender
google_account
has_email
has_insights
has_orders
hide_location
hide_search
ins_id
is_ad_fake
is_binded_weibo
is_gov_media_vip
is_mirror
is_phone_binded
is_star_atlas
is_verified
language
live_agreement
live_agreement_time
live_commerce
live_verify
need_recommend
neiguang_shield
nickname
original_music_cover
original_music_qrcode
prevent_download
react_setting
reflow_page_gid
reflow_page_uid
region
region_of_residence
register_from
room_id
school_name
school_poi_id
school_type
sec_uid
secret
share_qrcode_uri
shield_comment_notice
shield_digg_notice
shield_follow_notice
short_id
signature
special_lock
status
story_open
twitter_id
twitter_name
uid
unique_id
unique_id_modify_time
user_canceled
user_mode
user_period
user_rate
verification_type
verify_info
video_icon/uri
video_icon_virtual_URI
weibo_name
weibo_schema
weibo_url
weibo_verify
with_commerce_entry
with_fusion_shop_entry
youtube_channel_id
youtube_channel_title