I Found a Vulnerability in Croniter

(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