Skip to content

Instantly share code, notes, and snippets.

@Troll338cz
Last active June 12, 2026 09:16
Show Gist options
  • Select an option

  • Save Troll338cz/308e298a5fb1b18da0fd14d5c061f7e1 to your computer and use it in GitHub Desktop.

Select an option

Save Troll338cz/308e298a5fb1b18da0fd14d5c061f7e1 to your computer and use it in GitHub Desktop.
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <b64/cencode.h>
#include <b64/cdecode.h>
typedef uint32_t _DWORD;
typedef uint8_t _BYTE;
// Build, OpenSSL and libb64-dev needed:
// gcc Zyxel_encrypt_.c -o Zyxel_encrypt_ -lcrypto -lb64
//
// Rewriten code from /bin/zcmd
// Does not support "_encryp1_", algo is the same key is dynamic ("%s%s%s", zcmdData1, (const char *)(objNameShmAddr+*(unsigned __int16 *)(schemaShmAddr + 18036)), paramNameShmAddr)
// Inspired by https://th0mas.nl/2020/03/26/getting-root-on-a-zyxel-vmg8825-t50-router/
// Mentions of "but the actual key is derived from the supervisor password" probably means this wont work on those configs ither, unless you know it
//
// Tested on default configs from /etc/sysconfig.tar.gz configs on VMG4005-B60A with software V5.15(ABQA.2)b4_D0
EVP_CIPHER_CTX *aes_init(bool decrypt, const char *key) {
_BYTE v17[32]; // [sp+20h] [bp-54h] BYREF
_BYTE v18[32]; // [sp+40h] [bp-34h] BYREF
int v19;
int keyl = strlen(key);
// Salt
_DWORD v6[2];
v6[0] = *(_DWORD *)"90";
v6[1] = 54321;
// Create keys
v19 = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), (const unsigned char *)v6, key, keyl, 5, v18, v17);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
// Create EVP context
if (decrypt)
EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), 0, v18, v17);
else
EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), 0, v18, v17);
return ctx;
}
char *aes_encrypt(EVP_CIPHER_CTX *ctx, const unsigned char *a2, size_t *a3)
{
int v8; // [sp+18h] [bp-14h] BYREF
int v9; // [sp+1Ch] [bp-10h] BYREF
char *v10; // [sp+20h] [bp-Ch]
char *v11; // [sp+24h] [bp-8h]
v9 = *a3;
v8 = 0;
v11 = (char *)calloc(1u, v9 + 33);
if ( !v11 )
printf( "aes_encrypt, fail to calloc memory, size = %zu", v9 + 33);
v10 = v11;
if ( v11 )
{
v9 = 0;
EVP_EncryptInit_ex(ctx, 0, 0, 0, 0);
EVP_EncryptUpdate(ctx, v10, &v9, a2, *a3);
EVP_EncryptFinal_ex(ctx, &v10[v9], &v8);
*a3 = v9 + v8;
return v10;
}
else
{
printf("aes_encrypt memory insufficient");
return 0;
}
}
char *aes_decrypt(EVP_CIPHER_CTX *ctx, const unsigned char *a2, size_t *a3)
{
int v8; // [sp+18h] [bp-14h] BYREF
int v9; // [sp+1Ch] [bp-10h] BYREF
char *v10; // [sp+20h] [bp-Ch]
char *v11; // [sp+24h] [bp-8h]
v9 = *a3;
v8 = 0;
v11 = (char *)calloc(1u, v9 + 33);
if ( !v11 )
printf("aes_decrypt fail to calloc memory, size = %zu", v9 + 33);
v10 = v11;
if ( v11 )
{
EVP_DecryptInit_ex(ctx, 0, 0, 0, 0);
EVP_DecryptUpdate(ctx, v10, &v9, a2, *a3);
EVP_DecryptFinal_ex(ctx, &v10[v9], &v8);
*a3 = v9 + v8;
return v10;
}
else
{
printf("aes_decrypt memory insufficient");
return 0;
}
}
// Example impl from: https://github.com/BuLogics/libb64/blob/master/examples/c-example1.c
#define SIZE 1024
char* b64_encode(const char* input)
{
/* set up a destination buffer large enough to hold the encoded data */
char* output = (char*)malloc(SIZE);
/* keep track of our encoded position */
char* c = output;
/* store the number of bytes encoded by a single call */
int cnt = 0;
/* we need an encoder state */
base64_encodestate s;
/*---------- START ENCODING ----------*/
/* initialise the encoder state */
base64_init_encodestate(&s);
/* gather data from the input and send it to the output */
cnt = base64_encode_block(input, strlen(input), c, &s);
c += cnt;
/* since we have encoded the entire input string, we know that
there is no more input data; finalise the encoding */
cnt = base64_encode_blockend(c, &s);
c += cnt;
/*---------- STOP ENCODING ----------*/
/* we want to print the encoded data, so null-terminate it: */
*c = 0;
return output;
}
char* b64_decode(const char* input)
{
/* set up a destination buffer large enough to hold the encoded data */
char* output = (char*)malloc(SIZE);
/* keep track of our decoded position */
char* c = output;
/* store the number of bytes decoded by a single call */
int cnt = 0;
/* we need a decoder state */
base64_decodestate s;
/*---------- START DECODING ----------*/
/* initialise the decoder state */
base64_init_decodestate(&s);
/* decode the input data */
cnt = base64_decode_block(input, strlen(input), c, &s);
c += cnt;
/* note: there is no base64_decode_blockend! */
/*---------- STOP DECODING ----------*/
/* we want to print the decoded data, so null-terminate it: */
*c = 0;
return output;
}
int main(int argc, char *argv[]) {
// Example usage:
// "_encrypt_YLXO6f1ShezIxTe2KLE9TQ==" => "1234567890"
// "1234567890" => "_encrypt_YLXO6f1ShezIxTe2KLE9TQ=="
// From funcs zcmdInit1 ... zcmdInit6
const char *key = "ThiSISEncryptioNKeY";
if(argc != 2 && argc != 3) {
printf("%s <string> <key>\n", argv[0]);
exit(1);
} else {
if(argc == 3)
{
key = argv[2];
}
if( strncmp(argv[1], "_encrypt_", 9) == 0)
{
printf("Decrypting: %s\n", argv[1]);
EVP_CIPHER_CTX *dec = aes_init(true, key);
unsigned char *dec_raw;
dec_raw = b64_decode( (char *)argv[1]+9 );
size_t raw_size = strlen(dec_raw);
char *output;
output = aes_decrypt(dec, dec_raw, &raw_size);
printf("Result: %s\n", output);
} else {
printf("Encrypting: %s\n", argv[1]);
EVP_CIPHER_CTX *enc = aes_init(false, key);
size_t raw_size = strlen(argv[1])+1;
char *ptr;
ptr = aes_encrypt(enc, argv[1], &raw_size);
unsigned char *enc_raw;
enc_raw = b64_encode( ptr );
printf("Result: %s%s", "_encrypt_", (const char *)enc_raw);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment