RC4加密算法结合CTF逆向签到

本问带你深入理解RC4加密流程,并结合一道CTF题目分析。

算法原理

 

流密码(Stream Cipher)属于对称密码,加解密使用与明文长度相同的同一个密钥流。这个密钥流通常是某一个确定状态的伪随机数发生器所产生的比特流,双方将伪随机数生成器的种子作为密钥。明文流通常通过和密钥流进行异或得到密文流。由异或操作可知,密文流和密钥流再进行异或也就得到了明文流。
RC4是对称加密中特殊的流加密算法,主要有三个操作:

 

 

 

  1. 密钥调度KSA,初始化一个数组,并通过密钥打乱数组
  2. 伪随机字节流生成,使用KSA生成的状态数组S来生成伪随机字节流
  3. 通过伪随机字节流,也就是密钥流来与明文流进行异或

 

下面以python为例子

 

def RC4(key, plaintext):  
    """ RC4 encryption/decryption """  
    key = [ord(c) for c in key]  
    S = KSA(key)  
    keystream = PRGA(S)  

    result = []  
    for char in plaintext:  
        val = ("%02x" % (ord(char) ^ next(keystream)))  # XOR and format as hex  
        result.append(val)  

    return ''.join(result)  


# 示例  
key = 'woodpecker'  
plaintext = 'woodpecker{this_is_simple_rc4}'  
ciphertext = RC4(key, plaintext)  
print(f"密文: {ciphertext}")

 

下面进行一个初步的解释,

 

key = [ord(c) for c in key]  # key为:woodpecker
# 将字符串转为十进制数组
# [119, 111, 111, 100, 112, 101, 99, 107, 101, 114]

 

 

f18fadc89b102759b64d3fc86bd0ee53.png

 

对照ascii码表对比应该不难理解,下一步便是密钥调度KSA

 

密钥调度KSA

 

def KSA(key):  
    """ Key Scheduling Algorithm (KSA) """  
    key_length = len(key)  
    S = list(range(256))  
    j = 0  

    for i in range(256):  
        j = (j + S[i] + key[i % key_length]) % 256  
        S[i], S[j] = S[j], S[i]  # swap  

    return S

 

首先是计算key的长度和初始化一个S数组,通常称为S盒或S向量

 

key_length = len(key)  
S = list(range(256)) 

 

 

4e0be62dcee2d6f3e3ce357b0f2c68b1.png

 

然后就是使用密钥打乱状态数组,根据密钥的值,对状态数组进行置换。这个置换过程确保状态数组S与密钥相关联。

 

j = 0 # j初始值为0
for i in range(256):  
# 第i(0->255)个位置与通过特定公式计算出的位置j进行替换
    j = (j + S[i] + key[i % key_length]) % 256 
    S[i], S[j] = S[j], S[i]  # swap  

 

 

377c93a6804cd54d98b267b46aed93c4.png

 

i=0为例子,j默认初始值为0S[i]也就是S[0]此时的值为0key[i % key_length]也就是key[0 % 10]key[0]也就是119,所以最终计算的结果j=119,所以S[0]S[119]进行置换

 

 

5b0ffa39065eac2a41fe077a3b8f073e.png

 

后面类似即可,最终生成一个调度后的密钥S

 

 

5990f38fc4725529058f1fcd86cfaf0e.png

 

PRGA (Pseudo-Random Generation Algorithm)

 

PRGA是RC4算法生成伪随机字节流的部分。它使用KSA生成的状态数组S作为种子来生成伪随机字节流

 

def PRGA(S):  
    """ Pseudo-Random Generation Algorithm (PRGA) """
    # 初始化i,j为0位置
    i = 0  
    j = 0
    # 生成器不断生成密钥流
    while True:  
        i = (i + 1) % 256  
        j = (j + S[i]) % 256  
        S[i], S[j] = S[j], S[i]  # 交换i,j的两个位置  
        K = S[(S[i] + S[j]) % 256] # 取S中的S[i]与S[j]相加%256结果的位置作为密钥流的值 
        yield K

 

 

1b098e0b36813d8c91510f9205ef4cd3.png

 

同样以i=0,j=0为例子,计算出i=1,j=(0+231)%256=231

 

 

4963aea0bd7542ac50807e976080ef98.png

 

所以交换S[1],S[231]的位置

 

 

2ec31e2ba9ac9f90b81b048339861354.png

 

 

d8465f2249a541128093122d544e32c3.png

 

最后生成K的值,S[1]=0, S[231]=231,所以K的值为S[231]=231

 

 

a781ece3d52ef77bf70d52a6b55c4b89.png

 

关于yield,next的简单使用

 

def simple_generator():
    yield 1
    yield 2
    yield 3

gen = simple_generator()  # 创建生成器对象

print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3
# print(next(gen))  # 如果再调用一次,将会引发StopIteration异常

 

yield关键字用于定义生成器函数。一个带有yield关键字的函数会返回一个生成器对象,而不是一个普通的函数返回值。当生成器函数被调用时,函数体中的代码不会立即执行,而是返回一个生成器对象。每次调用生成器对象的__next__()方法(通常使用内置的next()函数),函数会执行到下一个yield语句并返回yield语句后的值。函数的状态(包括局部变量的值)会被保留,以便下一次继续执行。

 

gen = simple_generator()

print(next(gen))  # 输出: 1
print(next(gen))  # 输出: 2
print(next(gen))  # 输出: 3
# print(next(gen))  # 如果再调用一次,将会引发StopIteration异常

 

next函数用于从生成器或迭代器中获取下一个值。当你调用next()时,生成器函数执行到下一个yield语句并返回该语句后的值。如果生成器没有更多值要生成,它会引发StopIteration异常。

 

通过密钥流加密明文流

 

def RC4(key, plaintext):  
    """ RC4 encryption/decryption """  
    key = [ord(c) for c in key]  
    S = KSA(key)  
    keystream = PRGA(S)  

    result = []  
    for char in plaintext:  
        val = ("%02x" % (ord(char) ^ next(keystream)))  # XOR and format as hex  
        result.append(val)  

    return ''.join(result)

 

通过yield和next关键字我们知道keystream每次会生成一个k值

 

result = []  
for char in plaintext:  
    # 取出一个字符,与生成的key进行异或操作
    val = ("%02x" % (ord(char) ^ next(keystream)))  # XOR and format as hex 
    result.append(val)
# plaintext为woodpecker{this_is_simple_rc4}

 

  • % 是格式化操作符。
  • 02 表示格式化结果至少为两位,不足两位时前面补零。
  • x 表示将数字格式化为小写的十六进制。

 

同样,依然只解释第一个操作,ord(char)=ord(w)=119,由前文可知第一次生成的k=231,则

 

  0111 0111 
^ 1110 0111 
----------- 
  1001 0000 = 144
     9    0

 

结果为144,转为两位十六进制的结果为90,RC4加密后的结果是904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f可见,90确实是前两位十六进制数。
至此RC4的解密流程便已经理清了

 

RC4解密流程

 

我们已经知道904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f我们依然取我们熟悉的90,我们只需要直到k的值也就是231,那么

 

  1110 0111
^ 1001 0000
----------- 
  0111 0111 = 119 = w

 

此时我们便知道了第一个字母就是w了,也就是说我们只需要知道密钥流便可以通过密文流得到明文流,而密钥流的生成只需要知道密钥即可。

 

题目描述:

 

签到题,简单的加密,抓个特征就好

 

题目源代码:

 

#include <stdio.h>  
#include <string.h>  

#define KEY "woodpecker"  
#define TARGET_ENCRYPTED_FLAG "904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f"  

typedef struct {  
    unsigned char S[256];  
    int i, j;  
} Crypto_CTX;  

void crypto_init(Crypto_CTX *ctx, const unsigned char *key, int keylen) {  
    int i, j = 0, k;  
    unsigned char tmp;  

    for (i = 0; i < 256; i++) {  
        ctx->S[i] = i;  
    }  

    for (i = 0; i < 256; i++) {  
        j = (j + ctx->S[i] + key[i % keylen]) % 256;  
        tmp = ctx->S[i];  
        ctx->S[i] = ctx->S[j];  
        ctx->S[j] = tmp;  
    }  

    ctx->i = 0;  
    ctx->j = 0;  
}  

void crypto_crypt(Crypto_CTX *ctx, const unsigned char *inbuf, unsigned char *outbuf, int buflen) {  
    int i;  
    unsigned char tmp;  

    for (i = 0; i < buflen; i++) {  
        ctx->i = (ctx->i + 1) % 256;  
        ctx->j = (ctx->j + ctx->S[ctx->i]) % 256;  

        tmp = ctx->S[ctx->i];  
        ctx->S[ctx->i] = ctx->S[ctx->j];  
        ctx->S[ctx->j] = tmp;  

        outbuf[i] = inbuf[i] ^ ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) % 256];  
    }  
}  

void encrypt_flag(const char *flag, unsigned char *encrypted_flag) {  
    Crypto_CTX crypto_ctx;  
    crypto_init(&crypto_ctx, (const unsigned char *)KEY, strlen(KEY));  
    crypto_crypt(&crypto_ctx, (const unsigned char *)flag, encrypted_flag, strlen(flag));  
}  

void bytes_to_hex(const unsigned char *bytes, char *hex, int len) {  
    for (int i = 0; i < len; i++) {  
        sprintf(hex + 2 * i, "%02x", bytes[i]);  
    }  
}  

int main() {  
    char input_flag[256];  
    unsigned char encrypted_flag[256];  
    char encrypted_flag_hex[256 * 2 + 1];  

    printf("Enter the flag: ");  
    fgets(input_flag, sizeof(input_flag), stdin);  

    // Remove the newline character from the input if present  
    input_flag[strcspn(input_flag, "\n")] = '\0';  

    // Encrypt the input flag  
    encrypt_flag(input_flag, encrypted_flag);  

    // Convert encrypted flag to hex string  
    bytes_to_hex(encrypted_flag, encrypted_flag_hex, strlen(input_flag));  

    // Compare the encrypted flag with the target encrypted flag  
    if (strcmp(encrypted_flag_hex, TARGET_ENCRYPTED_FLAG) == 0) {  
        printf("congratulation\n");  
    } else {  
        printf("try again\n");  
    }  

    return 0;  
}

 

解题

 

 

28d7b25513fc7a371a2d83b54579d3ce.png

 

扔进IDA分析,结合题目表述:签到题,简单的加密,抓个特征就好

 

 

e90977f9cddf2fcb6f607288f80bb702.png

 

 

5485d2f16f29619007c17508592c911f.png

 

 

d645f4b5ab2dca3b8ac08f2037e8eea4.png

 

由猜测是RC4加密,并且key为woodpecker,加密后的结果为904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f所以解题脚本为

 

import binascii  
from Crypto.Cipher import ARC4  
print(ARC4.new(b"woodpecker").decrypt(binascii.a2b_hex("904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f")))

 

当然也可以用刚才的python脚本,只需要简单修改一下RC4函数的部分即可

 

def RC4_decrypt(key, ciphertext_hex):  
    """ RC4 decryption """  
    key = [ord(c) for c in key]  
    S = KSA(key)  
    keystream = PRGA(S)  

    ciphertext_bytes = binascii.a2b_hex(ciphertext_hex)  
    result = []  
    for byte in ciphertext_bytes:  
        val = chr(byte ^ next(keystream))  # XOR with keystream and convert to char  
        result.append(val)  

    return ''.join(result)  


# 示例  
key = 'woodpecker'  
plaintext = "904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f"  
ciphertext = RC4_decrypt(key, plaintext)  
print(f"密文: {ciphertext}")

 

完整的脚本如下

 

import binascii


def KSA(key):  
    """ Key Scheduling Algorithm (KSA) """  
    key_length = len(key)  
    S = list(range(256))  
    j = 0  

    for i in range(256):  
        j = (j + S[i] + key[i % key_length]) % 256  
        S[i], S[j] = S[j], S[i]  # swap  

    return S  


def PRGA(S):  
    """ Pseudo-Random Generation Algorithm (PRGA) """  
    i = 0  
    j = 0  
    while True:  
        i = (i + 1) % 256  
        j = (j + S[i]) % 256  
        S[i], S[j] = S[j], S[i]  # swap  
        K = S[(S[i] + S[j]) % 256]  
        yield K  


def RC4(key, plaintext):  
    """ RC4 encryption/decryption """  
    key = [ord(c) for c in key]  
    S = KSA(key)  
    keystream = PRGA(S)  

    result = []  
    for char in plaintext:  
        val = ("%02x" % (ord(char) ^ next(keystream)))  # XOR and format as hex  
        result.append(val)  

    return ''.join(result)  

def RC4_decrypt(key, ciphertext_hex):  
    """ RC4 decryption """  
    key = [ord(c) for c in key]  
    S = KSA(key)  
    keystream = PRGA(S)  

    ciphertext_bytes = binascii.a2b_hex(ciphertext_hex)  
    result = []  
    for byte in ciphertext_bytes:  
        val = chr(byte ^ next(keystream))  # XOR with keystream and convert to char  
        result.append(val)  

    return ''.join(result)  


# 示例  
key = 'woodpecker'  
plaintext = "904fc1ff4dc92eed7555acfcbbbfad161b8f6313bb8fc3602c8c7108de3f"  
ciphertext = RC4_decrypt(key, plaintext)  
print(f"密文: {ciphertext}")

 

 

  • 38
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值