Skip to content

Commit ceed948

Browse files
committed
Add module http_post
Check if credentials are transported over an encrypted channel
1 parent 26d16d0 commit ceed948

File tree

7 files changed

+110
-3
lines changed

7 files changed

+110
-3
lines changed

make_exe.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def build_file_list(results, dest, root, src=""):
110110
"wapitiCore.attack.mod_methods",
111111
"wapitiCore.attack.mod_nikto",
112112
"wapitiCore.attack.mod_permanentxss",
113+
"wapitiCore.attack.mod_http_post",
113114
"wapitiCore.attack.mod_redirect",
114115
"wapitiCore.attack.mod_shellshock",
115116
"wapitiCore.attack.mod_sql",

wapitiCore/attack/attack.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@
5858
"mod_redirect",
5959
"mod_xxe",
6060
"mod_wapp",
61-
"mod_wp_enum"
61+
"mod_wp_enum",
62+
"mod_http_post"
6263
]
6364

6465
# Modules that will be used if option -m isn't used

wapitiCore/attack/mod_http_post.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from httpx import RequestError
2+
3+
from wapitiCore.attack.attack import Attack
4+
from wapitiCore.net.web import Request
5+
from wapitiCore.language.vulnerability import MEDIUM_LEVEL, _
6+
from wapitiCore.definitions.post_http import NAME
7+
8+
9+
MSG_NO_CSP = _("CSP is not set")
10+
MSG_CSP_MISSING = _("CSP attribute \"{0}\" is missing")
11+
MSG_CSP_UNSAFE = _("CSP \"{0}\" value is not safe")
12+
13+
# This module check the basics recommendations of CSP
14+
class mod_http_post(Attack):
15+
"""Evaluate the security level of Content Security Policies of the web server."""
16+
name = "http_post"
17+
18+
async def must_attack(self, request: Request):
19+
# We leverage the fact that the crawler will fill password entries with a known placeholder
20+
if "Letm3in_" not in request.encoded_data + request.encoded_params:
21+
return False
22+
23+
# We may want to remove this but if not available fallback to target URL
24+
if not request.referer:
25+
return False
26+
27+
return True
28+
29+
async def attack(self, request: Request):
30+
try:
31+
page = await self.crawler.async_send(request, follow_redirects=True)
32+
except RequestError:
33+
self.network_errors += 1
34+
return
35+
36+
login_form, username_field, password_field = page.find_login_form()
37+
if not login_form:
38+
return
39+
40+
self.finished = True
41+
42+
if "http://" in login_form.url:
43+
self.log_red(_("Credentials transported over an Unencrypted Channel on : {0}"), login_form.url)
44+
45+
await self.add_vuln_medium(
46+
request_id=request.path_id,
47+
category=NAME,
48+
request=request,
49+
info=_("Credentials transported over an Unencrypted Channel on : {0}").format(login_form.url)
50+
)

wapitiCore/definitions/post_http.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
# This file is part of the Wapiti project (https://wapiti.sourceforge.io)
5+
# Copyright (C) 2021 Nicolas Surribas
6+
#
7+
# This program is free software; you can redistribute it and/or modify
8+
# it under the terms of the GNU General Public License as published by
9+
# the Free Software Foundation; either version 2 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# This program is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU General Public License
18+
# along with this program; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20+
from wapitiCore.language.language import _
21+
22+
TYPE = "vulnerability"
23+
NAME = _("POST HTTP")
24+
SHORT_NAME = NAME
25+
26+
DESCRIPTION = _(
27+
"The application configuration should ensure that SSL is used for all access controlled pages.\\n)."
28+
) + " " + _(
29+
"If an application uses SSL to guarantee confidential communication with client browsers, "
30+
) + " " + _(
31+
"the application configuration should make it impossible to view any access controlled page without SSL."
32+
)
33+
34+
SOLUTION = _(
35+
"Force the use of HTTPS for all authentication requests"
36+
)
37+
38+
REFERENCES = [
39+
{
40+
"title": "OWASP: Insecure Transport",
41+
"url": "https://owasp.org/www-community/vulnerabilities/Insecure_Transport"
42+
},
43+
{
44+
"title": "Acunetix: Insecure Authentication",
45+
"url": "https://owasp.org/www-project-mobile-top-10/2016-risks/m4-insecure-authentication"
46+
}
47+
]

wapitiCore/language_sources/en.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ msgstr ""
175175
"Permanent XSS vulnerability found in {0} by injecting the parameter {1} of "
176176
"{2}"
177177

178+
msgid "Credentials transported over an Unencrypted Channel on : {0}"
179+
msgstr "Credentials transported over an Unencrypted Channel on : {0}"
180+
178181
msgid "Warning: Content-Security-Policy is present!"
179182
msgstr "Warning: Content-Security-Policy is present!"
180183

wapitiCore/language_sources/fr.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ msgstr ""
177177
"Vulnérabilité de XSS permanent trouvée dans {0} via une injection dans la "
178178
"paramètre {1} de {2}"
179179

180+
msgid "Credentials transported over an Unencrypted Channel on : {0}"
181+
msgstr "Identifiants transportés sur un canal non chiffré sur : {0}"
182+
180183
msgid "Warning: Content-Security-Policy is present!"
181184
msgstr "Avertissement: L'entête Content-Security-Policy est présent!"
182185

wapitiCore/net/page.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -908,8 +908,10 @@ def find_login_form(self):
908908
username_field_idx.append(i)
909909

910910
elif input_type == "text" and (
911-
any(field_name in input_name for field_name in ["mail", "user", "login", "name"]) or
912-
any(field_id in input_id for field_id in ["mail", "user", "login", "name"])
911+
any(field_name in input_name for field_name in \
912+
["mail", "user", "login", "name", "id", "client", "nom"])
913+
or any(field_id in input_id for field_id in \
914+
["mail", "user", "login", "name", "id", "client", "nom"])
913915
):
914916
username_field_idx.append(i)
915917

0 commit comments

Comments
 (0)