Skip to content

Commit

Permalink
Add specific module for LDAP injection
Browse files Browse the repository at this point in the history
  • Loading branch information
devl00p committed Jul 30, 2024
1 parent 20bcd67 commit d08fc52
Show file tree
Hide file tree
Showing 27 changed files with 1,113 additions and 277 deletions.
2 changes: 1 addition & 1 deletion tests/attack/test_mod_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def test_warning_false_postitives():
"<b>Warning</b>: file_get_contents() expects parameter 1 to be a valid path, string given in "
"<b>/home/blah/public_html/skin/blah/page-boatprice.php</b> on line <b>34</b><br />"
)
assert find_warning_message(pattern, "http://wapiti3.ovh/e.php\0") is None
assert find_warning_message(pattern, "http://wapiti3.ovh/e.php[NULL]") is None

pattern = (
"<b>Warning</b>: file_get_contents(): Filename cannot be empty in "
Expand Down
9 changes: 3 additions & 6 deletions tests/attack/test_mod_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@ def process(http_request):

assert persister.add_payload.call_count
# One request for error-based, one to get normal response, four to test boolean-based attack
# Five requests for the ldap injection attack
assert respx.calls.call_count == 11
assert respx.calls.call_count == 6


@pytest.mark.asyncio
Expand All @@ -186,8 +185,7 @@ async def test_negative_blind():
# - 1 request for error-based test
# - 1 request to get normal response
# - 2*3 requests for the first test of each "session" (as the first test fails others are skipped)
# - 5 requests for the ldap injection attack
assert respx.calls.call_count == 13
assert respx.calls.call_count == 8


@pytest.mark.asyncio
Expand Down Expand Up @@ -250,5 +248,4 @@ def process(http_request):
# - 1 request for boolean True test without parenthesis => this check fails
# - 2 requests for boolean False test WITH parenthesis
# - 2 requests for boolean True test WITH parenthesis
# - 5 requests for the ldap injection attack
assert respx.calls.call_count == 14
assert respx.calls.call_count == 9
1 change: 1 addition & 0 deletions tests/integration/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test_mod_cms \
test_mod_htaccess \
test_mod_http_headers \
test_mod_https_redirect \
test_mod_ldap \
test_mod_exec \
test_mod_file \
test_mod_log4shell \
Expand Down
10 changes: 10 additions & 0 deletions tests/integration/test_mod_ldap/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM php:8.0-apache

RUN \
apt-get update && \
apt-get install libldap2-dev -y && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu/ && \
docker-php-ext-install ldap

ADD ./src/public /var/www/html/
90 changes: 90 additions & 0 deletions tests/integration/test_mod_ldap/assertions/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash

# Symlink this file to each module so it can
# check the assertions.

#define some colors
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # no colors

# exit upon any error
set -o errexit

# exit upon using undeclared variables
set -o nounset

die(){
echo >&2 "$@"
exit 1
}

# Ensure we are on the good execution directory
cd "$(dirname "$0")"
MODULE_NAME=$(basename "$(realpath ..)")

# get all the assertions (paths to text files that we want to
# find in the report).
# 1 assertion per target, one or more targets per module,
# assertions can be found in the same directory than this file
declare -a assertions
mapfile -t assertions < <(find . -name "*.json")
if [[ ${#assertions[@]} -eq 0 ]]; then
die "Error: No assertions found in module ${MODULE_NAME}"
fi

# Since the assertions and the target reports must have the same name
# (except their extention names, .json and .out), we extract the targets names
# from the assertion array. We also use their names to log some informations
# so keeping an array is somewhat useful
declare -a targets_name
for i in "${assertions[@]}"; do
targets_name+=("$(basename "$i" .json)")
done

# We store the target reports paths in an array with respect to the target_name
# array order. That way, our third array is ordered with the 2 other ones and
# we can safely use indexes to compare
declare -a outputs
for target in "${targets_name[@]}"; do
outputs+=("$(realpath "../../.test/${MODULE_NAME}/${target}.out")")
done
if [[ ${#outputs[@]} -eq 0 ]]; then
die "Error: No targets found in module ${MODULE_NAME}"
fi

# A special case is if we don't get the same number of reports as they are targets.
# Wapiti may not detect some targets or it can be due to various misconfigurations
# so we check the size of each array to be sure they match
if [[ ${#assertions[@]} -ne ${#targets_name[@]} || ${#targets_name[@]} -ne ${#outputs[@]} ]] ; then
die "Error: different number of reports/assertion files, found ${#outputs[@]} outputs for ${#assertions[@]} assertions"
fi

# Function to remove PHPSESSID from cookie in http_request
remove_phpsessid() {
jq 'walk(
if type == "object" and has("http_request") then
.http_request |= gsub("cookie: PHPSESSID=[^;\\n]*;?"; "cookie: ")
else
.
end
)' "$1"
}

EXIT_CODE=0
# Comparing outputs and assertions :
for i in "${!outputs[@]}"; do
processed_output=$(remove_phpsessid "${outputs[$i]}")
processed_assertion=$(remove_phpsessid "${assertions[$i]}")
if [[ "$processed_output" != "$processed_assertion" ]]; then
echo -e "Assertion $(basename "${assertions[$i]}" .json) of module ${MODULE_NAME} is ${RED}not respected:${NC}"
echo "< : assertion"
echo "> : output"
diff <(echo "$processed_output" | jq --sort-keys .) <(echo "$processed_assertion" | jq --sort-keys .) || echo "---End of diff of assertion $(basename "${assertions[$i]}" .json) module ${MODULE_NAME}---"
EXIT_CODE=1
else
echo -e "Assertion $(basename "${assertions[$i]}" .json) of module ${MODULE_NAME} is ${GREEN}respected${NC}"
fi
done

exit $EXIT_CODE
64 changes: 64 additions & 0 deletions tests/integration/test_mod_ldap/assertions/ldap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"vulnerabilities": {
"LDAP Injection": [
{
"method": "POST",
"path": "/login.php",
"info": "LDAP Injection via injection in the parameter user_id",
"parameter": "user_id",
"module": "ldap",
"http_request": "POST /login.php HTTP/1.1\nhost: ldap\nconnection: keep-alive\nuser-agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0\naccept-language: en-US\naccept-encoding: gzip, deflate, br\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\ncontent-type: application/x-www-form-urlencoded\nreferer: http://ldap/login.php\ncookie: PHPSESSID=fab84e2ce831e3a6f8b5449b046f98c6\ncontent-length: 71\nContent-Type: application/x-www-form-urlencoded\n\nuser_id=%2A%29%29%00nosuchvalue&password=Letm3in_&login=1&submit=Submit",
"wstg": [
"WSTG-INPV-06"
]
},
{
"method": "POST",
"path": "/login2.php",
"info": "LDAP Injection via injection in the parameter user_id",
"parameter": "user_id",
"module": "ldap",
"http_request": "POST /login2.php HTTP/1.1\nhost: ldap\nconnection: keep-alive\nuser-agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0\naccept-language: en-US\naccept-encoding: gzip, deflate, br\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\ncontent-type: application/x-www-form-urlencoded\nreferer: http://ldap/login2.php\ncookie: PHPSESSID=fab84e2ce831e3a6f8b5449b046f98c6\ncontent-length: 71\nContent-Type: application/x-www-form-urlencoded\n\nuser_id=%2A%29%29%00nosuchvalue&password=Letm3in_&login=1&submit=Submit",
"wstg": [
"WSTG-INPV-06"
]
},
{
"method": "POST",
"path": "/login2.php",
"info": "Potential LDAP injection via injection in the parameter password",
"parameter": "password",
"module": "ldap",
"http_request": "POST /login2.php HTTP/1.1\nhost: ldap\nconnection: keep-alive\nuser-agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0\naccept-language: en-US\naccept-encoding: gzip, deflate, br\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\ncontent-type: application/x-www-form-urlencoded\nreferer: http://ldap/login2.php\ncookie: PHPSESSID=fab84e2ce831e3a6f8b5449b046f98c6\ncontent-length: 65\nContent-Type: application/x-www-form-urlencoded\n\nuser_id=alice&password=%2A%29%00nosuchvalue&login=1&submit=Submit",
"wstg": [
"WSTG-INPV-06"
]
},
{
"method": "POST",
"path": "/search.php",
"info": "LDAP Injection via injection in the parameter searchTerm",
"parameter": "searchTerm",
"module": "ldap",
"http_request": "POST /search.php HTTP/1.1\nhost: ldap\nconnection: keep-alive\nuser-agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0\naccept-language: en-US\naccept-encoding: gzip, deflate, br\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\ncontent-type: application/x-www-form-urlencoded\nreferer: http://ldap/search.php\ncookie: PHPSESSID=fab84e2ce831e3a6f8b5449b046f98c6\ncontent-length: 34\nContent-Type: application/x-www-form-urlencoded\n\nsearchTerm=%2A%29%29%00nosuchvalue",
"wstg": [
"WSTG-INPV-06"
]
},
{
"method": "POST",
"path": "/search_exact.php",
"info": "LDAP Injection via injection in the parameter searchTerm",
"parameter": "searchTerm",
"module": "ldap",
"http_request": "POST /search_exact.php HTTP/1.1\nhost: ldap\nconnection: keep-alive\nuser-agent: Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0\naccept-language: en-US\naccept-encoding: gzip, deflate, br\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\ncontent-type: application/x-www-form-urlencoded\nreferer: http://ldap/search_exact.php\ncookie: PHPSESSID=fab84e2ce831e3a6f8b5449b046f98c6\ncontent-length: 34\nContent-Type: application/x-www-form-urlencoded\n\nsearchTerm=%2A%29%29%00nosuchvalue",
"wstg": [
"WSTG-INPV-06"
]
}
]
},
"infos": {
"target": "http://ldap/"
}
}
29 changes: 29 additions & 0 deletions tests/integration/test_mod_ldap/behavior.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"test_mod_ldap": {
"modules": "ldap",
"supplementary_argument": "",
"report_filter_tree": {
"vulnerabilities": {
"LDAP Injection": [
{
"method": "",
"path": "",
"info": "",
"parameter": "",
"module": "",
"http_request": "",
"wstg": ""
}
]
},
"infos": {
"target": ""
}
},
"targets": [
{
"name": "http://ldap"
}
]
}
}
71 changes: 71 additions & 0 deletions tests/integration/test_mod_ldap/docker-compose.setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
version: '3.9'

x-healthcheck_web:
&healthcheck_web
healthcheck:
test: ${DEFAULT_WEB_HEALTHCHECK_COMMAND}
interval: ${DEFAULT_HEALTHCHECKS_INTERVAL}
timeout: ${DEFAULT_HEALTHCHECKS_TIMEOUT}
start_period: ${DEFAULT_HEALTHCHECKS_START_PERIOD}
retries: ${DEFAULT_HEALTHCHECKS_RETRIES}

services:
# Apache container for the ldap module
ldap:
build:
context: ./test_mod_ldap/
dockerfile: Dockerfile
<<: *healthcheck_web
depends_on:
- ldap-server
volumes:
- ./test_mod_ldap/src/public:/var/www/html
networks:
- test-network

# LDAP server
ldap-server:
image: osixia/openldap:1.5.0
container_name: openldap-container
environment:
LDAP_ORGANISATION: "Example Inc."
LDAP_DOMAIN: "example.org"
LDAP_ADMIN_PASSWORD: "mysecretpw"
volumes:
- ldap_data:/var/lib/ldap
- ldap_config:/etc/ldap/slapd.d
- ./test_mod_ldap/ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom
networks:
- test-network
command: --copy-service
healthcheck:
test: ["CMD", "ldapsearch", "-x", "-H", "ldap://localhost", "-b", "dc=example,dc=org", "-D", "cn=admin,dc=example,dc=org", "-w", "mysecretpw"]
interval: 30s
timeout: 10s
retries: 5

# Wapiti container
# requires all the targets containers to work perfectly
wapiti:
build:
context: "../../"
dockerfile: "./tests/integration/wapiti/Dockerfile.integration"
no_cache: true
container_name: wapiti
volumes:
- ./.test:/home/
networks:
- test-network
command: "${TESTS}"
depends_on:
ldap-server:
condition: service_healthy
ldap:
condition: service_healthy

volumes:
ldap_data:
ldap_config:

networks:
test-network:
48 changes: 48 additions & 0 deletions tests/integration/test_mod_ldap/ldif/init.ldif
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Admin user
dn: cn=admin,dc=example,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
userPassword: {SSHA}WEPHcYzko/HnrL/aCm+I1PhL4Btyp/P/

# Organizational Units
dn: ou=users,dc=example,dc=org
objectClass: organizationalUnit
ou: users

dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
ou: groups

# Sample user
dn: uid=jdoe,ou=users,dc=example,dc=org
objectClass: inetOrgPerson
cn: John Doe
sn: Doe
givenName: John
mail: jdoe@example.org
uid: jdoe
userPassword: {SSHA}IZONwW7UI6YaXgXHAe2aMB4PSyZy17VW
description: Password is mustang
title: purple77

# Sample user
dn: uid=acooper,ou=users,dc=example,dc=org
objectClass: inetOrgPerson
cn: Alice Cooper
sn: Cooper
givenName: Alice
mail: acooper@example.org
uid: acooper
userPassword: {SSHA}dLY8YmdSwjRp/K6+mQhucUu5DHo+oExF
description: My favorite singer is tge godfather of punk
title: yellow88

# Sample group
dn: cn=developers,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: developers
member: uid=jdoe,ou=users,dc=example,dc=org


9 changes: 9 additions & 0 deletions tests/integration/test_mod_ldap/src/public/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

const LDAP_HOST = "ldap-server";
const LDAP_PORT = 389;
const LDAP_DC = "dc=example,dc=org";
const LDAP_DN = "cn=admin,dc=example,dc=org";
const LDAP_PASS = "mysecretpw";

?>
6 changes: 6 additions & 0 deletions tests/integration/test_mod_ldap/src/public/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<html>
<a href="login.php">LDAP based login (LDAP Bind)</a><br />
<a href="login2.php">Login based on AND condition on two string attributes</a><br />
<a href="search.php">Seach someone in the directory</a><br />
<a href="search_exact.php">Exact seach someone in the directory</a><br />
</html>
Loading

0 comments on commit d08fc52

Please sign in to comment.