(This was in 2021, and the maintainer fixed it the next day.)
croniter is a python library for evaluating cron expressions. Given a cron expression and a datetime, it can calculate the nearest next datetime that matches the given cron expression.
I was fuzzing the croniter library with atheris, and found an expression that caused the python process to eat CPU for about 10 seconds and then crash:
>>> it = croniter("0-1000000000 * * * *", datetime.now())
Killed
When I made the number bigger, I got a MemoryError:
>>> it = croniter("0-1000000000000 * * * *", datetime.now())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 115, in __init__
self.expanded, self.nth_weekday_of_month = self.expand(expr_format)
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 634, in expand
return cls._expand(expr_format)
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 584, in _expand
e_list += (["{0}#{1}".format(item, nth) for item in rng]
MemoryError
And when I made it even bigger, I got an OverflowError:
>>> it = croniter("0-1000000000000000000000000 * * * *", datetime.now())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 115, in __init__
self.expanded, self.nth_weekday_of_month = self.expand(expr_format)
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 634, in expand
return cls._expand(expr_format)
File "/home/user/venvs/croniter-fuzzing/lib/python3.8/site-packages/croniter/croniter.py", line 584, in _expand
e_list += (["{0}#{1}".format(item, nth) for item in rng]
OverflowError: Python int too large to convert to C ssize_t
Clearly, this version of croniter is missing a range check somewhere, and is attempting to do an O(n) operation with a user-supplied n. How bad is it? It depends on where and how the library is used:
- If this can be triggered in a web application by a web request, an attacker can mount an easy DOS attack.
- If a bad expression slips deeper in a scheduling or monitoring system, it could cause crashes or even crash loops in its internal processes.
I reported the issue privately to the maintainer, they acknowledged the issue the same day, and had a fixed version (v1.0.5) out the next day.
I upgraded Healthchecks.io to use the fixed version, and also contacted some of the larger croniter users I could find (Apache Airflow, Sentry.io, Cronitor.io) to let them know about the issue and the fix.
Since then I’ve written a separate, smaller cron expression handling library, cronsim, and switched Healthchecks.io to using it. Some of the other bugs I found by fuzzing croniter were hard to fix due to how the code has evolved. At one point I realized a clean rewrite made more sense.
PS. One extra thing before I go: crontab.guru in Chrome does not like this expression either 🙂
Happy bug hunting,
Pēteris