2022/1/29复习WEB

今天看了看SSRF就是通过构造恶意URL使服务器执行恶意代码在内网中查询我们无法穿透的机器,然后再通过服务器返回到我们的主机。

今天又看了看文件包含,主要是远程文件包含,在PHP5.5以前吧,

1
file_url_fopen和file_include_fopen是打开的,也就是允许代码中包含远程php端文件。

执行远程文件包含漏洞的技巧

注意每个浏览器可能记录访问日志,要写一句话木马的话要写在User-Agent中,写在URL中都会被储存了。

image-20220129224104916

或者是这样,利用PHP伪协议,来执行

2.或者是写SESSION文件

PHP中的SESSION文件用于存储SESSION数据,它是根据PHPSESSID来命名存储文件,常见路径:

/var/lib/php/sessions/sess_{PHPSESSID}

/var/lib/php/sess_{PHPSESSID}

/tmp/sess_{PHPSESSID}

/tmp/sessions/sess_{PHPSESSID}

远程文件包含漏洞检测:

(1)向目标参数指定远程 URL 发起请求,确定远程服务器是否收到相应请求,此步骤可利用前面介绍过的 Burp Collaborator 进行测试。若发起请求说明有可能存在远程请求文件,同时也可能出现 SSRF 漏洞。

(2)如果第 1 步失败,尝试提交一个不存在的 IP 地址,并确认服务器尝试连接此 IP 时是否会出现超时。

(3)若第 1 步确认访问成功或者第 2 步确认请求超时,那么按前面介绍的利用方法,尝试利用远程文件包含漏洞以作最终确认。

防御:

在php.ini中设置open_basedir将可以打开的php文件限制在指定的目录中,防止跨目录访问

或者是在php.ini中关闭

1
allow_url_include=True

即可,避免了远程文件包含和一些伪协议执行。

下面看题

[De1CTF 2019]SSRF Me

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

secert_key = os.urandom(16)#16字节字符串


class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)

def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:#不光上一步相等,且scan中要有action
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')#%s就是后面的self.sandbox
resp = scan(self.param)
if (resp == "Connection Timeout"):#正好sacn函数超时了
result['data'] = resp#即数据记录为timeout
else:
print resp#如果没超时就返回前50个字符
tmpfile.write(resp)#写入文件
tmpfile.close()#关闭
result['code'] = 200#记录成功打开log
if "read" in self.action:#如果里面有read就是可以读取
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result

def checkSign(self):#此函数必须返回True不然上面一堆没用了
if (getSign(self.action, self.param) == self.sign):#即对传入action和传入param的签名和传入的sign相等时
return True
else:
return False


#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))#即param就是后面还没有传值的空,传值收集后进行解码
action = "scan"
return getSign(action, param)


@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))#unquote就是解url码
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())#将python格式的数据转化陈json格式,其实和python格式差不多。
@app.route('/')
def index():
return open("code.txt","r").read()


def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]#阅读打开param url之后显示的前50个字符。
except:
return "Connection Timeout"#1S为超时。



def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()


def md5(content):
return hashlib.md5(content).hexdigest()


def waf(param):#判断是否成功过滤到,即当没挡住WAF
check=param.strip().lower()#strip用来去除空格
if check.startswith("gopher") or check.startswith("file"):#判断字符串是否以指定字符开头,gopher以及file都是伪协议也就是不让用这两种伪协议。
return True
else:
return False


if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')

先拿到代码美化之后的代码

回到本题,阅读完代码之后可以知道getsign是用来返回md5值的,在genesign函数中赋值Action为scan

也就是说getsign的参数就是

secert_key + param + action = secert_key + “” + “scan”,我们可以对中间的param进行传参改变sign值。第一次不传param时访问geneSign得到image-20220130005339725

我们构造参数时,第一个参数action要包含read, 而param应该是文件名,第三个参数不知道,我们只能用genesign来加密,不能自己搞加密。

如果我们传参param = flag.txt,也就得到了md5(secert_key+flag.txtscan)

image-20220130010135124

image-20220130010300961

可以看到action = scan是用来写入的,把param对应文件路径的文件写入这个文件。也就是把flag.txt写入这个文件了

image-20220130011026302

至于它的检验,scan和Read是否在action中,我们只需要修改cookie之后一同传参即可,将cookie中的action改为scanread将sign改为最后传参加密生成的md5,然后repeat即可!

下面是burpsuite操作步骤

image-20220130011615311

image-20220130011739658

image-20220130011955432

最后这张图好好理解一下,刚才flag.txtead是为了得到read和scan的拼接,现在已经在cookie中拼接上了,在get请求中就不必重复了,只有flag.txt即可。

image-20220129230255295

看到又是flask模板注入,实在受不了了,干脆把它学会

直接建立简单server之后访问127.0.0.1

可以用python语法看出,变量app是Flask实例,通过下面的方式

1
2
3
4
@app.route("/");
def hello_world():
return "hello world!"

当客户端用get方法访问”/“时,就调用函数hello_world()就这么简单!所以我们在执行程序结果的地方看到了http请求有’/‘

image-20220129230703245

上面的代码中,python内置变量__name__的值是字符串__main__ 。Flask类将这个参数作为程序名称。当然这个是可以自定义的,比如app = Flask("my-app")

image-20220129231559457

如图改了名字,也就是name的值为my-app

绑定IP和端口,默认为127.0.0.1:5000也可以自定义

1
app.run(host = "0.0.0.0", port = 80, debug = True)

0.0.0.0代表电脑所有IP, 80端口即用http协议。