0-Day, Copy and Paste ReDoS in github.com
2023-08-05 09:00:53

Summary

In summary, I found a Self-ReDoS vulnerability in the issue feature of github.com and reported it to HackerOne.


Analysis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function onPaste$1(event) {
const { currentTarget: el } = event;
if (shouldSkipFormatting(el))
return;
if (!event.clipboardData)
return;
const textToPaste = generateText(event.clipboardData);
if (!textToPaste)
return;
event.stopPropagation();
event.preventDefault();
const field = event.currentTarget;
if (field instanceof HTMLTextAreaElement) {
insertText(field, textToPaste);
}
}

Once the user performs a paste action, the onPaste$1() function is executed first. Upon examining the code of this function, you can observe that it calls the generateText() function, passing the ClipboardData as an argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function generateText(transfer) {
if (Array.from(transfer.types).indexOf('text/html') === -1)
return;
const html = transfer.getData('text/html');
if (!/<table/i.test(html))
return;
const parser = new DOMParser();
const parsedDocument = parser.parseFromString(html, 'text/html');
let table = parsedDocument.querySelector('table');
table = !table || table.closest('[data-paste-markdown-skip]') ? null : table;
if (!table)
return;
const formattedTable = tableMarkdown(table);
return html.replace(/<meta.*?>/, '').replace(/<table[.\S\s]*<\/table>/, `\n${formattedTable}`);
}

In the generateText() function, the first logic checks the type of the received Clipboard data. If the type is ‘text/html’, the function immediately returns. However, since the type is divided using Array.from(), it bypasses the aforementioned filtering. Additionally, the Clipboard data must contain the value `<table` to meet the condition. Once these conditions are met, the function uses DomParser to parse the Clipboard data and checks if there is a table tag within the parsed Element. Subsequently, the function employs replace() method to replace the meta tags in the html variable with an empty value.

First Redos gadget

1
/<meta.*?>/

However, looking at the regular expression used here, it appears vulnerable to ReDos.


Indeed, when I tried using ReDos Checker, I was able to confirm that it is vulnerable.

According to the above, performing an infinite check on the value <meta causes ReDoS (Regular Expression Denial of Service). However, when using .getData(‘text/html’) to retrieve values, it automatically adds the value <meta charset='utf-8'> to the beginning of the content, preventing ReDoS from occurring.

Second Redos gadget

1
/<table[.\S\s]*<\/table>/

However, the second regular expression is also vulnerable to ReDoS, so I have decided to perform a ReDoS attack using it. According to the aforementioned report, the parsed data must contain the FORM tag element unconditionally.

The ReDoS payload will be written as <table<table<table<table and similar patterns. However, since this is not a valid table tag, it cannot bypass the IF statement. But if we close it with </table> and then use <table> to insert the table tag element, ReDoS will not occur. Therefore, to bypass this logic, I have inserted an arbitrary <div> tag to add the <table> tag.

1
'<table'.repeat(99999) + '<div><table></div>'

So, the payload has been created as described above

0-day validation

Now, it is confirmed that ReDoS is functioning properly. By applying this payload in a GitHub issue, it should work as intended. The exploit is based on the table tag rather than the meta tag, enabling the execution of a 0-day ReDoS attack.


Applying 0-Day to Github.com

For this test, I have set up the breakpoint as described above.
If ReDoS does not occur within the generateText() function, the code execution will proceed directly without any delay from line 352. However, if ReDoS occurs within the generateText() function, due to the DoS effect, line 352 will not be executed immediately after line 351.

Let’s watch the video!

In the first scenario, the result shows the insertion of a non-vulnerable payload, where ReDoS does not occur within the generateText() function. As a result, the code execution proceeds smoothly without any delays, starting from line 352.

In the second scenario, the result displays the insertion of a ReDoS payload. ReDoS occurs within the generateText() function, causing a significant delay in that section of the code. As seen in the video, generateText() function stays for an extended period.


Reference

Prev
2023-08-05 09:00:53
Next