Manage Windows Advanced Firewall ports with PHP trough COM class
March 15, 2012 – 11:38 amOne day you want to be able to quickly open and close a port for a certain service running on your windows server.
I agree most security preachers would be amazed what you can do with a default installation of Apache and PHP and would simply disable COM objects in php or simply rule out such a functionalities by editing process rights on the Apache or PHP instances, but my machine isn't the one where the whole world hangs on to so I have the liberty to do some fun proto POC stuff.
As such I wrote a simple script that allows you to load the Microsoft Advanced Firewall as a COM object and edit it's rules.
With 'editing' I mean allowing or blocking a certain port / process rule or enabling / disabling a rule completely.
Simply said this script allows you to alter existing rules but not to edit or add ports.
You can give the script a set of ports that it has to list, or you can switch a config boolean and list all configurable ports.
If it is a remote server I advice you not to list port 80 ![]()
You can leave out the the authentication block if you like, because most of the time people have setup htaccess themselves.
Code preview below image
or DOWNLOAD here: source code firewall demo
*****
PHP:
*****
session_start();
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Content-Type: text/html; charset=ansi");
/**
* Windows Firewall with Advanced Security browser editor: enable / disable or block / allow existing rules.
* @license Use, modify, destroy, improve and glue any license on this as long as I can keep using it myself ![]()
* @author Stijn De Ryck
*/
/******* ************* ********/
/******* USER SETTINGS ********/
/******* ************* ********/
//WWW-Auth
const USERNAME = 'admin';
const PASSWORD = 'password';
//CHANGE this to ANY STRING of choice!
const HASH_SECRET = "VPS_LATCHO_SECRET";
//if MANAGE_ALL_PORTS is set to TRUE then the application wil ignore the compacted lists:
// ( MANAGE_EXISTING_INBOUND_PORTS and MANAGE_EXISTING_OUTBOUND_PORTS ) and manage all ports that are found in existing rules
const MANAGE_ALL_PORTS = FALSE;
//The in the firewall existing predefined rules whereof you want to display the following INBOUND ports, "*" is also a valid option
static $MANAGE_EXISTING_INBOUND_PORTS = array(80,21,23,3306,110,25,443,465,993,3389,6900,6800,8080,4389,81,9696);
//The in the firewall existing predefined rules whereof you want to display the following OUTBOUND ports, "*" is also a valid option
static $MANAGE_EXISTING_OUTBOUND_PORTS = array(110,25,443,465,993,3389,6900,8080,23,80,"*");
/******* **** ********/
/******* AUTH ********/
/******* **** ********/
//improvable.... TODO post ?logout ?login with href
$authorized = false;
if(isset($_GET['logout']) && ($_SESSION['auth'])) {
$_SESSION['auth'] = null;
session_destroy();
}
if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
$authuser = strtolower($_SERVER['PHP_AUTH_USER']);
if ( (strtolower(USERNAME) == $authuser)
&& (strtolower(PASSWORD) == strtolower($_SERVER['PHP_AUTH_PW']))
&& (!empty($_SESSION['auth']))
){ $authorized = true; }
}
if ( ! $authorized ) {
if (!isset($_GET['logout']))
{
header('WWW-Authenticate: Basic Realm="Login please"');
header('HTTP/1.0 401 Unauthorized');
$_SESSION['auth'] = true;
}
exit;
return;
}
/******* ********* ********/
/******* FW CONSTS ********/
/******* ********* ********/
//Domain
const NET_FW_PROFILE2_DOMAIN = 1;
const NET_FW_PROFILE2_PRIVATE = 2;
const NET_FW_PROFILE2_PUBLIC = 4;
//Enabled rule
const NET_FW_RULE_ENABLED = TRUE;
const NET_FW_RULE_DISABLED = FALSE;
//Direction
const NET_FW_RULE_DIR_IN = 1;
const NET_FW_RULE_DIR_OUT = 2;
//Action
const NET_FW_ACTION_ALLOW = 1;
const NET_FW_ACTION_BLOCK = 0;
//Protocol
const NET_FW_IP_PROTOCOL_TCP = 6;
const NET_FW_IP_PROTOCOL_UDP = 17;
const NET_FW_IP_PROTOCOL_ICMPv4 = 1;
const NET_FW_IP_PROTOCOL_ICMPv6 = 58;
//human readable protocols
$protoLabels = array(NET_FW_IP_PROTOCOL_TCP => 'TCP', NET_FW_IP_PROTOCOL_UDP => 'UDP', NET_FW_IP_PROTOCOL_ICMPv4=>'ICMPv4',NET_FW_IP_PROTOCOL_ICMPv6=>'ICMPv6' );
//human readable profiles ( public, private,...)
$profileLabels = array(1=> 'Domain',2=>'Private',4=>'Public',3=>'Dom+Priv',5=>'Dom+Pub',6=>'Priv+Pub',7=>'Dom+Priv+Pub',2147483647=>'All Profiles');
/******* ******************* ********/
/******* RULES ENUM & EDITOR ********/
/******* ******************* ********/
$objFirewall = new COM("HNetCfg.FwPolicy2");
$inboundForm="";
$outboundForm="";
foreach($objFirewall->Rules as $key => $rule) {
//NOTE: http://msdn.microsoft.com/en-us/library/windows/desktop/aa364724%28v=vs.85%29.aspx
//rule's current settings:
$name = $rule->Name;
$appname = $rule->ApplicationName ;
$svcname = $rule->ServiceName ;
$desc = $rule->Description;
$direction = $rule->Direction;
$proto = $rule->Protocol;
$action = $rule->Action;
$profile = $rule->Profiles;
$enabled = $rule->Enabled;
$localPorts = $rule->LocalPorts;
$remotePorts = $rule->RemotePorts;
//the local ports that are managed by this rule as array
$aRuleLocalPorts = explode(',',$localPorts);
$aRuleRemotePorts = explode(',',$remotePorts);
//the md5 identifier that enables us to filter a rule.
$identifier = md5(HASH_SECRET.$name.$appname.$svcname.$desc.$direction.$proto.$profile.$localPorts.$remotePorts);
if( ! in_array( $proto , array( NET_FW_IP_PROTOCOL_TCP
, NET_FW_IP_PROTOCOL_UDP
, NET_FW_IP_PROTOCOL_ICMPv4
, NET_FW_IP_PROTOCOL_ICMPv6
)
)
||
( ! (MANAGE_ALL_PORTS)
&&
count(array_intersect($MANAGE_EXISTING_INBOUND_PORTS, $aRuleLocalPorts)) == 0
&&
count(array_intersect($MANAGE_EXISTING_OUTBOUND_PORTS, $aRuleRemotePorts)) == 0
)
) { continue; };
//if we received the md5 rule identifier as variable 'ToggleBlockAllow', toggle block/allow state
if( isset($_POST['ToggleBlockAllow']) && $_POST['ToggleBlockAllow'] === $identifier ) {
$action = ($rule->Action == NET_FW_ACTION_ALLOW) ? NET_FW_ACTION_BLOCK : NET_FW_ACTION_ALLOW;
$rule->Action = $action ;
};
//if we received the md5 rule identifier as variable 'ToggleEnabled', toggle enabled/disabled state
if( isset($_POST['ToggleEnabled']) && $_POST['ToggleEnabled'] === $identifier ) {
$enabled = ($rule->Enabled == NET_FW_RULE_ENABLED) ? NET_FW_RULE_DISABLED : NET_FW_RULE_ENABLED;
$rule->Enabled = $enabled ;
};
//rule allowed/blocked state settings
$allowChecked = ($action == NET_FW_ACTION_ALLOW) ? "checked=\"checked\"" : "";
$allowLabel = ($action == NET_FW_ACTION_ALLOW) ? "<b>Allowed </b>" : "<u>Blocked </u>";
//rule enabled state settings
$enableChecked = ($enabled == NET_FW_RULE_ENABLED) ? "checked=\"checked\"" : "";
$enableLabel = ($enabled == NET_FW_RULE_ENABLED) ? "<b>Rule enabled</b>" : "<u>Rule disabled</u>";
$portListByDirection = ($direction == NET_FW_RULE_DIR_IN) ? $localPorts : $remotePorts ;
$portListByDirection = str_replace(',',', ',$portListByDirection) ;
//form color based on enabled/disabled rule, allowed/blocked state
$col = ($enabled == NET_FW_RULE_ENABLED) ? (($action == NET_FW_ACTION_ALLOW) ? "#009922" : "#992200") : "#aaaaaF";
//human readable protocol
$protoLabel = $protoLabels[$proto];
//human readable profile ( public, private,...)
$profileLabel = $profileLabels[$profile];
//messy form-item html for this rule
$formItem ="<tr>";
$formItem .="<td valign='top'>".$protoLabel."</td>";
$formItem .="<td width='100' valign='top'> <font color='".$col."'>[<b> $portListByDirection </b>]</font></td>";
$formItem .="<td width='100' valign='top'> $profileLabel</td>";
$formItem .="<td width='100' valign='top'><font color='".$col."'> ".$allowLabel."</font><input type=\"checkbox\" id=\"".$identifier."\" name=\"allowcheck\" onChange=\"toggle(this.id,'ToggleBlockAllow')\" ".$allowChecked." /></td>";
$formItem .="<td width='120' valign='top'> ".$enableLabel."<input type=\"checkbox\" id=\"".$identifier."\" name=\"enablecheck\" onChange=\"toggle(this.id,'ToggleEnabled')\" ".$enableChecked." /></td>";
$formItem .="<td valign='top'><font color='".$col."'> ".$name."</font>";
$formItem .="<i> <font size=2>".( strpos(strtoupper($appname),"SVCHOST") ? "SVCHOST: ":strtoupper($appname) ) .' '.strtoupper($svcname)."</font></i></label></td>\n";
$formItem ."</tr>\n";
if( $direction == NET_FW_RULE_DIR_IN && ( count(array_intersect($MANAGE_EXISTING_INBOUND_PORTS, $aRuleLocalPorts)) > 0 || MANAGE_ALL_PORTS == TRUE ) ) {
//attach inbound form item if we manage this port (MANAGE_INBOUND_PORTS)
$inboundForm .= $formItem;
}else if( $direction == NET_FW_RULE_DIR_OUT && count(array_intersect($MANAGE_EXISTING_OUTBOUND_PORTS, $aRuleRemotePorts)) > 0 || MANAGE_ALL_PORTS == TRUE) {
//attach outbound form item if we manage this port (MANAGE_OUTBOUND_PORTS)
$outboundForm .= $formItem;
}
}
$html = "<html>\n<head>\n";
$toggleRuleScript = "<script>\n";
$toggleRuleScript .= "function toggle(identifier,switchOption){\n";
$toggleRuleScript .= "document.body.innerHTML += '<form id=\"dynForm\" action=\"\" method=\"post\"><input type=\"hidden\" name=\"'+switchOption+'\" value=\"'+identifier+'\"/></form>'; \n";
$toggleRuleScript .= "document.getElementById(\"dynForm\").submit(); \n}\n";
$toggleRuleScript .= "</script>\n";
$html .=$toggleRuleScript;
$html .="</head>\n<body>\n";
$html .="<h3>Inbound Rules</h3>";
$html .= "<table border=1 bordercolor=#aaaaaa>".$inboundForm."</table>";
$html .="<h3>Outbound Rules</h3>";
$html .= "<table border=1 bordercolor=#aaaaaa>".$outboundForm."</table>";
$html .="</body></html>";
echo $html;
Enjoy!










