rc4+ctf常用编码加密shellcode
2022-10-5 17:31:43 Author: E条咸鱼(查看原文) 阅读量:12 收藏

前言

本文不会解释rc4加密是什么,以及ctf编码在我的理解中为一个大类,并非单独一种编码形式,当然不管是rc4还是ctf编码,其宗旨都是为了使字符串变得“毫无意义”从而达成无法被杀软正确的检查出是shellcode。
其实这篇文章更多的算是踩坑记录,因为本身代码的实现复制粘贴即可,讲加密代码的原理也大可不必,不可逆就行了。
目前针对rc4网上有不同的写法,主要根本是环境的不同,分别有

  • Python2

  • Python3

经过测试,Python3在脚本中计算rc4存在问题,常常导致过长的字符串加密后就无法还原,所以按照我看的某篇文章的说法是,环境最好使用Python2,实际环境测试,确实Python2的运算不会报错,那么下面写一下优缺点。
Python2

  • 优点

  1. 能通过定义函数的方式完成rc4的加解密

  • 缺点

  1. 大部分的工具(如pyinstaller最新版)都已经停止了2版本的支持,旧版本的pyinstaller打包的效果并不好,并且本身旧Pyinstaller打包的exe就算只print,有的杀软都会查

  1. 像自己用的比较好的nuitka,在python2上运行还是挺折磨的,必须存在双版本(因为其中一个组件不支持2),同时打包的exe无法正常执行。

Python3

  • 优点

  1. 能用最新版的Pyinstaller打包程序,目前来看,比python2所支持的旧版本的pyinstaller打包的效果好(vt中美那么多默认杀的)

  2. 至少不会有那么多因为版本不适配导致的模块无法使用

  • 缺点

  1. python3没法直接把rc4的加解密写代码里,所以只能通过加载第三方库的方式使用

  2. 像nuitka这种打包软件,加载第三方库后打包,有点大。

Python2版本实现

rc4实现代码如下

from ctypes import *
import ctypes,codecs
import sys, os, hashlib, time, base64
import random, string
import time

def GenPassword(length):
    numOfNum = random.randint(1,length-1)
    numOfLetter = length - numOfNum
    slcNum = [random.choice(string.digits) for i in range(numOfNum)]
    slcLetter = [random.choice(string.ascii_letters) for i in range(numOfLetter)]
    slcChar = slcNum + slcLetter
    random.shuffle(slcChar)
    getPwd = ''.join([i for i in slcChar])
    return getPwd

def rc4(string, op='encode', public_key=GenPassword(7), expirytime=0):
    ckey_lenth = 4
    public_key = public_key and public_key or ''
    key = hashlib.md5(public_key).hexdigest()
    keya = hashlib.md5(key[0:16]).hexdigest()
    keyb = hashlib.md5(key[16:32]).hexdigest()
    keyc = ckey_lenth and (op == 'decode' and string[0:ckey_lenth] or hashlib.md5(str(time.time())).hexdigest()[32 - ckey_lenth:32]) or ''
    cryptkey = keya + hashlib.md5(keya + keyc).hexdigest()
    key_lenth = len(cryptkey)
    string = op == 'decode' and base64.b64decode(string[4:]) or '0000000000' + hashlib.md5(string + keyb).hexdigest()[0:16] + string
    string_lenth = len(string)
    result = ''
    box = list(range(256))
    randkey = []
    for i in xrange(255):
        randkey.append(ord(cryptkey[i % key_lenth]))
    for i in xrange(255):
        j = 0
        j = (j + box[i] + randkey[i]) % 256
        tmp = box[i]
        box[i] = box[j]
        box[j] = tmp
    for i in xrange(string_lenth):
        a = j = 0
        a = (a + 1) % 256
        j = (j + box[a]) % 256
        tmp = box[a]
        box[a] = box[j]
        box[j] = tmp
        result += chr(ord(string[i]) ^ (box[(box[a] + box[j]) % 256]))
    if op == 'decode':
        if (result[0:10] == '0000000000' or int(result[0:10]) - int(time.time()) > 0and result[10:26] == hashlib.md5(
                result[26:] + keyb).hexdigest()[0:16]:
            return result[26:]
        else:
            return None
    else:
        return keyc + base64.b64encode(result)

https://cloud.tencent.com/developer/article/1591312

按照原文的说法,每次加密的密钥都是随机生成的字符串,以此来减少特征,不是完全赞同,但是也没什么反驳的例子吧

有了上面的加密代码后,直接用即可

def kaisa_jiemi(s,k):
    lower=string.ascii_lowercase
    upper=string.ascii_uppercase
    before=string.ascii_letters
    after=lower[k:]+lower[:k]+upper[k:]+upper[:k]
    table=string.maketrans(after,before)
    return s.translate(table)

...

rcpw = GenPassword(13)
buf = rc4(kaisa_jiemi("123",<此处是恺撒的key>),'encode',rcpw)
scode = rc4(buf, 'decode', rcpw)

由于脚本中只需要密文,所以加密的方法是可以不写在文件里的,方法如下

def kaisa_jiami(s,k):
    lower=string.ascii_lowercase#小写英文字母
    upper=string.ascii_uppercase#大写英文字母
    before=string.ascii_letters#全部英文字母字母
    after=lower[k:]+lower[:k]+upper[k:]+upper[:k]#建立循环字母
    table=''.maketrans(before,after)#创建映射表
    return s.translate(table)

特别注意的是,python2的注释貌似不允许有中文,反正我是一直报错,所以请删除注释
ctf编码用的是,恺撒编码,其实也就是根据一个偏移量去变换位置
那么细心的你此时肯定发现了,加密的代码和解密的代码,除了maketrans中before和after的位置发生变化外,前面string.和''.也有区别
这是因为我运行的平台,生成shellcode.py的平台是python3的,所以加密部分的代码是按照python3来的,而在python2中不允许这么写,所以需要改为string.maketrans()

Python3版本实现

其实跟python2版本的内容差不多的

from Crypto.Cipher import ARC4
import base64
import random
import string
import codecs
import ctypes
import time

def GenPassword(length):
    numOfNum = random.randint(1,length-1)
    numOfLetter = length - numOfNum
    slcNum = [random.choice(string.digits) for i in range(numOfNum)]
    slcLetter = [random.choice(string.ascii_letters) for i in range(numOfLetter)]
    slcChar = slcNum + slcLetter
    random.shuffle(slcChar)
    getPwd = ''.join([i for i in slcChar])
    return getPwd

def kaisa_jiemi(s,k):
    lower=string.ascii_lowercase
    upper=string.ascii_uppercase
    before=string.ascii_letters
    after=lower[k:]+lower[:k]+upper[k:]+upper[:k]
    table=''.maketrans(after,before)
    return s.translate(table)

def rc4_encrypt(data, key1): 
    key = bytes(key1, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.encrypt(data.encode('utf-8'))
    res=base64.b64encode(res)
    res = str(res,'utf-8')
    return res

def rc4_decrypt(data, key1):
    data = base64.b64decode(data)
    key = bytes(key1, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.decrypt(data)
    res = str(res,'gbk')
    return res

key=GenPassword(13)
rc4_encrypt("123",key)

在后续编写负责上线的.py文件时,千万要注意,rc4的key是字符串,恺撒的key需要的是数字(偏移量),两个变量名要区分一下。
和2版本一样,加密的方法是无需写在文件中的,方法如下

import string
import random

def kaisa_jiami(s,k):
    lower=string.ascii_lowercase#小写英文字母
    upper=string.ascii_uppercase#大写英文字母
    before=string.ascii_letters#全部英文字母字母
    after=lower[k:]+lower[:k]+upper[k:]+upper[:k]#建立循环字母
    table=''.maketrans(before,after)#创建映射表
    return s.translate(table)

k=random.randint(1,6)
kaisa_jiami("123",k)


文章来源: http://mp.weixin.qq.com/s?__biz=MzU1Mjk3MDE2Mg==&mid=2247485442&idx=1&sn=ee193c98f4efd0d2e16b17b98c1ec99e&chksm=fbf8badfcc8f33c985dcfdea47e48ce99b50a999d39cb39b103a3d7c43530867fa808ecca2a3#rd
如有侵权请联系:admin#unsafe.sh