Skip to content

Commit

Permalink
2.11.26 Add auth to SOAP services
Browse files Browse the repository at this point in the history
  • Loading branch information
webpwnized committed Nov 20, 2024
1 parent a191a7a commit cdd7131
Show file tree
Hide file tree
Showing 7 changed files with 554 additions and 502 deletions.
2 changes: 1 addition & 1 deletion src/includes/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* ------------------------------------------
* @VERSION
* ------------------------------------------*/
$C_VERSION = "2.11.25";
$C_VERSION = "2.11.26";
$C_VERSION_STRING = "Version: " . $C_VERSION;
$C_MAX_HINT_LEVEL = 1;

Expand Down
3 changes: 2 additions & 1 deletion src/webservices/includes/ws-constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
]);

define('ERROR_MESSAGE_METHOD_NOT_ALLOWED', '<?xml version="1.0" encoding="UTF-8"?><error><message>Method Not Allowed. Use POST for this endpoint.</message></error>');
define('ERROR_MESSAGE_UNAUTHORIZED', '<?xml version="1.0" encoding="UTF-8"?><error><message>Unauthorized</message></error>');
define('ERROR_MESSAGE_UNAUTHORIZED_PREFIX', '<?xml version="1.0" encoding="UTF-8"?><error><message>');
define('ERROR_MESSAGE_UNAUTHORIZED_SUFFIX', '</message></error>');

?>
119 changes: 59 additions & 60 deletions src/webservices/soap/ws-dns-lookup.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class LookupException extends Exception {}

// Pull in the NuSOAP library
require_once './lib/nusoap.php';
require_once '../includes/ws-constants.php';

$lServerName = $_SERVER['SERVER_NAME'];

Expand Down Expand Up @@ -47,6 +48,53 @@ class LookupException extends Exception {}
)
);

/**
* Function: authenticateRequest
* Handles request authentication and CORS headers.
*
* @param int $lSecurityLevel The security level.
* @throws InvalidTokenException If the authentication fails.
*/
function authenticateRequest($lSecurityLevel) {

// Set CORS headers
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
header(CONTENT_TYPE_XML); // Set content type as XML

// Handle preflight requests (OPTIONS)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
exit();
}

// Allow only POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
header(CONTENT_TYPE_XML);
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
exit();
}

// Shared: Include the shared JWT token authentication function
require_once '../includes/ws-authenticate-jwt-token.php';

// Shared: Authenticate the user if required
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
try {
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
} catch (InvalidTokenException $e) {
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
header(CONTENT_TYPE_XML);
echo ERROR_MESSAGE_UNAUTHORIZED_PREFIX . 'Unauthorized: ' . htmlspecialchars($e->getMessage()) . ERROR_MESSAGE_UNAUTHORIZED_SUFFIX;
exit();
}
}
}

/**
* Method: lookupDNS
* Performs a DNS lookup for a given target host.
Expand All @@ -69,68 +117,18 @@ function lookupDNS($pTargetHost) {
$LogHandler = new LogHandler($lSecurityLevel);
$Encoder = new EncodingHandler();

require_once '../includes/ws-constants.php';

// Set CORS headers
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
header(CONTENT_TYPE_XML); // Set content type as XML

// Handle preflight requests (OPTIONS)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
exit();
}

// Allow only POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
header(CONTENT_TYPE_XML);
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
exit();
}
// Authenticate the request using the shared function
authenticateRequest($lSecurityLevel);

switch ($lSecurityLevel) {
default:
case SECURITY_LEVEL_INSECURE:
$lProtectAgainstCommandInjection = false;
$lProtectAgainstXSS = false;
$lRequireAuthentication = false;
break;
case SECURITY_LEVEL_MEDIUM:
$lProtectAgainstCommandInjection = false;
$lProtectAgainstXSS = false;
$lRequireAuthentication = true;
break;
case 2:
case 3:
case 4:
case SECURITY_LEVEL_SECURE:
$lProtectAgainstCommandInjection = true;
$lProtectAgainstXSS = true;
$lRequireAuthentication = true;
break;
}

// Shared: Include the shared JWT token authentication function
require_once '../includes/ws-authenticate-jwt-token.php';

// Shared: Authenticate the user if required
if ($lRequireAuthentication) {
try {
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
} catch (InvalidTokenException $e) {
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
header(CONTENT_TYPE_XML);
echo '<?xml version="1.0" encoding="UTF-8"?><error><message>Unauthorized: ' . htmlspecialchars($e->getMessage()) . '</message></error>';
exit();
}
// Validate the target host to protect against command injection, if security is enabled
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
$lProtectAgainstCommandInjection = true;
$lProtectAgainstXSS = true;
} else {
$lProtectAgainstCommandInjection = false;
$lProtectAgainstXSS = false;
}

// Validate the target host to protect against command injection, if security is enabled
if ($lProtectAgainstCommandInjection) {
$lTargetHostValidated = preg_match(IPV4_REGEX_PATTERN, $pTargetHost) ||
preg_match(DOMAIN_NAME_REGEX_PATTERN, $pTargetHost) ||
Expand All @@ -157,7 +155,7 @@ function lookupDNS($pTargetHost) {
}

// Get the current timestamp
$lTimestamp = date('Y-m-d H:i:s');
$lTimestamp = date(DATE_TIME_FORMAT);

// Create a structured response as an associative array
$response = array(
Expand All @@ -184,4 +182,5 @@ function lookupDNS($pTargetHost) {
// Send a fault response back to the client if an error occurs
$lSOAPWebService->fault('Server', "SOAP Service Error: " . htmlspecialchars($e->getMessage()));
}

?>
123 changes: 82 additions & 41 deletions src/webservices/soap/ws-echo.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<?php

class MethodExecutionException extends Exception {}
class CommandExecutionException extends Exception {}

// Include the nusoap library
// Include the nusoap library and required constants
require_once './lib/nusoap.php';
require_once '../includes/ws-constants.php';
require_once '../includes/ws-authenticate-jwt-token.php';

$lServerName = $_SERVER['SERVER_NAME'];

Expand Down Expand Up @@ -37,86 +40,124 @@ class MethodExecutionException extends Exception {}
'',
array(
'message' => array('name' => 'message', 'type' => 'xsd:string'),
'command' => array('name' => 'command', 'type' => 'xsd:string'),
'securityLevel' => array('name' => 'securityLevel', 'type' => 'xsd:string'),
'timestamp' => array('name' => 'timestamp', 'type' => 'xsd:string'),
'output' => array('name' => 'output', 'type' => 'xsd:string')
)
);

// Define the "echoMessage" method
/**
* Function: authenticateRequest
* Handles request authentication and CORS headers.
*
* @param int $lSecurityLevel The security level.
* @throws InvalidTokenException If the authentication fails.
*/
function authenticateRequest($lSecurityLevel) {

// Set CORS headers
header(CORS_ACCESS_CONTROL_ALLOW_ORIGIN);
header('Access-Control-Allow-Methods: POST, OPTIONS'); // Allowed methods
header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Specify allowed headers
header('Access-Control-Expose-Headers: Authorization'); // Expose headers if needed
header(CONTENT_TYPE_XML); // Set content type as XML

// Handle preflight requests (OPTIONS)
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header(CORS_ACCESS_CONTROL_MAX_AGE); // Cache the preflight response for 600 seconds (10 minutes)
http_response_code(RESPONSE_CODE_NO_CONTENT); // No Content
exit();
}

// Allow only POST requests
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(RESPONSE_CODE_METHOD_NOT_ALLOWED);
header(CONTENT_TYPE_XML);
echo ERROR_MESSAGE_METHOD_NOT_ALLOWED;
exit();
}

// Authenticate the user if required
if ($lSecurityLevel >= SECURITY_LEVEL_MEDIUM) {
try {
$lDecodedToken = authenticateJWTToken(); // Authenticate using the shared function
} catch (InvalidTokenException $e) {
http_response_code(RESPONSE_CODE_UNAUTHORIZED);
header(CONTENT_TYPE_XML);
echo ERROR_MESSAGE_UNAUTHORIZED_PREFIX . 'Unauthorized: ' . htmlspecialchars($e->getMessage()) . ERROR_MESSAGE_UNAUTHORIZED_SUFFIX;
exit();
}
}
}

/**
* Define the "echoMessage" method
*
* @param string $pMessage The message to echo.
* @return array An associative array containing the echoed message and metadata.
* @throws MethodExecutionException If there is an error executing the method.
*/
function echoMessage($pMessage) {

try{
try {
// Include required constants and utility classes
require_once '../../includes/constants.php';
require_once '../../classes/EncodingHandler.php';
require_once '../../classes/SQLQueryHandler.php';

$SQLQueryHandler = new SQLQueryHandler(0);

$lSecurityLevel = $SQLQueryHandler->getSecurityLevelFromDB();

$Encoder = new EncodingHandler();

switch ($lSecurityLevel){
default: // Default case: This code is insecure
case "0": // This code is insecure
case "1": // This code is insecure
$lProtectAgainstCommandInjection=false;
$lProtectAgainstXSS = false;
break;

case "2":
case "3":
case "4":
case "5": // This code is fairly secure
$lProtectAgainstCommandInjection=true;
$lProtectAgainstXSS = true;
break;
}// end switch

// Authenticate the request using the shared function
authenticateRequest($lSecurityLevel);

// Set security-related variables
$lProtectAgainstCommandInjection = $lSecurityLevel >= SECURITY_LEVEL_MEDIUM;
$lProtectAgainstXSS = $lSecurityLevel >= SECURITY_LEVEL_MEDIUM;

// Apply XSS protection if enabled
if ($lProtectAgainstXSS) {
$lMessage = $Encoder->encodeForHTML($pMessage);
} else {
$lMessage = $pMessage;
}
$lMessage = $lProtectAgainstXSS ? $Encoder->encodeForHTML($pMessage) : $pMessage;

// Construct the command
$lCommand = $lProtectAgainstCommandInjection
? escapeshellcmd("echo " . escapeshellarg($lMessage))
: "echo $lMessage";

// Handle command execution based on the protection flag
if ($lProtectAgainstCommandInjection) {
$lResult = $lMessage;
} else {
// Allow command injection vulnerability (insecure)
$lResult = shell_exec("echo " . $lMessage);
// Execute the command and capture output
$lOutput = shell_exec($lCommand);
if ($lOutput === null) {
throw new CommandExecutionException("Command execution failed.");
}

// Get the current timestamp
$lTimestamp = date('Y-m-d H:i:s');
$lTimestamp = date(DATE_TIME_FORMAT);

// Create a structured response as an associative array
$lResponse = array(
'message' => $lMessage,
'command' => $lCommand,
'securityLevel' => $lSecurityLevel,
'timestamp' => $lTimestamp,
'output' => $lResult
'output' => $lOutput
);

return $lResponse; // Return as an array for NuSOAP to serialize

}catch(Exception $e){
$lMessage = "Error executing method echoMessage in webservice ws-echo.php";
} catch (Exception $e) {
$lMessage = "Error executing method echoMessage in webservice ws-echo.php: " . $e->getMessage();
throw new MethodExecutionException($lMessage);
}// end try

} // end function echoMessage
}
}

// Handle the SOAP request with error handling
try {
// Process the incoming SOAP request
$lSOAPWebService->service(file_get_contents("php://input"));
} catch (Exception $e) {
// Send a fault response back to the client
$lSOAPWebService->fault('Server', "SOAP Service Error: " . $e->getMessage());
$lSOAPWebService->fault('Server', "SOAP Service Error: " . htmlspecialchars($e->getMessage()));
}

?>
Loading

0 comments on commit cdd7131

Please sign in to comment.