C
C
CTF-WriteUp
Search
⌃K

pop_master

构造POP链的题目,主要核心点:
  • 每个class和function名都是唯一的,虽然有9999个类,但是调用链在选定调用的function后只有一种class对应。
  • 大部分的方法只有一种调用链,只有用了method_exists会有分叉。
  • POP链构造:深度优先搜索,沿着每个方法走下去,直到遇到eval函数命令执行。
  • POP链剪枝:POP链中,如果传入的参数被替换(注意不是拼接,拼接可以通过注释来绕过),是不可以执行命令的,具体用正则表示就是含有=但不含有.
虽然写正则很搞人心态,但是题目整体的思路很清晰,直接撸脚本就可以了(写的正则很垃圾,随便看看就好)。
import os
import requests
import re
classPop = []
funcPop = []
array = []
def deleteIfAndFor(f):
text = f.read()
s = re.findall("(if\((\d+)>(\d+)\){([\w\W]+?)})", text)
for i in s:
if int(i[1]) < int(i[2]):
text = text.replace(i[0], "")
else:
text = text.replace(i[0], i[3].strip())
s = re.findall("(for\(\$i = 0; \$i < (\d+); \$i \+\+\){([\w\W]+?)})", text)
for i in s:
if int(i[1]) == 0:
text = text.replace(i[0], "")
else:
text = text.replace(i[0], i[2].strip())
with open("result.txt", "w") as g:
g.write(text)
def search(key):
# func 入栈
funcPop.append(key)
for i in array:
if i.find("function " + key) != -1:
# class入栈
classPop.append(re.findall(r'class\s([a-zA-Z0-9_]{6})', i)[0])
tmp = fr'public\sfunction\s{key}' + '\(\$[a-zA-Z0-9]{5}\)\{[\w\W]*?\}\n'
# 提取调用方法
func = re.findall(tmp, i)
# 进行过滤,如果传入的参数被顶掉,直接返回
tmp2 = fr'public\sfunction\s{key}' + '\(\$([a-zA-Z0-9]{5})\)\{[\w\W]*?\}\n'
e = re.findall(tmp2, i)
tmp3 = fr'{e[0]}\s*?=\s*?'
if (re.findall(tmp3, func[0]).__len__() > 0) and (func[0].find(".") == -1):
classPop.pop()
return
# 检测是否存在eval,如果存在则打印classPop
if func[0].find('eval') != -1:
print(classPop)
# 提取调用链
nextFunc = re.findall(r'\$this->[a-zA-Z0-9]{7}->([a-zA-Z0-9]{6})\(\$[a-zA-Z0-9]{5}\);', func[0])
# 遍历所有func
for j in nextFunc:
search(j)
# func 出栈
funcPop.pop()
# class 出栈
classPop.pop()
if __name__ == "__main__":
# f = open("./class.php", "r+")
# deleteIfAndFor(f)
# f.close()
f = open("./result.txt", "r+")
content = f.read()
array = re.findall(r'class\s[a-zA-Z0-9_]+{[\w\W]*?}\n\n', content)
search("lgeDXz")
跑出来的所有POP链都是可以用的,最后直接拿flag就行。