该题是CCCAMP / ALLES!CTF 2023 中一道Crypto加密题。
0x01 审题
这道题没有附件,通过提示和给出得接口,大概得知这道题要与服务器交互来获取flag。
我们访问接口后,弹出菜单
Welcome to SeeBeeSee
(1) run script
(2) get source code of this program
(3) get sample script
(4) exit
>
这里(2)获取源代码,可以得到源代码,我把有用的信息注释了起来。
#!/usr/bin/env pypy3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import sys
import base64
import re
KEY = b'redactedredacted' # 真实key我们是不知道的
def repl():
print("Welcome to SeeBeeSee")
while True:
print(" (1) run script")
print(" (2) get source code of this program")
print(" (3) get sample script")
print(" (4) exit")
choice = input(">")
if choice == "1":
print("Please provide your script! (format: base64(aes(script)) )")
script = input(">")
runscript(base64.b64decode(script))
elif choice == "2":
with open(__file__) as f:
print(re.sub(r"KEY = b'[a-zA-Z0-9]+'","KEYx20= b'redactedredacted'",f.read()))
elif choice == "3":
print(getsample())
elif choice == "4":
print("bye!")
exit()
def runscript(data): # 对应菜单(1),该功能进行了解密,并将解密数据通过exec执行了
try:
global result
result = b""
decrypted = decrypt(data, KEY)
print("---DEC---")
print(decrypted)
print("---EOF---")
dec = decrypted.decode("utf-8", "replace")
print(dec)
print("---")
exec(dec, globals()) # 执行命令
print("r:", result)
return True
except Exception as e:
print("decryption error?")
print(e)
return False
def getsample(): # 对应菜单(3),这串sample代码有点问题,一开就是exit()退出,最后一行对result进行了重置
code = b"""
exit()
#AAAAAAAAA
#AAAAAAAAAAAAAAAA
a = r"0000000000000000000000000000"
if a!=r"0000000000000000000000000000":
testok = True
if testok:
result = b"See? " + KEY[:-4]
#reset 4 security
result = b"nope"
"""
cipher = AES.new(KEY, AES.MODE_CBC,iv=b'x00'*16)
ciphertext = cipher.encrypt(pad(code, AES.block_size))
return base64.b64encode(ciphertext)
def decrypt(data, key): # 解密程序,知道了iv
cipher = AES.new(key, AES.MODE_CBC,iv=b'x00'*16)
dec = cipher.decrypt(data)
try:
return unpad(dec ,AES.block_size)
except ValueError:
if len(dec) % AES.block_size == 0:
return dec
else:
raise ValueError
repl()
通过代码的审计,
(1)发现命令执行漏洞,需要我们传递一个加过密的payload。只要我们可以获取到key,就可以执行命令。
(2)simple代码中,当判断!=时,KEY[:-4]可以泄露key前十二位字节(AES最常见的是128bits,也就是十六字节),我们又知道IV,这时我们就可以对KEY进行枚举,拿到完整的KEY
0x02 获取Key
我们知道,CBC加密是十六字节分组加密
接着,我们把simple代码按十六位进行分组
nexit()n#AAAAAAA
AA#nAAAAAAAAAAAA
AAAAna = r"00000
0000000000000000
0000000"nif a!=r
"000000000000000
0000000000000":n
testok = Tru
enif testok:n
result = b"See?
" + KEY[:-4]n#r
eset 4 securityn
result = b"nope"
n000000000000000
这里是这道题的难点,首先,我们需要把第一块exit()删掉,把最后两块result=b"nope"删掉。
然后,通过CBC Bit-flipping Attack使判断生效,只需要在(1)处篡改,让(3)处a=后几个字节发生变化,让(2)首个字符等于注释符#,需要在尾部几个字节进行fuzz,以下是我的代码
from pwn import *
import base64
# 通过菜单3获取的代码加密base64数据
ct = base64.b64decode("jprE+UxCYdKR6twNxfMB653JHEXpluTlXgDGainAZlCbiLbcjFu1VNIyebAa8lDdBQy6/b/ES6JuwB25SxIBU4KbCxq1LmTi1SnoGYpOYcYRJ4qy6uaSYOl8g1TcD908J6SUMOhl4VWeGwHCF0W+WDthbCyi2df6/usFim7RjBPMY2YDC1Gfap3zCTKrEwVxkNt+pV7fRhGOm4W8QClF1Hsyre57tRPm1bbbXOawSQ39kVXStrBWKCXCqSl9Hr2VAhsbpQcxx5Q+dqOhqSYpN3xsAbHD8OVEhmdEuGHoRyo=")
ct = ct[16:] # 去掉第一块
ct = ct[:-32] # 去掉倒数两块
p = remote("b5dbc8e98086c0f84cdb2a17-1024-seebeesee.challenge.master.camp.allesctf.net","31337",ssl=True)
for i in range(13,16):
for j in range(0,256):
print(i,j)
ct1 = bytearray(ct)
ct1[i] = j
p.sendlineafter(">","1")
p.sendlineafter(">",base64.b64encode(ct1))
p.recvline()
result = p.recvuntil("(1) run script")
if "#" == result.decode()[2:3] and "decryption error" not in result.decode():
print("大哥搞定")
print(result.decode())
exit()
得到key前十二位,e87feb770447
接着,我对后4位key进行枚举
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
import sys
import base64
import re
pt = b"""
exit()
#AAAAAAAAA
#AAAAAAAAAAAAAAAA
a = r"0000000000000000000000000000"
if a!=r"0000000000000000000000000000":
testok = True
if testok:
result = b"See? " + KEY[:-4]
#reset 4 security
result = b"nope"
"""[0:16] # 比对第一块
ct = base64.b64decode("jprE+UxCYdKR6twNxfMB653JHEXpluTlXgDGainAZlCbiLbcjFu1VNIyebAa8lDdBQy6/b/ES6JuwB25SxIBU4KbCxq1LmTi1SnoGYpOYcYRJ4qy6uaSYOl8g1TcD908J6SUMOhl4VWeGwHCF0W+WDthbCyi2df6/usFim7RjBPMY2YDC1Gfap3zCTKrEwVxkNt+pV7fRhGOm4W8QClF1Hsyre57tRPm1bbbXOawSQ39kVXStrBWKCXCqSl9Hr2VAhsbpQcxx5Q+dqOhqSYpN3xsAbHD8OVEhmdEuGHoRyo=")
ct = ct[:16]
for i in range(0,0xFFFF):
key = "e87feb770447" + hex(i)[2:].zfill(4)
key = key.encode()
cipher = AES.new(key,AES.MODE_CBC,iv=b'x00'*16)
ciphertext = cipher.encrypt(pt)
if ciphertext==ct:
print("Key =",key)
break
Key = b'e87feb7704477bbc'
0x03 构造payload
调用python os即可执行命令
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad,unpad
from pwn import *
import base64
key = b'e87feb7704477bbc'
code =b"""
import os
result = os.system('cat /flag')
"""
p = remote("b5dbc8e98086c0f84cdb2a17-1024-seebeesee.challenge.master.camp.allesctf.net","31337",ssl=True)
cipher = AES.new(key,AES.MODE_CBC,iv=b'x00'*16)
ct = cipher.encrypt(pad(code,AES.block_size))
p.sendlineafter(">","1")
p.sendlineafter(">",base64.b64encode(ct))
p.interactive()
flag = ALLES!{1m_n3ver_us1ng_cbc_4gaiN!!!1}
0x04 免责声明
本文仅限于技术研究学习,切勿将文中技术细节用作非法用途,如有违者后果自负。
关于我们
“TERRA星环”安全团队正式成立于2020年,是贵州泰若数字科技有限公司旗下以互联网攻防技术研究为目标的安全团队。团队核心成员长期从事渗透测试、代码审计、应急响应等安服工作,多次参与国家、省级攻防演练行动,具备丰富的安服及攻防对抗经验。
团队专注于漏洞挖掘、漏洞研究、红蓝对抗、CTF夺旗、溯源取证、威胁情报、代码审计、逆向分析等研究。对外提供安全评估、安全培训、安全咨询、安全集成、应急响应等服务。
原文始发于微信公众号(TERRA星环安全团队):CTF | CCCAMP / ALLES! 2023 SeeBeeSee Writeups
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论