Skip to content

Commit 42db986

Browse files
b-c-dsbcaller
authored andcommitted
ReDoS
I give up the fight. If it's Regular Expression the E should be capitalised. Anyway, ReDoS does look cooler so I guess we'll stick with it (Regexp DoS)
1 parent 55fa729 commit 42db986

File tree

10 files changed

+29
-29
lines changed

10 files changed

+29
-29
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Regexploit
22

3-
Regular Expression Denial of Service (REDoS).
3+
Regular Expression Denial of Service (ReDoS).
44

55
Most default regular expression parsers (non-deterministic finite automata) have unbounded worst-case complexity. Regex matching may be quick when presented with a matching input string. However, certain non-matching input strings can make the regular expression matcher go into crazy loops and take ages to process. This can cause denial of service, as the CPU will be stuck trying to match the regex.
66

77
This tool is designed to:
8-
* find regular expressions which are vulnerable to REDoS
8+
* find regular expressions which are vulnerable to ReDoS
99
* give an example malicious string which will cause catastrophic backtracking
1010

1111
Something something regexes are bad.
@@ -15,7 +15,7 @@ Something something regexes are bad.
1515
This reflects the complexity of the regular expression matcher's backtracking procedure with respect to the length of the entered string.
1616

1717
Cubic complexity here means that if the vulnerable part of the string is doubled in length, the execution time should be about 8 times longer (2^3).
18-
For exponential REDoS with starred stars e.g. `(a*)*$` a fudge factor is used and the complexity will be greater than 10.
18+
For exponential ReDoS with starred stars e.g. `(a*)*$` a fudge factor is used and the complexity will be greater than 10.
1919

2020
For explotability, a cubic complexity or higher is typically required unless truly giant strings are allowed as input.
2121

@@ -34,9 +34,9 @@ Final character to cause backtracking: [^[a-z]]
3434
Example: 'ab' + 'c' * 3456 + '0'
3535
```
3636

37-
The part `c*[a-z]+c+` contains three overlapping repeating groups. As showed in the line `Repeated character: [c]`, a long string of `c` will match this section in many different ways. The worst-case complexity is 3 as there are 3 infinitely repeating groups. An example to cause REDoS is given: it consists of the required prefix `ab`, a long string of `c` and then a `0` to cause backtracking. Not all REDoSes require a particular character at the end, but in this case, a long string of `c` will match the regex successfully and won't backtrack. The line `Final character to cause backtracking: [^[a-z]]` shows that a non-matching character not in the range `[a-z]` is required at the end to prevent matching and cause REDoS.
37+
The part `c*[a-z]+c+` contains three overlapping repeating groups. As showed in the line `Repeated character: [c]`, a long string of `c` will match this section in many different ways. The worst-case complexity is 3 as there are 3 infinitely repeating groups. An example to cause ReDoS is given: it consists of the required prefix `ab`, a long string of `c` and then a `0` to cause backtracking. Not all ReDoSes require a particular character at the end, but in this case, a long string of `c` will match the regex successfully and won't backtrack. The line `Final character to cause backtracking: [^[a-z]]` shows that a non-matching character not in the range `[a-z]` is required at the end to prevent matching and cause ReDoS.
3838

39-
As another example, install a module version vulnerable to REDoS such as `pip install ua-parser==0.9.0`.
39+
As another example, install a module version vulnerable to ReDoS such as `pip install ua-parser==0.9.0`.
4040
To scan the installed python modules run `regexploit-python-env`.
4141

4242
```
@@ -63,7 +63,7 @@ Example: ';0 Build/HuaweiA' + '0' * 3456
6363
...
6464
```
6565

66-
For each vulnerable regular expression it prints one or more malicious string to trigger REDoS. Setting your user agent to `;0 Build/HuaweiA000000000000000...` and browsing a website using an old version of ua-parser may cause the server to take a long time to process your request, probably ending in status 502.
66+
For each vulnerable regular expression it prints one or more malicious string to trigger ReDoS. Setting your user agent to `;0 Build/HuaweiA000000000000000...` and browsing a website using an old version of ua-parser may cause the server to take a long time to process your request, probably ending in status 502.
6767

6868
# Installation
6969

@@ -94,7 +94,7 @@ or via a file
9494
cat myregexes.txt | regexploit
9595
```
9696

97-
Nothing is printed when no REDoS is found.
97+
Nothing is printed when no ReDoS is found.
9898

9999
## Python imports
100100

@@ -109,7 +109,7 @@ N.B. this doesn't parse the python code to an AST and will only find regexes com
109109

110110
## Python code
111111

112-
Parses Python code (without executing it) via the AST to find regexes (with some false positives). The regexes are then analysed for REDoS.
112+
Parses Python code (without executing it) via the AST to find regexes (with some false positives). The regexes are then analysed for ReDoS.
113113

114114
```bash
115115
regexploit-py my-project/stuff.py
@@ -120,7 +120,7 @@ regexploit-py "my-project/**/*.py" --glob
120120

121121
This will use the bundled NodeJS package in `regexploit/bin/javascript` which parses your javascript as an AST with [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser) and prints out all regexes.
122122

123-
Those regexes are fed into the python REDoS finder.
123+
Those regexes are fed into the python ReDoS finder.
124124

125125
```bash
126126
regexploit-js my-module/my-file.js another/file.js
@@ -139,7 +139,7 @@ TODO: not so straight forward to extract the regexes because of the way they are
139139

140140
## Golang / anything using re2
141141

142-
Unless you specifically use a non-deterministic finite automata, Go code is not vulnerable to this type of REDoS. It uses `re2` which does not have catastrophic backtracking.
142+
Unless you specifically use a non-deterministic finite automata, Go code is not vulnerable to this type of ReDoS. It uses `re2` which does not have catastrophic backtracking.
143143

144144
## JSON / YAML
145145

regexploit/bin/regexploit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def javascript(pattern: str, flags: int):
4848

4949
def main():
5050
parser = argparse.ArgumentParser(
51-
description="Parse regexes from stdin and scan them for REDoS"
51+
description="Parse regexes from stdin and scan them for ReDoS"
5252
)
5353
parser.add_argument(
5454
"-f",
@@ -88,7 +88,7 @@ def main():
8888
):
8989
found = True
9090
if isatty and not found:
91-
print("No REDoS found.")
91+
print("No ReDoS found.")
9292
except KeyboardInterrupt:
9393
pass
9494

regexploit/bin/regexploit_csharp.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def handle_file(filename: str, output: TextOutput):
2121
if len(pattern) < 5:
2222
continue # (.+)+
2323
if pattern.count("*") + pattern.count("+") + pattern.count(",}") < 2:
24-
continue # no REDoS possible
24+
continue # no ReDoS possible
2525
try:
2626
logging.debug("%s#%s: %s", filename, regex.lineno, pattern)
2727
parsed = SreOpParser().parse_sre(pattern, regex.flags)
@@ -58,7 +58,7 @@ def handle_file(filename: str, output: TextOutput):
5858
context=context,
5959
)
6060
except Exception:
61-
print(f"Error finding REDoS: {pattern} from {filename} #{regex.lineno}")
61+
print(f"Error finding ReDoS: {pattern} from {filename} #{regex.lineno}")
6262
print(traceback.format_exc())
6363

6464

@@ -68,7 +68,7 @@ def main():
6868
"ignore", category=FutureWarning
6969
) # Some csharp/js regexes are weird
7070
parser = argparse.ArgumentParser(
71-
description="Parse regexes out of C# files and scan them for REDoS"
71+
description="Parse regexes out of C# files and scan them for ReDoS"
7272
)
7373
parser.add_argument("files", nargs="+", help="C# files")
7474
parser.add_argument(

regexploit/bin/regexploit_js.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def handle_line_from_node(line: str, output: TextOutput):
2525
if pattern_len == 8059 and pattern.startswith("\\u{1F3F4}(?:\\u{E0067"):
2626
return # annoying emoji regex
2727
if pattern.count("*") + pattern.count("+") + pattern.count(",}") < 2:
28-
return # no REDoS possible
28+
return # no ReDoS possible
2929
filename = regex["filename"]
3030
lineno = regex["lineno"]
3131
try:
@@ -50,7 +50,7 @@ def handle_line_from_node(line: str, output: TextOutput):
5050
if redos.starriness > 2:
5151
output.record(redos, pattern, filename=filename, lineno=lineno)
5252
except Exception:
53-
print(f"Error finding REDoS: {pattern} from {filename}")
53+
print(f"Error finding ReDoS: {pattern} from {filename}")
5454
print(traceback.format_exc())
5555
elif error := regex.get("error"):
5656
print("ERR", error, regex.get("filename"))
@@ -84,7 +84,7 @@ def main():
8484
"ignore", category=FutureWarning
8585
) # Some js regexes are weird
8686
parser = argparse.ArgumentParser(
87-
description="Parse regexes out of javascript files and scan them for REDoS"
87+
description="Parse regexes out of javascript files and scan them for ReDoS"
8888
)
8989
parser.add_argument("files", nargs="+", help="Javascript or typescript files")
9090
parser.add_argument(

regexploit/bin/regexploit_python_ast.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def handle_file(filename: str, output: TextOutput):
4949
)
5050
except Exception:
5151
print(
52-
f"Error finding REDoS: {regex.pattern} from {filename} #{regex.lineno}"
52+
f"Error finding ReDoS: {regex.pattern} from {filename} #{regex.lineno}"
5353
)
5454
print(traceback.format_exc())
5555

@@ -60,7 +60,7 @@ def main():
6060
warnings.simplefilter("ignore", category=FutureWarning)
6161
warnings.simplefilter("ignore", category=DeprecationWarning)
6262
parser = argparse.ArgumentParser(
63-
description="Parse regexes out of python files and scan them for REDoS"
63+
description="Parse regexes out of python files and scan them for ReDoS"
6464
)
6565
parser.add_argument("files", nargs="+", help="Python files or directories")
6666
parser.add_argument(

regexploit/bin/regexploit_yaml.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def handle(self, elem):
4747
filename=self.filename,
4848
)
4949
except Exception:
50-
print(f"Error finding REDoS: {elem} from {self.filename}")
50+
print(f"Error finding ReDoS: {elem} from {self.filename}")
5151
print(traceback.format_exc())
5252
elif isinstance(elem, list):
5353
for _elem in elem:
@@ -63,7 +63,7 @@ def main(get_object=get_json):
6363
warnings.simplefilter("ignore", category=FutureWarning)
6464
warnings.simplefilter("ignore", category=DeprecationWarning)
6565
parser = argparse.ArgumentParser(
66-
description="Parse regexes out of YAML files (strings, lists and dictionary values) and scan them for REDoS"
66+
description="Parse regexes out of YAML files (strings, lists and dictionary values) and scan them for ReDoS"
6767
)
6868
parser.add_argument("files", nargs="+", help="YAML files")
6969
parser.add_argument(

regexploit/languages/python_node_visitor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ def __init__(self):
2727
self.patterns: List[FoundRegex] = []
2828

2929
def maybe_pattern(self, lineno: int, pattern: str):
30-
"""Check if the pattern could possibly have REDoS: if so, add it."""
30+
"""Check if the pattern could possibly have ReDoS: if so, add it."""
3131
if pattern.count("*") + pattern.count("+") + pattern.count(",}") >= 2:
32-
# Could have REDoS
32+
# Could have ReDoS
3333
# Now check if it still looks like a docstring
3434
if " * * *" in pattern:
3535
return # Looks like cron (of course could just be really silly regex)

regexploit/redos.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def make_redos(
122122
# TODO branches
123123
character_history = [repeated_character]
124124
logging.debug(
125-
"Make REDoS %d %d %s %d",
125+
"Make ReDoS %d %d %s %d",
126126
sequence_start,
127127
continue_from,
128128
repeated_character,
@@ -174,7 +174,7 @@ def make_redos(
174174
repeated_character = new_c
175175
character_history.append(new_c)
176176

177-
# Everything matched! We need to work backwards and find a 'killer' to cause backtracking if we want REDoS
177+
# Everything matched! We need to work backwards and find a 'killer' to cause backtracking if we want ReDoS
178178
logging.debug("Backtracking: %s", character_history)
179179
for current_index in reversed(range(continue_from, len(seq))):
180180
elem = seq.elements[current_index]
@@ -215,7 +215,7 @@ def redos_found(
215215
killer: Optional[Character],
216216
) -> Redos:
217217
# TODO: Try to include some skipped optional parts (like `?`) just to make it nicer
218-
logging.debug("REDoS found")
218+
logging.debug("ReDoS found")
219219
return Redos(
220220
starriness,
221221
Sequence(seq.elements[:start]),

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
version="0.0.1",
99
author="Ben Caller :: Doyensec",
1010
author_email="[email protected]",
11-
description="Find regular expressions vulnerable to REDoS",
11+
description="Find regular expressions vulnerable to ReDoS",
1212
long_description=long_description,
1313
long_description_content_type="text/markdown",
1414
url="https://github.com/doyensec/regexploit",

tests/test_redos.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def test_dollar():
9696

9797

9898
def test_real_cpython_cookielib():
99-
# We don't support the (?!) assertions, but can still find REDoS
99+
# We don't support the (?!) assertions, but can still find ReDoS
100100
LOOSE_HTTP_DATE_RE = r"""^
101101
(\d\d?) # day
102102
(?:\s+|[-\/])

0 commit comments

Comments
 (0)