kgretzky / evilginx2

Standalone man-in-the-middle attack framework used for phishing login credentials along with session cookies, allowing for the bypass of 2-factor authentication
BSD 3-Clause "New" or "Revised" License
10.26k stars 1.87k forks source link

Need for Contextual Validation of </body> Tag in JS Injection Mechanic #973

Open M41KL-N41TT opened 8 months ago

M41KL-N41TT commented 8 months ago

injectJavascriptIntoBody function incorrectly places the injected script when a </body> tag is found within a script string, rather than the closing tag of the HTML body.

Here is the current implementation as reference:

func (p *HttpProxy) injectJavascriptIntoBody(body []byte, script string, src_url string) []byte {
    js_nonce_re := regexp.MustCompile(`(?i)<script.*nonce=['"]([^'"]*)`)
    m_nonce := js_nonce_re.FindStringSubmatch(string(body))
    js_nonce := ""
    if m_nonce != nil {
        js_nonce = " nonce=\"" + m_nonce[1] + "\""
    }
    re := regexp.MustCompile(`(?i)(<\s*/body\s*>)`)
    var d_inject string
    if script != "" {
        d_inject = "<script" + js_nonce + ">" + script + "</script>\n${1}"
    } else if src_url != "" {
        d_inject = "<script" + js_nonce + " type=\"application/javascript\" src=\"" + src_url + "\"></script>\n${1}"
    } else {
        return body
    }
    ret := []byte(re.ReplaceAllString(string(body), d_inject))
    return ret
}

This function, with its current setup, can mistakenly inject into a </body> tag if it is found inside a string in the script. There are a couple of ways this could be solved.

A negative look-ahead would've been my first bet but since the Golang regexp stack is meant/enforced to always operate at O(n) and look-ahead categorically breaks the O(n) guarantee, lookaheads aren't in the vanilla regexp package and it would need to be replaced with regexp2 or some other alternative.

RegExes and HTML editing don't really go together, instead of RegExp it should be trying to parse the HTML and use whatever parser to safely add the script. I don't know any Go HTML parser, but luckily there's one HTML parser that every visitor supports: their own browser. Basically what I'm suggesting is to generate the scripts in JS and append them to end of body, all in JS. The JS itself could be injected into first occurance of element start/close tag.

Other solutions exists as well, most are in essence modifying the RegExp pattern. Problem I found with current method, is that it injects/rewrites on each instance of </body> due to ReplaceAllString, even if matched is not a HTML tag and is referenced to in the string of a script tag string

M41KL-N41TT commented 8 months ago

https://regex101.com/r/09nDMW/1