[2024鹏城杯]exec
本文最后更新于51 天前,其中的信息可能已经过时,如有错误可以直接在文章下留言

题目描述

#just exec

题目给了py文件,都是Base64的数据

但是非常大,脚本文件大小达到5M。

根据题目描述直接执行

额,还报错了,应该是解密最后的代码少了个单引号,也不知道是疏漏还是故意的😅

那我们就大概知道,这题应该是一直解密,然后得到最后的Python代码,得到的话基本题目就出了。

取Base64编码解密得到如图

那大概就是毫无意义的Base加密套娃,每次解密大概都会有exec(basexx.xxxdecode(r”””………”””)),编码方式大概有Base64、Base32、Base58、Base85

这里又学到了个知识,原始字符串

【Python基础教程】第78篇 原始字符串 – 知乎

我们需要一直执行代码解密直到得到最后的Python代码。当时比赛情况下,我是手搓的,并且还花了挺长时间的,因为发现Python的Base64模块用的Base编码和解密方式在一些在线网站解密的话不行,我做了很久才发现这一点,所以只能在Python里面解密了。

最后解密了非常多次,成功将题目的Python代码弄出来了。

这题并不难,复现是为了记录下如何自动化利用Python脚本解密那一大坨Base编码,问题主要出现在每次都会出现exec(basexx.xxxdecode(r”””………”””)),所以肯定不能无脑decode,要有一些处理方式,当然也并不难。

目前看到的方法大概有两种,大概是

//提取exec()函数中的代码,再执行
//对exec函数进行hook

第一种方法的Python代码如下

import base64
with open(r"C:\Users\Daki\Desktop\chall.py",'r') as f:
    data=f.read()
#读文件数据
data=data.replace("import base64","")
#把文件里导入模块的代码去掉
while "exec" in data:
    data=eval(data.replace("exec","")).decode()
#每次都把exec去掉,执行括号内的代码
print(data)

这种方法应该是最简便的,这里的eval和exec有一个区别就是,eval会返回代码执行后的结果,但是exec不会返回结果。所以我们用eval执行,用data接收。

然后群友还提出了hook exec函数的做法,感觉这种想法挺有意思的,我想了一下,然后试了一下,代码如下

import base64
with open(r"C:\Users\Daki\Desktop\chall.py",'r') as f:
    data=f.read()
data=data.replace("import base64","")
def my_exec(x):
    x=eval(x.replace("exec","")).decode()
    if "exec" in x:
        my_exec(x)
    if "exec" not in x:
        print(x)
exec = my_exec
exec(data)

但是感觉其实和第一种做法异曲同工,不知道有没有更帅的hook方法。但是做法应该大概都是这样把。

得到的Python代码如下

a = True
d = len
G = list
g = range
s = next
R = bytes
o = input
Y = print
def l(S):
    i = 0
    j = 0
    while a:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K
def N(key, O):
    I = d(key)
    S = G(g(256))
    j = 0
    for i in g(256):
        j = (j + S[i] + key[i % I]) % 256
        S[i], S[j] = S[j], S[i]
    z = l(S)
    n = []
    for k in O:
        n.append(k ^ s(z) + 2)   #这里魔改了一下
    return R(n)
def E(s, parts_num):
    Q = d(s.decode())
    S = Q // parts_num
    u = Q % parts_num
    W = []
    j = 0
    for i in g(parts_num):
        T = j + S
        if u > 0:
            T += 1
            u -= 1
        W.append(s[j:T])
        j = T
    return W
if __name__ == '__main__':
    L = o('input the flag: >>> ').encode()
    assert d(L) % 2 == 0, 'flag length should be even'
    t = b'v3ry_s3cr3t_p@ssw0rd'
    O = E(L, 2)
    U = []
    for i in O:
        U.append(N(t, i).hex())
    if U == ['1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60', '2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a']:
#这里的单引号我自己补上去的
             Y('Congratulations! You got the flag!')
    else:
        Y('Wrong flag!')

很明显的一个RC4加密算法,这个E函数自己代码拉出来执行了一下,其实就是将输入平均分成两半。

但是奇怪的是U数组中的两个十六进制字符串位数却不相等。

这里的RC4算法小小改了一下最后异或的时候

for k in O:
    n.append(k ^ s(z) + 2)

需要注意的是,会先执行s(z) + 2,再执行异或,一开始以为是先异或再加法,导致没解出flag

可以用以下代码验证一下运算顺序

data1=66^14+2
data2=(66^14)+2
data3=66^(14+2)
print(data1)
#82
print(data2)
#78
print(data3)
#82

挺神奇的,一直以为是异或先算。

以下是解题脚本

def KSA(key):
    """ Key-Scheduling Algorithm (KSA) """
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]
    return S
def PRGA(S):
    """ Pseudo-Random Generation Algorithm (PRGA) """
    i, j = 0, 0
    while True:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        K = S[(S[i] + S[j]) % 256]
        yield K
def RC4(key, text):
    """ RC4 encryption/decryption """
    S = KSA(key)
    keystream = PRGA(S)
    res = []
    for char in text:
        res.append(char ^ next(keystream)+2)
    return bytes(res)
key = b'v3ry_s3cr3t_p@ssw0rd'
plaintext1 =bytes.fromhex('1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60')
ciphertext1 = RC4(key, plaintext1)
print(ciphertext1)
#b'flag{thEn_I_Ca5_BE_YoUR_Onl7_ExeCUti6n'
plaintext2 =bytes.fromhex('2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a')
ciphertext2 = RC4(key, plaintext2)
print(ciphertext2)
#b'_So_Use_m3_t0_R0n_tH17_Ex3Cuti0n}'
#flag{thEn_I_Ca5_BE_YoUR_Onl7_ExeCUti6n_So_Use_m3_t0_R0n_tH17_Ex3Cuti0n}

所以flag就是flag{thEn_I_Ca5_BE_YoUR_Onl7_ExeCUti6n_So_Use_m3_t0_R0n_tH17_Ex3Cuti0n}

这个脚本用GPT写的,然后才发现RC4加密和解密可以用同一个代码,还是做题少了😅

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇