最近做的[SWPU2019]Web3和[pasecactf_2019]flask_ssti好像都涉及到了/proc这个目录,所以找篇文章来学习下,看了这篇文章才发现作者是搬运之前我就关注的一个大佬WHOAMI的文章,之前复现漏洞看的就是大佬的文章,这会回去看才发现大佬的博客好像关了,这也让我下定决心要把自己所学的东西记录下来,就算是搬运也要自己记录下来,一昧的收藏文章哪天大佬们把博客关了那就真的GG了。话不多说进入正题
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
还有的是一些以数字命名的目录,他们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的PID号为目录名,他们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link
右边的蓝色字就是进程号PID
下面我们简单介绍一下 /proc 目录中的常见文件夹与文件。
上面列出的是 /proc 目录中一些进程相关的目录,每个目录中是其进程本身相关信息的文件。下面是系统上运行的一个PID为1083 的进程的相关文件,其中有些文件是每个进程都会具有的:
cmdline 文件存储着启动当前进程的完整命令,但僵尸进程目录中的此文件不包含任何信息。可以通过查看cmdline目录获取启动指定进程的完整命令:
cat /proc/1083/cmdline
可知PID为1083的进程的启动命令为/usr/lib/gvfs/gvfsd-fuse/run/user/131/gvfs-f-obig_writes
cwd 文件是一个指向当前进程运行目录的符号链接。可以通过查看cwd文件获取目标指定进程环境的运行目录
ls -al /proc/1083/cwd
可见PID为1083的进程的运行目录为/var/lib/gdm3
我们访问这两个目录可以发现它们所拥有的文件是一致的
exe 是一个指向启动当前进程的可执行文件(完整路径)的符号链接。通过exe文件我们可以获得指定进程的可执行文件的完整路径
ls -al /proc/1090/exe
environ文件存储着当前进程的环境变量列表,彼此间用空字符(NULL)隔开,变量用大写字母表示,其值用小写字母表示。可以通过查看environ目录来获取指定进程的环境变量信息:
cat /proc/2889/environ
fd是一个目录,里面包含着当前进程打开的每一个文件的描述符(file descriptor)差不多就是路径啦,这些文件描述符是指向实际文件的一个符号连接,即每个通过这个进程打开的文件都会显示在这里。所以我们可以通过fd目录的文件获取进程,从而打开每个文件的路径以及文件内容
ls -al /proc/1083/fd
查看指定进程打开的某个文件的内容。加上那个数字即可
这个fd比较重要,因为在Linux系统中,如果一个程序用 open() 打开了一个文件,但是最终没有关闭它,即使从外部(如:os.remove(SECRET_FILE))删除这个文件之后,在/proc这个进程的 pid目录下的fd文件描述符 目录下 还是会有这个文件的文件描述符,通过这个文件描述符我们即可以得到被删除的文件的内容
上面的这些操作列出的都是目标环境指定进程的信息,但是我们在做题的时候往往需要的当前进程的信息,这时候就用到了/proc 目录中的self子目录了。
/proc/self表示当前进程目录。前面说了通过/proc/ p i d / 来 获 取 指 定 进 程 的 信 息 。 如 果 某 个 进 程 想 要 获 取 当 前 进 程 的 系 统 信 息 , 就 可 以 通 过 进 程 的 p i d 来 访 问 / p r o c / pid/来获取指定进程的信息。如果某个进程想要获取当前进程的系统信息,就可以通过进程的pid来访问/proc/ pid/来获取指定进程的信息。如果某个进程想要获取当前进程的系统信息,就可以通过进程的pid来访问/proc/pid/目录。但是这个方法还需要获取进程pid。
在fork、daemon等情况下pid还可能发生变化。
为了更方便的获取本进程的信息,Linux提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息时不同的,内容等价于/proc/本进程pid/。进程可以通过访问/proc/self/目录来获取自己的系统信息,而不用每次都获取pid。
注意:在真正做题的时候,我们是不能通过命令的方式执行通过cat命令读取cmdline的。因为如果 cat读取/proc/self/cmdline/的话,得到的是 cat进程的信息。所以我们要通过题目的当前进程使用读取文件(比如,文件包含漏洞,,SSTI,,file:\\本地读取,,…/…/…/目录穿越,,SSRF)的方式读取/proc/self/cmdline
这题的WP我前面也写过了,这题源代码是这样的
import random
from flask import Flask, render_template_string, render_template, request
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = 'folow @osminogka.ann on instagram =)'
#Tiaonmmn don't remember to remove this part on deploy so nobody will solve that hehe
'''
def encode(line, key, key2):
return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))
app.config['flag'] = encode('', 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
'''
def encode(line, key, key2):
return ''.join(chr(x ^ ord(line[x]) ^ ord(key[::-1][x]) ^ ord(key2[x])) for x in range(len(line)))
file = open("/app/flag", "r")
flag = file.read()
flag = flag[:42]
app.config['flag'] = encode(flag, 'GQIS5EmzfZA1Ci8NslaoMxPXqrvFB7hYOkbg9y20W3', 'xwdFqMck1vA0pl7B8WO3DrGLma4sZ2Y6ouCPEHSQVT')
flag = ""
os.remove("/app/flag")
这题拿flag来加密后就把flag文件给删掉了,所以很多大佬都是用_frozen_importlib_external.FileLoader这个类,然后用get_data读取/proc/self/fd/3读取flag文件。
学了上面我们不难理解self指的是当前进程的目录,也就是这个flask进程的目录,fd是一个目录,里面包含着当前进程打开的每一个文件的描述符,然后虽然他把flag删掉了,但进程并没有关闭它,所以目录下 还是会有这个文件的文件描述符,通过这个文件描述符我们即可以得到被删除的文件的内容
重点来了,我之前试过通过popen执行系统命令直接cat /proc/self/fd/3但是却什么也没有,这是为什么呢??
原来是因为如果我们用popen去cat /proc/self/fd/3,这个self是相对popen来说的,但popen本身和flag一点关系也没有,没有用过flag,所以说我们在popen里面cat /proc/self/fd/3是没有flag的。而很多大佬用的FileLoader这个类是通过flask去文件读取的,这个时候它的self就是flask进程了,所以可以读到flag
这下清楚了以后我们可以先差flask进程的pid
可以看到flask进程的pid是1,接下来访问cat /proc/1/fd/3即可
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()[127]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["popen"]("cat /proc/1/fd/3")["read"]()}}
参考文章:
https://blog.csdn.net/Zero_Adam/article/details/114853022